From 5ebd5816f709e8a5949ed4c01310c703f0bdea90 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tommy=20Barv=C3=A5g?=
Date: Fri, 17 Mar 2023 14:08:51 +0100
Subject: [PATCH] Feature/app router (#85)
* 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
---
.eslintrc | 7 -
.eslintrc.json | 22 +
.prettierignore | 9 +-
.prettierrc | 3 +-
.vscode/settings.json | 22 +-
app/(auth)/layout.tsx | 8 +
app/(auth)/login/page.tsx | 26 +
.../dashboard/_components/avatar.tsx | 22 +
.../calendar-month-with-salary.tsx | 99 +
.../dashboard/_components/calendar-month.tsx | 84 +
.../dashboard/_components/calendar-year.tsx | 72 +
.../_components/company-benefits.tsx | 43 +
.../dashboard/_components/footer.tsx | 72 +
.../dashboard/_components/next-paycheck.tsx | 22 +
.../dashboard/_components/salary-form.tsx | 22 +
.../_components/yearly-economic-overview.tsx | 52 +
app/(dashboard)/dashboard/layout.tsx | 38 +
app/(dashboard)/dashboard/page.tsx | 38 +
app/(dashboard)/dashboard/profile/page.tsx | 21 +
.../year/[year]/month/[month]/page.tsx | 38 +
.../dashboard/year/[year]/month/page.tsx | 21 +
.../dashboard/year/[year]/page.tsx | 19 +
app/(dashboard)/dashboard/year/layout.tsx | 20 +
app/(dashboard)/dashboard/year/page.tsx | 11 +
app/layout.tsx | 36 +
app/route.ts | 5 +
components/auth/LoginButtons.tsx | 32 -
components/auth/hooks/index.tsx | 3 -
components/auth/hooks/useAuthProviders.tsx | 8 -
components/auth/index.tsx | 4 -
components/auth/login-button.tsx | 24 +
components/calendar/calendar-day.tsx | 92 +
components/calendar/components/calendar.tsx | 220 --
.../calendar/components/calendarDay.tsx | 264 ---
components/calendar/components/index.ts | 4 -
components/calendar/index.ts | 4 -
components/calendar/month-select.tsx | 77 +
.../calendar/providers/calendarProvider.tsx | 198 --
components/calendar/providers/index.ts | 3 -
components/companyBenefits.tsx | 129 --
components/container.tsx | 14 -
.../feedback/components/feedbackDialog.tsx | 21 -
.../components/feedbackEmojiSelector.tsx | 94 -
.../feedback/components/feedbackForm.tsx | 85 -
.../feedback/components/feedbackPopover.tsx | 26 -
components/feedback/components/index.ts | 6 -
components/feedback/index.ts | 3 -
components/form/controlledCheckbox.tsx | 49 -
components/form/index.ts | 3 -
components/header.tsx | 56 -
components/{icons/svgs/logo.tsx => icons.tsx} | 95 +-
components/icons/index.tsx | 1 -
components/layouts/authenticatedLayout.tsx | 169 --
components/salary/components/index.ts | 5 -
.../salary/components/salaryStatistics.tsx | 68 -
.../salary/components/yearStatistic.tsx | 49 -
.../salary/components/yearlyEarnings.tsx | 47 -
components/salary/index.ts | 4 -
components/salary/providers/index.ts | 3 -
.../salary/providers/salaryProvider.tsx | 72 -
components/salary/salary-details-card.tsx | 19 +
components/statistic.tsx | 51 -
components/statisticGroup.tsx | 24 -
components/theme-provider.tsx | 8 +
components/theme-select.tsx | 60 +
components/theme/index.ts | 9 -
components/theme/themeSelector.tsx | 116 -
components/ui/alert.tsx | 114 -
components/ui/appearInBox.tsx | 43 -
components/ui/box.tsx | 27 +-
components/ui/button.tsx | 412 +---
components/ui/card.tsx | 126 +-
components/ui/checkbox.tsx | 43 +-
components/ui/container.tsx | 61 -
components/ui/dialog.tsx | 248 +-
components/ui/expandingHelperText.tsx | 42 -
components/ui/fieldContainer.tsx | 33 -
components/ui/fieldset.tsx | 10 -
components/ui/flex-item.tsx | 22 +
components/ui/flex.tsx | 236 +-
components/ui/flexItem.tsx | 111 -
components/ui/footer.tsx | 19 -
components/ui/form.tsx | 7 -
components/ui/grid.tsx | 184 --
components/ui/heading.tsx | 57 -
components/ui/hover-card.tsx | 32 +
components/ui/iconButton.tsx | 290 ---
components/ui/image.tsx | 6 -
components/ui/index.ts | 130 --
components/ui/info-button.tsx | 55 +
components/ui/infoButton.tsx | 49 -
components/ui/input.tsx | 21 +
components/ui/keyframes.tsx | 40 -
components/ui/label.tsx | 63 +-
components/ui/li.tsx | 10 -
components/ui/link.tsx | 140 +-
components/ui/linkButton.tsx | 21 -
components/ui/main.tsx | 10 -
components/ui/nav.tsx | 17 -
components/ui/overlay.tsx | 9 -
components/ui/panel.tsx | 12 -
components/ui/paragraph.tsx | 55 -
components/ui/popover.tsx | 133 +-
components/ui/radioGroup.tsx | 51 -
components/ui/select.tsx | 239 +-
components/ui/separator.tsx | 57 +-
components/ui/show.tsx | 7 +
components/ui/skeleton.tsx | 96 +-
components/ui/svg.tsx | 95 -
components/ui/text.tsx | 209 --
components/ui/textArea.tsx | 62 -
components/ui/textField.tsx | 120 -
components/ui/toast.tsx | 127 ++
components/ui/toaster.tsx | 34 +
components/ui/ul.tsx | 10 -
components/user/hooks/index.tsx | 4 -
components/user/hooks/useUser.ts | 3 -
components/user/hooks/useUserAvatar.ts | 20 -
components/user/index.tsx | 7 -
components/user/providers/userProvider.tsx | 73 -
components/user/user-avatar.tsx | 59 +
.../user/user-edit-salary-details-dialog.tsx | 44 +
.../user/user-edit-work-day-detail-dialog.tsx | 38 +
components/user/user-salary-details-form.tsx | 168 ++
components/user/user-work-day-detail-form.tsx | 209 ++
components/user/userAvatar.tsx | 66 -
components/user/userAvatarPopover.tsx | 81 -
components/user/userNavDetails.tsx | 63 -
components/user/userProfile.tsx | 98 -
components/user/userWorkDayDetails.tsx | 223 --
constants/date-constants.ts | 116 +
constants/dateConstants.ts | 29 -
...ltUserSalary.ts => default-user-salary.ts} | 0
...rningConstants.ts => earning-constants.ts} | 0
constants/theme-constants.ts | 40 +
environment.d.ts | 26 +
hooks/use-toast.ts | 186 ++
lib/auth.ts | 232 ++
lib/date.ts | 5 +
lib/index.ts | 3 -
lib/prismaUser.ts | 134 --
lib/session.ts | 12 +
lib/user.ts | 130 ++
lib/utils.ts | 19 +
lib/validations/user.ts | 16 +
logic/{calendarLogic.ts => calendar-logic.ts} | 4 +-
logic/{earningsLogic.ts => earnings-logic.ts} | 8 +-
logic/{objectLogic.ts => object-logic.ts} | 0
logic/{userLogic.ts => user-logic.ts} | 0
...validationLogic.ts => validation-logic.ts} | 0
middleware.ts | 44 +
next-env.d.ts | 1 +
next.config.js | 66 +-
package.json | 63 +-
pages/_app.tsx | 66 -
pages/_document.tsx | 24 -
pages/access-denied.tsx | 83 -
pages/api/auth/[...nextauth].tsx | 274 +--
pages/api/health-check/index.ts | 17 +-
pages/api/user/[id]/index.ts | 32 +-
pages/api/user/avatar.ts | 14 +-
pages/api/user/feedback.ts | 24 +-
pages/api/user/index.ts | 7 +-
pages/api/user/work-day-detail/index.ts | 59 +
pages/feedback.tsx | 47 -
pages/index.tsx | 49 -
pages/login.tsx | 189 --
pages/profile.tsx | 31 -
pages/salary-calculator.tsx | 133 --
pnpm-lock.yaml | 2000 +++++++++++------
postcss.config.js | 6 +
prettier.config.js | 33 +
stitches.config.ts | 300 ---
styles/globals.css | 3 +
tailwind.config.js | 36 +
tsconfig.json | 37 +-
types/index.d.ts | 51 +-
types/next-auth.d.ts | 50 +-
utils/calendar-utils.ts | 425 ++++
utils/calendar/calendarUtils.ts | 217 --
utils/common-utils.ts | 31 +
utils/commonUtils.ts | 20 -
.../{currencyFormat.ts => currency-format.ts} | 0
utils/{dateUtils.ts => date-utils.ts} | 68 +-
utils/{emailUtils.ts => email-utils.ts} | 0
utils/pageUtils.ts | 50 -
utils/sessionUtils.ts | 9 -
utils/{userUtils.ts => user-utils.ts} | 4 +-
188 files changed, 5605 insertions(+), 8120 deletions(-)
delete mode 100644 .eslintrc
create mode 100644 .eslintrc.json
create mode 100644 app/(auth)/layout.tsx
create mode 100644 app/(auth)/login/page.tsx
create mode 100644 app/(dashboard)/dashboard/_components/avatar.tsx
create mode 100644 app/(dashboard)/dashboard/_components/calendar-month-with-salary.tsx
create mode 100644 app/(dashboard)/dashboard/_components/calendar-month.tsx
create mode 100644 app/(dashboard)/dashboard/_components/calendar-year.tsx
create mode 100644 app/(dashboard)/dashboard/_components/company-benefits.tsx
create mode 100644 app/(dashboard)/dashboard/_components/footer.tsx
create mode 100644 app/(dashboard)/dashboard/_components/next-paycheck.tsx
create mode 100644 app/(dashboard)/dashboard/_components/salary-form.tsx
create mode 100644 app/(dashboard)/dashboard/_components/yearly-economic-overview.tsx
create mode 100644 app/(dashboard)/dashboard/layout.tsx
create mode 100644 app/(dashboard)/dashboard/page.tsx
create mode 100644 app/(dashboard)/dashboard/profile/page.tsx
create mode 100644 app/(dashboard)/dashboard/year/[year]/month/[month]/page.tsx
create mode 100644 app/(dashboard)/dashboard/year/[year]/month/page.tsx
create mode 100644 app/(dashboard)/dashboard/year/[year]/page.tsx
create mode 100644 app/(dashboard)/dashboard/year/layout.tsx
create mode 100644 app/(dashboard)/dashboard/year/page.tsx
create mode 100644 app/layout.tsx
create mode 100644 app/route.ts
delete mode 100644 components/auth/LoginButtons.tsx
delete mode 100644 components/auth/hooks/index.tsx
delete mode 100644 components/auth/hooks/useAuthProviders.tsx
delete mode 100644 components/auth/index.tsx
create mode 100644 components/auth/login-button.tsx
create mode 100644 components/calendar/calendar-day.tsx
delete mode 100644 components/calendar/components/calendar.tsx
delete mode 100644 components/calendar/components/calendarDay.tsx
delete mode 100644 components/calendar/components/index.ts
delete mode 100644 components/calendar/index.ts
create mode 100644 components/calendar/month-select.tsx
delete mode 100644 components/calendar/providers/calendarProvider.tsx
delete mode 100644 components/calendar/providers/index.ts
delete mode 100644 components/companyBenefits.tsx
delete mode 100644 components/container.tsx
delete mode 100644 components/feedback/components/feedbackDialog.tsx
delete mode 100644 components/feedback/components/feedbackEmojiSelector.tsx
delete mode 100644 components/feedback/components/feedbackForm.tsx
delete mode 100644 components/feedback/components/feedbackPopover.tsx
delete mode 100644 components/feedback/components/index.ts
delete mode 100644 components/feedback/index.ts
delete mode 100644 components/form/controlledCheckbox.tsx
delete mode 100644 components/form/index.ts
delete mode 100644 components/header.tsx
rename components/{icons/svgs/logo.tsx => icons.tsx} (67%)
delete mode 100644 components/icons/index.tsx
delete mode 100644 components/layouts/authenticatedLayout.tsx
delete mode 100644 components/salary/components/index.ts
delete mode 100644 components/salary/components/salaryStatistics.tsx
delete mode 100644 components/salary/components/yearStatistic.tsx
delete mode 100644 components/salary/components/yearlyEarnings.tsx
delete mode 100644 components/salary/index.ts
delete mode 100644 components/salary/providers/index.ts
delete mode 100644 components/salary/providers/salaryProvider.tsx
create mode 100644 components/salary/salary-details-card.tsx
delete mode 100644 components/statistic.tsx
delete mode 100644 components/statisticGroup.tsx
create mode 100644 components/theme-provider.tsx
create mode 100644 components/theme-select.tsx
delete mode 100644 components/theme/index.ts
delete mode 100644 components/theme/themeSelector.tsx
delete mode 100644 components/ui/alert.tsx
delete mode 100644 components/ui/appearInBox.tsx
delete mode 100644 components/ui/container.tsx
delete mode 100644 components/ui/expandingHelperText.tsx
delete mode 100644 components/ui/fieldContainer.tsx
delete mode 100644 components/ui/fieldset.tsx
create mode 100644 components/ui/flex-item.tsx
delete mode 100644 components/ui/flexItem.tsx
delete mode 100644 components/ui/footer.tsx
delete mode 100644 components/ui/form.tsx
delete mode 100644 components/ui/grid.tsx
delete mode 100644 components/ui/heading.tsx
create mode 100644 components/ui/hover-card.tsx
delete mode 100644 components/ui/iconButton.tsx
delete mode 100644 components/ui/image.tsx
delete mode 100644 components/ui/index.ts
create mode 100644 components/ui/info-button.tsx
delete mode 100644 components/ui/infoButton.tsx
create mode 100644 components/ui/input.tsx
delete mode 100644 components/ui/keyframes.tsx
delete mode 100644 components/ui/li.tsx
delete mode 100644 components/ui/linkButton.tsx
delete mode 100644 components/ui/main.tsx
delete mode 100644 components/ui/nav.tsx
delete mode 100644 components/ui/overlay.tsx
delete mode 100644 components/ui/panel.tsx
delete mode 100644 components/ui/paragraph.tsx
delete mode 100644 components/ui/radioGroup.tsx
create mode 100644 components/ui/show.tsx
delete mode 100644 components/ui/svg.tsx
delete mode 100644 components/ui/text.tsx
delete mode 100644 components/ui/textArea.tsx
delete mode 100644 components/ui/textField.tsx
create mode 100644 components/ui/toast.tsx
create mode 100644 components/ui/toaster.tsx
delete mode 100644 components/ui/ul.tsx
delete mode 100644 components/user/hooks/index.tsx
delete mode 100644 components/user/hooks/useUser.ts
delete mode 100644 components/user/hooks/useUserAvatar.ts
delete mode 100644 components/user/index.tsx
delete mode 100644 components/user/providers/userProvider.tsx
create mode 100644 components/user/user-avatar.tsx
create mode 100644 components/user/user-edit-salary-details-dialog.tsx
create mode 100644 components/user/user-edit-work-day-detail-dialog.tsx
create mode 100644 components/user/user-salary-details-form.tsx
create mode 100644 components/user/user-work-day-detail-form.tsx
delete mode 100644 components/user/userAvatar.tsx
delete mode 100644 components/user/userAvatarPopover.tsx
delete mode 100644 components/user/userNavDetails.tsx
delete mode 100644 components/user/userProfile.tsx
delete mode 100644 components/user/userWorkDayDetails.tsx
create mode 100644 constants/date-constants.ts
delete mode 100644 constants/dateConstants.ts
rename constants/{defaultUserSalary.ts => default-user-salary.ts} (100%)
rename constants/{earningConstants.ts => earning-constants.ts} (100%)
create mode 100644 constants/theme-constants.ts
create mode 100644 environment.d.ts
create mode 100644 hooks/use-toast.ts
create mode 100644 lib/auth.ts
create mode 100644 lib/date.ts
delete mode 100644 lib/index.ts
delete mode 100644 lib/prismaUser.ts
create mode 100644 lib/session.ts
create mode 100644 lib/user.ts
create mode 100644 lib/utils.ts
create mode 100644 lib/validations/user.ts
rename logic/{calendarLogic.ts => calendar-logic.ts} (90%)
rename logic/{earningsLogic.ts => earnings-logic.ts} (96%)
rename logic/{objectLogic.ts => object-logic.ts} (100%)
rename logic/{userLogic.ts => user-logic.ts} (100%)
rename logic/{validationLogic.ts => validation-logic.ts} (100%)
create mode 100644 middleware.ts
delete mode 100644 pages/_app.tsx
delete mode 100644 pages/_document.tsx
delete mode 100644 pages/access-denied.tsx
create mode 100644 pages/api/user/work-day-detail/index.ts
delete mode 100644 pages/feedback.tsx
delete mode 100644 pages/index.tsx
delete mode 100644 pages/login.tsx
delete mode 100644 pages/profile.tsx
delete mode 100644 pages/salary-calculator.tsx
create mode 100644 postcss.config.js
create mode 100644 prettier.config.js
delete mode 100644 stitches.config.ts
create mode 100644 styles/globals.css
create mode 100644 tailwind.config.js
create mode 100644 utils/calendar-utils.ts
delete mode 100644 utils/calendar/calendarUtils.ts
create mode 100644 utils/common-utils.ts
delete mode 100644 utils/commonUtils.ts
rename utils/{currencyFormat.ts => currency-format.ts} (100%)
rename utils/{dateUtils.ts => date-utils.ts} (55%)
rename utils/{emailUtils.ts => email-utils.ts} (100%)
delete mode 100644 utils/pageUtils.ts
delete mode 100644 utils/sessionUtils.ts
rename utils/{userUtils.ts => user-utils.ts} (96%)
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 3a3a598..0000000
--- a/.eslintrc
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extends": ["next", "next/core-web-vitals"],
- "rules": {
- "react/prop-types": 0,
- "react/no-unescaped-entities": 0
- }
-}
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..0a3aeb1
--- /dev/null
+++ b/.eslintrc.json
@@ -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/*/"]
+ }
+ }
+}
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
index b9dc034..ff32919 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,2 +1,9 @@
.next
-.vercel
\ No newline at end of file
+.vercel
+cache
+.cache
+package.json
+package-lock.json
+public
+CHANGELOG.md
+.yarn
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
index 9c62621..d1de84c 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -3,5 +3,6 @@
"semi": true,
"singleQuote": false,
"arrowParens": "avoid",
- "trailingComma": "none"
+ "trailingComma": "none",
+ "plugins": ["prettier-plugin-tailwindcss"]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index f6598f8..4d598ad 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -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/typescript@4.9.5/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\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]]
}
diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx
new file mode 100644
index 0000000..1b93301
--- /dev/null
+++ b/app/(auth)/layout.tsx
@@ -0,0 +1,8 @@
+interface AuthLayoutProps {
+ children: React.ReactNode
+ }
+
+ export default function AuthLayout({ children }: AuthLayoutProps) {
+ return {children}
+ }
+
\ No newline at end of file
diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx
new file mode 100644
index 0000000..521d52f
--- /dev/null
+++ b/app/(auth)/login/page.tsx
@@ -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 (
+
+
+
+
+
Welcome back
+
+ Click to login with your Knowit AD account.
+
+
+
+
+
+
+ );
+}
diff --git a/app/(dashboard)/dashboard/_components/avatar.tsx b/app/(dashboard)/dashboard/_components/avatar.tsx
new file mode 100644
index 0000000..d105f50
--- /dev/null
+++ b/app/(dashboard)/dashboard/_components/avatar.tsx
@@ -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 Not logged in
;
+ }
+
+ const userAvatar = await getUserAvatar(user?.activeDirectoryId);
+
+ return ;
+}
+
+function AvatarSkeleton() {
+ return ;
+}
+
+export { AvatarSkeleton };
diff --git a/app/(dashboard)/dashboard/_components/calendar-month-with-salary.tsx b/app/(dashboard)/dashboard/_components/calendar-month-with-salary.tsx
new file mode 100644
index 0000000..a74d87b
--- /dev/null
+++ b/app/(dashboard)/dashboard/_components/calendar-month-with-salary.tsx
@@ -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 (
+
+
+
+ Salary details for {calendarMonth.month} {calendarMonth.year}
+
+
+
+ {userEarnings?.activeCalendarMonthStatistics.workDays.length.toString()}
+
+
+ {userEarnings?.activeCalendarMonthStatistics.workHours.toString()}
+
+
+ {userEarnings?.activeCalendarMonthStatistics.grossFormatted}
+
+
+ {userEarnings?.activeCalendarMonthStatistics.netFormatted}
+
+
+
+
+
+
+
+
+
+ Salary for {calendarMonth.month} paid with{" "}
+ half tax at{" "}
+ {userEarnings?.nextMonthStatistics?.payDay}
+
+
+
+
+
+
+
+
+
+
{calendarMonth.year}
+
+
+
+ {/* Go to previous month if month.MonthNumber is 0, go to previous year and month 11 */}
+
+ Forrige måned
+
+
+ {/* Go to current month */}
+
+ I dag
+
+ {/* Go to next month if month.MonthNumber is 11, go to next year and month 0 */}
+
+ Neste måned
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/(dashboard)/dashboard/_components/calendar-month.tsx b/app/(dashboard)/dashboard/_components/calendar-month.tsx
new file mode 100644
index 0000000..09c8d44
--- /dev/null
+++ b/app/(dashboard)/dashboard/_components/calendar-month.tsx
@@ -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 (
+
+
+
+ {calendarEntries.map((calendarDay, index) => {
+ switch (calendarDay.type) {
+ case "day":
+ return (
+
+ );
+ default:
+ return (
+
+ );
+ }
+ })}
+
+
0}>
+
+ {holidayInfos.map((day, index) => (
+
+ {`${day.date.getDate()}.${day.date.getMonth() + 1}: ${day.holidayInformation?.name}${
+ holidayInfos.length === index + 1 ? "" : ","
+ }`}
+
+ ))}
+
+
+
+ );
+};
+
+export { CalendarMonth };
diff --git a/app/(dashboard)/dashboard/_components/calendar-year.tsx b/app/(dashboard)/dashboard/_components/calendar-year.tsx
new file mode 100644
index 0000000..5f052e9
--- /dev/null
+++ b/app/(dashboard)/dashboard/_components/calendar-year.tsx
@@ -0,0 +1,72 @@
+import { getCalendarYear } from "@/utils/calendar-utils";
+import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons";
+import { getYear } from "date-fns";
+import Link from "next/link";
+import * as React from "react";
+
+import { getRequestDateNow } from "@/lib/date";
+import { cn } from "@/lib/utils";
+import { CalendarMonth } from "./calendar-month";
+
+const CalendarYear: React.FC<{ date: Date }> = ({ date }) => {
+ const currentDate = getRequestDateNow();
+ const year = date.getFullYear();
+ const calendarYear = getCalendarYear(year);
+
+ return (
+
+
+ Norsk kalender med helligdager
+ - Kalender {getYear(date)}
+
+
+
Kalender {getYear(date)}
+
+ {/* Go to previous year */}
+
+ Forrige år
+
+
+ {/* Go to current year */}
+
+ I dag
+
+ {/* Go to next year */}
+
+ Neste år
+
+
+
+
+
+ {calendarYear.months.map((month, index) => (
+
+
+
+ {month.month}
+
+
+
+
+ ))}
+
+
+ );
+};
+
+export { CalendarYear };
diff --git a/app/(dashboard)/dashboard/_components/company-benefits.tsx b/app/(dashboard)/dashboard/_components/company-benefits.tsx
new file mode 100644
index 0000000..24c4cec
--- /dev/null
+++ b/app/(dashboard)/dashboard/_components/company-benefits.tsx
@@ -0,0 +1,43 @@
+import { Icons } from "@/components/icons";
+import { Card } from "@/components/ui/card";
+
+const CompanyBenefit = ({ text }: { text: string }) => (
+
+
+
+
+
+ {text}
+
+
+);
+
+export default function CompanyBenefits() {
+ return (
+
+
+
Knowit Experience Bergen
+
Company Benefits
+
+ You get paid by commission or guaranteed salary. Knowit covers both employer's
+ national insurance contributions (14.10%) and holyday payment (12%). This means you can
+ calculate your next payment by the following formulae{" "}
+
+ work hours in month x hourly rate x commission = your salary.
+
+
+
+
+ {/* transform all to CompanyBenefit component */}
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/(dashboard)/dashboard/_components/footer.tsx b/app/(dashboard)/dashboard/_components/footer.tsx
new file mode 100644
index 0000000..0d71838
--- /dev/null
+++ b/app/(dashboard)/dashboard/_components/footer.tsx
@@ -0,0 +1,72 @@
+import { Icons } from "@/components/icons";
+import { ThemeSelect } from "@/components/theme-select";
+import { Box } from "@/components/ui/box";
+import { Flex } from "@/components/ui/flex";
+import Link from "@/components/ui/link";
+
+export default function Footer() {
+ return (
+
+
+
+
+ Resources
+
+
+
+ Personal handbook
+
+
+
+ CV Partner
+
+
+ Helpit
+
+
+ Timekeeping
+
+
+ Slack
+
+
+
+ Shareit
+
+
+
+ Brand book
+
+
+
+ Office templates
+
+
+
+
+
+ Site
+
+
+ Home
+
+
+ Profile
+
+
+ Feedback
+
+
+
+
+ Feedback
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/(dashboard)/dashboard/_components/next-paycheck.tsx b/app/(dashboard)/dashboard/_components/next-paycheck.tsx
new file mode 100644
index 0000000..cea4647
--- /dev/null
+++ b/app/(dashboard)/dashboard/_components/next-paycheck.tsx
@@ -0,0 +1,22 @@
+import { getCurrentUser } from "@/lib/session";
+import { getUserEarnings } from "@/lib/user";
+
+export default async function NextPaycheck() {
+ const user = await getCurrentUser();
+
+ if (!user) {
+ return Not logged in
;
+ }
+
+ const userEarnings = await getUserEarnings(user?.activeDirectoryId);
+
+ return (
+
+
Next paycheck
+
{userEarnings?.nextPayDayStatistics.payDay}
+
+ {userEarnings?.nextPayDayStatistics.netFormatted}
+
+
+ );
+}
diff --git a/app/(dashboard)/dashboard/_components/salary-form.tsx b/app/(dashboard)/dashboard/_components/salary-form.tsx
new file mode 100644
index 0000000..f40ab88
--- /dev/null
+++ b/app/(dashboard)/dashboard/_components/salary-form.tsx
@@ -0,0 +1,22 @@
+import { UserSalaryDetailsForm } from "@/components/user/user-salary-details-form";
+import { getCurrentUser } from "@/lib/session";
+
+export default async function SalaryForm() {
+ const user = await getCurrentUser();
+
+ if (!user) {
+ return Not logged in
;
+ }
+
+ return (
+
+ );
+}
diff --git a/app/(dashboard)/dashboard/_components/yearly-economic-overview.tsx b/app/(dashboard)/dashboard/_components/yearly-economic-overview.tsx
new file mode 100644
index 0000000..1e607a1
--- /dev/null
+++ b/app/(dashboard)/dashboard/_components/yearly-economic-overview.tsx
@@ -0,0 +1,52 @@
+import { SalaryDetailsCard } from "@/components/salary/salary-details-card";
+import { getCurrentUser } from "@/lib/session";
+import { getUserEarnings } from "@/lib/user";
+
+export default async function YearlyEconomicOverview() {
+ const user = await getCurrentUser();
+
+ if (!user) {
+ return Not logged in
;
+ }
+
+ const userEarnings = await getUserEarnings(user?.activeDirectoryId);
+
+ return (
+
+
+
{userEarnings?.yearSalaryStatistics.year} overview
+
+
+ {userEarnings?.yearSalaryStatistics.workDays}
+
+
+ {userEarnings?.yearSalaryStatistics.workHours}
+
+
+ {userEarnings?.yearSalaryStatistics.grossFormatted}
+
+
+ {userEarnings?.yearSalaryStatistics.netFormatted}
+
+
+
+
+
{userEarnings?.nextYearSalaryStatistics.year} overview
+
+
+ {userEarnings?.nextYearSalaryStatistics.workDays}
+
+
+ {userEarnings?.nextYearSalaryStatistics.workHours}
+
+
+ {userEarnings?.nextYearSalaryStatistics.grossFormatted}
+
+
+ {userEarnings?.nextYearSalaryStatistics.netFormatted}
+
+
+
+
+ );
+}
diff --git a/app/(dashboard)/dashboard/layout.tsx b/app/(dashboard)/dashboard/layout.tsx
new file mode 100644
index 0000000..442a37b
--- /dev/null
+++ b/app/(dashboard)/dashboard/layout.tsx
@@ -0,0 +1,38 @@
+import { Icons } from "@/components/icons";
+import { getCurrentUser } from "@/lib/session";
+import Link from "next/link";
+import { redirect } from "next/navigation";
+import { Suspense } from "react";
+import Avatar, { AvatarSkeleton } from "./_components/avatar";
+import Footer from "./_components/footer";
+import NextPaycheck from "./_components/next-paycheck";
+
+export default async function DashboardLayout({ children }) {
+ const user = await getCurrentUser();
+
+ if (!user) {
+ redirect("/login");
+ }
+
+ return (
+ <>
+
+
+
+
+
+
}>
+ {/* @ts-expect-error Async Server Component */}
+
+
+ }>
+ {/* @ts-expect-error Async Server Component */}
+
+
+
+
+ {children}
+
+ >
+ );
+}
diff --git a/app/(dashboard)/dashboard/page.tsx b/app/(dashboard)/dashboard/page.tsx
new file mode 100644
index 0000000..7880b6e
--- /dev/null
+++ b/app/(dashboard)/dashboard/page.tsx
@@ -0,0 +1,38 @@
+import { getRequestDateNow } from "@/lib/date";
+import { getCurrentUser } from "@/lib/session";
+import { getUserEarnings } from "@/lib/user";
+import { getCalendarMonth } from "@/utils/calendar-utils";
+import { redirect } from "next/navigation";
+import { Suspense } from "react";
+import CalendarMonthWithSalary from "./_components/calendar-month-with-salary";
+import CompanyBenefits from "./_components/company-benefits";
+import YearlyEconomicOverview from "./_components/yearly-economic-overview";
+
+export default async function DashboardPage() {
+ const user = await getCurrentUser();
+
+ if (!user) {
+ return redirect("/login");
+ }
+
+ const currentDate = getRequestDateNow();
+
+ const calendarMonth = getCalendarMonth(currentDate);
+
+ const userEarnings = await getUserEarnings(user.activeDirectoryId);
+
+ return (
+ <>
+
+
+ }>
+ {/* @ts-expect-error Async Server Component */}
+
+
+ >
+ );
+}
diff --git a/app/(dashboard)/dashboard/profile/page.tsx b/app/(dashboard)/dashboard/profile/page.tsx
new file mode 100644
index 0000000..96d61e2
--- /dev/null
+++ b/app/(dashboard)/dashboard/profile/page.tsx
@@ -0,0 +1,21 @@
+import { getCurrentUser } from "@/lib/session";
+import { redirect } from "next/navigation";
+import { Suspense } from "react";
+import SalaryForm from "../_components/salary-form";
+
+export default async function ProfilePage() {
+ const user = await getCurrentUser();
+
+ if (!user) {
+ return redirect("/login");
+ }
+
+ return (
+
+ loading...
}>
+ {/* @ts-expect-error Async Server Component */}
+
+
+
+ );
+}
diff --git a/app/(dashboard)/dashboard/year/[year]/month/[month]/page.tsx b/app/(dashboard)/dashboard/year/[year]/month/[month]/page.tsx
new file mode 100644
index 0000000..df3a230
--- /dev/null
+++ b/app/(dashboard)/dashboard/year/[year]/month/[month]/page.tsx
@@ -0,0 +1,38 @@
+import CalendarMonthWithSalary from "@/app/(dashboard)/dashboard/_components/calendar-month-with-salary";
+import { getRequestDateNow } from "@/lib/date";
+import { getCurrentUser } from "@/lib/session";
+import { getUserEarnings } from "@/lib/user";
+import { getCalendarMonth } from "@/utils/calendar-utils";
+import { redirect } from "next/navigation";
+
+interface SelectedYearMonthPageProps {
+ params: { year: string; month: string };
+}
+
+export const dynamic = "force-dynamic";
+export const dynamicParams = true;
+
+export default async function SelectedYearMonthPage({ params }: SelectedYearMonthPageProps) {
+ const user = await getCurrentUser();
+
+ if (!user) {
+ return redirect("/login");
+ }
+
+ const currentDate = getRequestDateNow();
+
+ const date = new Date(+(params.year ?? currentDate.getFullYear()), +params.month);
+ const calendarMonth = getCalendarMonth(date);
+
+ const userEarnings = await getUserEarnings(user.activeDirectoryId, date);
+
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/app/(dashboard)/dashboard/year/[year]/month/page.tsx b/app/(dashboard)/dashboard/year/[year]/month/page.tsx
new file mode 100644
index 0000000..99d7bba
--- /dev/null
+++ b/app/(dashboard)/dashboard/year/[year]/month/page.tsx
@@ -0,0 +1,21 @@
+import { getRequestDateNow } from "@/lib/date";
+import { redirect } from "next/navigation";
+
+interface SelectedYearPageProps {
+ params: { year: string };
+}
+
+export const dynamic = "force-dynamic";
+export const dynamicParams = true;
+
+export default async function SelectedYearMonthPageRoot({ params }: SelectedYearPageProps) {
+ const dateNow = getRequestDateNow();
+
+ if (params.year === dateNow.getFullYear().toString()) {
+ redirect(`/dashboard/year/${params.year}/month/${dateNow.getMonth()}`);
+ }
+
+ redirect(`/dashboard/year/${params.year}/month/0`);
+
+ return ...
;
+}
diff --git a/app/(dashboard)/dashboard/year/[year]/page.tsx b/app/(dashboard)/dashboard/year/[year]/page.tsx
new file mode 100644
index 0000000..1fd8690
--- /dev/null
+++ b/app/(dashboard)/dashboard/year/[year]/page.tsx
@@ -0,0 +1,19 @@
+import { getRequestDateNow } from "@/lib/date";
+import { CalendarYear } from "../../_components/calendar-year";
+
+interface SelectedYearPageProps {
+ params: { year: string };
+}
+
+export const dynamic = "force-dynamic";
+export const dynamicParams = true;
+
+export default async function SelectedYearPage({ params }: SelectedYearPageProps) {
+ const date = new Date(params.year ?? getRequestDateNow().getFullYear());
+
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/app/(dashboard)/dashboard/year/layout.tsx b/app/(dashboard)/dashboard/year/layout.tsx
new file mode 100644
index 0000000..521f531
--- /dev/null
+++ b/app/(dashboard)/dashboard/year/layout.tsx
@@ -0,0 +1,20 @@
+import { Suspense } from "react";
+import CompanyBenefits from "../_components/company-benefits";
+import YearlyEconomicOverview from "../_components/yearly-economic-overview";
+
+interface YearLayoutProps {
+ children: React.ReactNode;
+}
+
+export default async function YearLayout({ children }: YearLayoutProps) {
+ return (
+ <>
+ {children}
+
+ }>
+ {/* @ts-expect-error Async Server Component */}
+
+
+ >
+ );
+}
diff --git a/app/(dashboard)/dashboard/year/page.tsx b/app/(dashboard)/dashboard/year/page.tsx
new file mode 100644
index 0000000..0f1304b
--- /dev/null
+++ b/app/(dashboard)/dashboard/year/page.tsx
@@ -0,0 +1,11 @@
+import { getRequestDateNow } from "@/lib/date";
+import { redirect } from "next/navigation";
+
+export const dynamic = "force-dynamic";
+export const dynamicParams = true;
+
+export default async function RootYearPage() {
+ redirect("/dashboard/year/" + getRequestDateNow().getFullYear());
+
+ return ...
;
+}
diff --git a/app/layout.tsx b/app/layout.tsx
new file mode 100644
index 0000000..e710cb9
--- /dev/null
+++ b/app/layout.tsx
@@ -0,0 +1,36 @@
+import { ThemeProvider } from "@/components/theme-provider";
+import { Toaster } from "@/components/ui/toaster";
+import { cn } from "@/lib/utils";
+import "@/styles/globals.css";
+import { Inter as FontSans } from "next/font/google";
+
+const fontSans = FontSans({
+ subsets: ["latin"],
+ variable: "--font-sans",
+ display: "swap"
+});
+
+interface RootLayoutProps {
+ children: React.ReactNode;
+}
+
+export default function RootLayout({ children }: RootLayoutProps) {
+ return (
+ <>
+
+
+
+
+ {children}
+
+
+
+
+ >
+ );
+}
diff --git a/app/route.ts b/app/route.ts
new file mode 100644
index 0000000..0a12c7a
--- /dev/null
+++ b/app/route.ts
@@ -0,0 +1,5 @@
+import { redirect } from "next/navigation";
+
+export async function GET(request: Request) {
+ redirect("/login");
+}
diff --git a/components/auth/LoginButtons.tsx b/components/auth/LoginButtons.tsx
deleted file mode 100644
index c8d81ca..0000000
--- a/components/auth/LoginButtons.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useAuthProviders } from "@/components/auth";
-import { Button } from "@/components/ui";
-import { signIn } from "next-auth/react";
-import * as React from "react";
-
-type LoginButtonsProps = {} & React.HTMLAttributes;
-
-const LoginButtons = ({ onClick, ...other }: LoginButtonsProps) => {
- const authProviders = useAuthProviders();
- const providers = React.useMemo(() => Object.keys(authProviders ?? {}), [authProviders]);
-
- return (
- {
- if (onClick) {
- onClick(e);
- }
-
- if (providers.length === 1) {
- signIn(providers[0]);
- } else {
- signIn();
- }
- }}
- {...other}
- >
- Login
-
- );
-};
-
-export default LoginButtons;
diff --git a/components/auth/hooks/index.tsx b/components/auth/hooks/index.tsx
deleted file mode 100644
index 26f6ede..0000000
--- a/components/auth/hooks/index.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-import useAuthProviders from "@/components/auth/hooks/useAuthProviders";
-
-export { useAuthProviders };
diff --git a/components/auth/hooks/useAuthProviders.tsx b/components/auth/hooks/useAuthProviders.tsx
deleted file mode 100644
index c656c32..0000000
--- a/components/auth/hooks/useAuthProviders.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-import { getProviders } from "next-auth/react";
-import useSWR from "swr";
-
-export default function useAuthProviders() {
- const { data } = useSWR("/auth/providers", () => getProviders());
-
- return data;
-}
diff --git a/components/auth/index.tsx b/components/auth/index.tsx
deleted file mode 100644
index 89323a8..0000000
--- a/components/auth/index.tsx
+++ /dev/null
@@ -1,4 +0,0 @@
-import { useAuthProviders } from "@/components/auth/hooks";
-import LoginButtons from "@/components/auth/LoginButtons";
-
-export { LoginButtons, useAuthProviders };
diff --git a/components/auth/login-button.tsx b/components/auth/login-button.tsx
new file mode 100644
index 0000000..82b728e
--- /dev/null
+++ b/components/auth/login-button.tsx
@@ -0,0 +1,24 @@
+"use client";
+
+import { signIn } from "next-auth/react";
+import { useSearchParams } from "next/navigation";
+import { Button } from "../ui/button";
+
+function LoginButton() {
+ const searchParams = useSearchParams();
+
+ return (
+
+ signIn("azure-ad", {
+ redirect: false,
+ callbackUrl: searchParams?.get("from") || "/dashboard"
+ })
+ }
+ >
+ Login
+
+ );
+}
+
+export { LoginButton };
diff --git a/components/calendar/calendar-day.tsx b/components/calendar/calendar-day.tsx
new file mode 100644
index 0000000..a22b730
--- /dev/null
+++ b/components/calendar/calendar-day.tsx
@@ -0,0 +1,92 @@
+import { cn } from "@/lib/utils";
+import { CalendarDay, CalendarEntries } from "@/types";
+import { ComponentPropsWithoutRef, forwardRef } from "react";
+import { Icons } from "../icons";
+import { HoverCard, HoverCardContent, HoverCardTrigger } from "../ui/hover-card";
+import { Show } from "../ui/show";
+
+type CalendarDayProps = ComponentPropsWithoutRef<"div"> & {
+ calendarDay: CalendarEntries;
+ big?: boolean;
+ holidayInfos?: CalendarDay[];
+};
+
+const CalendarDay = forwardRef, CalendarDayProps>(
+ ({ className, calendarDay, big = false, holidayInfos = [], ...other }, ref) => {
+ return (
+
+
+ {big && calendarDay.isStartOfWeek ? (
+
{calendarDay.week}
+ ) : null}
+
+
+
+ {calendarDay.value}
+
+ Edit work day details
+
+
+
+ {calendarDay.value}
+ 0}>
+
+
+
+
+
+
+
+ {big &&
+ (calendarDay.type === "day" || calendarDay.type === "spacing") &&
+ holidayInfos
+ .filter(
+ x =>
+ x.day === calendarDay.value &&
+ x.weekNumber === calendarDay.week &&
+ !!x?.holidayInformation?.name
+ )
+ .map((x, index) => (
+
+
+ {x.holidayInformation?.name}
+
+
+
+ ))}
+
+ );
+ }
+);
+
+CalendarDay.displayName = "CalendarDay";
+
+export { CalendarDay };
diff --git a/components/calendar/components/calendar.tsx b/components/calendar/components/calendar.tsx
deleted file mode 100644
index c981ee9..0000000
--- a/components/calendar/components/calendar.tsx
+++ /dev/null
@@ -1,220 +0,0 @@
-import { CalendarDay } from "@/components/calendar";
-import { useCalendar } from "@/components/calendar/providers/calendarProvider";
-import { Box, Button, Card, Flex, Grid, IconButton, Svg, Text } from "@/components/ui";
-import { CalendarDay as CalendarDayType, WithChildren } from "@/types";
-import { motion } from "framer-motion";
-import * as React from "react";
-import { HiChevronLeft, HiChevronRight } from "react-icons/hi";
-import { useToggle } from "react-use";
-
-type CalendarDayColorDescriptionProps = WithChildren<{
- color: string;
-}>;
-
-const CalendarDayColorDescription = ({ children, color }: CalendarDayColorDescriptionProps) => {
- return (
-
-
-
- {children}
-
-
- );
-};
-
-const CalendarDayHeading = ({ children }: WithChildren<{}>) => {
- return (
-
-
- {children}
-
-
- );
-};
-
-const Calendar = ({ ...other }) => {
- const { yearName, activeCalendarMonthDetail, years, setYear, incrementMonth, decrementMonth } =
- useCalendar();
-
- const [showYearPicker, toggleYearPicker] = useToggle(false);
-
- const renderSpacingDays = (days: CalendarDayType[]): JSX.Element[] | null => {
- if (days === null || days === undefined || days?.length <= 0) {
- return null;
- }
-
- const spacingDaysToRender: Record = {
- MONDAY: 0,
- TUESDAY: 1,
- WEDNESDAY: 2,
- THURSDAY: 3,
- FRIDAY: 4,
- SATURDAY: 5,
- SUNDAY: 6
- };
-
- return [...Array(spacingDaysToRender[days[0].name?.toUpperCase()] ?? 0)].map((key, index) => (
-
- ));
- };
-
- return (
-
-
- {
- toggleYearPicker(false);
- decrementMonth();
- }}
- >
-
-
-
- {`${activeCalendarMonthDetail?.month} ${yearName}`}
-
- {
- toggleYearPicker(false);
- incrementMonth();
- }}
- >
-
-
-
-
- Off work
- Work
- Non commissioned
-
-
- mon.
- tue.
- wed.
- thu.
- fri.
- sat.
- sun.
- {renderSpacingDays(activeCalendarMonthDetail?.days)}
- {activeCalendarMonthDetail?.days?.map((day, i) => (
-
- ))}
-
-
- {showYearPicker &&
- years.map(year => (
- {
- setYear(year);
- toggleYearPicker();
- }}
- exit={{ opacity: 0 }}
- css={{
- cursor: "pointer",
- padding: "$1",
- fontSize: "$4",
- textAlign: "center"
- }}
- >
- {year}
-
- ))}
-
-
-
-
- );
-};
-
-export default Calendar;
diff --git a/components/calendar/components/calendarDay.tsx b/components/calendar/components/calendarDay.tsx
deleted file mode 100644
index 8e69dd3..0000000
--- a/components/calendar/components/calendarDay.tsx
+++ /dev/null
@@ -1,264 +0,0 @@
-import {
- Box,
- Dialog,
- DialogContent,
- DialogNonRemoveScrollOverlay,
- DialogTrigger,
- Svg
-} from "@/components/ui";
-import { UserWorkDayDetails, useUser } from "@/components/user";
-import { getUserWorkDayDetails } from "@/logic/userLogic";
-import { CalendarDay as CalendarDayType, UserWorkDayDetail, WithChildren } from "@/types";
-import { Presence } from "@radix-ui/react-presence";
-import * as React from "react";
-import { HiChevronDoubleUp } from "react-icons/hi";
-import { IoBugOutline } from "react-icons/io5";
-import { CSS, styled } from "stitches.config";
-
-type BaseDay = {
- isWorkDay?: boolean;
- isNonCommissionedToggled?: boolean;
- isExtraHoursToggled?: boolean;
- isSickDayToggled?: boolean;
-};
-
-type CalendarDayDateProps = WithChildren<{
- as?: React.ElementType;
- className?: string;
- isExpanded?: boolean;
- onClick?: React.MouseEventHandler;
- css?: CSS;
-}> &
- BaseDay;
-
-type RegularCalendarDayProps = {
- day: CalendarDayType;
- onExpand: () => void;
-} & BaseDay;
-
-type ExpandedCalendarDayProps = {
- day: CalendarDayType;
- workDayDetails: UserWorkDayDetail;
- onCollapse: () => void;
-} & BaseDay;
-
-type CalendarDayProps = {
- day: CalendarDayType;
- isWorkDay?: boolean;
-};
-
-const DayText = styled("div", {
- display: "flex",
-
- fontWeight: "bold",
- borderWidth: "2px",
- borderStyle: "solid",
- borderColor: "transparent",
- borderRadius: "$round",
- transition: "border-color 0.2s ease-in-out",
- variants: {
- expanded: {
- true: {
- height: "auto",
- width: "auto",
- mb: "$2",
- border: "none",
- fontSize: "$4"
- },
- false: {
- height: "$6",
- width: "$6",
- justifyContent: "center",
- alignItems: "center"
- }
- },
- workDay: {
- true: {
- color: "$green"
- }
- },
- nonCommissioned: {
- true: {
- color: "$red"
- }
- }
- },
- compoundVariants: [
- {
- expanded: false,
- workDay: true,
- css: {
- "&:hover": {
- borderColor: "$green"
- }
- }
- },
- {
- expanded: false,
- nonCommissioned: true,
- css: {
- "&:hover": {
- borderColor: "$red"
- }
- }
- }
- ]
-});
-
-const CalendarDayDate = React.forwardRef, CalendarDayDateProps>(
- function CalendarDayDate(
- {
- children,
- className,
- isWorkDay,
- isNonCommissionedToggled,
- isExtraHoursToggled,
- isSickDayToggled,
- isExpanded = false,
- ...other
- },
- ref
- ) {
- const boxCss: CSS = isExpanded
- ? {
- display: "block"
- }
- : {
- display: "flex",
- justifyContent: "center",
- alignItems: "center",
- cursor: "pointer"
- };
-
- return (
-
-
- {children}
- {isExtraHoursToggled ? : null}
- {isSickDayToggled ? : null}
-
-
- );
- }
-);
-
-const RegularCalendarDay = React.forwardRef<
- React.ElementRef,
- RegularCalendarDayProps
->(function RegularCalendarDay(
- { day, isWorkDay = false, isNonCommissionedToggled = false, onExpand = () => {}, ...other },
- ref
-) {
- return (
- onExpand()}
- ref={ref}
- {...other}
- >
- {day?.day}
-
- );
-});
-
-const ExpandedCalendarDay = ({
- day,
- isWorkDay = false,
- isNonCommissionedToggled = false,
- isExtraHoursToggled = false,
- workDayDetails,
- onCollapse = () => {},
- ...other
-}: ExpandedCalendarDayProps) => {
- return (
-
-
- {day?.formattedShortDate}
-
-
-
- );
-};
-
-const CalendarDay = ({ day, isWorkDay = false, ...other }: CalendarDayProps) => {
- const { user } = useUser();
-
- const [isExpanded, setIsExpanded] = React.useState(false);
-
- const workDayDetails = React.useMemo(
- () => getUserWorkDayDetails(user, day?.formattedDate),
- [user, day?.formattedDate]
- );
-
- const isNonCommissionedToggled = React.useMemo(
- () => workDayDetails?.nonCommissionedHours > 0,
- [workDayDetails.nonCommissionedHours]
- );
-
- const isExtraHoursToggled = React.useMemo(
- () => workDayDetails?.extraHours > 0,
- [workDayDetails.extraHours]
- );
-
- const isSickDayToggled = React.useMemo(
- () => (workDayDetails?.sickDay ?? false) && isNonCommissionedToggled,
- [workDayDetails?.sickDay, isNonCommissionedToggled]
- );
-
- return (
- setIsExpanded(open)}
- overlayProps={{
- enabled: false
- }}
- >
-
- setIsExpanded(true)}
- {...other}
- />
-
-
-
-
-
- setIsExpanded(false)}
- {...other}
- />
-
-
- );
-};
-
-export default CalendarDay;
diff --git a/components/calendar/components/index.ts b/components/calendar/components/index.ts
deleted file mode 100644
index 97c3615..0000000
--- a/components/calendar/components/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import Calendar from "./calendar";
-import CalendarDay from "./calendarDay";
-
-export { Calendar, CalendarDay };
diff --git a/components/calendar/index.ts b/components/calendar/index.ts
deleted file mode 100644
index ea9da9e..0000000
--- a/components/calendar/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { Calendar, CalendarDay } from "./components";
-import { CalendarProvider, useCalendar } from "./providers";
-
-export { Calendar, CalendarDay, CalendarProvider, useCalendar };
diff --git a/components/calendar/month-select.tsx b/components/calendar/month-select.tsx
new file mode 100644
index 0000000..cc0c41f
--- /dev/null
+++ b/components/calendar/month-select.tsx
@@ -0,0 +1,77 @@
+"use client";
+
+import { MONTH } from "@/constants/date-constants";
+import { useRouter } from "next/navigation";
+import { forwardRef, useTransition, type ComponentPropsWithoutRef, type ElementRef } from "react";
+
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue
+} from "@/components/ui/select";
+import { cn } from "@/lib/utils";
+
+const MonthSelect = forwardRef<
+ ElementRef,
+ ComponentPropsWithoutRef & {
+ year: number;
+ month: number;
+ isSelected?: boolean;
+ }
+>(({ className, year, month, isSelected = false, ...other }, forwardedRef) => {
+ const router = useRouter();
+ const [isPending, startTransition] = useTransition();
+
+ return (
+ {
+ router.prefetch(`/dashboard/year/${year}/month/${value}`);
+ // update url
+ router.push(`/dashboard/year/${year}/month/${value}`, {
+ forceOptimisticNavigation: true
+ });
+
+ // start transition
+ startTransition(() => {
+ // Refresh the current route and fetch new data from the server without
+ // losing client-side browser or React state.
+ router.refresh();
+ });
+ }}
+ disabled={isPending}
+ >
+
+
+ Toggle month
+
+
+ {Object.values(MONTH).map(m => (
+ {
+ router.prefetch(`/dashboard/year/${year}/month/${m.value}`);
+ }}
+ >
+ {m.i18n.en}
+
+ ))}
+
+
+ );
+});
+
+MonthSelect.displayName = "MonthSelect";
+
+export { MonthSelect };
diff --git a/components/calendar/providers/calendarProvider.tsx b/components/calendar/providers/calendarProvider.tsx
deleted file mode 100644
index 26efcab..0000000
--- a/components/calendar/providers/calendarProvider.tsx
+++ /dev/null
@@ -1,198 +0,0 @@
-import { getPayDay } from "@/logic/calendarLogic";
-import { CalendarMonth, CalendarYear } from "@/types";
-import { getCalendarYear } from "@/utils/calendar/calendarUtils";
-import { getThisYearAndTwoYearsIntoTheFuture } from "@/utils/dateUtils";
-import * as React from "react";
-
-type CalendarContextProps = {
- isLoadingCalendar: boolean;
- year: CalendarYear;
- lastYear: CalendarYear;
- nextYear: CalendarYear;
- calendarYear: CalendarYear;
- yearName: string;
- years: number[];
- month: number;
- activeCalendarMonthDetail: CalendarMonth;
- currentMonthDetail: CalendarMonth;
- lastMonthDetail: CalendarMonth;
- nextMonthDetail: CalendarMonth;
- setYear: (year: number) => void;
- incrementYear: () => void;
- decrementYear: () => void;
- incrementMonth: () => void;
- decrementMonth: () => void;
- date: Date;
-};
-
-const CalendarContext = React.createContext(null);
-CalendarContext.displayName = "CalendarContext";
-
-const now = new Date();
-
-const initialState = {
- year: now.getFullYear(),
- month: now.getMonth()
-};
-
-const getLastMonth = (month, year, lastYear) =>
- month === 0 ? lastYear?.months?.[11] : year?.months?.[month - 1];
-const getNextMonth = (month, year, nextYear) =>
- month === 11 ? nextYear?.months?.[0] : year?.months?.[month + 1];
-
-const getDateFromYearDataAndMonth = (year, month) => {
- const now = new Date();
- return new Date(year?.year ?? now.getFullYear(), month ?? now.getMonth(), 1);
-};
-
-function calendarReducer(state = initialState, action) {
- switch (action.type) {
- case "SET_YEAR": {
- return { ...state, year: action.year };
- }
- case "SET_MONTH": {
- return { ...state, month: action.month };
- }
- default: {
- throw new Error(`Unhandled action type: ${action.type}`);
- }
- }
-}
-
-function CalendarProvider({ children, year = initialState.year, month = initialState.month }) {
- const [state, dispatch] = React.useReducer(calendarReducer, { year, month });
-
- const calendarYear = React.useMemo(() => getCalendarYear(state.year), [state.year]);
-
- const currentYear = new Date().getFullYear();
- const data = React.useMemo(() => getCalendarYear(currentYear), [currentYear]);
- const lastYear = React.useMemo(() => getCalendarYear(currentYear - 1), [currentYear]);
- const nextYear = React.useMemo(() => getCalendarYear(currentYear + 1), [currentYear]);
- const setYear = (year: number) => dispatch({ type: "SET_YEAR", year: year });
-
- const incrementYear = React.useCallback(
- () => dispatch({ type: "SET_YEAR", year: state.year + 1 }),
- [state.year]
- );
- const decrementYear = React.useCallback(
- () => dispatch({ type: "SET_YEAR", year: state.year - 1 }),
- [state.year]
- );
-
- const setMonth = month => dispatch({ type: "SET_MONTH", month: month });
-
- const incrementMonth = React.useCallback(() => {
- const newMonth = state.month + 1;
- const shouldIncrementYear = newMonth > 11;
-
- if (shouldIncrementYear) {
- incrementYear();
- }
-
- dispatch({ type: "SET_MONTH", month: shouldIncrementYear ? 0 : newMonth });
- }, [incrementYear, state.month]);
-
- const decrementMonth = React.useCallback(() => {
- const newMonth = state.month - 1;
- const shouldDecrementYear = newMonth < 0;
-
- if (shouldDecrementYear) {
- decrementYear();
- }
-
- dispatch({ type: "SET_MONTH", month: shouldDecrementYear ? 11 : newMonth });
- }, [decrementYear, state.month]);
-
- React.useEffect(() => {
- setYear(year);
- setMonth(month);
- }, [year, month]);
-
- const calendarValue = React.useMemo<{
- date: Date;
- yearName: string;
- years: number[];
- month: number;
- activeCalendarMonthDetail: CalendarMonth;
- calendarYear: CalendarYear;
- }>(() => {
- const date = getDateFromYearDataAndMonth(state.year, state.month);
-
- const activeCalendarMonthDetail = {
- ...calendarYear?.months?.[date.getMonth()],
- payDay: getPayDay(
- getNextMonth(date.getMonth(), calendarYear, getCalendarYear(+calendarYear.year + 1))
- )
- };
-
- return {
- date,
- calendarYear,
- yearName: state.year,
- month: state.month,
- years: getThisYearAndTwoYearsIntoTheFuture(),
- activeCalendarMonthDetail
- };
- }, [calendarYear, state.month, state.year]);
-
- const value = React.useMemo(() => {
- const currentMonth = new Date().getMonth();
-
- const nextMonthDetail = {
- ...getNextMonth(currentMonth, data, nextYear),
- payDay: getPayDay(
- currentMonth === 11
- ? nextYear?.months?.[1]
- : currentMonth === 10
- ? nextYear?.months?.[0]
- : data?.months?.[currentMonth + 2]
- )
- };
-
- const currentMonthDetail = {
- ...data?.months?.[currentMonth],
- payDay: getPayDay(nextMonthDetail)
- };
-
- const lastMonthDetail = {
- ...getLastMonth(currentMonth, data, lastYear),
- payDay: getPayDay(currentMonthDetail)
- };
-
- return {
- year: data,
- lastYear,
- nextYear,
- currentMonthDetail,
- lastMonthDetail,
- nextMonthDetail,
- isLoadingCalendar: !data
- };
- }, [data, lastYear, nextYear]);
-
- return (
-
- {children}
-
- );
-}
-function useCalendar() {
- const context = React.useContext(CalendarContext);
-
- if (context === undefined) {
- throw new Error("useCalendar must be used within a CalendarProvider");
- }
-
- return context;
-}
-export { CalendarProvider, useCalendar };
diff --git a/components/calendar/providers/index.ts b/components/calendar/providers/index.ts
deleted file mode 100644
index 6bc61a8..0000000
--- a/components/calendar/providers/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { CalendarProvider, useCalendar } from "./calendarProvider";
-
-export { CalendarProvider, useCalendar };
diff --git a/components/companyBenefits.tsx b/components/companyBenefits.tsx
deleted file mode 100644
index f41fb9d..0000000
--- a/components/companyBenefits.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-import { Box, Card, Flex, FlexItem, Grid, Heading, Paragraph, Svg } from "@/components/ui";
-import { WithChildren } from "@/types";
-import * as React from "react";
-import { HiOutlineCheckCircle } from "react-icons/hi";
-import { VariantProps } from "stitches.config";
-
-type BenefitProps = WithChildren<{
- icon?: React.ElementType;
- iconProps?: VariantProps;
-}>;
-
-const Benefit = ({
- children,
- icon = HiOutlineCheckCircle,
- iconProps = {
- variant: "green"
- }
-}: BenefitProps) => {
- const Icon = icon;
- return (
-
-
-
-
-
- {children}
-
-
- );
-};
-
-const CompanyBenefits = () => {
- return (
-
-
-
- Knowit Experience Bergen
-
-
- Company Benefits
-
-
- You get paid by commission or guaranteed salary. Knowit covers both employer's
- national insurance contributions (14.10%) and holyday payment (12%). This means you can
- calculate your next payment by the following formulae{" "}
-
- Work hours in month x hourly rate x commission = your salary.
-
-
-
-
- Full pay on paternity leave
- Pension 5,5% of salary from 1G to 12G
- Health insurance for all employees
- New training facilities + scheduled training programmes
- Free phone with free calls, free SMS and 15 GB data every month
- Your choice of computer equipment, PC or Mac, mouse and keyboard
- Subsidized good canteen
- Generous social budget that guarantees lots of fun
-
-
- );
-};
-
-export default CompanyBenefits;
diff --git a/components/container.tsx b/components/container.tsx
deleted file mode 100644
index 4588aa2..0000000
--- a/components/container.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { WithChildren } from "@/types";
-import * as React from "react";
-
-type ContainerProps = WithChildren<{
- as?: React.ElementType;
- className?: string;
-}>;
-
-const Container = ({ children, as = "div", ...other }: ContainerProps) => {
- const Component = as;
- return {children} ;
-};
-
-export default Container;
diff --git a/components/feedback/components/feedbackDialog.tsx b/components/feedback/components/feedbackDialog.tsx
deleted file mode 100644
index 9be1f0a..0000000
--- a/components/feedback/components/feedbackDialog.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Button, Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/components/ui";
-import * as React from "react";
-import FeedbackForm from "./feedbackForm";
-
-const FeedbackDialog = () => {
- const [open, setOpen] = React.useState(false);
-
- return (
- setOpen(open)}>
-
- Feedback
-
-
- Feedback
-
-
-
- );
-};
-
-export default FeedbackDialog;
diff --git a/components/feedback/components/feedbackEmojiSelector.tsx b/components/feedback/components/feedbackEmojiSelector.tsx
deleted file mode 100644
index 051d2a1..0000000
--- a/components/feedback/components/feedbackEmojiSelector.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import { Flex } from "@/components/ui";
-import Image from "next/image";
-import * as React from "react";
-import { styled } from "stitches.config";
-
-const EmojiButton = styled("button", {
- all: "unset",
- outline: "none",
- margin: 0,
- padding: 0,
- display: "inline-flex",
- justifyContent: "center",
- alignItems: "center",
- width: "34px",
- height: "34px",
- backgroundColor: "$grayDark",
- border: "1px solid $gray",
- borderRadius: "$round",
- cursor: "pointer",
- transition: "all .2s cubic-bezier(.5,-1,.5,2)",
- "&:hover": {
- transform: "scale(1.12)"
- },
- "&:focus": {
- transform: "scale(1.12)"
- },
- variants: {
- selected: {
- true: {
- transform: "scale(1.12)",
- border: "1px solid $green"
- }
- }
- }
-});
-
-type FeedbackEmojiSelectorProps = {
- onSelected: (emojiValue: number) => void;
-};
-
-const twemojis = {
- "1f60d": {
- src: "/twemoji/1f60d.svg",
- alt: "Emoji with heart eyes",
- value: 1
- },
- "1f600": {
- src: "/twemoji/1f600.svg",
- alt: "Emoji with happy expression",
- value: 2
- },
- "1f615": {
- src: "/twemoji/1f615.svg",
- alt: "Emoji with indifferent expression",
- value: 3
- },
- "1f662d": {
- src: "/twemoji/1f62d.svg",
- alt: "Emoji with sad expression",
- value: 4
- }
-};
-
-const FeedbackEmojiSelector = ({ onSelected = () => {} }: FeedbackEmojiSelectorProps) => {
- const [selectedEmoji, setSelectedEmoji] = React.useState(0);
-
- const handleEmojiSelect = (emojiValue: number) => {
- setSelectedEmoji(emojiValue);
- onSelected(emojiValue);
- };
-
- return (
-
- {Object.entries(twemojis).map(([emoji, { src, alt, value }]) => (
- handleEmojiSelect(value)}
- >
-
-
- ))}
-
- );
-};
-
-export default FeedbackEmojiSelector;
diff --git a/components/feedback/components/feedbackForm.tsx b/components/feedback/components/feedbackForm.tsx
deleted file mode 100644
index 28154b6..0000000
--- a/components/feedback/components/feedbackForm.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { Button, Fieldset, Flex, Form, Svg, Text, TextArea, TextField } from "@/components/ui";
-import * as React from "react";
-import { useForm } from "react-hook-form";
-import { IoCheckmark, IoChevronForward, IoSyncOutline } from "react-icons/io5";
-import FeedbackEmojiSelector from "./feedbackEmojiSelector";
-
-type FeedbackFormProps = {
- compact?: boolean;
-};
-
-const FeedbackForm = ({ compact = false }: FeedbackFormProps) => {
- const [isSubmitting, setIsSubmitting] = React.useState(false);
- const [isCompleted, setIsCompleted] = React.useState(false);
- const {
- register,
- handleSubmit,
- formState: { errors },
- setValue
- } = useForm({
- defaultValues: {
- message: "",
- reaction: 0
- }
- });
-
- const onSubmit = async (data: any) => {
- setIsSubmitting(true);
-
- await fetch("/api/user/feedback", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json"
- },
- body: JSON.stringify(data)
- });
-
- setIsSubmitting(false);
- setIsCompleted(true);
- };
-
- return (
-
- );
-};
-
-export default FeedbackForm;
diff --git a/components/feedback/components/feedbackPopover.tsx b/components/feedback/components/feedbackPopover.tsx
deleted file mode 100644
index b14116a..0000000
--- a/components/feedback/components/feedbackPopover.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Button, Popover, PopoverArrow, PopoverContent, PopoverTrigger } from "@/components/ui";
-import * as React from "react";
-import FeedbackForm from "./feedbackForm";
-
-const FeedbackPopover = ({}) => {
- return (
-
-
- Feedback
-
-
-
-
-
-
- );
-};
-
-export default FeedbackPopover;
diff --git a/components/feedback/components/index.ts b/components/feedback/components/index.ts
deleted file mode 100644
index db85603..0000000
--- a/components/feedback/components/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import FeedbackDialog from "./feedbackDialog";
-import FeedbackEmojiSelector from "./feedbackEmojiSelector";
-import FeedbackForm from "./feedbackForm";
-import FeedbackPopover from "./feedbackPopover";
-
-export { FeedbackDialog, FeedbackEmojiSelector, FeedbackForm, FeedbackPopover };
diff --git a/components/feedback/index.ts b/components/feedback/index.ts
deleted file mode 100644
index 10c84ea..0000000
--- a/components/feedback/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { FeedbackDialog, FeedbackEmojiSelector, FeedbackForm, FeedbackPopover } from "./components";
-
-export { FeedbackDialog, FeedbackEmojiSelector, FeedbackForm, FeedbackPopover };
diff --git a/components/form/controlledCheckbox.tsx b/components/form/controlledCheckbox.tsx
deleted file mode 100644
index ad877b2..0000000
--- a/components/form/controlledCheckbox.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import * as React from "react";
-import { Controller } from "react-hook-form";
-import { IoCheckmark } from "react-icons/io5";
-import { CSS } from "stitches.config";
-import { Checkbox, CheckboxIndicator, Flex, Label } from "../ui";
-
-type ControlledCheckboxProps = {
- control: any;
- id: string;
- name: string;
- labelText: string;
- css?: CSS;
-};
-
-const ControlledCheckbox = ({
- control,
- id,
- name,
- labelText,
- ...other
-}: ControlledCheckboxProps) => {
- return (
-
- (
- field.onChange(checked)}
- ref={field.ref}
- {...other}
- >
-
-
-
-
- )}
- >
-
- {labelText}
-
-
- );
-};
-
-export default ControlledCheckbox;
diff --git a/components/form/index.ts b/components/form/index.ts
deleted file mode 100644
index bc2481d..0000000
--- a/components/form/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import ControlledCheckbox from "./controlledCheckbox";
-
-export { ControlledCheckbox };
diff --git a/components/header.tsx b/components/header.tsx
deleted file mode 100644
index 53d5a38..0000000
--- a/components/header.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { Logo } from "@/components/icons";
-import { Box, Container, Flex, Link, LinkButton, Nav } from "@/components/ui";
-import { UserNavDetails, useUser } from "@/components/user";
-import * as React from "react";
-import { FeedbackPopover } from "./feedback";
-
-export default function Header({ userNavDetails = true, ...other }) {
- const { user } = useUser();
-
- return (
-
-
-
-
-
-
-
-
-
- {user.isAdmin ? (
-
- Salary calculator
-
- ) : null}
-
-
- {userNavDetails && }
-
-
- );
-}
diff --git a/components/icons/svgs/logo.tsx b/components/icons.tsx
similarity index 67%
rename from components/icons/svgs/logo.tsx
rename to components/icons.tsx
index 22f0b4f..ad82bbf 100644
--- a/components/icons/svgs/logo.tsx
+++ b/components/icons.tsx
@@ -1,30 +1,34 @@
-import * as React from "react";
-import { styled } from "stitches.config";
+import { cn } from "@/lib/utils";
+import {
+ ArrowTopRightIcon,
+ CheckCircledIcon,
+ CheckIcon,
+ ChevronLeftIcon,
+ ChevronRightIcon,
+ Cross2Icon,
+ GearIcon,
+ InfoCircledIcon,
+ MinusCircledIcon,
+ PlusCircledIcon,
+ PlusIcon,
+ RocketIcon
+} from "@radix-ui/react-icons";
+import { IconProps } from "@radix-ui/react-icons/dist/types";
-const LogoRoot = styled("svg", {
- width: "$12",
- height: "auto",
- color: "$text",
- "@bp1": {
- width: "$14"
- }
-});
+export type Icon = IconProps;
-type LogoProps = {
- size?: number;
-} & React.HTMLAttributes;
-
-const Logo = ({ color = "currentColor", size = 24, ...rest }: LogoProps) => {
- return (
- (
+
-
+
@@ -33,10 +37,45 @@ const Logo = ({ color = "currentColor", size = 24, ...rest }: LogoProps) => {
-
- );
+
+ ),
+ Gear: GearIcon,
+ ChevronLeft: ChevronLeftIcon,
+ ChevronRight: ChevronRightIcon,
+ Close: Cross2Icon,
+ Check: CheckIcon,
+ ArrowTopRight: ArrowTopRightIcon,
+ Info: InfoCircledIcon,
+ Plus: PlusIcon,
+ PlusCircled: PlusCircledIcon,
+ Rocket: RocketIcon,
+ CheckCircled: CheckCircledIcon,
+ MinusCircled: MinusCircledIcon,
+ Loader: ({ className, ...other }) => (
+
+
+
+
+
+
+
+
+
+
+ )
};
-
-Logo.displayName = "Logo";
-
-export default Logo;
diff --git a/components/icons/index.tsx b/components/icons/index.tsx
deleted file mode 100644
index a96bed4..0000000
--- a/components/icons/index.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export { default as Logo } from "@/components/icons/svgs/logo";
diff --git a/components/layouts/authenticatedLayout.tsx b/components/layouts/authenticatedLayout.tsx
deleted file mode 100644
index f2cd3dc..0000000
--- a/components/layouts/authenticatedLayout.tsx
+++ /dev/null
@@ -1,169 +0,0 @@
-import { CalendarProvider } from "@/components/calendar";
-import Header from "@/components/header";
-import { SalaryProvider } from "@/components/salary";
-import { Box, Container, Flex, Footer, Heading, Li, Link, Main, Ul } from "@/components/ui";
-import { UserProvider } from "@/components/user/providers/userProvider";
-import { WithChildren } from "@/types";
-import { SessionProvider } from "next-auth/react";
-import * as React from "react";
-import { FeedbackForm } from "../feedback";
-import { Logo } from "../icons";
-import { ThemeSelector } from "../theme";
-
-type LayoutProps = WithChildren<{
- pageProps?: Record;
- layoutProps?: Record;
-}>;
-
-export default function AuthenticatedLayout({
- children,
- pageProps,
- layoutProps,
- ...other
-}: LayoutProps) {
- return (
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
- Resources
-
-
-
- Personal handbook
-
-
-
-
- CV Partner
-
-
-
-
- Helpit
-
-
-
-
- Timekeeping
-
-
-
-
- Slack
-
-
-
-
- Shareit
-
-
-
-
- Brand book
-
-
-
-
- Office templates
-
-
-
-
-
- Site
-
-
- Home
-
-
- Profile
-
-
- Feedback
-
-
-
-
- Feedback
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/components/salary/components/index.ts b/components/salary/components/index.ts
deleted file mode 100644
index 90d54e5..0000000
--- a/components/salary/components/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import SalaryStatistics from "./salaryStatistics";
-import YearlyEarnings from "./yearlyEarnings";
-import YearStatistic from "./yearStatistic";
-
-export { SalaryStatistics, YearlyEarnings, YearStatistic };
diff --git a/components/salary/components/salaryStatistics.tsx b/components/salary/components/salaryStatistics.tsx
deleted file mode 100644
index 6a19f66..0000000
--- a/components/salary/components/salaryStatistics.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { useCalendar } from "@/components/calendar";
-import { useSalary } from "@/components/salary";
-import Statistic from "@/components/statistic";
-import StatisticGroup from "@/components/statisticGroup";
-import { Alert, AppearInBox, Text } from "@/components/ui";
-import { CalendarMonthEarnings } from "@/types";
-import * as React from "react";
-
-type SalaryStatisticsProps = {
- salaryStatistics?: CalendarMonthEarnings;
-};
-
-const SalaryStatistics = ({ salaryStatistics, ...other }: SalaryStatisticsProps) => {
- const { activeCalendarMonthDetail, isLoadingCalendar } = useCalendar();
- const { activeCalendarMonthStatistics, isLoadingSalary } = useSalary();
-
- const isLoading = React.useMemo(
- () => (salaryStatistics ? isLoadingCalendar : isLoadingCalendar || isLoadingSalary),
- [salaryStatistics, isLoadingCalendar, isLoadingSalary]
- );
-
- const statistics: CalendarMonthEarnings = salaryStatistics ?? activeCalendarMonthStatistics;
-
- return (
- <>
-
-
-
- Salary for {statistics.monthName} paid with{" "}
-
- half tax
- {" "}
- at {statistics.payDay}
-
-
-
-
-
-
-
-
-
- >
- );
-};
-
-export default SalaryStatistics;
diff --git a/components/salary/components/yearStatistic.tsx b/components/salary/components/yearStatistic.tsx
deleted file mode 100644
index 81dd4b1..0000000
--- a/components/salary/components/yearStatistic.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import Statistic from "@/components/statistic";
-import StatisticGroup from "@/components/statisticGroup";
-import { Box, Heading } from "@/components/ui";
-import { CalendarYearEarnings } from "@/types";
-import * as React from "react";
-
-type YearStatisticProps = {
- title: string;
- yearStatistic: CalendarYearEarnings;
- isLoading?: boolean;
- showNinetyPercentBillableHours?: boolean;
-};
-
-const YearStatistic = ({
- title,
- yearStatistic,
- isLoading = false,
- showNinetyPercentBillableHours = false,
- ...other
-}: YearStatisticProps) => {
- return (
-
- {title}
-
-
-
-
-
-
-
- );
-};
-
-export default YearStatistic;
diff --git a/components/salary/components/yearlyEarnings.tsx b/components/salary/components/yearlyEarnings.tsx
deleted file mode 100644
index 2499b77..0000000
--- a/components/salary/components/yearlyEarnings.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Calendar } from "@/components/calendar";
-import { SalaryStatistics } from "@/components/salary";
-import { Flex, FlexItem } from "@/components/ui";
-import { UserProfile } from "@/components/user";
-import * as React from "react";
-
-export default function YearlyEarnings() {
- return (
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/components/salary/index.ts b/components/salary/index.ts
deleted file mode 100644
index b8fba4c..0000000
--- a/components/salary/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import { SalaryStatistics, YearlyEarnings, YearStatistic } from "./components";
-import { SalaryProvider, useSalary } from "./providers";
-
-export { SalaryProvider, useSalary, SalaryStatistics, YearStatistic, YearlyEarnings };
diff --git a/components/salary/providers/index.ts b/components/salary/providers/index.ts
deleted file mode 100644
index 33692ed..0000000
--- a/components/salary/providers/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { SalaryProvider, useSalary } from "./salaryProvider";
-
-export { SalaryProvider, useSalary };
diff --git a/components/salary/providers/salaryProvider.tsx b/components/salary/providers/salaryProvider.tsx
deleted file mode 100644
index 013813b..0000000
--- a/components/salary/providers/salaryProvider.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { useCalendar } from "@/components/calendar";
-import { useUser } from "@/components/user";
-import { UserEarningsDetails } from "@/types";
-import { getUserEarningsDetails, getUserSalaryDetails } from "@/utils/userUtils";
-import * as React from "react";
-
-type SalaryContextProps = {
- isLoadingSalary: boolean;
-} & UserEarningsDetails;
-
-const SalaryContext = React.createContext(null);
-SalaryContext.displayName = "SalaryContext";
-
-function SalaryProvider({ children }) {
- const { user, isLoadingUser } = useUser();
-
- const {
- year,
- nextYear,
- activeCalendarMonthDetail,
- currentMonthDetail,
- lastMonthDetail,
- nextMonthDetail,
- isLoadingCalendar
- } = useCalendar();
-
- const userSalaryDetails = React.useMemo(() => getUserSalaryDetails(user), [user]);
-
- const userEarningsDetails = React.useMemo(
- () =>
- getUserEarningsDetails(
- userSalaryDetails,
- year,
- nextYear,
- activeCalendarMonthDetail,
- currentMonthDetail,
- lastMonthDetail,
- nextMonthDetail,
- user.workDayDetails
- ),
- [
- userSalaryDetails,
- year,
- nextYear,
- activeCalendarMonthDetail,
- currentMonthDetail,
- lastMonthDetail,
- nextMonthDetail,
- user.workDayDetails
- ]
- );
-
- const value = React.useMemo(
- () => ({
- ...userEarningsDetails,
- isLoadingSalary: !userEarningsDetails || isLoadingUser || isLoadingCalendar
- }),
- [userEarningsDetails, isLoadingUser, isLoadingCalendar]
- );
-
- return {children} ;
-}
-function useSalary() {
- const context = React.useContext(SalaryContext);
-
- if (context === undefined) {
- throw new Error("useSalary must be used within a SalaryProvider");
- }
-
- return context;
-}
-export { SalaryProvider, useSalary };
diff --git a/components/salary/salary-details-card.tsx b/components/salary/salary-details-card.tsx
new file mode 100644
index 0000000..34c04ee
--- /dev/null
+++ b/components/salary/salary-details-card.tsx
@@ -0,0 +1,19 @@
+import { cn } from "@/lib/utils";
+import { ComponentPropsWithoutRef } from "react";
+import { Card } from "../ui/card";
+
+const SalaryDetailsCard = ({
+ children,
+ className,
+ heading,
+ ...other
+}: ComponentPropsWithoutRef & { heading: string }) => {
+ return (
+
+ {heading}
+ {children}
+
+ );
+};
+
+export { SalaryDetailsCard };
diff --git a/components/statistic.tsx b/components/statistic.tsx
deleted file mode 100644
index f38b580..0000000
--- a/components/statistic.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Box, Card, Heading, Skeleton, Text } from "@/components/ui";
-import * as React from "react";
-
-type StatisticProps = {
- title: string;
- value: string | number;
- isLoading?: boolean;
-};
-
-const Statistic = ({ title, value, isLoading = false }: StatisticProps) => {
- return (
-
-
-
-
-
- {title}
-
-
- {value}
-
-
-
-
-
- );
-};
-
-export default Statistic;
diff --git a/components/statisticGroup.tsx b/components/statisticGroup.tsx
deleted file mode 100644
index 8824feb..0000000
--- a/components/statisticGroup.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Grid } from "@/components/ui";
-import { WithChildren } from "@/types";
-import * as React from "react";
-
-type StatisticGroupProps = WithChildren<{}>;
-
-const StatisticGroup = ({ children }: StatisticGroupProps) => {
- return (
-
- {children}
-
- );
-};
-
-export default StatisticGroup;
diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx
new file mode 100644
index 0000000..f362f08
--- /dev/null
+++ b/components/theme-provider.tsx
@@ -0,0 +1,8 @@
+"use client"
+
+import { ThemeProvider as NextThemesProvider } from "next-themes"
+import { ThemeProviderProps } from "next-themes/dist/types"
+
+export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
+ return {children}
+}
diff --git a/components/theme-select.tsx b/components/theme-select.tsx
new file mode 100644
index 0000000..109b852
--- /dev/null
+++ b/components/theme-select.tsx
@@ -0,0 +1,60 @@
+"use client";
+
+import { THEME_CONSTANTS } from "@/constants/theme-constants";
+import { DesktopIcon, MoonIcon, SunIcon } from "@radix-ui/react-icons";
+import { useTheme } from "next-themes";
+import { FC, forwardRef, type ComponentPropsWithoutRef, type ElementRef } from "react";
+
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue
+} from "@/components/ui/select";
+import { cn } from "@/lib/utils";
+
+const ThemeSelectIcon: FC<{ theme: string }> = ({ theme }) => {
+ switch (theme) {
+ case THEME_CONSTANTS.LIGHT.value:
+ return ;
+ case THEME_CONSTANTS.DARK.value:
+ return ;
+ case THEME_CONSTANTS.SYSTEM.value:
+ return ;
+ default:
+ return ;
+ }
+};
+
+const ThemeSelect = forwardRef<
+ ElementRef,
+ ComponentPropsWithoutRef
+>(({ className, ...other }, forwardedRef) => {
+ const { theme: selectedTheme, setTheme } = useTheme();
+ return (
+ setTheme(value)}
+ >
+
+
+ Toggle theme
+
+
+ {Object.values(THEME_CONSTANTS).map(theme => (
+
+
+
+ {theme.i18n.no}
+
+
+ ))}
+
+
+ );
+});
+
+ThemeSelect.displayName = "ThemeSelect";
+
+export { ThemeSelect };
diff --git a/components/theme/index.ts b/components/theme/index.ts
deleted file mode 100644
index 8087c08..0000000
--- a/components/theme/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import dynamic from "next/dynamic";
-
-// React 18 hydration issues - disable SSR for now
-// https://github.com/vercel/next.js/discussions/35773
-const ThemeSelector = dynamic(() => import("./themeSelector"), {
- ssr: false
-});
-
-export { ThemeSelector };
diff --git a/components/theme/themeSelector.tsx b/components/theme/themeSelector.tsx
deleted file mode 100644
index 31dc5e5..0000000
--- a/components/theme/themeSelector.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import { stringToTitleCase } from "@/utils/commonUtils";
-import { useTheme } from "next-themes";
-import * as React from "react";
-import { HiOutlineDesktopComputer } from "react-icons/hi";
-import {
- IoCheckmark,
- IoChevronDown,
- IoChevronUp,
- IoMoonOutline,
- IoSunnyOutline
-} from "react-icons/io5";
-import { CSS } from "stitches.config";
-import {
- Flex,
- Label,
- Select,
- SelectContent,
- SelectGroup,
- SelectIcon,
- SelectItem,
- SelectItemIndicator,
- SelectItemText,
- SelectLabel,
- SelectScrollDownButton,
- SelectScrollUpButton,
- SelectTrigger,
- SelectValue,
- SelectViewport,
- Svg
-} from "../ui";
-
-const SelectValueIcon = ({ theme }) => {
- const Icon = React.useMemo(() => {
- switch (theme) {
- case "dark":
- return IoMoonOutline;
- case "light":
- return IoSunnyOutline;
- case "system":
- return HiOutlineDesktopComputer;
- default:
- return HiOutlineDesktopComputer;
- }
- }, [theme]);
-
- return ;
-};
-
-const ThemeSelect = ({
- theme,
- setTheme,
- themes = [],
- ...other
-}: {
- theme: string;
- setTheme: (newThemeValue: string) => void;
- themes: string[];
- css?: CSS;
-}) => {
- return (
- setTheme(value)}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
- Theme
- {themes.map(theme => (
-
- {stringToTitleCase(theme)}
-
-
-
-
- ))}
-
-
-
-
-
-
-
- );
-};
-
-const ThemeSelector = ({ showLabel = false, ...other }: { showLabel?: boolean; css?: CSS }) => {
- const { theme, setTheme, themes } = useTheme();
-
- return (
-
- {showLabel ? Theme : null}
-
-
- );
-};
-
-export default ThemeSelector;
diff --git a/components/ui/alert.tsx b/components/ui/alert.tsx
deleted file mode 100644
index fd76e08..0000000
--- a/components/ui/alert.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import { WithChildren } from "@/types";
-import React from "react";
-import { IconType } from "react-icons";
-import {
- IoAlertCircleOutline,
- IoCheckmarkCircleOutline,
- IoInformationCircleOutline,
- IoWarningOutline
-} from "react-icons/io5";
-import { CSS, styled, VariantProps } from "stitches.config";
-import Svg from "./svg";
-import Text from "./text";
-
-const DEFAULT_TAG = "div";
-
-const StyledAlert = styled(DEFAULT_TAG, {
- // Reset
- boxSizing: "border-box",
- "&::before": {
- boxSizing: "border-box"
- },
- "&::after": {
- boxSizing: "border-box"
- },
-
- display: "flex",
- alignItems: "center",
- gap: "$3",
- borderRadius: "$2",
- backgroundColor: "$main",
- variants: {
- variant: {
- info: {
- boxShadow: "$alertInfo",
- [`& ${Text}`]: {
- color: "$text"
- }
- },
- success: {
- boxShadow: "$alertSuccess",
- [`& ${Text}`]: {
- color: "$green"
- }
- },
- warning: {
- boxShadow: "$alertWarning",
- [`& ${Text}`]: {
- color: "$red"
- }
- },
- error: {
- boxShadow: "$alertError",
- [`& ${Text}`]: {
- color: "$red"
- }
- }
- },
- padding: {
- small: {
- padding: "$3"
- },
- medium: {
- padding: "$4"
- },
- large: {
- padding: "$6"
- },
- xl: {
- padding: "$8"
- }
- }
- },
- defaultVariants: {
- variant: "info",
- padding: "small"
- }
-});
-
-type AlertVariants = "info" | "success" | "warning" | "error";
-type SvgVariants = Pick, "variant">;
-type AlertProps = Omit, "variant"> &
- WithChildren<{ variant?: AlertVariants; css?: CSS }>;
-
-const Alert = React.forwardRef, AlertProps>(function Alert(
- { children, variant = "info", ...other },
- ref
-) {
- const svgVariant: Record = {
- info: { "@initial": "text" },
- success: { "@initial": "green" },
- warning: { "@initial": "red" },
- error: { "@initial": "red" }
- };
-
- const svgIcon: Record = {
- info: IoInformationCircleOutline,
- success: IoCheckmarkCircleOutline,
- warning: IoWarningOutline,
- error: IoAlertCircleOutline
- };
-
- return (
-
-
- {children}
-
- );
-});
-
-Alert.displayName = "Alert";
-
-Alert.toString = () => `.${StyledAlert.className}`;
-
-export default Alert;
diff --git a/components/ui/appearInBox.tsx b/components/ui/appearInBox.tsx
deleted file mode 100644
index 626e0ba..0000000
--- a/components/ui/appearInBox.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { WithChildren } from "@/types";
-import { motion } from "framer-motion";
-import * as React from "react";
-import { CSS, styled, VariantProps } from "stitches.config";
-
-const Box = styled(motion.div, {
- // Reset
- boxSizing: "border-box"
-});
-
-type AppearInBoxProps = VariantProps &
- WithChildren<{
- appear: boolean;
- css?: CSS;
- }>;
-
-const AppearInBox = React.forwardRef, AppearInBoxProps>(
- ({ appear, ...other }, ref) => {
- return (
-
- );
- }
-);
-
-AppearInBox.displayName = "AppearInBox";
-
-export default AppearInBox;
diff --git a/components/ui/box.tsx b/components/ui/box.tsx
index 1442710..03c3422 100644
--- a/components/ui/box.tsx
+++ b/components/ui/box.tsx
@@ -1,8 +1,23 @@
-import { styled } from "stitches.config";
+import { cn } from "@/lib/utils";
+import { forwardRef } from "react";
-const Box = styled("div", {
- // Reset
- boxSizing: "border-box"
-});
+type BoxElementRef = React.ElementRef<"div">;
+type BoxProps = React.ComponentPropsWithoutRef<"div"> & {
+ children?: React.ReactNode;
+ className?: string;
+};
-export default Box;
+const Box = forwardRef(
+ ({ children, className, ...other }, forwardedRef) => {
+ return (
+
+ {children}
+
+ );
+ }
+);
+
+Box.displayName = "Box";
+
+export { Box };
+export type { BoxProps };
diff --git a/components/ui/button.tsx b/components/ui/button.tsx
index 4bc337f..8654462 100644
--- a/components/ui/button.tsx
+++ b/components/ui/button.tsx
@@ -1,376 +1,48 @@
-import { css, styled } from "stitches.config";
-import Svg from "./svg";
-import Text from "./text";
-
-export const buttonStyles = css({
- // Reset
- all: "unset",
- alignItems: "center",
- boxSizing: "border-box",
- userSelect: "none",
- "&::before": {
- boxSizing: "border-box"
- },
- "&::after": {
- boxSizing: "border-box"
- },
-
- // Custom reset?
- display: "inline-flex",
- flexShrink: 0,
- justifyContent: "center",
- lineHeight: "1",
- letterSpacing: "normal",
- WebkitTapHighlightColor: "rgba(0,0,0,0)",
- fontWeight: 400,
- // Custom
- cursor: "pointer",
- height: "$5",
- px: "$2",
- fontSize: "$3",
- transition: "all 0.2s ease-in-out",
-
- // fontVariantNumeric: "tabular-nums",
-
- "&:disabled": {
- opacity: "0.6",
- cursor: "not-allowed",
- pointerEvents: "none"
- },
-
- variants: {
- size: {
- 1: {
- borderRadius: "$2",
- height: "$5",
- px: "$2",
- fontSize: "$1",
- lineHeight: "$sizes$5"
- },
- 2: {
- borderRadius: "$2",
- height: "$6",
- px: "$4",
- fontSize: "$3",
- lineHeight: "$sizes$6"
- },
- 3: {
- borderRadius: "$2",
- height: "$7",
- px: "$4",
- fontSize: "$4",
- lineHeight: "$sizes$7"
+import { cva, VariantProps } from "class-variance-authority";
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2 dark:hover:bg-neutral-800 dark:hover:text-neutral-100 disabled:opacity-50 dark:focus:ring-neutral-400 disabled:pointer-events-none dark:focus:ring-offset-neutral-900 data-[state=open]:bg-neutral-100 dark:data-[state=open]:bg-neutral-800",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-neutral-900 text-white hover:bg-neutral-700 dark:bg-neutral-200 dark:text-neutral-900",
+ destructive: "bg-red-500 text-white hover:bg-red-600 dark:hover:bg-red-600",
+ outline:
+ "bg-transparent border border-neutral-200 hover:bg-neutral-100 dark:border-neutral-700 dark:text-neutral-100",
+ subtle:
+ "bg-neutral-100 text-neutral-900 hover:bg-neutral-200 dark:bg-neutral-700 dark:text-neutral-100",
+ ghost:
+ "bg-transparent hover:bg-neutral-100 dark:hover:bg-neutral-800 dark:text-neutral-100 dark:hover:text-neutral-100 data-[state=open]:bg-transparent dark:data-[state=open]:bg-transparent",
+ link: "bg-transparent underline-offset-4 hover:underline text-neutral-900 dark:text-neutral-100 hover:bg-transparent dark:hover:bg-transparent"
+ },
+ size: {
+ default: "h-10 py-2 px-4",
+ sm: "h-9 px-2 rounded-md",
+ lg: "h-11 px-8 rounded-md"
}
},
- variant: {
- green: {
- backgroundColor: "$green",
- border: "2px solid $colors$green",
- color: "$black",
- [`${Text}`]: {
- color: "$black"
- },
- [`${Svg}`]: {
- color: "$black"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "$greenDark",
- border: "2px solid $colors$greenDark"
- }
- },
- "&:active": {
- backgroundColor: "$green",
- border: "2px solid $colors$greenDark"
- },
- "&:focus": {
- backgroundColor: "$green",
- border: "2px solid $colors$greenDark"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- red: {
- backgroundColor: "$red",
- border: "2px solid $colors$red",
- color: "$black",
- [`${Text}`]: {
- color: "$black"
- },
- [`${Svg}`]: {
- color: "$black"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "$redDark",
- border: "2px solid $colors$redDark"
- }
- },
- "&:active": {
- backgroundColor: "$red",
- border: "2px solid $colors$redDark"
- },
- "&:focus": {
- backgroundColor: "$red",
- border: "2px solid $colors$redDark"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- white: {
- backgroundColor: "$white",
- border: "2px solid $colors$white",
- color: "$black",
- [`${Text}`]: {
- color: "$black"
- },
- [`${Svg}`]: {
- color: "$black"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "$white",
- border: "2px solid $colors$gray"
- }
- },
- "&:active": {
- backgroundColor: "$white",
- border: "2px solid $colors$grayDark"
- },
- "&:focus": {
- backgroundColor: "$white",
- border: "2px solid $colors$grayDark"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- black: {
- backgroundColor: "$black",
- border: "2px solid $colors$black",
- color: "$white",
- [`${Text}`]: {
- color: "$white"
- },
- [`${Svg}`]: {
- color: "$white"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "$black",
- border: "2px solid $colors$grayDark"
- }
- },
- "&:active": {
- backgroundColor: "$black",
- border: "2px solid $colors$white"
- },
- "&:focus": {
- backgroundColor: "$black",
- border: "2px solid $colors$white"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- text: {
- backgroundColor: "transparent",
- border: "2px solid transparent",
- color: "$text",
- [`${Text}`]: {
- color: "$text"
- },
- [`${Svg}`]: {
- color: "$text"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "transparent",
- border: "2px solid $colors$textDark"
- }
- },
- "&:active": {
- backgroundColor: "transparent",
- border: "2px solid $colors$text"
- },
- "&:focus": {
- backgroundColor: "transparent",
- border: "2px solid $colors$text"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- textDark: {
- backgroundColor: "transparent",
- border: "2px solid transparent",
- color: "$textDark",
- [`${Text}`]: {
- color: "$textDark"
- },
- [`${Svg}`]: {
- color: "$textDark"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "transparent",
- border: "2px solid $colors$textDark"
- }
- },
- "&:active": {
- backgroundColor: "transparent",
- border: "2px solid $colors$text"
- },
- "&:focus": {
- backgroundColor: "transparent",
- border: "2px solid $colors$text"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- }
- },
-
- state: {
- active: {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight",
- color: "$grayLight",
- "@hover": {
- "&:hover": {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- "&:active": {
- backgroundColor: "$grayLight"
- },
- "&:focus": {
- boxShadow: "inset 0 0 0 1px $grayLight, 0 0 0 1px $grayLight"
- }
- },
- waiting: {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $",
- color: "transparent",
- pointerEvents: "none",
- "@hover": {
- "&:hover": {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- "&:active": {
- backgroundColor: "$grayLight"
- },
- "&:focus": {
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- }
- },
- ghost: {
- true: {
- backgroundColor: "transparent",
- boxShadow: "none"
- }
- },
- fullWidth: {
- true: {
- width: "100%"
- }
- }
- },
- compoundVariants: [
- {
- ghost: true,
- variant: "green",
- css: {
- backgroundColor: "transparent",
- border: "2px solid transparent",
- color: "$green",
- "@hover": {
- "&:hover": {
- backgroundColor: "transparent",
- border: "2px solid $colors$greenDark"
- }
- },
- "&:active": {
- backgroundColor: "transparent",
- border: "2px solid $colors$green"
- },
- "&:focus": {
- backgroundColor: "transparent",
- border: "2px solid $colors$green"
- }
- }
- },
- {
- ghost: true,
- variant: "red",
- css: {
- backgroundColor: "transparent",
- border: "2px solid transparent",
- color: "$red",
- "@hover": {
- "&:hover": {
- backgroundColor: "transparent",
- border: "2px solid $colors$redDark"
- }
- },
- "&:active": {
- backgroundColor: "transparent",
- border: "2px solid $colors$red"
- },
- "&:focus": {
- backgroundColor: "transparent",
- border: "2px solid $colors$red"
- }
- }
- },
- {
- ghost: true,
- variant: "black",
- css: {
- backgroundColor: "transparent",
- border: "2px solid transparent",
- color: "$white",
- "@hover": {
- "&:hover": {
- backgroundColor: "transparent",
- border: "2px solid $colors$grayLightest"
- }
- },
- "&:active": {
- backgroundColor: "transparent",
- border: "2px solid $colors$gray"
- },
- "&:focus": {
- backgroundColor: "transparent",
- border: "2px solid $colors$gray"
- }
- }
+ defaultVariants: {
+ variant: "default",
+ size: "default"
}
- ],
- defaultVariants: {
- size: "2",
- variant: "green"
}
-});
+);
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {}
-const Button = styled("button", buttonStyles);
+const Button = React.forwardRef(
+ ({ className, variant, size, ...props }, ref) => {
+ return (
+
+ );
+ }
+);
+Button.displayName = "Button";
-export default Button;
+export { Button, buttonVariants };
diff --git a/components/ui/card.tsx b/components/ui/card.tsx
index eb19c7d..7d32b6a 100644
--- a/components/ui/card.tsx
+++ b/components/ui/card.tsx
@@ -1,55 +1,71 @@
-import { styled } from "stitches.config";
-
-const Card = styled("div", {
- appearance: "none",
- boxShadow: "$card",
- boxSizing: "border-box",
- font: "inherit",
- lineHeight: "1",
- userSelect: "none",
- outline: "none",
- padding: 0,
- WebkitTapHighlightColor: "rgba(0, 0, 0, 0)",
- backgroundColor: "$main",
- display: "block",
- textDecoration: "none",
- color: "inherit",
- flexShrink: 0,
- borderRadius: "$2",
- position: "relative",
-
- variants: {
- padding: {
- small: {
- padding: "$3"
- },
- medium: {
- padding: "$4"
- },
- large: {
- padding: "$6"
- },
- xl: {
- padding: "$8"
- }
- },
- fullWidth: {
- true: {
- width: "100%"
- }
- },
- ghost: {
- true: {
- backgroundColor: "transparent",
- boxShadow: "0px 24px 32px rgba(0, 0, 0, 0.08)",
- borderColor: "transparent",
- borderRadius: "6px"
- }
- }
- },
- defaultVariants: {
- padding: "small"
- }
-});
-
-export default Card;
+import { Skeleton } from "@/components/ui/skeleton";
+import { cn } from "@/lib/utils";
+
+interface CardProps extends React.HTMLAttributes {}
+
+export function Card({ className, ...props }: CardProps) {
+ return (
+
+ );
+}
+
+interface CardHeaderProps extends React.HTMLAttributes {}
+
+Card.Header = function CardHeader({ className, ...props }: CardHeaderProps) {
+ return
;
+};
+
+interface CardContentProps extends React.HTMLAttributes {}
+
+Card.Content = function CardContent({ className, ...props }: CardContentProps) {
+ return
;
+};
+
+interface CardFooterProps extends React.HTMLAttributes {}
+
+Card.Footer = function CardFooter({ className, ...props }: CardFooterProps) {
+ return (
+
+ );
+};
+
+interface CardTitleProps extends React.HTMLAttributes {}
+
+Card.Title = function CardTitle({ className, ...props }: CardTitleProps) {
+ return ;
+};
+
+interface CardDescriptionProps extends React.HTMLAttributes {}
+
+Card.Description = function CardDescription({ className, ...props }: CardDescriptionProps) {
+ return (
+
+ );
+};
+
+Card.Skeleton = function CardSeleton() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/components/ui/checkbox.tsx b/components/ui/checkbox.tsx
index c41fe17..562796a 100644
--- a/components/ui/checkbox.tsx
+++ b/components/ui/checkbox.tsx
@@ -1,23 +1,28 @@
+"use client";
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
-import { styled } from "stitches.config";
+import * as React from "react";
-const Checkbox = styled(CheckboxPrimitive.Root, {
- all: "unset",
- backgroundColor: "$main",
- width: "$4",
- height: "$4",
- borderRadius: "$2",
- display: "flex",
- alignItems: "center",
- justifyContent: "center",
- boxShadow: `$input`,
- "&:hover": { boxShadow: "$inputHover" },
- "&:focus": { boxShadow: "$inputFocus" }
-});
+import { cn } from "@/lib/utils";
+import { Icons } from "../icons";
-const CheckboxIndicator = styled(CheckboxPrimitive.Indicator, {
- color: "$secondary"
-});
+const Checkbox = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+));
+Checkbox.displayName = CheckboxPrimitive.Root.displayName;
-export { CheckboxIndicator };
-export default Checkbox;
+export { Checkbox };
diff --git a/components/ui/container.tsx b/components/ui/container.tsx
deleted file mode 100644
index ed541db..0000000
--- a/components/ui/container.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { styled } from "stitches.config";
-
-const Container = styled("div", {
- // Reset
- boxSizing: "border-box",
- flexShrink: 0,
- width: "100%",
-
- variants: {
- size: {
- 1: {
- maxWidth: "$smContainer"
- },
- 2: {
- maxWidth: "$mdContainer"
- },
- 3: {
- maxWidth: "$lgContainer"
- },
- 4: {
- maxWidth: "$xlContainer"
- },
- full: {
- maxWidth: "none"
- }
- },
- backgroundColor: {
- main: {
- backgroundColor: "$main"
- },
- secondary: {
- backgroundColor: "$secondary"
- },
- grayDark: {
- backgroundColor: "$grayDark"
- },
- gray: {
- backgroundColor: "$gray"
- },
- grayLight: {
- backgroundColor: "$grayLight"
- },
- grayLighter: {
- backgroundColor: "$grayLighter"
- },
- grayLightest: {
- backgroundColor: "$grayLightest"
- }
- },
- center: {
- true: {
- margin: "0 auto"
- }
- }
- },
- defaultVariants: {
- size: "full"
- }
-});
-
-export default Container;
diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx
index 66af54a..39109fc 100644
--- a/components/ui/dialog.tsx
+++ b/components/ui/dialog.tsx
@@ -1,167 +1,107 @@
-import * as DialogPrimitive from "@radix-ui/react-dialog";
-import React from "react";
-import { IoClose } from "react-icons/io5";
-import { CSS, styled, VariantProps } from "stitches.config";
-import IconButton from "./iconButton";
-import { fadeIn, fadeOut } from "./keyframes";
-import { overlayStyles } from "./overlay";
-import { panelStyles } from "./panel";
-import Svg from "./svg";
+"use client";
-type DialogProps = React.ComponentProps & {
- children: React.ReactNode;
- overlayProps?: {
- enabled?: boolean;
- css?: CSS;
- variants?: VariantProps;
- };
-};
+import * as DialogPrimitive from "@radix-ui/react-dialog";
+import * as React from "react";
-export const DialogNonRemoveScrollOverlay = styled("div", overlayStyles, {
- position: "fixed",
- top: 0,
- right: 0,
- bottom: 0,
- left: 0,
- '&[data-state="open"]': {
- animation: `${fadeIn} 500ms forwards`
- },
- '&[data-state="closed"]': {
- animation: `${fadeOut} 500ms forwards`
- },
- variants: {
- variant: {
- absolute: {
- position: "absolute"
- }
- }
- }
-});
+import { cn } from "@/lib/utils";
+import { Icons } from "../icons";
-const StyledOverlay = styled(DialogPrimitive.Overlay, overlayStyles, {
- position: "fixed",
- top: 0,
- right: 0,
- bottom: 0,
- left: 0,
- '&[data-state="open"]': {
- animation: `${fadeIn} 500ms forwards`
- },
- '&[data-state="closed"]': {
- animation: `${fadeOut} 500ms forwards`
- },
- variants: {
- variant: {
- absolute: {
- position: "absolute"
- }
- }
- }
-});
+const Dialog = DialogPrimitive.Root;
-export function Dialog({
- children,
- overlayProps = {
- enabled: true,
- css: {},
- variants: {}
- },
- ...props
-}: DialogProps) {
- const {
- enabled: overlayEnabled = false,
- variants: overlayVariants,
- ...otherOverlayProps
- } = overlayProps;
+const DialogTrigger = DialogPrimitive.Trigger;
- return (
-
- {overlayEnabled ? : null}
+const DialogPortal = ({ className, children, ...props }: DialogPrimitive.DialogPortalProps) => (
+
+
{children}
-
- );
-}
-
-const StyledContent = styled(DialogPrimitive.Content, panelStyles, {
- position: "fixed",
- top: "50%",
- left: "50%",
- transform: "translate(-50%, -50%)",
- minWidth: 200,
- maxWidth: "95vw",
- maxHeight: "85vh",
- padding: "$5",
- marginTop: "-5vh",
- willChange: "transform",
- border: "1px sold white",
- "&:focus": {
- outline: "none"
- },
- '&[data-state="open"]': {
- animation: `${fadeIn} 500ms forwards`
- },
- '&[data-state="closed"]': {
- animation: `${fadeOut} 500ms forwards`
- },
- variants: {
- variant: {
- absolute: {
- position: "absolute"
- }
- }
- }
-});
+
+
+);
+DialogPortal.displayName = DialogPrimitive.Portal.displayName;
-const StyledCloseButton = styled(DialogPrimitive.Close, {
- position: "absolute",
- top: "$5",
- right: "$5"
-});
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
-const StyledDialogTitle = styled(DialogPrimitive.Title, {
- fontSize: "$5",
- marginBottom: "$3",
- "@bp1": {
- fontSize: "$6"
- }
-});
-
-const StyledDialogDescription = styled(DialogPrimitive.Description, {
- fontSize: "$3",
- marginBottom: "$3"
-});
-
-type DialogVariantVariants = "absolute";
-type DialogVariants = Pick, "variant">;
-type DialogContentPrimitiveProps = React.ComponentProps;
-type DialogContentProps = DialogContentPrimitiveProps &
- DialogVariants & { variant?: DialogVariantVariants; css?: CSS; portal?: boolean };
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+));
+DialogContent.displayName = DialogPrimitive.Content.displayName;
-export const DialogContent = React.forwardRef<
- React.ElementRef,
- DialogContentProps
->(({ children, ...props }, forwardedRef) => {
- const { variant, ...other } = props;
+const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => (
+
+);
+DialogHeader.displayName = "DialogHeader";
- const DialogWrapper = props.portal ? DialogPrimitive.Portal : React.Fragment;
+const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
+
+);
+DialogFooter.displayName = "DialogFooter";
- return (
-
-
- {children}
-
-
-
-
-
-
-
- );
-});
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogTitle.displayName = DialogPrimitive.Title.displayName;
-DialogContent.displayName = "DialogContent";
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogDescription.displayName = DialogPrimitive.Description.displayName;
-export const DialogTrigger = DialogPrimitive.Trigger;
-export const DialogClose = DialogPrimitive.Close;
-export const DialogTitle = StyledDialogTitle;
-export const DialogDescription = StyledDialogDescription;
+export {
+ Dialog,
+ DialogTrigger,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription
+};
diff --git a/components/ui/expandingHelperText.tsx b/components/ui/expandingHelperText.tsx
deleted file mode 100644
index d204dc3..0000000
--- a/components/ui/expandingHelperText.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { motion } from "framer-motion";
-import * as React from "react";
-import { styled } from "stitches.config";
-import Box from "./box";
-
-export const HelperText = styled(Box, {});
-
-type ExpandingHelperTextProps = {
- expanded: boolean;
- text?: string;
-};
-
-const ExpandingHelperText = ({ text, expanded, ...other }: ExpandingHelperTextProps) => {
- return (
-
- {expanded ? text :
}
-
- );
-};
-
-export default ExpandingHelperText;
diff --git a/components/ui/fieldContainer.tsx b/components/ui/fieldContainer.tsx
deleted file mode 100644
index cb319fa..0000000
--- a/components/ui/fieldContainer.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { WithChildren } from "@/types";
-import * as React from "react";
-import { CSS, styled } from "stitches.config";
-
-type FieldContainerProps = WithChildren<{
- hidden?: boolean;
- css?: CSS;
-}>;
-
-const FieldContainerRoot = styled("div", {
- display: "flex",
- flexDirection: "column",
- mb: "$4",
- width: "100%",
- position: "relative",
- variants: {
- hidden: {
- true: {
- display: "none"
- }
- }
- }
-});
-
-const FieldContainer = ({ children, hidden = false, ...other }: FieldContainerProps) => {
- return (
-
- {children}
-
- );
-};
-
-export default FieldContainer;
diff --git a/components/ui/fieldset.tsx b/components/ui/fieldset.tsx
deleted file mode 100644
index 612e59f..0000000
--- a/components/ui/fieldset.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { styled } from "stitches.config";
-
-const Fieldset = styled("fieldset", {
- all: "unset",
- display: "flex",
- gap: 20,
- alignItems: "center"
-});
-
-export default Fieldset;
diff --git a/components/ui/flex-item.tsx b/components/ui/flex-item.tsx
new file mode 100644
index 0000000..51e874e
--- /dev/null
+++ b/components/ui/flex-item.tsx
@@ -0,0 +1,22 @@
+import { forwardRef } from "react";
+import { Flex } from "./flex";
+
+type FlexItemElementRef = React.ElementRef;
+type FlexItemProps = React.ComponentPropsWithoutRef & {
+ children?: React.ReactNode;
+};
+
+const FlexItem = forwardRef(
+ ({ children, direction = "column", ...other }, forwardedRef) => {
+ return (
+
+ {children}
+
+ );
+ }
+);
+
+FlexItem.displayName = "FlexItem";
+
+export { FlexItem };
+export type { FlexItemProps };
diff --git a/components/ui/flex.tsx b/components/ui/flex.tsx
index 3882033..87c21de 100644
--- a/components/ui/flex.tsx
+++ b/components/ui/flex.tsx
@@ -1,116 +1,130 @@
-import { styled } from "stitches.config";
+import { cn } from "@/lib/utils";
+import { forwardRef } from "react";
-const Flex = styled("div", {
- boxSizing: "border-box",
- display: "flex",
+type FlexElementRef = React.ElementRef<"div">;
+type FlexProps = React.ComponentPropsWithoutRef<"div"> & {
+ children?: React.ReactNode;
+ className?: string;
+ direction?: "row" | "column" | "rowReverse" | "columnReverse";
+ align?: "start" | "center" | "end" | "stretch" | "baseline";
+ justify?: "start" | "center" | "end" | "between" | "even";
+ wrap?: "no-wrap" | "wrap" | "wrap-reverse";
+ grow?: "grow" | "no-grow";
+ gap?:
+ | "0"
+ | "1"
+ | "2"
+ | "3"
+ | "4"
+ | "5"
+ | "6"
+ | "7"
+ | "8"
+ | "9"
+ | "10"
+ | "11"
+ | "12"
+ | "14"
+ | "16"
+ | "20"
+ | "24"
+ | "28"
+ | "32"
+ | "36"
+ | "40"
+ | "44"
+ | "48"
+ | "52"
+ | "56"
+ | "60"
+ | "64"
+ | "72"
+ | "80"
+ | "96";
+};
- variants: {
- direction: {
- row: {
- flexDirection: "row"
- },
- column: {
- flexDirection: "column"
- },
- rowReverse: {
- flexDirection: "row-reverse"
- },
- columnReverse: {
- flexDirection: "column-reverse"
- }
+const Flex = forwardRef(
+ (
+ {
+ children,
+ className,
+ direction = "row",
+ align = "stretch",
+ justify = "start",
+ wrap = "no-wrap",
+ grow,
+ gap,
+ ...other
},
- alignItems: {
- start: {
- alignItems: "flex-start"
- },
- center: {
- alignItems: "center"
- },
- end: {
- alignItems: "flex-end"
- },
- stretch: {
- alignItems: "stretch"
- },
- baseline: {
- alignItems: "baseline"
- }
- },
- justifyContent: {
- start: {
- justifyContent: "flex-start"
- },
- center: {
- justifyContent: "center"
- },
- end: {
- justifyContent: "flex-end"
- },
- between: {
- justifyContent: "space-between"
- },
- even: {
- justifyContent: "space-evenly"
- }
- },
- wrap: {
- noWrap: {
- flexWrap: "nowrap"
- },
- wrap: {
- flexWrap: "wrap"
- },
- wrapReverse: {
- flexWrap: "wrap-reverse"
- }
- },
- grow: {
- grow: {
- flexGrow: "1"
- },
- noGrow: {
- flexGrow: "revert"
- }
- },
- gap: {
- 0: {
- gap: "0"
- },
- 1: {
- gap: "$1"
- },
- 2: {
- gap: "$2"
- },
- 3: {
- gap: "$3"
- },
- 4: {
- gap: "$4"
- },
- 5: {
- gap: "$5"
- },
- 6: {
- gap: "$6"
- },
- 7: {
- gap: "$7"
- },
- 8: {
- gap: "$8"
- },
- 9: {
- gap: "$9"
- }
- }
- },
- defaultVariants: {
- direction: "row",
- alignItems: "stretch",
- justifyContent: "start",
- wrap: "noWrap"
+ forwardedRef
+ ) => {
+ return (
+
+ {children}
+
+ );
}
-});
+);
+
+Flex.displayName = "Flex";
-export default Flex;
+export { Flex };
+export type { FlexProps };
diff --git a/components/ui/flexItem.tsx b/components/ui/flexItem.tsx
deleted file mode 100644
index c30e719..0000000
--- a/components/ui/flexItem.tsx
+++ /dev/null
@@ -1,111 +0,0 @@
-import { styled } from "stitches.config";
-import Flex from "./flex";
-
-const FlexItem = styled(Flex, {
- boxSizing: "border-box",
- display: "flex",
-
- variants: {
- direction: {
- row: {
- flexDirection: "row"
- },
- column: {
- flexDirection: "column"
- },
- rowReverse: {
- flexDirection: "row-reverse"
- },
- columnReverse: {
- flexDirection: "column-reverse"
- }
- },
- align: {
- start: {
- alignItems: "flex-start"
- },
- center: {
- alignItems: "center"
- },
- end: {
- alignItems: "flex-end"
- },
- stretch: {
- alignItems: "stretch"
- },
- baseline: {
- alignItems: "baseline"
- }
- },
- justify: {
- start: {
- justifyContent: "flex-start"
- },
- center: {
- justifyContent: "center"
- },
- end: {
- justifyContent: "flex-end"
- },
- between: {
- justifyContent: "space-between"
- },
- even: {
- justifyContent: "space-evenly"
- }
- },
- wrap: {
- noWrap: {
- flexWrap: "nowrap"
- },
- wrap: {
- flexWrap: "wrap"
- },
- wrapReverse: {
- flexWrap: "wrap-reverse"
- }
- },
- grow: {
- grow: {
- flexGrow: "1"
- },
- noGrow: {
- flexGrow: "revert"
- }
- },
- gap: {
- 1: {
- gap: "$1"
- },
- 2: {
- gap: "$2"
- },
- 3: {
- gap: "$3"
- },
- 4: {
- gap: "$4"
- },
- 5: {
- gap: "$5"
- },
- 6: {
- gap: "$6"
- },
- 7: {
- gap: "$7"
- },
- 8: {
- gap: "$8"
- },
- 9: {
- gap: "$9"
- }
- }
- },
- defaultVariants: {
- direction: "column"
- }
-});
-
-export default FlexItem;
diff --git a/components/ui/footer.tsx b/components/ui/footer.tsx
deleted file mode 100644
index ce1f535..0000000
--- a/components/ui/footer.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { styled } from "stitches.config";
-
-const Footer = styled("footer", {
- // Reset
- boxSizing: "border-box",
- flexShrink: 0,
- backgroundColor: "$main",
- padding: "0 0 $12",
-
- variants: {
- variant: {
- main: {
- borderTop: "1px solid $colors$gray"
- }
- }
- }
-});
-
-export default Footer;
diff --git a/components/ui/form.tsx b/components/ui/form.tsx
deleted file mode 100644
index 9ba3c5e..0000000
--- a/components/ui/form.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import { styled } from "stitches.config";
-
-const Form = styled("form", {
- all: "unset"
-});
-
-export default Form;
diff --git a/components/ui/grid.tsx b/components/ui/grid.tsx
deleted file mode 100644
index b578d80..0000000
--- a/components/ui/grid.tsx
+++ /dev/null
@@ -1,184 +0,0 @@
-import { styled } from "stitches.config";
-
-const Grid = styled("div", {
- boxSizing: "border-box",
- display: "grid",
-
- variants: {
- alignItems: {
- start: {
- alignItems: "start"
- },
- center: {
- alignItems: "center"
- },
- end: {
- alignItems: "end"
- },
- stretch: {
- alignItems: "stretch"
- },
- baseline: {
- alignItems: "baseline"
- }
- },
- justifyContent: {
- start: {
- justifyContent: "start"
- },
- center: {
- justifyContent: "center"
- },
- end: {
- justifyContent: "end"
- },
- between: {
- justifyContent: "space-between"
- }
- },
- gridAutoFlow: {
- row: {
- gridAutoFlow: "row"
- },
- column: {
- gridAutoFlow: "column"
- },
- dense: {
- gridAutoFlow: "dense"
- },
- rowDense: {
- gridAutoFlow: "row dense"
- },
- columnDense: {
- gridAutoFlow: "column dense"
- }
- },
- gridTemplateColumns: {
- 1: {
- gridTemplateColumns: "repeat(1, 1fr)"
- },
- 2: {
- gridTemplateColumns: "repeat(2, 1fr)"
- },
- 3: {
- gridTemplateColumns: "repeat(3, 1fr)"
- },
- 4: {
- gridTemplateColumns: "repeat(4, 1fr)"
- },
- 5: {
- gridTemplateColumns: "repeat(5, 1fr)"
- },
- 6: {
- gridTemplateColumns: "repeat(6, 1fr)"
- },
- 7: {
- gridTemplateColumns: "repeat(7, 1fr)"
- },
- 8: {
- gridTemplateColumns: "repeat(8, 1fr)"
- },
- 9: {
- gridTemplateColumns: "repeat(9, 1fr)"
- },
- 10: {
- gridTemplateColumns: "repeat(10, 1fr)"
- },
- 11: {
- gridTemplateColumns: "repeat(11, 1fr)"
- },
- 12: {
- gridTemplateColumns: "repeat(12, 1fr)"
- }
- },
- gap: {
- 1: {
- gap: "$1"
- },
- 2: {
- gap: "$2"
- },
- 3: {
- gap: "$3"
- },
- 4: {
- gap: "$4"
- },
- 5: {
- gap: "$5"
- },
- 6: {
- gap: "$6"
- },
- 7: {
- gap: "$7"
- },
- 8: {
- gap: "$8"
- },
- 9: {
- gap: "$9"
- }
- },
- gapX: {
- 1: {
- columnGap: "$1"
- },
- 2: {
- columnGap: "$2"
- },
- 3: {
- columnGap: "$3"
- },
- 4: {
- columnGap: "$4"
- },
- 5: {
- columnGap: "$5"
- },
- 6: {
- columnGap: "$6"
- },
- 7: {
- columnGap: "$7"
- },
- 8: {
- columnGap: "$8"
- },
- 9: {
- columnGap: "$9"
- }
- },
- gapY: {
- 1: {
- rowGap: "$1"
- },
- 2: {
- rowGap: "$2"
- },
- 3: {
- rowGap: "$3"
- },
- 4: {
- rowGap: "$4"
- },
- 5: {
- rowGap: "$5"
- },
- 6: {
- rowGap: "$6"
- },
- 7: {
- rowGap: "$7"
- },
- 8: {
- rowGap: "$8"
- },
- 9: {
- rowGap: "$9"
- }
- }
- }
-});
-
-export default Grid;
diff --git a/components/ui/heading.tsx b/components/ui/heading.tsx
deleted file mode 100644
index 2ad2cfa..0000000
--- a/components/ui/heading.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import merge from "lodash.merge";
-import React from "react";
-import { CSS, VariantProps } from "stitches.config";
-import Text from "./text";
-
-const DEFAULT_TAG = "h1";
-
-type TextSizeVariants = Pick, "size">;
-type HeadingSizeVariants = "1" | "2" | "3" | "4" | "5";
-type HeadingVariants = { size?: HeadingSizeVariants; noMargin?: boolean } & Omit<
- VariantProps,
- "size"
->;
-export type HeadingProps = React.ComponentProps &
- HeadingVariants & { css?: CSS; as?: any };
-
-const Heading = React.forwardRef, HeadingProps>(
- (props, forwardedRef) => {
- // '2' here is the default heading size variant
- const { size = "1", noMargin = false, ...textProps } = props;
- // This is the mapping of Heading Variants to Text variants
- const textSize: Record = {
- 1: { "@initial": "4", "@bp2": "5" },
- 2: { "@initial": "5", "@bp2": "6" },
- 3: { "@initial": "6", "@bp2": "7" },
- 4: { "@initial": "7", "@bp2": "8" },
- 5: { "@initial": "8", "@bp2": "9" }
- };
-
- // This is the mapping of Heading Variants to Text css
- const textCss: Record = {
- 1: { fontWeight: "bold", lineHeight: "130%", marginBottom: noMargin ? 0 : "$2" },
- 2: { fontWeight: "bold", lineHeight: "130%", marginBottom: noMargin ? 0 : "$2" },
- 3: { fontWeight: "bold", lineHeight: "130%", marginBottom: noMargin ? 0 : "$3" },
- 4: { fontWeight: "bold", lineHeight: "120%", marginBottom: noMargin ? 0 : "$4" },
- 5: { fontWeight: "bold", lineHeight: "122%", marginBottom: noMargin ? 0 : "$5" }
- };
-
- return (
-
- );
- }
-);
-
-Heading.displayName = "Heading";
-
-Heading.toString = () => ".heading";
-
-export default Heading;
diff --git a/components/ui/hover-card.tsx b/components/ui/hover-card.tsx
new file mode 100644
index 0000000..b977d8e
--- /dev/null
+++ b/components/ui/hover-card.tsx
@@ -0,0 +1,32 @@
+"use client";
+
+import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
+
+const HoverCard = HoverCardPrimitive.Root;
+
+const HoverCardTrigger = HoverCardPrimitive.Trigger;
+
+const HoverCardContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ children, className, align = "center", sideOffset = 4, ...props }, ref) => (
+
+ {children}
+
+
+));
+HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
+
+export { HoverCard, HoverCardTrigger, HoverCardContent };
diff --git a/components/ui/iconButton.tsx b/components/ui/iconButton.tsx
deleted file mode 100644
index 56b9dbb..0000000
--- a/components/ui/iconButton.tsx
+++ /dev/null
@@ -1,290 +0,0 @@
-import { styled } from "stitches.config";
-import Svg from "./svg";
-import Text from "./text";
-
-const IconButton = styled("button", {
- // Reset
- alignItems: "center",
- appearance: "none",
- borderWidth: "0",
- boxSizing: "border-box",
- display: "inline-flex",
- flexShrink: 0,
- fontFamily: "inherit",
- fontSize: "14px",
- justifyContent: "center",
- lineHeight: "1",
- outline: "none",
- padding: "0",
- textDecoration: "none",
- userSelect: "none",
- WebkitTapHighlightColor: "transparent",
- cursor: "pointer",
- color: "$black",
- "&::before": {
- boxSizing: "border-box"
- },
- "&::after": {
- boxSizing: "border-box"
- },
- backgroundColor: "$transparent",
- border: "1px solid $transparent",
-
- "@hover": {
- "&:hover": {
- borderColor: "$grayLight"
- }
- },
- "&:active": {
- backgroundColor: "$grayLight"
- },
- "&:focus": {
- borderColor: "$grayLight",
- boxShadow: "0 0 0 1px $colors$grayLight"
- },
- "&:disabled": {
- pointerEvents: "none",
- backgroundColor: "transparent",
- color: "$grayLight"
- },
-
- variants: {
- variant: {
- green: {
- backgroundColor: "$green",
- border: "2px solid $colors$green",
- color: "$black",
- [`${Text}`]: {
- color: "$black"
- },
- [`${Svg}`]: {
- color: "$black"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "$greenDark",
- border: "2px solid $colors$greenDark"
- }
- },
- "&:active": {
- backgroundColor: "$green",
- border: "2px solid $colors$greenDark"
- },
- "&:focus": {
- backgroundColor: "$green",
- border: "2px solid $colors$greenDark"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- red: {
- backgroundColor: "$red",
- border: "2px solid $colors$red",
- color: "$black",
- [`${Text}`]: {
- color: "$black"
- },
- [`${Svg}`]: {
- color: "$black"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "$redDark",
- border: "2px solid $colors$redDark"
- }
- },
- "&:active": {
- backgroundColor: "$red",
- border: "2px solid $colors$redDark"
- },
- "&:focus": {
- backgroundColor: "$red",
- border: "2px solid $colors$redDark"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- white: {
- backgroundColor: "$white",
- border: "2px solid $colors$white",
- color: "$black",
- [`${Text}`]: {
- color: "$black"
- },
- [`${Svg}`]: {
- color: "$black"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "$white",
- border: "2px solid $colors$gray"
- }
- },
- "&:active": {
- backgroundColor: "$white",
- border: "2px solid $colors$grayDark"
- },
- "&:focus": {
- backgroundColor: "$white",
- border: "2px solid $colors$grayDark"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- black: {
- backgroundColor: "$black",
- border: "2px solid $colors$black",
- color: "$white",
- [`${Text}`]: {
- color: "$white"
- },
- [`${Svg}`]: {
- color: "$white"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "$black",
- border: "2px solid $colors$grayDark"
- }
- },
- "&:active": {
- backgroundColor: "$black",
- border: "2px solid $colors$white"
- },
- "&:focus": {
- backgroundColor: "$black",
- border: "2px solid $colors$white"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- text: {
- backgroundColor: "transparent",
- border: "2px solid transparent",
- color: "$text",
- [`${Text}`]: {
- color: "$text"
- },
- [`${Svg}`]: {
- color: "$text"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "transparent",
- border: "2px solid $colors$textDark"
- }
- },
- "&:active": {
- backgroundColor: "transparent",
- border: "2px solid $colors$text"
- },
- "&:focus": {
- backgroundColor: "transparent",
- border: "2px solid $colors$text"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- textDark: {
- backgroundColor: "transparent",
- border: "2px solid transparent",
- color: "$textDark",
- [`${Text}`]: {
- color: "$textDark"
- },
- [`${Svg}`]: {
- color: "$textDark"
- },
- "@hover": {
- "&:hover": {
- backgroundColor: "transparent",
- border: "2px solid $colors$textDark"
- }
- },
- "&:active": {
- backgroundColor: "transparent",
- border: "2px solid $colors$textDark"
- },
- "&:focus": {
- backgroundColor: "transparent",
- border: "2px solid $colors$textDark"
- },
- '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
- {
- backgroundColor: "$grayLight",
- boxShadow: "inset 0 0 0 1px $grayLight"
- }
- },
- ghost: {
- backgroundColor: "transparent",
- border: "none",
- "@hover": {
- "&:hover": {
- backgroundColor: "transparent",
- border: "none"
- }
- },
- "&:active": {
- backgroundColor: "transparent",
- border: "none"
- },
- "&:focus": {
- backgroundColor: "transparent",
- border: "none"
- }
- }
- },
- size: {
- info: {
- borderRadius: "$2",
- height: "$4",
- width: "$4"
- },
- 1: {
- borderRadius: "$2",
- height: "$5",
- width: "$5"
- },
- 2: {
- borderRadius: "$2",
- height: "$6",
- width: "$6"
- },
- 3: {
- borderRadius: "$2",
- height: "$7",
- width: "$7"
- },
- 4: {
- borderRadius: "$3",
- height: "$8",
- width: "$8"
- }
- },
- round: {
- true: {
- borderRadius: "$pill"
- }
- }
- },
- defaultVariants: {
- size: "1"
- }
-});
-
-export default IconButton;
diff --git a/components/ui/image.tsx b/components/ui/image.tsx
deleted file mode 100644
index 75fb2a3..0000000
--- a/components/ui/image.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import NextImage from "next/image";
-import { styled } from "stitches.config";
-
-const Image = styled(NextImage, {});
-
-export default Image;
diff --git a/components/ui/index.ts b/components/ui/index.ts
deleted file mode 100644
index 700df61..0000000
--- a/components/ui/index.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-import Alert from "./alert";
-import AppearInBox from "./appearInBox";
-import Box from "./box";
-import Button from "./button";
-import Card from "./card";
-import Checkbox, { CheckboxIndicator } from "./checkbox";
-import Container from "./container";
-import {
- Dialog,
- DialogClose,
- DialogContent,
- DialogDescription,
- DialogNonRemoveScrollOverlay,
- DialogTitle,
- DialogTrigger
-} from "./dialog";
-import ExpandingHelperText from "./expandingHelperText";
-import FieldContainer from "./fieldContainer";
-import Fieldset from "./fieldset";
-import Flex from "./flex";
-import FlexItem from "./flexItem";
-import Footer from "./footer";
-import Form from "./form";
-import Grid from "./grid";
-import Heading from "./heading";
-import IconButton from "./iconButton";
-import Image from "./image";
-import InfoButton from "./infoButton";
-import Label from "./label";
-import Li from "./li";
-import Link from "./link";
-import LinkButton from "./linkButton";
-import Main from "./main";
-import Nav from "./nav";
-import Overlay from "./overlay";
-import Panel from "./panel";
-import Paragraph from "./paragraph";
-import Popover, { PopoverArrow, PopoverClose, PopoverContent, PopoverTrigger } from "./popover";
-import RadioGroup, { RadioGroupIndicator, RadioGroupRadio } from "./radioGroup";
-import {
- Select,
- SelectContent,
- SelectGroup,
- SelectIcon,
- SelectItem,
- SelectItemIndicator,
- SelectItemText,
- SelectLabel,
- SelectScrollDownButton,
- SelectScrollUpButton,
- SelectSeparator,
- SelectTrigger,
- SelectValue,
- SelectViewport
-} from "./select";
-import Separator from "./separator";
-import Skeleton from "./skeleton";
-import Svg from "./svg";
-import Text from "./text";
-import TextArea from "./textArea";
-import TextField from "./textField";
-import Ul from "./ul";
-
-export {
- Alert,
- AppearInBox,
- Box,
- Button,
- Card,
- Checkbox,
- CheckboxIndicator,
- Container,
- Dialog,
- DialogClose,
- DialogContent,
- DialogDescription,
- DialogNonRemoveScrollOverlay,
- DialogTitle,
- DialogTrigger,
- ExpandingHelperText,
- FieldContainer,
- Fieldset,
- Flex,
- FlexItem,
- Footer,
- Form,
- Grid,
- Heading,
- IconButton,
- Image,
- InfoButton,
- Label,
- Li,
- Link,
- LinkButton,
- Main,
- Nav,
- Overlay,
- Panel,
- Paragraph,
- Popover,
- PopoverArrow,
- PopoverClose,
- PopoverContent,
- PopoverTrigger,
- RadioGroup,
- Skeleton,
- RadioGroupIndicator,
- RadioGroupRadio,
- Select,
- SelectContent,
- SelectGroup,
- SelectIcon,
- SelectItem,
- SelectItemIndicator,
- SelectItemText,
- SelectLabel,
- SelectScrollDownButton,
- SelectScrollUpButton,
- SelectSeparator,
- SelectTrigger,
- SelectValue,
- SelectViewport,
- Separator,
- Svg,
- Text,
- TextArea,
- TextField,
- Ul
-};
diff --git a/components/ui/info-button.tsx b/components/ui/info-button.tsx
new file mode 100644
index 0000000..ca9c5c6
--- /dev/null
+++ b/components/ui/info-button.tsx
@@ -0,0 +1,55 @@
+import { cva, VariantProps } from "class-variance-authority";
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
+import { Icons } from "../icons";
+import { Button } from "./button";
+import { Popover, PopoverArrow, PopoverContent, PopoverTrigger } from "./popover";
+
+const infoButtonVariants = cva(
+ "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2 dark:hover:bg-neutral-800 dark:hover:text-neutral-100 disabled:opacity-50 dark:focus:ring-neutral-400 disabled:pointer-events-none dark:focus:ring-offset-neutral-900",
+ {
+ variants: {
+ variant: {
+ default: "bg-transparent text-neutral-900 hover:bg-neutral-700 dark:text-neutral-50"
+ },
+ size: {
+ default: "h-9 p-2",
+ sm: "h-7 p-2 rounded-md",
+ lg: "h-11 p-8 rounded-md"
+ }
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default"
+ }
+ }
+);
+
+export type InfoButtonProps = React.ComponentPropsWithoutRef &
+ VariantProps;
+
+const InfoButton = React.forwardRef, InfoButtonProps>(
+ ({ children, className, variant, size, ...props }, ref) => {
+ return (
+
+
+
+
+
+
+
+ {children}
+
+
+
+ );
+ }
+);
+InfoButton.displayName = "InfoButton";
+
+export { InfoButton };
diff --git a/components/ui/infoButton.tsx b/components/ui/infoButton.tsx
deleted file mode 100644
index d33dd18..0000000
--- a/components/ui/infoButton.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import {
- IconButton,
- Popover,
- PopoverArrow,
- PopoverContent,
- PopoverTrigger,
- Svg
-} from "@/components/ui";
-import { WithChildren } from "@/types";
-import { PopoverContentProps } from "@radix-ui/react-popover";
-import * as React from "react";
-import { IoInformationCircleOutline } from "react-icons/io5";
-import { CSS, VariantProps } from "stitches.config";
-import Button from "./button";
-
-type PopoverContentSizeVariants = 1 | 2 | 3;
-
-type InfoButtonProps = VariantProps &
- WithChildren<{
- css?: CSS;
- popoverSide?: Pick["side"];
- popoverSize?: PopoverContentSizeVariants;
- }>;
-
-const InfoButton = React.forwardRef, InfoButtonProps>(
- function InfoButton({ children, popoverSide = "top", popoverSize = "2", ...other }, ref) {
- const popoverContentSize = {
- 1: { "@initial": "1" },
- 2: { "@initial": "1", "@bp1": "2" },
- 3: { "@initial": "1", "@bp2": "3" }
- };
-
- return (
-
-
-
-
-
-
-
- {children}
-
-
-
- );
- }
-);
-
-export default InfoButton;
diff --git a/components/ui/input.tsx b/components/ui/input.tsx
new file mode 100644
index 0000000..f451ae8
--- /dev/null
+++ b/components/ui/input.tsx
@@ -0,0 +1,21 @@
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
+
+export interface InputProps extends React.InputHTMLAttributes {}
+
+const Input = React.forwardRef(({ className, ...props }, ref) => {
+ return (
+
+ );
+});
+Input.displayName = "Input";
+
+export { Input };
diff --git a/components/ui/keyframes.tsx b/components/ui/keyframes.tsx
deleted file mode 100644
index aaec2d8..0000000
--- a/components/ui/keyframes.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { keyframes } from "stitches.config";
-
-export const slideUpAndFade = keyframes({
- "0%": { opacity: 0, transform: "translateY(20px)" },
- "100%": { opacity: 1, transform: "translateY(0)" }
-});
-
-export const slideRightAndFade = keyframes({
- "0%": { opacity: 0, transform: "translateX(-20px)" },
- "100%": { opacity: 1, transform: "translateX(0)" }
-});
-
-export const slideDownAndFade = keyframes({
- "0%": { opacity: 0, transform: "translateY(-20px)" },
- "100%": { opacity: 1, transform: "translateY(0)" }
-});
-
-export const slideLeftAndFade = keyframes({
- "0%": { opacity: 0, transform: "translateX(20px)" },
- "100%": { opacity: 1, transform: "translateX(0)" }
-});
-
-export const fadeIn = keyframes({
- "0%": { opacity: 0 },
- "100%": { opacity: 1 }
-});
-
-export const fadeOut = keyframes({
- "100%": { opacity: 0 }
-});
-
-export const pulse = keyframes({
- "0%": { opacity: 0 },
- "100%": { opacity: 1 }
-});
-
-export const spin = keyframes({
- from: { transform: "rotate(0deg)" },
- to: { transform: "rotate(360deg)" }
-});
diff --git a/components/ui/label.tsx b/components/ui/label.tsx
index b551b60..eb1760b 100644
--- a/components/ui/label.tsx
+++ b/components/ui/label.tsx
@@ -1,46 +1,23 @@
+"use client";
+
import * as LabelPrimitive from "@radix-ui/react-label";
-import { styled } from "stitches.config";
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
-const Label = styled(LabelPrimitive.Root, {
- fontWeight: 400,
- color: "$textDark",
- userSelect: "none",
- variants: {
- size: {
- tiny: {
- fontSize: "$tiny"
- },
- 1: {
- fontSize: "$1"
- },
- 2: {
- fontSize: "$2"
- },
- 3: {
- fontSize: "$3"
- },
- 4: {
- fontSize: "$4"
- }
- },
- textTransform: {
- uppercase: {
- textTransform: "uppercase"
- },
- lowercase: {
- textTransform: "lowercase"
- },
- capitalize: {
- textTransform: "capitalize"
- },
- none: {
- textTransform: "none"
- }
- }
- },
- defaultVariants: {
- size: "1"
- }
-});
+const Label = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+Label.displayName = LabelPrimitive.Root.displayName;
-export default Label;
+export { Label };
diff --git a/components/ui/li.tsx b/components/ui/li.tsx
deleted file mode 100644
index 8a82d5b..0000000
--- a/components/ui/li.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { styled } from "stitches.config";
-
-const Li = styled("li", {
- // Reset
- listStyle: "none",
- margin: 0,
- padding: "$2 0"
-});
-
-export default Li;
diff --git a/components/ui/link.tsx b/components/ui/link.tsx
index 22a719e..a847f62 100644
--- a/components/ui/link.tsx
+++ b/components/ui/link.tsx
@@ -1,114 +1,52 @@
-import { WithChildren } from "@/types";
-import NextLink, { LinkProps as NextLinkProps } from "next/link";
-import { useRouter } from "next/router";
+import { cn } from "@/lib/utils";
+import NextLink from "next/link";
import * as React from "react";
-import { CSS, styled, VariantProps } from "stitches.config";
+import { Icons } from "../icons";
+import { Show } from "./show";
+
+type LinkElement = React.ElementRef;
+type LinkProps = React.ComponentPropsWithoutRef & {
+ href: string;
+ children: React.ReactNode;
+ className?: string;
+ showExternalLinkIcon?: boolean;
+};
-const LinkRoot = styled("a", {
- textDecoration: "none",
- transition: "color 0.2s ease-in-out",
- color: "$textDark",
- "&:hover": {
- color: "$text"
- },
+const Link = React.forwardRef(
+ ({ children, href, className, showExternalLinkIcon = true, ...other }, forwardedRef) => {
+ const isHrefExternal = React.useMemo(() => {
+ // server side rendering safe check for external links
+ return /^https?:\/\//.test(href);
+ }, [href]);
- variants: {
- variant: {
- full: {
- display: "block",
- width: "100%"
- }
- },
- color: {
- text: {
- color: "$text"
- },
- textDark: {
- color: "$textDark"
- },
- black: {
- color: "$black"
- },
- gray: {
- color: "$gray"
- },
- grayLight: {
- color: "$grayLight"
- },
- grayLighter: {
- color: "$grayLighter"
- },
- grayLightest: {
- color: "$grayLightest"
- },
- green: {
- color: "$green"
- },
- red: {
- color: "$red"
- },
- white: {
- color: "$white"
- }
- },
- textDecoration: {
- underline: {
- textDecoration: "underline"
+ const externalLinkProps = React.useMemo(() => {
+ if (!isHrefExternal) {
+ return {};
}
- },
- active: {
- true: {
- color: "$green"
- }
- }
- }
-});
-type LinkProps = VariantProps &
- WithChildren<{
- href: string;
- as?: string;
- onClick?: React.MouseEventHandler;
- isExternal?: boolean;
- css?: CSS;
- }> &
- NextLinkProps;
+ return {
+ target: "_blank",
+ rel: "noopener noreferrer"
+ };
+ }, [isHrefExternal]);
-const Link = ({ children, href, as, isExternal = false, ...other }: LinkProps) => {
- const router = useRouter();
- const path = router.asPath ?? router.pathname;
- const linkPath = as ?? href;
-
- const linkProps =
- as !== null && as !== undefined
- ? {
- href,
- as,
- passHref: true
- }
- : {
- href,
- passHref: true
- };
-
- const active = React.useMemo(
- () => linkPath !== "/" && (path === linkPath || path.startsWith(linkPath)),
- [path, linkPath]
- );
-
- if (isExternal) {
return (
-
+
{children}
-
+
+
+
+
);
}
+);
- return (
-
- {children}
-
- );
-};
+Link.displayName = "Link";
export default Link;
diff --git a/components/ui/linkButton.tsx b/components/ui/linkButton.tsx
deleted file mode 100644
index a262a3f..0000000
--- a/components/ui/linkButton.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import Link from "next/link";
-import * as React from "react";
-import { CSS, styled } from "stitches.config";
-import { buttonStyles } from "./button";
-
-const StyledLinkButton = styled("a", buttonStyles);
-
-type LinkButtonPrimitiveProps = React.ComponentProps;
-type LinkButtonProps = LinkButtonPrimitiveProps & { href: string; css?: CSS };
-
-export const LinkButton = ({ children, href, ...props }: LinkButtonProps) => {
- return (
-
- {children}
-
- );
-};
-
-LinkButton.displayName = "LinkButton";
-
-export default LinkButton;
diff --git a/components/ui/main.tsx b/components/ui/main.tsx
deleted file mode 100644
index e80920f..0000000
--- a/components/ui/main.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { styled } from "stitches.config";
-
-const Main = styled("main", {
- // Reset
- boxSizing: "border-box",
- flexShrink: 0,
- backgroundColor: "$background"
-});
-
-export default Main;
diff --git a/components/ui/nav.tsx b/components/ui/nav.tsx
deleted file mode 100644
index efad717..0000000
--- a/components/ui/nav.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { styled } from "stitches.config";
-
-const Nav = styled("nav", {
- padding: "$4",
- width: "100%",
- minHeight: "100px",
-
- variants: {
- variant: {
- header: {
- boxShadow: "inset 0 -1px $colors$gray"
- }
- }
- }
-});
-
-export default Nav;
diff --git a/components/ui/overlay.tsx b/components/ui/overlay.tsx
deleted file mode 100644
index 39a9694..0000000
--- a/components/ui/overlay.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { css, styled } from "stitches.config";
-
-export const overlayStyles = css({
- backgroundColor: "$overlay"
-});
-
-const Overlay = styled("div", overlayStyles);
-
-export default Overlay;
diff --git a/components/ui/panel.tsx b/components/ui/panel.tsx
deleted file mode 100644
index 27b6c04..0000000
--- a/components/ui/panel.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { css, styled } from "stitches.config";
-
-export const panelStyles = css({
- backgroundColor: "$panel",
- borderRadius: "$3",
- border: "2px solid $grayLight",
- boxShadow: "0 0 2px $colors$grayLight"
-});
-
-const Panel = styled("div", panelStyles);
-
-export default Panel;
diff --git a/components/ui/paragraph.tsx b/components/ui/paragraph.tsx
deleted file mode 100644
index 2e06de4..0000000
--- a/components/ui/paragraph.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import merge from "lodash.merge";
-import * as React from "react";
-import { CSS, VariantProps } from "stitches.config";
-import Text from "./text";
-
-const DEFAULT_TAG = "p";
-
-type TextSizeVariants = Pick, "size">;
-type ParagraphSizeVariants = "1" | "2";
-type ParagraphVariants = { size?: ParagraphSizeVariants } & Omit, "size">;
-type ParagraphProps = React.ComponentProps &
- ParagraphVariants & { css?: CSS; as?: any; noMargin?: boolean };
-
-const Paragraph = React.forwardRef, ParagraphProps>(
- (props, forwardedRef) => {
- // '2' here is the default Paragraph size variant
- const { size = "1", mb = "3", noMargin = false, ...textProps } = props;
-
- // This is the mapping of Paragraph Variants to Text variants
- const textSize: Record = {
- 1: { "@initial": "2", "@bp2": "3" },
- 2: { "@initial": "3", "@bp2": "4" }
- };
-
- // This is the mapping of Paragraph Variants to Text css
- const textCss: Record = {
- 1: { lineHeight: "125%", "@bp2": { lineHeight: "135%" } },
- 2: { lineHeight: "135%", "@bp2": { lineHeight: "145%" } }
- };
- return (
-
- );
- }
-);
-
-Paragraph.displayName = "Paragraph";
-
-export default Paragraph;
diff --git a/components/ui/popover.tsx b/components/ui/popover.tsx
index 5824b23..3333810 100644
--- a/components/ui/popover.tsx
+++ b/components/ui/popover.tsx
@@ -1,100 +1,45 @@
+"use client";
+
import * as PopoverPrimitive from "@radix-ui/react-popover";
import * as React from "react";
-import type { CSS, VariantProps } from "stitches.config";
-import { styled } from "stitches.config";
-import { slideDownAndFade, slideLeftAndFade, slideRightAndFade, slideUpAndFade } from "./keyframes";
-
-const StyledArrow = styled(PopoverPrimitive.Arrow, {
- fill: "$gray",
-
- variants: {
- variant: {
- gray: {
- fill: "$grayLightest"
- }
- }
- }
-});
-
-const StyledContent = styled(PopoverPrimitive.Content, {
- borderRadius: "$2",
- padding: "$4",
- maxWidth: 360,
- backgroundColor: "$main",
- boxShadow: "0 0 0 1px $colors$gray",
- "@media (prefers-reduced-motion: no-preference)": {
- animationDuration: "400ms",
- animationTimingFunction: "cubic-bezier(0.16, 1, 0.3, 1)",
- willChange: "transform, opacity",
- '&[data-state="open"]': {
- '&[data-side="top"]': { animationName: slideDownAndFade },
- '&[data-side="right"]': { animationName: slideLeftAndFade },
- '&[data-side="bottom"]': { animationName: slideUpAndFade },
- '&[data-side="left"]': { animationName: slideRightAndFade }
- }
- },
- "&:focus": {
- boxShadow: "0 0 0 1px $colors$grayLightest",
- [`${StyledArrow}`]: {
- fill: "$grayLightest"
- }
- },
- variants: {
- variant: {
- gray: {
- backgroundColor: "$grayDark",
- boxShadow: "0 0 0 1px $colors$grayLightest"
- }
- },
- size: {
- 1: {
- maxWidth: "200px"
- },
- 2: {
- maxWidth: "280px"
- },
- 3: {
- maxWidth: "360px"
- }
- }
- }
-});
-
-const StyledClose = styled(PopoverPrimitive.Close, {
- all: "unset",
- position: "absolute",
- top: 5,
- right: 5
-});
-
-type PopoverContentPrimitiveProps = React.ComponentProps;
-type PopoverContentProps = PopoverContentPrimitiveProps &
- VariantProps & {
- portal?: boolean;
- css?: CSS;
- };
-
-export const PopoverContent = React.forwardRef<
- React.ElementRef,
- PopoverContentProps
->(({ children, portal = false, ...other }, forwardedRef) => {
- const PopoverWrapper = portal ? PopoverPrimitive.Portal : React.Fragment;
-
- return (
-
-
- {children}
-
-
- );
-});
-PopoverContent.displayName = "PopoverContent";
+import { cn } from "@/lib/utils";
-// Exports
const Popover = PopoverPrimitive.Root;
-export const PopoverTrigger = PopoverPrimitive.Trigger;
-export const PopoverArrow = StyledArrow;
-export const PopoverClose = StyledClose;
-export default Popover;
+const PopoverTrigger = PopoverPrimitive.Trigger;
+
+const PopoverContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+
+
+
+));
+
+PopoverContent.displayName = PopoverPrimitive.Content.displayName;
+
+const PopoverArrow = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+PopoverArrow.displayName = PopoverPrimitive.Arrow.displayName;
+
+export { Popover, PopoverTrigger, PopoverContent, PopoverArrow };
diff --git a/components/ui/radioGroup.tsx b/components/ui/radioGroup.tsx
deleted file mode 100644
index 68d4f1b..0000000
--- a/components/ui/radioGroup.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { WithChildren } from "@/types";
-import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
-import * as React from "react";
-import { CSS, styled, VariantProps } from "stitches.config";
-
-const StyledRadioGroupRadio = styled(RadioGroupPrimitive.Item, {
- all: "unset",
- backgroundColor: "white",
- width: 25,
- height: 25,
- borderRadius: "100%",
- boxShadow: `0 2px 10px $colors$black`,
- "&:hover": { backgroundColor: "$gray" },
- "&:focus": { boxShadow: `0 0 0 2px $black` }
-});
-
-const RadioGroupIndicator = styled(RadioGroupPrimitive.Indicator, {
- display: "flex",
- alignItems: "center",
- justifyContent: "center",
- width: "100%",
- height: "100%",
- position: "relative",
- "&::after": {
- content: '""',
- display: "block",
- width: 11,
- height: 11,
- borderRadius: "50%",
- backgroundColor: "$black"
- }
-});
-
-type RadioGroupRadioProps = VariantProps &
- WithChildren<{
- id: string;
- value: string;
- css?: CSS;
- }>;
-
-const RadioGroupRadio = React.forwardRef<
- React.ElementRef,
- RadioGroupRadioProps
->(function RadioGroupRadio({ id, value, ...other }, ref) {
- return ;
-});
-
-const RadioGroup = RadioGroupPrimitive.Root;
-
-export { RadioGroupIndicator, RadioGroupRadio };
-export default RadioGroup;
diff --git a/components/ui/select.tsx b/components/ui/select.tsx
index 0a532ce..d10b912 100644
--- a/components/ui/select.tsx
+++ b/components/ui/select.tsx
@@ -1,133 +1,114 @@
+"use client";
+
+import { cn } from "@/lib/utils";
+import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "@radix-ui/react-icons";
import * as SelectPrimitive from "@radix-ui/react-select";
import * as React from "react";
-import type { CSS, VariantProps } from "stitches.config";
-import { styled } from "stitches.config";
-
-const StyledTrigger = styled(SelectPrimitive.SelectTrigger, {
- all: "unset",
- display: "inline-flex",
- alignItems: "center",
- justifyContent: "center",
- borderRadius: 4,
- padding: "0 15px",
- fontSize: 13,
- lineHeight: 1,
- height: 35,
- gap: 5,
- backgroundColor: "panel",
- color: "$text",
- cursor: "pointer",
- boxShadow: `$select`,
- "&:hover": { boxShadow: "$selectHover" },
- "&:focus": { boxShadow: `$select` }
-});
-
-const StyledContent = styled(SelectPrimitive.Content, {
- overflow: "hidden",
- backgroundColor: "$panel",
- borderRadius: 6,
- boxShadow: "$select"
-});
-
-const StyledViewport = styled(SelectPrimitive.Viewport, {
- padding: 5
-});
-
-const StyledItem = styled(SelectPrimitive.Item, {
- all: "unset",
- fontSize: 13,
- lineHeight: 1,
- color: "$text",
- borderRadius: 3,
- display: "flex",
- alignItems: "center",
- height: 25,
- padding: "0 35px 0 25px",
- position: "relative",
- userSelect: "none",
-
- "&[data-disabled]": {
- color: "$gray",
- pointerEvents: "none"
- },
-
- "&:focus": {
- backgroundColor: "$grayLightest",
- color: "$grayDark"
- }
-});
-
-const StyledLabel = styled(SelectPrimitive.Label, {
- padding: "0 25px",
- fontSize: 12,
- lineHeight: "25px",
- color: "$textDark"
-});
-
-const StyledSeparator = styled(SelectPrimitive.Separator, {
- height: 1,
- backgroundColor: "$gray",
- margin: 5
-});
-const StyledItemIndicator = styled(SelectPrimitive.ItemIndicator, {
- position: "absolute",
- left: 0,
- width: 25,
- display: "inline-flex",
- alignItems: "center",
- justifyContent: "center"
-});
-
-const scrollButtonStyles = {
- display: "flex",
- alignItems: "center",
- justifyContent: "center",
- height: 25,
- backgroundColor: "white",
- color: "$text",
- cursor: "default"
+const Select = SelectPrimitive.Root;
+
+const SelectGroup = SelectPrimitive.Group;
+
+const SelectValue = SelectPrimitive.Value;
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ {children}
+
+
+));
+
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+ {children}
+
+
+
+
+
+));
+SelectContent.displayName = SelectPrimitive.Content.displayName;
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectLabel.displayName = SelectPrimitive.Label.displayName;
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+
+ {children}
+
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName;
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator
};
-
-const StyledScrollUpButton = styled(SelectPrimitive.ScrollUpButton, scrollButtonStyles);
-
-const StyledScrollDownButton = styled(SelectPrimitive.ScrollDownButton, scrollButtonStyles);
-
-type SelectContentPrimitiveProps = React.ComponentProps;
-type SelectContentProps = SelectContentPrimitiveProps &
- VariantProps & {
- portal?: boolean;
-
- css?: CSS;
- };
-
-export const SelectContent = React.forwardRef<
- React.ElementRef,
- SelectContentProps
->(({ children, portal = false, ...other }, forwardedRef) => {
- const SelectWrapper = portal ? SelectPrimitive.Portal : React.Fragment;
-
- return (
-
-
- {children}
-
-
- );
-});
-
-SelectContent.displayName = "SelectContent";
-
-export const Select = SelectPrimitive.Root;
-export const SelectTrigger = StyledTrigger;
-export const SelectValue = SelectPrimitive.Value;
-export const SelectIcon = SelectPrimitive.Icon;
-export const SelectViewport = StyledViewport;
-export const SelectGroup = SelectPrimitive.Group;
-export const SelectItem = StyledItem;
-export const SelectItemText = SelectPrimitive.ItemText;
-export const SelectItemIndicator = StyledItemIndicator;
-export const SelectLabel = StyledLabel;
-export const SelectSeparator = StyledSeparator;
-export const SelectScrollUpButton = StyledScrollUpButton;
-export const SelectScrollDownButton = StyledScrollDownButton;
diff --git a/components/ui/separator.tsx b/components/ui/separator.tsx
index 8206b7a..51bd4ae 100644
--- a/components/ui/separator.tsx
+++ b/components/ui/separator.tsx
@@ -1,39 +1,26 @@
+"use client";
+
import * as SeparatorPrimitive from "@radix-ui/react-separator";
-import { styled } from "stitches.config";
+import * as React from "react";
-const Separator = styled(SeparatorPrimitive.Root, {
- backgroundColor: "$gray",
- "&[data-orientation=horizontal]": { height: 1, width: "100%" },
- "&[data-orientation=vertical]": { height: "100%", width: 1 },
+import { cn } from "@/lib/utils";
- variants: {
- space: {
- 1: {
- "&[data-orientation=horizontal]": { my: "$1" },
- "&[data-orientation=vertical]": { mx: "$1" }
- },
- 2: {
- "&[data-orientation=horizontal]": { my: "$2" },
- "&[data-orientation=vertical]": { mx: "$2" }
- },
- 3: {
- "&[data-orientation=horizontal]": { my: "$3" },
- "&[data-orientation=vertical]": { mx: "$3" }
- },
- 4: {
- "&[data-orientation=horizontal]": { my: "$4" },
- "&[data-orientation=vertical]": { mx: "$4" }
- },
- 5: {
- "&[data-orientation=horizontal]": { my: "$5" },
- "&[data-orientation=vertical]": { mx: "$5" }
- },
- 6: {
- "&[data-orientation=horizontal]": { my: "$6" },
- "&[data-orientation=vertical]": { mx: "$6" }
- }
- }
- }
-});
+const Separator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
+
+));
+Separator.displayName = SeparatorPrimitive.Root.displayName;
-export default Separator;
+export { Separator };
diff --git a/components/ui/show.tsx b/components/ui/show.tsx
new file mode 100644
index 0000000..d53ff78
--- /dev/null
+++ b/components/ui/show.tsx
@@ -0,0 +1,7 @@
+import { FC, ReactNode } from "react";
+
+const Show: FC<{ children: ReactNode; when: boolean }> = ({ when, children }) => {
+ return when ? <>{children}> : null;
+};
+
+export { Show };
diff --git a/components/ui/skeleton.tsx b/components/ui/skeleton.tsx
index f5eb2ea..dbf9bf3 100644
--- a/components/ui/skeleton.tsx
+++ b/components/ui/skeleton.tsx
@@ -1,88 +1,12 @@
-import { css, styled } from "stitches.config";
-import { pulse } from "./keyframes";
+import { cn } from "@/lib/utils";
-export const skeletonStyles = css({
- variants: {
- loading: {
- true: {
- backgroundColor: "$gray",
- position: "relative",
- overflow: "hidden",
- "&::after": {
- animationName: `${pulse}`,
- animationDuration: "1000ms",
- animationDirection: "alternate",
- animationIterationCount: "infinite",
- animationTimingFunction: "ease-in-out",
- backgroundColor: "$grayLight",
- borderRadius: "inherit",
- bottom: 0,
- content: '""',
- left: 0,
- position: "absolute",
- right: 0,
- top: 0
- },
- "> *": {
- visibility: "hidden"
- }
- }
- },
- variant: {
- avatar1: {
- borderRadius: "$round",
- height: "$3",
- width: "$3"
- },
- avatar2: {
- borderRadius: "$round",
- height: "$5",
- width: "$5"
- },
- avatar3: {
- borderRadius: "$round",
- height: "$6",
- width: "$6"
- },
- avatar4: {
- borderRadius: "$round",
- height: "$7",
- width: "$7"
- },
- avatar5: {
- borderRadius: "$round",
- height: "$8",
- width: "$8"
- },
- avatar6: {
- borderRadius: "$round",
- height: "$9",
- width: "$9"
- },
- text: {
- height: "$1"
- },
- title: {
- height: "$5"
- },
- heading: {
- height: "$3"
- },
- button: {
- borderRadius: "$1",
- height: "$5",
- width: "$8"
- },
- cartItem: {
- minHeight: "120px"
- },
- deliveryDateSelector: {
- minHeight: "64px"
- }
- }
- }
-});
+interface SkeletonProps extends React.HTMLAttributes {}
-const Skeleton = styled("div", skeletonStyles);
-
-export default Skeleton;
+export function Skeleton({ className, ...props }: SkeletonProps) {
+ return (
+
+ );
+}
diff --git a/components/ui/svg.tsx b/components/ui/svg.tsx
deleted file mode 100644
index b91a53f..0000000
--- a/components/ui/svg.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { styled } from "stitches.config";
-import { spin } from "./keyframes";
-
-const Svg = styled("svg", {
- variants: {
- size: {
- 1: {
- height: "$2",
- width: "$2"
- },
- 2: {
- height: "$3",
- width: "$3"
- },
- 3: {
- height: "$4",
- width: "$4"
- },
- 4: {
- height: "$5",
- width: "$5"
- },
- 5: {
- height: "$6",
- width: "$6"
- },
- 6: {
- height: "$7",
- width: "$7"
- },
- 7: {
- height: "$8",
- width: "$8"
- },
- 8: {
- height: "$9",
- width: "$9"
- },
- 9: {
- height: "$10",
- width: "$10"
- },
- 10: {
- height: "$12",
- width: "$12"
- }
- },
- variant: {
- black: {
- color: "$black"
- },
- text: {
- color: "$text"
- },
- textDark: {
- color: "$textDark"
- },
- secondary: {
- color: "$secondary"
- },
- secondaryDark: {
- color: "$secondaryDark"
- },
- gray: {
- color: "$gray"
- },
- grayLight: {
- color: "$grayLight"
- },
- green: {
- color: "$green"
- },
- red: {
- color: "$red"
- },
- white: {
- color: "$white"
- },
- main: {
- color: "$main"
- }
- },
- spin: {
- true: {
- animation: `${spin} 1s linear infinite`
- }
- }
- },
- defaultVariants: {
- size: 3,
- variant: "black"
- }
-});
-
-export default Svg;
diff --git a/components/ui/text.tsx b/components/ui/text.tsx
deleted file mode 100644
index 5dd7057..0000000
--- a/components/ui/text.tsx
+++ /dev/null
@@ -1,209 +0,0 @@
-import { styled } from "stitches.config";
-
-const Text = styled("span", {
- // Reset
- lineHeight: "125%",
- margin: "0",
- fontWeight: 400,
- // fontVariantNumeric: "tabular-nums",
- display: "block",
-
- variants: {
- color: {
- text: {
- color: "$text"
- },
- textDark: {
- color: "$textDark"
- },
- black: {
- color: "$black"
- },
- gray: {
- color: "$gray"
- },
- grayLight: {
- color: "$grayLight"
- },
- grayLighter: {
- color: "$grayLighter"
- },
- grayLightest: {
- color: "$grayLightest"
- },
- green: {
- color: "$green"
- },
- red: {
- color: "$red"
- },
- white: {
- color: "$white"
- }
- },
- size: {
- tiny: {
- fontSize: "$tiny"
- },
- 1: {
- fontSize: "$1"
- },
- 2: {
- fontSize: "$2"
- },
- 3: {
- fontSize: "$3"
- },
- 4: {
- fontSize: "$4"
- },
- 5: {
- fontSize: "$5"
- },
- 6: {
- fontSize: "$6"
- },
- 7: {
- fontSize: "$7"
- },
- 8: {
- fontSize: "$8"
- },
- 9: {
- fontSize: "$9"
- }
- },
- fontStyle: {
- italic: {
- fontStyle: "italic"
- }
- },
- textAlign: {
- left: {
- textAlign: "left"
- },
- right: {
- textAlign: "right"
- },
- center: {
- textAlign: "center"
- },
- start: {
- textAlign: "start"
- },
- end: {
- textAlign: "end"
- },
- justify: {
- textAlign: "justify"
- }
- },
- textTransform: {
- uppercase: {
- textTransform: "uppercase"
- },
- lowercase: {
- textTransform: "lowercase"
- },
- capitalize: {
- textTransform: "capitalize"
- },
- none: {
- textTransform: "none"
- }
- },
- textDecoration: {
- underline: {
- textDecoration: "underline"
- },
- lineThrough: {
- textDecoration: "line-through"
- },
- none: {
- textDecoration: "none"
- }
- },
- fontWeight: {
- lighter: {
- fontWeight: "lighter"
- },
- normal: {
- fontWeight: "normal"
- },
- bold: {
- fontWeight: "bold"
- },
- bolder: {
- fontWeight: "bolder"
- },
- 100: {
- fontWeight: "100"
- },
- 200: {
- fontWeight: "200"
- },
- 300: {
- fontWeight: "300"
- },
-
- 400: {
- fontWeight: "400"
- },
-
- 500: {
- fontWeight: "500"
- },
-
- 600: {
- fontWeight: "600"
- },
-
- 700: {
- fontWeight: "700"
- },
- 800: {
- fontWeight: "800"
- },
- 900: {
- fontWeight: "900"
- }
- },
- mb: {
- 1: {
- marginBottom: "$1"
- },
- 2: {
- marginBottom: "$2"
- },
- 3: {
- marginBottom: "$3"
- },
- 4: {
- marginBottom: "$4"
- },
- 5: {
- marginBottom: "$5"
- },
- 6: {
- marginBottom: "$6"
- },
- 7: {
- marginBottom: "$7"
- },
- 8: {
- marginBottom: "$8"
- }
- },
- inline: {
- true: {
- display: "inline"
- }
- }
- },
- defaultVariants: {
- size: "3",
- color: "text"
- }
-});
-
-export default Text;
diff --git a/components/ui/textArea.tsx b/components/ui/textArea.tsx
deleted file mode 100644
index 6d543e6..0000000
--- a/components/ui/textArea.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import * as React from "react";
-import { styled } from "stitches.config";
-import ExpandingHelperText from "./expandingHelperText";
-import FieldContainer from "./fieldContainer";
-import Label from "./label";
-import { defaultInputStyles } from "./textField";
-
-const TextAreaRoot = styled("textarea", {
- ...defaultInputStyles,
- variants: {
- variant: {
- vertical: {
- resize: "vertical"
- },
- horizontal: {
- resize: "horizontal"
- },
- "no-resize": {
- resize: "none"
- }
- }
- },
-
- defaultVariants: {
- variant: "vertical"
- }
-});
-
-type TextFieldProps = {
- label: string;
- helperText?: string;
- disabled?: boolean;
- id?: string;
- error?: boolean;
- errorText?: string;
- rows?: number;
-};
-
-const TextArea = React.forwardRef, TextFieldProps>(function TextArea(
- props,
- ref
-) {
- const { id, label = "Label", error = false, helperText, rows = 4, ...other } = props;
-
- return (
-
-
-
- {label}
-
-
-
- );
-});
-
-export default TextArea;
diff --git a/components/ui/textField.tsx b/components/ui/textField.tsx
deleted file mode 100644
index 1a097ca..0000000
--- a/components/ui/textField.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import * as React from "react";
-import { CSS, css, styled, VariantProps } from "stitches.config";
-import { InfoButton } from ".";
-import Box from "./box";
-import ExpandingHelperText from "./expandingHelperText";
-import FieldContainer from "./fieldContainer";
-import Label from "./label";
-
-export const HelperText = styled(Box, {});
-
-export const defaultInputStyles = css({
- all: "unset",
- boxShadow: "$input",
- borderRadius: "2px",
- padding: "$2 $3",
- transition: "box-shadow 0.2s ease-in-out",
- backgroundColor: "$main",
- [`+ ${Label}`]: {
- color: "$textDark",
- transition: "color 0.2s ease-in-out",
- order: -1
- },
- "&:hover": {
- boxShadow: "$inputHover",
- [`+ ${Label}`]: {
- color: "$text"
- }
- },
- "&:focus": {
- boxShadow: "$inputFocus",
- [`+ ${Label}`]: {
- color: "$text"
- }
- },
- "&:disabled": {
- backgroundColor: "$grayDark",
- cursor: "not-allowed"
- }
-});
-
-const TextFieldInput = styled("input", {
- ...defaultInputStyles
-});
-
-type LabelSizeVariants = Pick, "size">;
-
-type TextFieldLabelSizeVariants = "tiny" | "1" | "2" | "3" | "4";
-
-type TextFieldProps = {
- label: string;
- helperText?: string;
- placeholder?: string;
- disabled?: boolean;
- onChange?: (event: React.ChangeEvent) => void;
- onBlur?: (event: React.FocusEvent) => void;
- onFocus?: (event: React.FocusEvent) => void;
- value?: string | number;
- type?: string;
- name?: string;
- id?: string;
- required?: boolean;
- error?: boolean;
- errorText?: string;
- step?: string;
- min?: string | number;
- css?: CSS;
- fieldContainerCss?: CSS;
- labelSize?: TextFieldLabelSizeVariants;
- info?: string;
-};
-
-const TextField = React.forwardRef, TextFieldProps>(function TextField(
- props,
- ref
-) {
- const {
- id,
- label = "Label",
- error = true,
- helperText,
- placeholder,
- type = "text",
- step,
- min,
- fieldContainerCss,
- labelSize,
- info,
- ...other
- } = props;
-
- return (
-
-
-
- {label}
-
-
- {info ? (
- {info}
- ) : null}
-
- );
-});
-
-export default TextField;
diff --git a/components/ui/toast.tsx b/components/ui/toast.tsx
new file mode 100644
index 0000000..8ec14e9
--- /dev/null
+++ b/components/ui/toast.tsx
@@ -0,0 +1,127 @@
+import * as ToastPrimitives from "@radix-ui/react-toast";
+import { cva, VariantProps } from "class-variance-authority";
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
+import { Icons } from "../icons";
+
+const ToastProvider = ToastPrimitives.Provider;
+
+const ToastViewport = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
+
+const toastVariants = cva(
+ "data-[swipe=move]:transition-none grow-1 group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full mt-4 data-[state=closed]:slide-out-to-right-full dark:border-neutral-700 last:mt-0 sm:last:mt-4",
+ {
+ variants: {
+ variant: {
+ default: "bg-white border-neutral-200 dark:bg-neutral-800 dark:border-neutral-700",
+ destructive: "group destructive bg-red-600 text-white border-red-600 dark:border-red-600",
+ success:
+ "group success bg-emerald-500 text-neutral-900 border-emerald-500 dark:border-emerald-500"
+ }
+ },
+ defaultVariants: {
+ variant: "default"
+ }
+ }
+);
+
+const Toast = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & VariantProps
+>(({ className, variant, ...props }, ref) => {
+ return (
+
+ );
+});
+Toast.displayName = ToastPrimitives.Root.displayName;
+
+const ToastAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+ToastAction.displayName = ToastPrimitives.Action.displayName;
+
+const ToastClose = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+));
+ToastClose.displayName = ToastPrimitives.Close.displayName;
+
+const ToastTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+ToastTitle.displayName = ToastPrimitives.Title.displayName;
+
+const ToastDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+ToastDescription.displayName = ToastPrimitives.Description.displayName;
+
+type ToastProps = React.ComponentPropsWithoutRef;
+
+type ToastActionElement = React.ReactElement;
+
+export {
+ type ToastProps,
+ type ToastActionElement,
+ ToastProvider,
+ ToastViewport,
+ Toast,
+ ToastTitle,
+ ToastDescription,
+ ToastClose,
+ ToastAction
+};
diff --git a/components/ui/toaster.tsx b/components/ui/toaster.tsx
new file mode 100644
index 0000000..3f356c1
--- /dev/null
+++ b/components/ui/toaster.tsx
@@ -0,0 +1,34 @@
+"use client";
+
+import { useToast } from "@/hooks/use-toast";
+
+import {
+ Toast,
+ ToastClose,
+ ToastDescription,
+ ToastProvider,
+ ToastTitle,
+ ToastViewport
+} from "@/components/ui/toast";
+
+export function Toaster() {
+ const { toasts } = useToast();
+
+ return (
+
+ {toasts.map(function ({ id, title, description, action, ...props }) {
+ return (
+
+
+ {title && {title} }
+ {description && {description} }
+
+ {action}
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/components/ui/ul.tsx b/components/ui/ul.tsx
deleted file mode 100644
index 78b79fa..0000000
--- a/components/ui/ul.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { styled } from "stitches.config";
-
-const Ul = styled("ul", {
- // Reset
- listStyle: "none",
- margin: 0,
- padding: 0
-});
-
-export default Ul;
diff --git a/components/user/hooks/index.tsx b/components/user/hooks/index.tsx
deleted file mode 100644
index 5e7099f..0000000
--- a/components/user/hooks/index.tsx
+++ /dev/null
@@ -1,4 +0,0 @@
-import useUser from "@/components/user/hooks/useUser";
-import useUserAvatar from "./useUserAvatar";
-
-export { useUser, useUserAvatar };
diff --git a/components/user/hooks/useUser.ts b/components/user/hooks/useUser.ts
deleted file mode 100644
index 987792a..0000000
--- a/components/user/hooks/useUser.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { useUser } from "@/components/user/providers/userProvider";
-
-export default useUser;
diff --git a/components/user/hooks/useUserAvatar.ts b/components/user/hooks/useUserAvatar.ts
deleted file mode 100644
index 4d302aa..0000000
--- a/components/user/hooks/useUserAvatar.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { fetcher } from "@/utils/fetcher";
-import { useLocalStorage } from "react-use";
-import useSWR from "swr";
-
-export default function useUserAvatar() {
- const [userAvatarSrc, setUserAvatarSrc] = useLocalStorage(
- "kxb-app-user-avatar-src",
- undefined
- );
-
- useSWR<{ src: string }>("/api/user/avatar", fetcher, {
- onSuccess: data => {
- setUserAvatarSrc(data.src);
- }
- });
-
- return {
- userAvatarSrc
- };
-}
diff --git a/components/user/index.tsx b/components/user/index.tsx
deleted file mode 100644
index 45701fb..0000000
--- a/components/user/index.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import { useUser } from "@/components/user/hooks";
-import UserAvatar from "@/components/user/userAvatar";
-import UserNavDetails from "@/components/user/userNavDetails";
-import UserProfile from "@/components/user/userProfile";
-import UserWorkDayDetails from "@/components/user/userWorkDayDetails";
-
-export { UserAvatar, UserNavDetails, UserProfile, useUser, UserWorkDayDetails };
diff --git a/components/user/providers/userProvider.tsx b/components/user/providers/userProvider.tsx
deleted file mode 100644
index 70069c0..0000000
--- a/components/user/providers/userProvider.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { User } from "@/types";
-import { fetcher } from "@/utils/fetcher";
-import * as React from "react";
-import { useDebounce } from "react-use";
-import useSWR, { useSWRConfig } from "swr";
-
-type UserContextProps = {
- user: User;
- update: (updatedUser: Partial) => Promise;
- isLoadingUser: boolean;
-};
-
-const UserContext = React.createContext(null);
-
-function UserProvider({ children, session = {}, user }) {
- const { data } = useSWR(() => `/api/user/${user.id}`, fetcher, {
- fallbackData: user,
- revalidateOnMount: true
- });
-
- const { mutate } = useSWRConfig();
-
- useDebounce(
- async () => {
- await fetch(`/api/user/${data.id}`, {
- method: "PUT",
- headers: {
- "Content-Type": "application/json",
- Accept: "application/json"
- },
- body: JSON.stringify(data)
- });
- },
- 2000,
- [data]
- );
-
- const update = React.useCallback(
- async (updatedUser: Partial) => {
- await mutate(
- `/api/user/${data.id}`,
- (user: User) => ({
- ...user,
- ...updatedUser
- }),
- false
- );
- },
- [mutate, data]
- );
-
- const value = React.useMemo(
- () => ({
- user: data,
- update,
- isLoadingUser: !data
- }),
- [data, update]
- );
-
- return {children} ;
-}
-function useUser() {
- const context = React.useContext(UserContext);
-
- if (context === undefined) {
- throw new Error("useUser must be used within a UserProvider");
- }
-
- return context;
-}
-
-export { UserProvider, useUser };
diff --git a/components/user/user-avatar.tsx b/components/user/user-avatar.tsx
new file mode 100644
index 0000000..07370d4
--- /dev/null
+++ b/components/user/user-avatar.tsx
@@ -0,0 +1,59 @@
+"use client";
+
+import { ThemeSelect } from "@/components/theme-select";
+import { Button } from "@/components/ui/button";
+import { Popover, PopoverArrow, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
+import { Separator } from "@/components/ui/separator";
+import { GearIcon } from "@radix-ui/react-icons";
+import { Session } from "next-auth";
+import { signOut } from "next-auth/react";
+import Image from "next/image";
+import Link from "next/link";
+
+function UserAvatar({ name, src }: { name: Session["user"]["name"]; src?: string }) {
+ return (
+
+
+
+ {src ? (
+
+ ) : (
+
+ )}
+ Open popover
+
+
+
+
+ {name}
+
+
+
+ Profile
+
+
+
+ Theme
+
+
+
+
+ signOut()}>Logout
+
+
+
+
+ );
+}
+
+export { UserAvatar };
diff --git a/components/user/user-edit-salary-details-dialog.tsx b/components/user/user-edit-salary-details-dialog.tsx
new file mode 100644
index 0000000..0ed90b7
--- /dev/null
+++ b/components/user/user-edit-salary-details-dialog.tsx
@@ -0,0 +1,44 @@
+"use client";
+
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger
+} from "@/components/ui/dialog";
+import { Session } from "next-auth";
+import { Icons } from "../icons";
+import { UserSalaryDetailsForm } from "./user-salary-details-form";
+
+export function UserEditSalaryDetailsDialog({ user }: { user: Session["user"] }) {
+ return (
+
+
+
+
+ Edit salary
+
+
+
+
+ Edit salary details
+
+ Make changes to your profile here. Click save when you're done.
+
+
+
+
+
+ );
+}
diff --git a/components/user/user-edit-work-day-detail-dialog.tsx b/components/user/user-edit-work-day-detail-dialog.tsx
new file mode 100644
index 0000000..37111c7
--- /dev/null
+++ b/components/user/user-edit-work-day-detail-dialog.tsx
@@ -0,0 +1,38 @@
+"use client";
+
+import { CalendarDay as CalendarDayType, CalendarEntries } from "@/types";
+import { CalendarDay } from "../calendar/calendar-day";
+import { Dialog, DialogContent, DialogDescription, DialogTitle, DialogTrigger } from "../ui/dialog";
+import { UserWorkDayDetailForm } from "./user-work-day-detail-form";
+
+export function UserEditWorkDayDetailDialog({
+ calendarDay,
+ big = false,
+ holidayInfos = [],
+ ...other
+}: {
+ calendarDay: CalendarEntries;
+ big?: boolean;
+ holidayInfos?: CalendarDayType[];
+}) {
+ return (
+
+
+
+
+
+ {calendarDay.formattedDate}
+ Edit work day details
+
+
+
+ );
+}
diff --git a/components/user/user-salary-details-form.tsx b/components/user/user-salary-details-form.tsx
new file mode 100644
index 0000000..50d8ed7
--- /dev/null
+++ b/components/user/user-salary-details-form.tsx
@@ -0,0 +1,168 @@
+"use client";
+
+import { toast } from "@/hooks/use-toast";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { User } from "@prisma/client";
+import { useRouter } from "next/navigation";
+import { useForm } from "react-hook-form";
+import * as z from "zod";
+
+import { Icons } from "@/components/icons";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { cn } from "@/lib/utils";
+import { userSalaryDetailSchema } from "@/lib/validations/user";
+import { useState, useTransition, type HTMLAttributes } from "react";
+import { Show } from "../ui/show";
+
+interface UserSalaryDetailsFormProps extends HTMLAttributes {
+ user: {
+ id: User["id"];
+ commission: number;
+ hourlyRate: number;
+ tax: number;
+ workHours: number;
+ };
+}
+
+type FormData = z.infer;
+
+export function UserSalaryDetailsForm({ user, className, ...props }: UserSalaryDetailsFormProps) {
+ const router = useRouter();
+ const [isPending, startTransition] = useTransition();
+
+ const {
+ handleSubmit,
+ register,
+ formState: { errors }
+ } = useForm({
+ resolver: zodResolver(userSalaryDetailSchema),
+ defaultValues: {
+ commission: user.commission,
+ hourlyRate: user.hourlyRate,
+ tax: user.tax,
+ workHours: user.workHours
+ }
+ });
+ const [isSaving, setIsSaving] = useState(false);
+
+ async function onSubmit(data: FormData) {
+ setIsSaving(true);
+
+ const response = await fetch(`/api/user/${user.id}`, {
+ method: "PUT",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ commission: data.commission,
+ hourlyRate: data.hourlyRate,
+ tax: data.tax,
+ workHours: data.workHours
+ })
+ });
+
+ setIsSaving(false);
+
+ if (!response?.ok) {
+ return toast({
+ title: "Something went wrong.",
+ description: "Your salary details was not updated. Please try again.",
+ variant: "destructive"
+ });
+ }
+
+ toast({
+ description: "Your salary details has been updated.",
+ duration: 5000,
+ variant: "success"
+ });
+
+ // start transition
+ startTransition(() => {
+ // Refresh the current route and fetch new data from the server without
+ // losing client-side browser or React state.
+ router.refresh();
+ });
+ }
+
+ return (
+
+
+
Hourly rate
+
+ {errors?.hourlyRate && (
+
{errors.hourlyRate.message}
+ )}
+
+
+
Commission
+
+ {errors?.commission && (
+
{errors.commission.message}
+ )}
+
+
+
Tax
+
+ {errors?.tax &&
{errors.tax.message}
}
+
+
+
Work hours
+
+ {errors?.workHours && (
+
{errors.workHours.message}
+ )}
+
+
+ Save
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/components/user/user-work-day-detail-form.tsx b/components/user/user-work-day-detail-form.tsx
new file mode 100644
index 0000000..c64e418
--- /dev/null
+++ b/components/user/user-work-day-detail-form.tsx
@@ -0,0 +1,209 @@
+"use client";
+
+import { toast } from "@/hooks/use-toast";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { useRouter } from "next/navigation";
+import { useForm } from "react-hook-form";
+import * as z from "zod";
+
+import { Icons } from "@/components/icons";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { cn } from "@/lib/utils";
+import { userWorkDayDetailSchema } from "@/lib/validations/user";
+import { UserWorkDayDetail } from "@/types";
+import { useEffect, useState, useTransition, type HTMLAttributes } from "react";
+import { Checkbox } from "../ui/checkbox";
+import { InfoButton } from "../ui/info-button";
+import Link from "../ui/link";
+import { Show } from "../ui/show";
+
+interface UserWorkDayDetailFormProps extends HTMLAttributes {
+ date: string;
+ userWorkDayDetail?: UserWorkDayDetail;
+}
+
+type FormData = z.infer;
+
+export function UserWorkDayDetailForm({
+ date,
+ userWorkDayDetail,
+ className,
+ ...props
+}: UserWorkDayDetailFormProps) {
+ const router = useRouter();
+ const [isPending, startTransition] = useTransition();
+
+ const {
+ handleSubmit,
+ register,
+ formState: { errors },
+ watch,
+ setValue
+ } = useForm({
+ resolver: zodResolver(userWorkDayDetailSchema),
+ defaultValues: {
+ id: userWorkDayDetail?.id,
+ date: date,
+ nonCommissionedHours: userWorkDayDetail?.nonCommissionedHours ?? 0,
+ extraHours: userWorkDayDetail?.extraHours ?? 0,
+ sickDay: userWorkDayDetail?.sickDay ?? false
+ }
+ });
+ const [isSaving, setIsSaving] = useState(false);
+
+ const nonCommissionedHours = watch("nonCommissionedHours");
+
+ useEffect(() => {
+ if (nonCommissionedHours <= 0) {
+ setValue("sickDay", false);
+ }
+ }, [nonCommissionedHours, setValue]);
+
+ async function onSubmit(data: FormData) {
+ setIsSaving(true);
+
+ const response = await fetch(`/api/user/work-day-detail`, {
+ method: "PATCH",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ id: data.id,
+ date: data.date,
+ nonCommissionedHours: data.nonCommissionedHours,
+ extraHours: data.extraHours,
+ sickDay: data.sickDay
+ })
+ });
+
+ setIsSaving(false);
+
+ if (!response?.ok) {
+ return toast({
+ title: "Something went wrong.",
+ description: "Your salary details was not updated. Please try again.",
+ variant: "destructive"
+ });
+ }
+
+ toast({
+ description: "Your salary details has been updated.",
+ duration: 5000,
+ variant: "success"
+ });
+
+ // start transition
+ startTransition(() => {
+ // Refresh the current route and fetch new data from the server without
+ // losing client-side browser or React state.
+ router.refresh();
+ });
+ }
+
+ return (
+
+ {
+ const number = Number(value);
+ return Number.isNaN(number) ? undefined : number;
+ }
+ })}
+ />
+
+
+
Non commissioned hours
+
+
+ 0 ? "destructive" : "outline"}
+ onClick={() => setValue("nonCommissionedHours", nonCommissionedHours > 0 ? 0 : 7.5)}
+ >
+
+
+ 7.5
+
+ 0}>
+
+ {nonCommissionedHours}
+
+
+
+ {errors?.nonCommissionedHours && (
+
{errors.nonCommissionedHours.message}
+ )}
+
+ 0
+ })}
+ >
+
setValue("sickDay", Boolean(checked))}
+ {...register("sickDay", {})}
+ />
+ Send as sick hours?
+
+
+ Sick leave or self-reported sickness grants payment upward limited to 6G. You can read
+ more in our{" "}
+
+ personal handbook
+
+ .
+
+
+ {errors?.sickDay && {errors.sickDay.message}
}
+
+
+
Extra hours
+
+ {errors?.extraHours && (
+
{errors.extraHours.message}
+ )}
+
+
+
+ Save
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/components/user/userAvatar.tsx b/components/user/userAvatar.tsx
deleted file mode 100644
index cfa049f..0000000
--- a/components/user/userAvatar.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-/* eslint-disable @next/next/no-img-element */
-/* next does not yet support blob usage in next image component */
-/* active PR: https://github.com/vercel/next.js/pull/23622 */
-import { Box } from "@/components/ui";
-import { useUser, useUserAvatar } from "@/components/user/hooks";
-import * as React from "react";
-import { styled } from "stitches.config";
-
-const AvatarImage = styled("img", {
- height: "100%",
- width: "100%"
-});
-
-const AvatarImageFallback = styled("div", {
- display: "flex",
- justifyContent: "center",
- alignItems: "center",
- height: "100%",
- width: "100%",
- backgroundColor: "$avatarImageFallback",
- color: "$text",
- fontSize: "$3",
- textTransform: "uppercase",
- letterSpacing: "$avatarImageFallback"
-});
-
-const UserAvatar = () => {
- const { user } = useUser();
- const { userAvatarSrc } = useUserAvatar();
- const [imageSrc, setImageSrc] = React.useState(undefined);
-
- // https://nextjs.org/docs/messages/react-hydration-error
- React.useEffect(() => setImageSrc(userAvatarSrc), [userAvatarSrc]);
-
- const initials = React.useMemo(
- () =>
- user?.name
- ?.split(" ")
- ?.map(name => name[0])
- ?.slice(0, 3)
- ?.join(""),
- [user?.name]
- );
-
- return (
-
- {imageSrc ? (
-
- ) : (
- {initials}
- )}
-
- );
-};
-
-export default UserAvatar;
diff --git a/components/user/userAvatarPopover.tsx b/components/user/userAvatarPopover.tsx
deleted file mode 100644
index 127dc3f..0000000
--- a/components/user/userAvatarPopover.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import {
- Button,
- Heading,
- IconButton,
- Li,
- Link,
- Popover,
- PopoverArrow,
- PopoverContent,
- PopoverTrigger,
- Separator,
- Ul
-} from "@/components/ui";
-import { signOut } from "next-auth/react";
-import * as React from "react";
-import { styled } from "stitches.config";
-import { useUser } from ".";
-import { ThemeSelector } from "../theme";
-import UserAvatar from "./userAvatar";
-
-const UserAvatarPopoverSection = styled("section", {
- padding: "0 $5",
- "&:nth-child(1)": {
- backgroundColor: "$gray-light"
- }
-});
-
-const UserAvatarPopover = ({}) => {
- const { user } = useUser();
- const [open, setOpen] = React.useState(false);
-
- return (
- setOpen(state)}>
-
-
-
-
-
-
-
- {user.name}
-
-
-
-
-
- setOpen(false)}>
- Profile
-
-
-
- setOpen(false)}>
- Feedback
-
-
-
-
-
-
-
-
-
-
- signOut()}>Logout
-
-
-
-
- );
-};
-
-export default UserAvatarPopover;
diff --git a/components/user/userNavDetails.tsx b/components/user/userNavDetails.tsx
deleted file mode 100644
index 6c340c6..0000000
--- a/components/user/userNavDetails.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { useSalary } from "@/components/salary";
-import { Box, Flex, Text } from "@/components/ui";
-import { useSession } from "next-auth/react";
-import * as React from "react";
-import UserAvatarPopover from "./userAvatarPopover";
-
-const UserNavDetails = () => {
- const { data: session } = useSession();
- const { nextPayDayStatistics } = useSalary();
-
- if (!session) {
- return null;
- }
-
- return (
- <>
-
-
-
- Next paycheck
-
- {nextPayDayStatistics.payDay}
-
- {nextPayDayStatistics.netFormatted}
-
- {nextPayDayStatistics.halfTax ? (
-
- Half tax
-
- ) : null}
-
-
-
-
-
- >
- );
-};
-
-export default UserNavDetails;
diff --git a/components/user/userProfile.tsx b/components/user/userProfile.tsx
deleted file mode 100644
index e401d26..0000000
--- a/components/user/userProfile.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { Box, Form, Heading, TextField } from "@/components/ui";
-import { useUser } from "@/components/user/hooks";
-import * as React from "react";
-import { useForm } from "react-hook-form";
-
-const UserProfile = () => {
- const { user, update } = useUser();
- const { register, setValue, watch } = useForm();
-
- const { hourlyRate, commission, tax, workHours } = watch();
-
- React.useEffect(() => {
- setValue("hourlyRate", user?.hourlyRate);
- setValue("commission", user?.commission);
- setValue("tax", user?.tax);
- setValue("workHours", user?.workHours);
- }, [user, setValue]);
-
- React.useEffect(() => {
- const newHourlyRate = +(hourlyRate ?? user?.hourlyRate ?? 0);
- const newCommission = +(commission ?? user?.commission ?? 0);
- const newTax = +(tax ?? user?.tax ?? 0);
- const newWorkHours = +(workHours ?? user?.workHours ?? 0);
-
- async function persistUser() {
- if (
- user &&
- newHourlyRate > 0 &&
- newCommission > 0 &&
- newTax > 0 &&
- newWorkHours > 0 &&
- (newHourlyRate !== user.hourlyRate ||
- newCommission !== user.commission ||
- newTax !== user.tax ||
- newWorkHours !== user.workHours)
- ) {
- await update({
- hourlyRate: newHourlyRate,
- commission: newCommission,
- tax: newTax,
- workHours: newWorkHours
- });
- }
- }
-
- persistUser();
- }, [user, hourlyRate, commission, tax, workHours, update]);
-
- return (
-
- Your salary settings
-
-
-
-
-
-
-
- );
-};
-
-export default UserProfile;
diff --git a/components/user/userWorkDayDetails.tsx b/components/user/userWorkDayDetails.tsx
deleted file mode 100644
index 4fd30a7..0000000
--- a/components/user/userWorkDayDetails.tsx
+++ /dev/null
@@ -1,223 +0,0 @@
-import {
- AppearInBox,
- Button,
- Flex,
- Form,
- InfoButton,
- Link,
- Text,
- TextField
-} from "@/components/ui";
-import { useUser } from "@/components/user/hooks";
-import { CalendarDay, UserWorkDayDetail } from "@/types";
-import * as React from "react";
-import { useForm } from "react-hook-form";
-import { ControlledCheckbox } from "../form";
-
-function upsertWorkDayDetail(
- workDayDetails: UserWorkDayDetail[] = [],
- workDayDetail: UserWorkDayDetail
-): UserWorkDayDetail[] {
- if (!workDayDetail) {
- return workDayDetails;
- }
-
- const newWorkDayDetails = [...workDayDetails];
-
- const i = workDayDetails.findIndex(item => item.date === workDayDetail.date);
-
- if (i === -1) {
- newWorkDayDetails.push(workDayDetail);
- return newWorkDayDetails;
- }
-
- return newWorkDayDetails.map(item => {
- if (item.date === workDayDetail.date) {
- return {
- ...item,
- nonCommissionedHours: workDayDetail.nonCommissionedHours,
- extraHours: workDayDetail.extraHours,
- sickDay: workDayDetail.sickDay
- };
- }
-
- return { ...item };
- });
-}
-
-export default function UserWorkDayDetails({ day }: { day: CalendarDay }) {
- const { user, update } = useUser();
- const { register, setValue, watch, control } = useForm();
-
- const { nonCommissionedHours, extraHours, sickDay } = watch();
-
- const workDayDetail = React.useMemo(
- () => user?.workDayDetails?.find(workDayDetail => workDayDetail.date === day.formattedDate),
- [user?.workDayDetails, day.formattedDate]
- );
-
- const isNonCommissionedToggled = React.useMemo(
- () => (workDayDetail?.nonCommissionedHours ?? 0) > 0,
- [workDayDetail?.nonCommissionedHours]
- );
-
- const { userNonCommissionedHours, userExtraHours, userSickDay } = React.useMemo(() => {
- return {
- userNonCommissionedHours: +(workDayDetail?.nonCommissionedHours ?? 0),
- userExtraHours: +(workDayDetail?.extraHours ?? 0),
- userSickDay: workDayDetail?.sickDay ?? false
- };
- }, [workDayDetail]);
-
- React.useEffect(() => {
- setValue("nonCommissionedHours", userNonCommissionedHours);
- setValue("extraHours", userExtraHours);
- setValue("sickDay", userSickDay);
- }, [userNonCommissionedHours, userExtraHours, userSickDay, day.formattedDate, setValue]);
-
- const isKnowitClosedDay = React.useMemo(
- () =>
- day.isKnowitClosed &&
- day.name.toUpperCase() !== "SATURDAY" &&
- day.name.toUpperCase() !== "SUNDAY",
- [day.isKnowitClosed, day.name]
- );
-
- React.useEffect(() => {
- async function persistUser() {
- if (
- extraHours !== undefined &&
- nonCommissionedHours !== undefined &&
- sickDay !== undefined &&
- (nonCommissionedHours !== userNonCommissionedHours ||
- extraHours !== userExtraHours ||
- sickDay !== userSickDay)
- ) {
- const minZeroFixedExtraHours = Math.max(0, +(extraHours ?? 0));
- const minZeroFixedNonCommissionedHours = Math.max(0, +(nonCommissionedHours ?? 0));
-
- await update({
- workDayDetails: upsertWorkDayDetail(user?.workDayDetails ?? [], {
- id: 0,
- date: day.formattedDate,
- extraHours: minZeroFixedExtraHours,
- sickDay: minZeroFixedNonCommissionedHours > 0 && sickDay,
- nonCommissionedHours: minZeroFixedNonCommissionedHours,
- userId: 0
- })
- });
- }
- }
-
- persistUser();
- }, [
- user,
- userNonCommissionedHours,
- userExtraHours,
- userSickDay,
- nonCommissionedHours,
- extraHours,
- sickDay,
- day.formattedDate,
- update
- ]);
-
- return (
-
-
- {day.isWorkDay ? (
- <>
-
- 0}
- {...register("nonCommissionedHours")}
- labelSize="1"
- css={{
- maxWidth: "110px"
- }}
- fieldContainerCss={{
- marginBottom: "0"
- }}
- />
- 0}
- onClick={() =>
- setValue(
- "nonCommissionedHours",
- isNonCommissionedToggled ? "0" : user.workHours?.toString()
- )
- }
- css={{
- maxWidth: "60px",
- fontSize: "$2",
- alignSelf: "flex-end",
- height: "37px",
- marginBottom: "6px"
- }}
- >
- {isNonCommissionedToggled ? `-${nonCommissionedHours}` : `+${user.workHours}`}
-
-
-
-
-
-
-
- Sick leave or self-reported sickness grants payment upward limited to 6G. You
- can read more in our{" "}
-
- personal handbook
-
- .
-
-
-
-
- >
- ) : null}
- {isKnowitClosedDay ? (
-
- Knowit Experience is closed. Vacation days are not deducted for this day.
-
- ) : null}
-
-
- );
-}
diff --git a/constants/date-constants.ts b/constants/date-constants.ts
new file mode 100644
index 0000000..7a729cb
--- /dev/null
+++ b/constants/date-constants.ts
@@ -0,0 +1,116 @@
+export const MONTH_NAMES = {
+ JANUARY: "JANUARY",
+ FEBRUARY: "FEBRUARY",
+ MARCH: "MARCH",
+ APRIL: "APRIL",
+ MAY: "MAY",
+ JUNE: "JUNE",
+ JULY: "JULY",
+ AUGUST: "AUGUST",
+ SEPTEMBER: "SEPTEMBER",
+ OCTOBER: "OCTOBER",
+ NOVEMBER: "NOVEMBER",
+ DECEMBER: "DECEMBER"
+};
+
+export const MONTH_VALUES = {
+ JANUARY: 0,
+ FEBRUARY: 1,
+ MARCH: 2,
+ APRIL: 3,
+ MAY: 4,
+ JUNE: 5,
+ JULY: 6,
+ AUGUST: 7,
+ SEPTEMBER: 8,
+ OCTOBER: 9,
+ NOVEMBER: 10,
+ DECEMBER: 11
+};
+
+export const MONTH = {
+ JANUARY: {
+ value: 0,
+ i18n: {
+ en: "January",
+ no: "januar"
+ }
+ },
+ FEBRUARY: {
+ value: 1,
+ i18n: {
+ en: "February",
+ no: "februar"
+ }
+ },
+ MARCH: {
+ value: 2,
+ i18n: {
+ en: "March",
+ no: "mars"
+ }
+ },
+ APRIL: {
+ value: 3,
+ i18n: {
+ en: "April",
+ no: "april"
+ }
+ },
+ MAY: {
+ value: 4,
+ i18n: {
+ en: "May",
+ no: "mai"
+ }
+ },
+ JUNE: {
+ value: 5,
+ i18n: {
+ en: "June",
+ no: "juni"
+ }
+ },
+ JULY: {
+ value: 6,
+ i18n: {
+ en: "July",
+ no: "juli"
+ }
+ },
+ AUGUST: {
+ value: 7,
+ i18n: {
+ en: "August",
+ no: "august"
+ }
+ },
+ SEPTEMBER: {
+ value: 8,
+ i18n: {
+ en: "September",
+ no: "september"
+ }
+ },
+ OCTOBER: {
+ value: 9,
+ i18n: {
+ en: "October",
+ no: "oktober"
+ }
+ },
+ NOVEMBER: {
+ value: 10,
+ i18n: {
+ en: "November",
+ no: "november"
+ }
+ },
+ DECEMBER: {
+ value: 11,
+ i18n: {
+ en: "December",
+ no: "desember"
+ }
+ }
+};
diff --git a/constants/dateConstants.ts b/constants/dateConstants.ts
deleted file mode 100644
index 3b4af17..0000000
--- a/constants/dateConstants.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-export const MONTH_NAMES = {
- JANUARY: "JANUARY",
- FEBRUARY: "FEBRUARY",
- MARCH: "MARCH",
- APRIL: "APRIL",
- MAY: "MAY",
- JUNE: "JUNE",
- JULY: "JULY",
- AUGUST: "AUGUST",
- SEPTEMBER: "SEPTEMBER",
- OCTOBER: "OCTOBER",
- NOVEMBER: "NOVEMBER",
- DECEMBER: "DECEMBER"
-};
-
-export const MONTH_VALUES = {
- JANUARY: 0,
- FEBRUARY: 1,
- MARCH: 2,
- APRIL: 3,
- MAY: 4,
- JUNE: 5,
- JULY: 6,
- AUGUST: 7,
- SEPTEMBER: 8,
- OCTOBER: 9,
- NOVEMBER: 10,
- DECEMBER: 11
-};
diff --git a/constants/defaultUserSalary.ts b/constants/default-user-salary.ts
similarity index 100%
rename from constants/defaultUserSalary.ts
rename to constants/default-user-salary.ts
diff --git a/constants/earningConstants.ts b/constants/earning-constants.ts
similarity index 100%
rename from constants/earningConstants.ts
rename to constants/earning-constants.ts
diff --git a/constants/theme-constants.ts b/constants/theme-constants.ts
new file mode 100644
index 0000000..82aafb1
--- /dev/null
+++ b/constants/theme-constants.ts
@@ -0,0 +1,40 @@
+type Theme = {
+ value: string;
+ i18n: {
+ en: string;
+ no: string;
+ };
+};
+
+type ThemeConstants = {
+ LIGHT: Theme;
+ DARK: Theme;
+ SYSTEM: Theme;
+};
+
+const THEME_CONSTANTS: ThemeConstants = {
+ LIGHT: {
+ value: "light",
+ i18n: {
+ en: "Light",
+ no: "Lys"
+ }
+ },
+ DARK: {
+ value: "dark",
+ i18n: {
+ en: "Dark",
+ no: "Mørk"
+ }
+ },
+ SYSTEM: {
+ value: "system",
+ i18n: {
+ en: "System",
+ no: "System"
+ }
+ }
+};
+
+export { THEME_CONSTANTS };
+export type { ThemeConstants, Theme };
diff --git a/environment.d.ts b/environment.d.ts
new file mode 100644
index 0000000..72545e2
--- /dev/null
+++ b/environment.d.ts
@@ -0,0 +1,26 @@
+declare global {
+ namespace NodeJS {
+ interface ProcessEnv {
+ SHOW_ME_THE_MONEY_SPECIALISTS_ONLY_MODE: string;
+ NEXT_PUBLIC_SALARY_DEFAULT_HOURLY_RATE: string;
+ NEXT_PUBLIC_SALARY_DEFAULT_COMMISSION: string;
+ NEXT_PUBLIC_SALARY_DEFAULT_TAX: string;
+ NEXT_PUBLIC_SALARY_DEFAULT_WORK_HOURS: string;
+ NEXTAUTH_URL: string;
+ NEXTAUTH_SECRET: string;
+ NEXTAUTH_AZURE_AD_CLIENT_ID: string;
+ NEXTAUTH_AZURE_AD_TENANT_ID: string;
+ NEXTAUTH_AZURE_AD_SECRET: string;
+ AZURE_AD_ADMIN_GROUP_ID: string;
+ AZURE_AD_SPECIALIST_GROUP_ID: string;
+ DATABASE_URL: string;
+ SHADOW_DATABASE_URL: string;
+ CV_PARTNER_API_KEY: string;
+ }
+ }
+}
+
+// If this file has no import/export statements (i.e. is a script)
+// convert it into a module by adding an empty export statement.
+export { };
+
diff --git a/hooks/use-toast.ts b/hooks/use-toast.ts
new file mode 100644
index 0000000..95074db
--- /dev/null
+++ b/hooks/use-toast.ts
@@ -0,0 +1,186 @@
+// Inspired by react-hot-toast library
+import * as React from "react";
+
+import { ToastActionElement, type ToastProps } from "@/components/ui/toast";
+
+const TOAST_LIMIT = 1;
+const TOAST_REMOVE_DELAY = 1000;
+
+type ToasterToast = ToastProps & {
+ id: string;
+ title?: React.ReactNode;
+ description?: React.ReactNode;
+ action?: ToastActionElement;
+};
+
+const actionTypes = {
+ ADD_TOAST: "ADD_TOAST",
+ UPDATE_TOAST: "UPDATE_TOAST",
+ DISMISS_TOAST: "DISMISS_TOAST",
+ REMOVE_TOAST: "REMOVE_TOAST"
+} as const;
+
+let count = 0;
+
+function genId() {
+ count = (count + 1) % Number.MAX_VALUE;
+ return count.toString();
+}
+
+type ActionType = typeof actionTypes;
+
+type Action =
+ | {
+ type: ActionType["ADD_TOAST"];
+ toast: ToasterToast;
+ }
+ | {
+ type: ActionType["UPDATE_TOAST"];
+ toast: Partial;
+ }
+ | {
+ type: ActionType["DISMISS_TOAST"];
+ toastId?: ToasterToast["id"];
+ }
+ | {
+ type: ActionType["REMOVE_TOAST"];
+ toastId?: ToasterToast["id"];
+ };
+
+interface State {
+ toasts: ToasterToast[];
+}
+
+const toastTimeouts = new Map>();
+
+const addToRemoveQueue = (toastId: string) => {
+ if (toastTimeouts.has(toastId)) {
+ return;
+ }
+
+ const timeout = setTimeout(() => {
+ toastTimeouts.delete(toastId);
+ dispatch({
+ type: "REMOVE_TOAST",
+ toastId: toastId
+ });
+ }, TOAST_REMOVE_DELAY);
+
+ toastTimeouts.set(toastId, timeout);
+};
+
+export const reducer = (state: State, action: Action): State => {
+ switch (action.type) {
+ case "ADD_TOAST":
+ return {
+ ...state,
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT)
+ };
+
+ case "UPDATE_TOAST":
+ return {
+ ...state,
+ toasts: state.toasts.map(t => (t.id === action.toast.id ? { ...t, ...action.toast } : t))
+ };
+
+ case "DISMISS_TOAST":
+ const { toastId } = action;
+
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
+ // but I'll keep it here for simplicity
+ if (toastId) {
+ addToRemoveQueue(toastId);
+ } else {
+ state.toasts.forEach(toast => {
+ addToRemoveQueue(toast.id);
+ });
+ }
+
+ return {
+ ...state,
+ toasts: state.toasts.map(t =>
+ t.id === toastId || toastId === undefined
+ ? {
+ ...t,
+ open: false
+ }
+ : t
+ )
+ };
+ case "REMOVE_TOAST":
+ if (action.toastId === undefined) {
+ return {
+ ...state,
+ toasts: []
+ };
+ }
+ return {
+ ...state,
+ toasts: state.toasts.filter(t => t.id !== action.toastId)
+ };
+ }
+};
+
+const listeners: Array<(state: State) => void> = [];
+
+let memoryState: State = { toasts: [] };
+
+function dispatch(action: Action) {
+ memoryState = reducer(memoryState, action);
+ listeners.forEach(listener => {
+ listener(memoryState);
+ });
+}
+
+interface Toast extends Omit {}
+
+function toast({ ...props }: Toast) {
+ const id = genId();
+
+ const update = (props: ToasterToast) =>
+ dispatch({
+ type: "UPDATE_TOAST",
+ toast: { ...props, id }
+ });
+ const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
+
+ dispatch({
+ type: "ADD_TOAST",
+ toast: {
+ ...props,
+ id,
+ open: true,
+ onOpenChange: open => {
+ if (!open) dismiss();
+ }
+ }
+ });
+
+ return {
+ id: id,
+ dismiss,
+ update
+ };
+}
+
+function useToast() {
+ const [state, setState] = React.useState(memoryState);
+
+ React.useEffect(() => {
+ listeners.push(setState);
+ return () => {
+ const index = listeners.indexOf(setState);
+ if (index > -1) {
+ listeners.splice(index, 1);
+ }
+ };
+ }, [state]);
+
+ return {
+ ...state,
+ toast,
+ dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId })
+ };
+}
+
+export { useToast, toast };
diff --git a/lib/auth.ts b/lib/auth.ts
new file mode 100644
index 0000000..6b75384
--- /dev/null
+++ b/lib/auth.ts
@@ -0,0 +1,232 @@
+import prisma from "@/lib/prisma";
+import { validateEmail } from "@/logic/validation-logic";
+import { AzureAdTokenClaims, GraphUser } from "@/types";
+import { fetchWithToken } from "@/utils/fetcher";
+import jwt_decode from "jwt-decode";
+import { Account, NextAuthOptions, User } from "next-auth";
+import { JWT } from "next-auth/jwt";
+import AzureAdProvider from "next-auth/providers/azure-ad";
+
+const AZURE_AD_CLIENT_ID = process.env.NEXTAUTH_AZURE_AD_CLIENT_ID;
+const AZURE_AD_TENANT_ID = process.env.NEXTAUTH_AZURE_AD_TENANT_ID;
+const AZURE_AD_SECRET = process.env.NEXTAUTH_AZURE_AD_SECRET;
+const AZURE_AD_SCOPE = "offline_access openid User.Read";
+
+const AZURE_AD_ADMIN_GROUP_ID = process.env.AZURE_AD_ADMIN_GROUP_ID;
+const AZURE_AD_SPECIALIST_GROUP_ID = process.env.AZURE_AD_SPECIALIST_GROUP_ID;
+
+const getAzureAdTokenClaims = (token: string): AzureAdTokenClaims => {
+ return jwt_decode(token) as AzureAdTokenClaims;
+};
+
+const getUserEmail = async (accessToken: string): Promise => {
+ const claims = getAzureAdTokenClaims(accessToken);
+
+ // If claims contains unique principal name then use that
+ if (claims.upn && validateEmail(claims.upn)) {
+ return claims.upn;
+ }
+
+ // Fetch user profile from graph and retrieve email
+ const response = await fetch(`https://graph.microsoft.com/v1.0/me/`, {
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to fetch user email");
+ }
+
+ const user: GraphUser = await response.json();
+
+ return user.mail ?? user.userPrincipalName;
+};
+
+const accessToGroupId = (groups: { id: string }[] = [], groupId: string) => {
+ if (groups === null || groups === undefined) {
+ return false;
+ }
+
+ if (Array.isArray(groups) && !(groups.length > 0)) {
+ return false;
+ }
+
+ if (groupId === null || groupId === undefined) {
+ return false;
+ }
+
+ return groups.some(group => group?.id?.toLowerCase() === groupId.toLowerCase());
+};
+
+const accessToAdminGroup = groups => accessToGroupId(groups, AZURE_AD_ADMIN_GROUP_ID);
+const accessToSpecialistGroup = groups => accessToGroupId(groups, AZURE_AD_SPECIALIST_GROUP_ID);
+
+async function getUserRoles(token: string) {
+ try {
+ const { value: groups } = await fetchWithToken(
+ "https://graph.microsoft.com/v1.0/me/memberOf?$select=displayName,id",
+ token
+ );
+
+ const isSpecialist = accessToSpecialistGroup(groups);
+
+ return {
+ // To determine the admin role the user needs to have access
+ // to the specialist and admin groups. Could revalidate this to
+ // only requiring access to admin group in the future.
+ isAdmin: isSpecialist && accessToAdminGroup(groups),
+ isSpecialist
+ };
+ } catch (error) {
+ console.error(error);
+
+ // If group lookup somehow crashes then return false
+ return {
+ isAdmin: false,
+ isSpecialist: false
+ };
+ }
+}
+
+async function initialSignIn(
+ account: Account & { accessToken?: string; refreshToken?: string; ext_expires_in?: number },
+ user: User,
+ token: JWT
+): Promise {
+ if (
+ !account?.access_token ||
+ !token?.sub ||
+ !user?.email ||
+ !user?.name ||
+ !account?.ext_expires_in
+ ) {
+ return;
+ }
+
+ const { isAdmin, isSpecialist } = await getUserRoles(account.access_token);
+
+ await prisma.user.upsert({
+ create: {
+ name: user.name,
+ email: user.email,
+ activeDirectoryId: token.sub,
+ refreshToken: account.refresh_token,
+ accessTokenExpires: Date.now() + account?.ext_expires_in * 1000,
+ isAdmin: isAdmin,
+ isSpecialist: isSpecialist
+ },
+ update: {
+ refreshToken: account.refresh_token,
+ isAdmin: isAdmin,
+ isSpecialist: isSpecialist
+ },
+ where: {
+ activeDirectoryId: token.sub
+ }
+ });
+}
+
+export const authOptions: NextAuthOptions = {
+ session: {
+ strategy: "jwt",
+ // max age 90 days
+ maxAge: 90 * 24 * 60 * 60
+ },
+ jwt: {
+ // max age 90 days
+ maxAge: 90 * 24 * 60 * 60
+ },
+ providers: [
+ AzureAdProvider({
+ clientId: AZURE_AD_CLIENT_ID,
+ clientSecret: AZURE_AD_SECRET,
+ // scope: AZURE_AD_SCOPE,
+ tenantId: AZURE_AD_TENANT_ID,
+ profilePhotoSize: 48,
+ authorization: {
+ params: {
+ scope: AZURE_AD_SCOPE
+ }
+ },
+ idToken: true,
+ checks: "state",
+ // next-auth v4 used sub claim for user id, but we need to use the oid claim
+ profile: async (profile, tokens) => {
+ if (!tokens?.access_token) {
+ return profile;
+ }
+
+ // Fetch user image
+ // https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0#examples
+
+ const claims = getAzureAdTokenClaims(tokens.access_token);
+
+ return {
+ id: claims.oid,
+ name:
+ profile.name ??
+ claims?.name ??
+ `${claims?.given_name}${claims?.family_name ? ` ${claims?.family_name}` : ""}`,
+ email: profile.email ?? (await getUserEmail(tokens.access_token))
+ };
+ }
+ })
+ ],
+ callbacks: {
+ async jwt({ token, user, account, profile, isNewUser }) {
+ // Initial sign in
+ if (account && user) {
+ await initialSignIn(account, user, token);
+ }
+
+ const dbUser = await prisma.user.findUnique({
+ where: {
+ activeDirectoryId: token.sub
+ },
+ include: {
+ feedback: false,
+ workDayDetails: false
+ }
+ });
+
+ return {
+ ...token,
+ id: dbUser?.id?.toString() ?? token.id,
+ email: dbUser?.email ?? token.email,
+ name: dbUser?.name ?? token.name,
+ isAdmin: dbUser?.isAdmin ?? false,
+ isSpecialist: dbUser?.isSpecialist ?? false,
+ activeDirectoryId: dbUser?.activeDirectoryId ?? token.sub ?? "unknown",
+ commission:
+ dbUser?.commission?.toNumber() ?? +(+process.env.NEXT_PUBLIC_SALARY_DEFAULT_COMMISSION),
+ hourlyRate: dbUser?.hourlyRate ?? +process.env.NEXT_PUBLIC_SALARY_DEFAULT_HOURLY_RATE,
+ tax: dbUser?.tax?.toNumber() ?? +process.env.NEXT_PUBLIC_SALARY_DEFAULT_TAX,
+ workHours:
+ dbUser?.workHours?.toNumber() ?? +process.env.NEXT_PUBLIC_SALARY_DEFAULT_WORK_HOURS
+ };
+ },
+ async session({ token, session }) {
+ if (token) {
+ session.user.id = token.id;
+ session.user.name = token.name;
+ session.user.email = token.email;
+ session.user.image = token.picture;
+ session.user.isAdmin = token.isAdmin;
+ session.user.isSpecialist = token.isSpecialist;
+ session.user.activeDirectoryId = token.activeDirectoryId;
+ session.user.commission = token.commission;
+ session.user.hourlyRate = token.hourlyRate;
+ session.user.tax = token.tax;
+ session.user.workHours = token.workHours;
+ }
+
+ return session;
+ }
+ },
+ secret: process.env.NEXTAUTH_SECRET,
+ pages: {
+ signIn: "/login"
+ },
+ debug: true
+};
diff --git a/lib/date.ts b/lib/date.ts
new file mode 100644
index 0000000..3ec20bf
--- /dev/null
+++ b/lib/date.ts
@@ -0,0 +1,5 @@
+"server-only";
+
+import { cache } from "react";
+
+export const getRequestDateNow = cache(() => new Date());
diff --git a/lib/index.ts b/lib/index.ts
deleted file mode 100644
index 01076e7..0000000
--- a/lib/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import prismaUser from "./prismaUser";
-
-export { prismaUser };
diff --git a/lib/prismaUser.ts b/lib/prismaUser.ts
deleted file mode 100644
index 4061723..0000000
--- a/lib/prismaUser.ts
+++ /dev/null
@@ -1,134 +0,0 @@
-import DEFAULT_USER_SALARY from "@/constants/defaultUserSalary";
-import { Unpacked, User } from "@/types";
-import { getFormattedIsoDateAndTime } from "@/utils/dateUtils";
-import { Prisma } from "@prisma/client";
-import prisma from "./prisma";
-
-export type PrismaUser = Unpacked>;
-
-// Convert decimals to number
-const createPrismaUser = (prismaUser: PrismaUser): User => {
- const { created, updated, ...user } = prismaUser;
- return {
- ...user,
- tax: +(user?.tax ?? DEFAULT_USER_SALARY.tax),
- commission: +(user?.commission ?? DEFAULT_USER_SALARY.commission),
- workHours: +(user?.workHours ?? DEFAULT_USER_SALARY.workHours),
- updated: getFormattedIsoDateAndTime(prismaUser.updated),
- created: getFormattedIsoDateAndTime(prismaUser.created),
- workDayDetails: (user?.workDayDetails ?? []).map(workDayDetail => ({
- ...workDayDetail,
- extraHours: +(workDayDetail?.extraHours ?? 0),
- nonCommissionedHours: +(workDayDetail?.nonCommissionedHours ?? 0)
- })),
- accessTokenExpires: Number(user?.accessTokenExpires ?? 0)
- };
-};
-
-const getAll = async () =>
- await prisma.user.findMany({
- include: {
- workDayDetails: true
- }
- });
-
-const get = async (includeWorkDayDetails = false): Promise => {
- const entries = await prisma.user.findMany({
- orderBy: {
- updated: "desc"
- },
- include: {
- workDayDetails: includeWorkDayDetails
- }
- });
-
- return entries.map(entry => createPrismaUser(entry));
-};
-
-const getById = async (
- id: number,
- options = {
- include: {
- workDayDetails: true
- }
- }
-): Promise => {
- const user = await prisma.user.findUnique({
- where: {
- id: +id
- },
- ...options
- });
-
- return createPrismaUser(user);
-};
-
-const getByActiveDirectoryId = async (
- activeDirectoryId: string,
- options = {
- include: {
- workDayDetails: true
- }
- }
-): Promise => {
- const user = await prisma.user.findUnique({
- where: {
- activeDirectoryId: activeDirectoryId
- },
- ...options
- });
-
- return createPrismaUser(user);
-};
-
-const getByEmail = async (
- email: string,
- options = {
- include: {
- workDayDetails: true
- }
- }
-): Promise => {
- const user = await prisma.user.findUnique({
- where: {
- email: email
- },
- ...options
- });
-
- return createPrismaUser(user);
-};
-
-const deleteById = async (id: number): Promise => {
- await prisma.user.delete({
- where: {
- id: +id
- }
- });
-
- await prisma.userWorkDayDetail.deleteMany({
- where: {
- userId: +id
- }
- });
-};
-
-const update = async (...parameters: Parameters): Promise => {
- await prisma.user.update(...parameters);
-};
-
-const upsert = async (...parameters: Parameters): Promise => {
- await prisma.user.upsert(...parameters);
-};
-
-const prismaUser = {
- get,
- getByActiveDirectoryId,
- getByEmail,
- getById,
- deleteById,
- update,
- upsert
-};
-
-export default prismaUser;
diff --git a/lib/session.ts b/lib/session.ts
new file mode 100644
index 0000000..86387ba
--- /dev/null
+++ b/lib/session.ts
@@ -0,0 +1,12 @@
+import { getServerSession } from "next-auth/next";
+import { authOptions } from "./auth";
+
+export async function getSession() {
+ return await getServerSession(authOptions);
+}
+
+export async function getCurrentUser() {
+ const session = await getSession();
+
+ return session?.user;
+}
diff --git a/lib/user.ts b/lib/user.ts
new file mode 100644
index 0000000..fa9909b
--- /dev/null
+++ b/lib/user.ts
@@ -0,0 +1,130 @@
+import "server-only";
+
+import { getCalendarMonth, getCalendarYear } from "@/utils/calendar-utils";
+import { getUserEarningsDetails } from "@/utils/user-utils";
+import { cache } from "react";
+import prisma from "./prisma";
+
+const getUserWithWorkDayDetails = cache(async (activeDirectoryId: string) => {
+ const user = await prisma.user.findUnique({
+ where: {
+ activeDirectoryId: activeDirectoryId
+ },
+ include: {
+ workDayDetails: true
+ }
+ });
+
+ return user;
+});
+
+const getUserEarnings = cache(async (activeDirectoryId: string, activeDate?: Date) => {
+ const user = await getUserWithWorkDayDetails(activeDirectoryId);
+
+ if (!user) {
+ return undefined;
+ }
+
+ const now = new Date();
+
+ const year = getCalendarYear(now.getFullYear());
+
+ const currentYear = new Date().getFullYear();
+
+ const lastYear = getCalendarYear(currentYear - 1);
+ const nextYear = getCalendarYear(currentYear + 1);
+
+ const currentMonth = activeDate ? activeDate.getMonth() : now.getMonth();
+
+ const month = getCalendarMonth(now);
+
+ const activeMonth = activeDate ? getCalendarMonth(activeDate) : month;
+
+ const lastMonth = getCalendarMonth(new Date(currentYear, currentMonth - 1));
+
+ const nextMonth = getCalendarMonth(new Date(currentYear, currentMonth + 1));
+
+ return getUserEarningsDetails(
+ {
+ commission: user.commission.toNumber(),
+ hourlyRate: user.hourlyRate,
+ tax: user.tax.toNumber(),
+ workHours: user.workHours.toNumber()
+ },
+ year,
+ nextYear,
+ activeMonth,
+ month,
+ lastMonth,
+ nextMonth,
+ (user.workDayDetails ?? []).map(x => ({
+ ...x,
+ extraHours: x.extraHours.toNumber(),
+ nonCommissionedHours: x.nonCommissionedHours.toNumber()
+ }))
+ );
+});
+
+const getUserAvatar = cache(async (activeDirectoryId: string) => {
+ const user = await prisma.user.findUnique({
+ where: {
+ activeDirectoryId: activeDirectoryId
+ },
+ select: {
+ refreshToken: true
+ }
+ });
+
+ if (!user?.refreshToken) {
+ return undefined;
+ }
+
+ const url = `https://login.microsoftonline.com/${process.env.NEXTAUTH_AZURE_AD_TENANT_ID}/oauth2/v2.0/token`;
+
+ const response = await fetch(url, {
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded"
+ },
+ method: "POST",
+ body: new URLSearchParams({
+ client_id: process.env.NEXTAUTH_AZURE_AD_CLIENT_ID,
+ client_secret: process.env.NEXTAUTH_AZURE_AD_SECRET,
+ grant_type: "refresh_token",
+ refresh_token: user.refreshToken,
+ scope: "offline_access openid User.Read"
+ })
+ });
+
+ const refreshedTokens = await response.json();
+
+ if (!response.ok) {
+ return undefined;
+ }
+
+ await prisma.user.update({
+ data: {
+ refreshToken: refreshedTokens.refresh_token,
+ accessTokenExpires: Date.now() + refreshedTokens?.ext_expires_in * 1000,
+ updated: new Date()
+ },
+ where: {
+ activeDirectoryId: activeDirectoryId
+ }
+ });
+
+ const avatarResponse = await fetch(`https://graph.microsoft.com/v1.0/me/photos/120x120/$value`, {
+ headers: {
+ Authorization: `Bearer ${refreshedTokens.access_token}`
+ }
+ });
+
+ if (!avatarResponse.ok) {
+ return undefined;
+ }
+
+ const pictureBuffer = await avatarResponse.arrayBuffer();
+
+ return `data:image/jpeg;base64,${Buffer.from(pictureBuffer).toString("base64")}`;
+});
+
+export { getUserEarnings, getUserAvatar };
diff --git a/lib/utils.ts b/lib/utils.ts
new file mode 100644
index 0000000..a17582e
--- /dev/null
+++ b/lib/utils.ts
@@ -0,0 +1,19 @@
+import { ClassValue, clsx } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
+
+export function formatDate(input: string | number): string {
+ const date = new Date(input)
+ return date.toLocaleDateString("en-US", {
+ month: "long",
+ day: "numeric",
+ year: "numeric",
+ })
+}
+
+export function absoluteUrl(path: string) {
+ return `${process.env.NEXT_PUBLIC_APP_URL}${path}`
+}
diff --git a/lib/validations/user.ts b/lib/validations/user.ts
new file mode 100644
index 0000000..83cdd42
--- /dev/null
+++ b/lib/validations/user.ts
@@ -0,0 +1,16 @@
+import * as z from "zod";
+
+export const userSalaryDetailSchema = z.object({
+ commission: z.number().min(0).max(100),
+ hourlyRate: z.number().min(0),
+ tax: z.number().min(0).max(100),
+ workHours: z.number().min(0).max(24)
+});
+
+export const userWorkDayDetailSchema = z.object({
+ id: z.number().min(0).optional(),
+ date: z.string(),
+ nonCommissionedHours: z.number().min(0).max(24),
+ extraHours: z.number().min(0).max(24),
+ sickDay: z.boolean()
+});
diff --git a/logic/calendarLogic.ts b/logic/calendar-logic.ts
similarity index 90%
rename from logic/calendarLogic.ts
rename to logic/calendar-logic.ts
index 1135c75..9b7508b 100644
--- a/logic/calendarLogic.ts
+++ b/logic/calendar-logic.ts
@@ -12,9 +12,9 @@ export const isWorkDay = (day: CalendarDay): boolean =>
export const getWorkDays = (month: CalendarMonth): CalendarDay[] =>
month?.days?.filter(day => isWorkDay(day)) ?? [];
-export const getPayDay = (month: CalendarMonth): CalendarDay => {
+export const getPayDay = (month: CalendarMonth): CalendarDay | undefined => {
if ((month?.days?.length ?? 0) === 0) {
- return null;
+ return undefined;
}
const workDays = getWorkDays(month);
diff --git a/logic/earningsLogic.ts b/logic/earnings-logic.ts
similarity index 96%
rename from logic/earningsLogic.ts
rename to logic/earnings-logic.ts
index 1bcc4bc..1341f30 100644
--- a/logic/earningsLogic.ts
+++ b/logic/earnings-logic.ts
@@ -1,5 +1,5 @@
-import EARNING_CONSTANTS from "@/constants/earningConstants";
-import { getWorkDays } from "@/logic/calendarLogic";
+import EARNING_CONSTANTS from "@/constants/earning-constants";
+import { getWorkDays } from "@/logic/calendar-logic";
import {
CalendarDay,
CalendarMonth,
@@ -8,7 +8,7 @@ import {
CalendarYearEarnings,
UserWorkDayDetail
} from "@/types";
-import { formatCurrency } from "@/utils/currencyFormat";
+import { formatCurrency } from "@/utils/currency-format";
const getHolidayPay = (gross: number): number => {
return gross * EARNING_CONSTANTS.WORK_HOLIDAY_PAY;
@@ -134,7 +134,7 @@ export const getEarningsForMonth = (
return {
monthName: month?.month,
- payDay: month?.payDay?.formattedShortDate,
+ payDay: month?.payDay?.formattedShortDate ?? "none",
workDays,
workHours: totalWorkHours,
gross,
diff --git a/logic/objectLogic.ts b/logic/object-logic.ts
similarity index 100%
rename from logic/objectLogic.ts
rename to logic/object-logic.ts
diff --git a/logic/userLogic.ts b/logic/user-logic.ts
similarity index 100%
rename from logic/userLogic.ts
rename to logic/user-logic.ts
diff --git a/logic/validationLogic.ts b/logic/validation-logic.ts
similarity index 100%
rename from logic/validationLogic.ts
rename to logic/validation-logic.ts
diff --git a/middleware.ts b/middleware.ts
new file mode 100644
index 0000000..08f6924
--- /dev/null
+++ b/middleware.ts
@@ -0,0 +1,44 @@
+import { getToken } from "next-auth/jwt";
+import { withAuth } from "next-auth/middleware";
+import { NextResponse } from "next/server";
+
+export default withAuth(
+ async function middleware(req) {
+ const token = await getToken({ req });
+
+ const isAuth = !!token;
+ const isAuthPage =
+ req.nextUrl.pathname.startsWith("/login") || req.nextUrl.pathname.startsWith("/register");
+
+ if (isAuthPage) {
+ if (isAuth) {
+ return NextResponse.redirect(new URL("/dashboard", req.url));
+ }
+
+ return null;
+ }
+
+ if (!isAuth) {
+ let from = req.nextUrl.pathname;
+ if (req.nextUrl.search) {
+ from += req.nextUrl.search;
+ }
+
+ return NextResponse.redirect(new URL(`/login?from=${encodeURIComponent(from)}`, req.url));
+ }
+ },
+ {
+ callbacks: {
+ async authorized() {
+ // This is a work-around for handling redirect on auth pages.
+ // We return true here so that the middleware function above
+ // is always called.
+ return true;
+ }
+ }
+ }
+);
+
+export const config = {
+ matcher: ["/dashboard/:path*", "/login", "/register"]
+};
diff --git a/next-env.d.ts b/next-env.d.ts
index 4f11a03..fd36f94 100644
--- a/next-env.d.ts
+++ b/next-env.d.ts
@@ -1,5 +1,6 @@
///
///
+///
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/next.config.js b/next.config.js
index 17ba21d..a18d8de 100644
--- a/next.config.js
+++ b/next.config.js
@@ -1,7 +1,65 @@
+const ContentSecurityPolicy = `
+ default-src 'self' vercel.live;
+ script-src 'self' 'unsafe-eval' 'unsafe-inline' cdn.vercel-insights.com vercel.live;
+ img-src * blob: data:;
+ media-src 'none';
+ connect-src *;
+ style-src 'self' 'unsafe-inline';
+ font-src 'self';
+`;
+
+const securityHeaders = [
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
+ {
+ key: "Content-Security-Policy",
+ value: ContentSecurityPolicy.replace(/\n/g, ""),
+ },
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
+ {
+ key: "Referrer-Policy",
+ value: "origin-when-cross-origin",
+ },
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+ {
+ key: "X-Frame-Options",
+ value: "DENY",
+ },
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
+ {
+ key: "X-Content-Type-Options",
+ value: "nosniff",
+ },
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control
+ {
+ key: "X-DNS-Prefetch-Control",
+ value: "on",
+ },
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
+ {
+ key: "Strict-Transport-Security",
+ value: "max-age=31536000; includeSubDomains; preload",
+ },
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
+ {
+ key: "Permissions-Policy",
+ value: "camera=(), microphone=(), geolocation=()",
+ },
+];
+
/** @type {import('next').NextConfig} */
-module.exports = {
+const nextConfig = {
reactStrictMode: true,
- images: {
- domains: ["images.unsplash.com"]
- }
+ experimental: {
+ appDir: true,
+ },
+ headers() {
+ return [
+ {
+ source: "/(.*)",
+ headers: securityHeaders,
+ },
+ ];
+ },
};
+
+module.exports = nextConfig;
\ No newline at end of file
diff --git a/package.json b/package.json
index fa1b69e..78ac437 100644
--- a/package.json
+++ b/package.json
@@ -17,40 +17,53 @@
"author": "Tommy Lunde Barvåg",
"license": "MIT",
"dependencies": {
- "@next/font": "^13.1.6",
- "@prisma/client": "^4.9.0",
- "@radix-ui/react-checkbox": "^1.0.1",
- "@radix-ui/react-dialog": "^1.0.2",
- "@radix-ui/react-label": "^2.0.0",
- "@radix-ui/react-popover": "^1.0.3",
+ "@hookform/resolvers": "^2.9.11",
+ "@prisma/client": "^4.11.0",
+ "@radix-ui/react-checkbox": "^1.0.3",
+ "@radix-ui/react-dialog": "^1.0.3",
+ "@radix-ui/react-hover-card": "^1.0.5",
+ "@radix-ui/react-icons": "^1.2.0",
+ "@radix-ui/react-label": "^2.0.1",
+ "@radix-ui/react-popover": "^1.0.5",
"@radix-ui/react-presence": "^1.0.0",
- "@radix-ui/react-radio-group": "^1.1.1",
- "@radix-ui/react-select": "^1.2.0",
- "@radix-ui/react-separator": "^1.0.1",
+ "@radix-ui/react-radio-group": "^1.1.2",
+ "@radix-ui/react-select": "^1.2.1",
+ "@radix-ui/react-separator": "^1.0.2",
+ "@radix-ui/react-toast": "^1.1.3",
"@sendgrid/mail": "^7.7.0",
- "@stitches/react": "^1.2.8",
+ "class-variance-authority": "^0.4.0",
+ "clsx": "^1.2.1",
"date-fns": "^2.29.3",
- "framer-motion": "^8.5.5",
"jwt-decode": "^3.1.2",
- "lodash.merge": "^4.6.2",
- "next": "^13.1.6",
- "next-auth": "^4.19.0",
+ "next": "^13.2.4",
+ "next-auth": "^4.20.1",
"next-themes": "^0.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-hook-form": "^7.43.0",
- "react-icons": "^4.7.1",
- "react-use": "^17.4.0",
- "swr": "2.0.3"
+ "react-hook-form": "^7.43.5",
+ "server-only": "^0.0.1",
+ "swr": "2.1.0",
+ "tailwind-merge": "^1.10.0",
+ "zod": "^3.21.4"
},
"devDependencies": {
- "@types/lodash.merge": "^4.6.7",
- "@types/node": "^18.11.18",
- "@types/react": "^18.0.27",
- "eslint": "8.33.0",
- "eslint-config-next": "^13.1.6",
- "prettier": "^2.8.3",
- "prisma": "^4.9.0",
+ "@ianvs/prettier-plugin-sort-imports": "^3.7.1",
+ "@tailwindcss/line-clamp": "^0.4.2",
+ "@types/node": "^18.15.3",
+ "@types/react": "^18.0.28",
+ "@types/react-dom": "^18.0.11",
+ "autoprefixer": "^10.4.14",
+ "eslint": "8.36.0",
+ "eslint-config-next": "^13.2.4",
+ "eslint-config-prettier": "^8.7.0",
+ "eslint-plugin-react": "^7.32.2",
+ "eslint-plugin-tailwindcss": "^3.10.1",
+ "postcss": "^8.4.21",
+ "prettier": "^2.8.4",
+ "prettier-plugin-tailwindcss": "^0.2.4",
+ "prisma": "^4.11.0",
+ "tailwindcss": "^3.2.7",
+ "tailwindcss-animate": "^1.0.5",
"typescript": "^4.9.5"
}
}
diff --git a/pages/_app.tsx b/pages/_app.tsx
deleted file mode 100644
index 1ad81c9..0000000
--- a/pages/_app.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import { Inter as FontSans } from "@next/font/google";
-import { ThemeProvider } from "next-themes";
-import * as React from "react";
-import { globalCss, lightTheme, theme } from "stitches.config";
-
-const fontSans = FontSans({
- subsets: ["latin"],
- variable: "--font-inter"
-});
-
-const globalStyles = globalCss({
- "*, ::before, ::after": {
- boxSizing: "border-box",
- borderWidth: 0,
- borderStyle: "solid",
- borderColor: "currentColor"
- },
- html: {},
- body: {
- backgroundColor: "$main",
- color: "$text",
- fontFamily: "$default",
- minWidth: "360px",
- scrollBehavior: "smooth",
- margin: 0,
- padding: 0,
- overflowX: "hidden"
- },
- "#__next": {
- display: "flex",
- flexDirection: "column",
- minHeight: "100vh"
- }
-});
-
-type MyAppProps = {
- Component: React.ComponentType & {
- layoutProps?: any;
- };
- pageProps?: Record;
-};
-
-function MyApp({ Component, pageProps }: MyAppProps) {
- globalStyles();
- const Layout = Component.layoutProps?.Layout || React.Fragment;
-
- const layoutProps = Component.layoutProps?.Layout
- ? { pageProps, layoutProps: { ...Component.layoutProps, className: fontSans.className } }
- : {};
-
- return (
-
-
-
-
-
- );
-}
-
-export default MyApp;
diff --git a/pages/_document.tsx b/pages/_document.tsx
deleted file mode 100644
index 5c3ec29..0000000
--- a/pages/_document.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Head, Html, Main, NextScript } from "next/document";
-import { getCssText } from "stitches.config";
-
-export default function Document() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/pages/access-denied.tsx b/pages/access-denied.tsx
deleted file mode 100644
index c14397b..0000000
--- a/pages/access-denied.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import { Box, Flex, Heading, Link, Paragraph, Svg } from "@/components/ui";
-import { sessionUserIsSpecialist } from "@/utils/sessionUtils";
-import { GetServerSideProps } from "next";
-import { getSession } from "next-auth/react";
-import * as React from "react";
-import { HiLockClosed } from "react-icons/hi";
-
-export default function AccessDenied({ session }) {
- return (
-
-
-
- {`Hi ${session?.user?.name}`}
-
- We found an account in your name, but you seem lack access to this app. If this is a
- mistake and you should have access, then please send an email to{" "}
-
- tommy.barvag@knowit.no
- {" "}
- requesting access.
-
-
-
- );
-}
-
-export const getServerSideProps: GetServerSideProps = async context => {
- const session = await getSession(context);
-
- // Session user is specialist and should not see
- // access denied page. Redirect to index
- if (sessionUserIsSpecialist(session)) {
- return {
- redirect: {
- destination: "/",
- permanent: false
- }
- };
- }
-
- return {
- props: {
- session
- }
- };
-};
diff --git a/pages/api/auth/[...nextauth].tsx b/pages/api/auth/[...nextauth].tsx
index bdb535e..0de23e2 100644
--- a/pages/api/auth/[...nextauth].tsx
+++ b/pages/api/auth/[...nextauth].tsx
@@ -1,275 +1,5 @@
-import prismaUser from "@/lib/prismaUser";
-import { validateEmail } from "@/logic/validationLogic";
-import type { User as PrismaUser } from "@/types";
-import { AzureAdTokenClaims, GraphUser } from "@/types";
-import { fetchWithToken } from "@/utils/fetcher";
-import jwt_decode from "jwt-decode";
-import NextAuth, { Account, NextAuthOptions, User } from "next-auth";
-import { JWT } from "next-auth/jwt";
-import AzureAdProvider from "next-auth/providers/azure-ad";
+import NextAuth from "next-auth";
-const AZURE_AD_CLIENT_ID = process.env.NEXTAUTH_AZURE_AD_CLIENT_ID;
-const AZURE_AD_TENANT_ID = process.env.NEXTAUTH_AZURE_AD_TENANT_ID;
-const AZURE_AD_SECRET = process.env.NEXTAUTH_AZURE_AD_SECRET;
-const AZURE_AD_SCOPE = "offline_access openid User.Read";
-
-const AZURE_AD_ADMIN_GROUP_ID = process.env.AZURE_AD_ADMIN_GROUP_ID;
-const AZURE_AD_SPECIALIST_GROUP_ID = process.env.AZURE_AD_SPECIALIST_GROUP_ID;
-
-const getAzureAdTokenClaims = (token: string): AzureAdTokenClaims => {
- return jwt_decode(token) as AzureAdTokenClaims;
-};
-
-const getUserEmail = async (accessToken: string): Promise => {
- const claims = getAzureAdTokenClaims(accessToken);
-
- // If claims contains unique principal name then use that
- if (claims.upn && validateEmail(claims.upn)) {
- return claims.upn;
- }
-
- // Fetch user profile from graph and retrieve email
- const response = await fetch(`https://graph.microsoft.com/v1.0/me/`, {
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
- });
-
- if (!response.ok) {
- throw new Error("Failed to fetch user email");
- }
-
- const user: GraphUser = await response.json();
-
- return user.mail ?? user.userPrincipalName;
-};
-
-const accessToGroupId = (groups = [], groupId) => {
- if (groups === null || groups === undefined) {
- return false;
- }
-
- if (Array.isArray(groups) && !(groups.length > 0)) {
- return false;
- }
-
- if (groupId === null || groupId === undefined) {
- return false;
- }
-
- return groups.some(group => group?.id?.toLowerCase() === groupId.toLowerCase());
-};
-
-const accessToAdminGroup = groups => accessToGroupId(groups, AZURE_AD_ADMIN_GROUP_ID);
-const accessToSpecialistGroup = groups => accessToGroupId(groups, AZURE_AD_SPECIALIST_GROUP_ID);
-
-async function getUserRoles(token: string) {
- try {
- const { value: groups } = await fetchWithToken(
- "https://graph.microsoft.com/v1.0/me/memberOf?$select=displayName,id",
- token
- );
-
- const isSpecialist = accessToSpecialistGroup(groups);
-
- return {
- // To determine the admin role the user needs to have access
- // to the specialist and admin groups. Could revalidate this to
- // only requiring access to admin group in the future.
- isAdmin: isSpecialist && accessToAdminGroup(groups),
- isSpecialist
- };
- } catch (error) {
- console.error(error);
-
- // If group lookup somehow crashes then return false
- return {
- isAdmin: false,
- isSpecialist: false
- };
- }
-}
-
-async function handleToken(token: JWT): Promise {
- const dbUser = await prismaUser.getByActiveDirectoryId(token.sub, {
- include: {
- workDayDetails: false
- }
- });
-
- const { refreshToken, ...restDbUser } = dbUser;
-
- if (Date.now() < new Date(dbUser.accessTokenExpires ?? 0).getTime()) {
- return {
- ...token,
- dbUser: restDbUser
- };
- }
-
- const refreshedToken = refreshAccessToken(token, dbUser);
-
- return {
- ...refreshedToken,
- dbUser: restDbUser
- };
-}
-
-async function refreshAccessToken(token: JWT, user: PrismaUser) {
- try {
- const url = `https://login.microsoftonline.com/${AZURE_AD_TENANT_ID}/oauth2/v2.0/token`;
-
- const response = await fetch(url, {
- headers: {
- "Content-Type": "application/x-www-form-urlencoded"
- },
- method: "POST",
- body: new URLSearchParams({
- client_id: AZURE_AD_CLIENT_ID,
- client_secret: AZURE_AD_SECRET,
- grant_type: "refresh_token",
- refresh_token: user.refreshToken,
- scope: AZURE_AD_SCOPE
- })
- });
-
- const refreshedTokens = await response.json();
-
- if (!response.ok) {
- throw refreshedTokens;
- }
-
- const { isAdmin, isSpecialist } = await getUserRoles(refreshedTokens.access_token);
-
- await prismaUser.update({
- data: {
- refreshToken: refreshedTokens.refresh_token,
- accessTokenExpires: Date.now() + refreshedTokens?.ext_expires_in * 1000,
- isAdmin: isAdmin ?? false,
- isSpecialist: isSpecialist ?? false,
- updated: new Date()
- },
- where: {
- id: user.id
- }
- });
-
- return {
- ...token,
- accessToken: refreshedTokens.access_token
- };
- } catch (error) {
- console.error(error);
-
- return {
- ...token,
- error: "RefreshAccessTokenError"
- };
- }
-}
-
-async function initialSignIn(
- account: Account & { accessToken?: string; refreshToken?: string; ext_expires_in?: number },
- user: User,
- token: JWT
-): Promise {
- const { isAdmin, isSpecialist } = await getUserRoles(account.access_token);
-
- await prismaUser.upsert({
- create: {
- name: user.name,
- email: user.email,
- activeDirectoryId: token.sub,
- refreshToken: account.refresh_token,
- accessTokenExpires: Date.now() + account?.ext_expires_in * 1000,
- isAdmin: isAdmin,
- isSpecialist: isSpecialist
- },
- update: {
- refreshToken: account.refresh_token,
- isAdmin: isAdmin,
- isSpecialist: isSpecialist
- },
- where: {
- activeDirectoryId: token.sub
- }
- });
-
- return {
- ...token
- };
-}
-
-export const authOptions: NextAuthOptions = {
- session: {
- strategy: "jwt",
- // max age 90 days
- maxAge: 90 * 24 * 60 * 60
- },
- jwt: {
- // max age 90 days
- maxAge: 90 * 24 * 60 * 60
- },
- providers: [
- AzureAdProvider({
- clientId: AZURE_AD_CLIENT_ID,
- clientSecret: AZURE_AD_SECRET,
- // scope: AZURE_AD_SCOPE,
- tenantId: AZURE_AD_TENANT_ID,
- profilePhotoSize: 48,
- authorization: {
- params: {
- scope: AZURE_AD_SCOPE
- }
- },
- idToken: true,
- checks: "state",
- // next-auth v4 used sub claim for user id, but we need to use the oid claim
- profile: async (profile, tokens) => {
- // Fetch user image
- // https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0#examples
-
- const claims = getAzureAdTokenClaims(tokens.access_token);
-
- return {
- id: claims.oid,
- name:
- profile.name ??
- claims?.name ??
- `${claims?.given_name}${claims?.family_name ? ` ${claims?.family_name}` : ""}`,
- email: profile.email ?? (await getUserEmail(tokens.access_token))
- };
- }
- })
- ],
- callbacks: {
- async jwt({ token, user, account, profile, isNewUser }) {
- // Initial sign in
- if (account && user) {
- return await initialSignIn(account, user, token);
- }
-
- return await handleToken(token);
- },
- async session({ session, token, user }) {
- const dbUser = token?.dbUser;
- // Add property to session, like an access_token from a provider.
-
- return {
- ...session,
- user: {
- ...session?.user,
- ...(dbUser ?? {}),
- image: `https://kxbspecialistappstorage.blob.core.windows.net/user-images/${
- dbUser?.activeDirectoryId ?? token.sub
- }.png`
- }
- };
- }
- },
- secret: process.env.NEXTAUTH_SECRET,
- pages: {
- signIn: "/login"
- }
-};
+import { authOptions } from "@/lib/auth";
export default NextAuth(authOptions);
diff --git a/pages/api/health-check/index.ts b/pages/api/health-check/index.ts
index 6772c84..0da8e88 100644
--- a/pages/api/health-check/index.ts
+++ b/pages/api/health-check/index.ts
@@ -1,11 +1,22 @@
-import prismaUser from "@/lib/prismaUser";
+import prisma from "@/lib/prisma";
import { NextApiRequest, NextApiResponse } from "next";
export default async function User(req: NextApiRequest, res: NextApiResponse) {
- const { refreshToken, ...user } = await prismaUser.getByEmail(
- "kxb.specialist.test.user@knowitgroup.com"
+ const user = await prisma.user.findUnique(
+ {
+ where: {
+ email: "kxb.specialist.test.user@knowitgroup.com"
+ },
+ include: {
+ workDayDetails: true
+ }
+ }
);
+ if (!user) {
+ return res.status(404).end();
+ }
+
if (req.method === "GET") {
return res.status(200).json({
...user,
diff --git a/pages/api/user/[id]/index.ts b/pages/api/user/[id]/index.ts
index 5eabe1c..c828169 100644
--- a/pages/api/user/[id]/index.ts
+++ b/pages/api/user/[id]/index.ts
@@ -1,6 +1,5 @@
-import prismaUser from "@/lib/prismaUser";
+import prisma from "@/lib/prisma";
import { UserWorkDayDetail } from "@/types";
-import { getSessionUserActiveDirectoryId, sessionUserIsAdmin } from "@/utils/sessionUtils";
import { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "next-auth/react";
@@ -17,13 +16,22 @@ export default async function User(req: NextApiRequest, res: NextApiResponse) {
return res.status(400).end();
}
- const sessionUserActiveDirectoryId = getSessionUserActiveDirectoryId(session);
+ const user = await prisma.user.findUnique({
+ where: {
+ id: +id
+ },
+ include: {
+ workDayDetails: true
+ }
+ });
- const { refreshToken, ...user } = await prismaUser.getById(+id);
+ if (!user) {
+ return res.status(404).end();
+ }
if (
- !sessionUserIsAdmin(session) &&
- user.activeDirectoryId !== sessionUserActiveDirectoryId?.toLowerCase()
+ !session.user.isAdmin &&
+ user.activeDirectoryId !== session.user.activeDirectoryId
) {
return res.status(403).end();
}
@@ -64,8 +72,8 @@ export default async function User(req: NextApiRequest, res: NextApiResponse) {
if (
existing &&
(curr.extraHours !== 0 || curr.nonCommissionedHours !== 0) &&
- (existing.extraHours !== curr.extraHours ||
- existing.nonCommissionedHours !== curr.nonCommissionedHours ||
+ (existing.extraHours.toNumber() !== curr.extraHours ||
+ existing.nonCommissionedHours.toNumber() !== curr.nonCommissionedHours ||
existing.sickDay !== curr.sickDay)
) {
return {
@@ -90,7 +98,7 @@ export default async function User(req: NextApiRequest, res: NextApiResponse) {
}
);
- await prismaUser.update({
+ await prisma.user.update({
data: {
commission,
hourlyRate,
@@ -135,7 +143,11 @@ export default async function User(req: NextApiRequest, res: NextApiResponse) {
}
if (req.method === "DELETE") {
- await prismaUser.deleteById(+id);
+ await prisma.user.delete({
+ where: {
+ id: +id
+ }
+ });
return res.status(204).json({});
}
diff --git a/pages/api/user/avatar.ts b/pages/api/user/avatar.ts
index 8b18fa6..c0add3c 100644
--- a/pages/api/user/avatar.ts
+++ b/pages/api/user/avatar.ts
@@ -15,16 +15,20 @@ export default async function Avatar(req: NextApiRequest, res: NextApiResponse)
}
if (req.method === "GET") {
- const { refreshToken } = await prisma.user.findUnique({
+ const user = await prisma.user.findUnique({
where: {
- id: session.user.id
+ activeDirectoryId: session.user.activeDirectoryId
},
select: {
refreshToken: true
}
});
- if (!refreshToken) {
+ if (!user) {
+ return res.status(404).end();
+ }
+
+ if (!user.refreshToken) {
return res.status(404).end();
}
@@ -39,7 +43,7 @@ export default async function Avatar(req: NextApiRequest, res: NextApiResponse)
client_id: AZURE_AD_CLIENT_ID,
client_secret: AZURE_AD_SECRET,
grant_type: "refresh_token",
- refresh_token: refreshToken,
+ refresh_token: user.refreshToken,
scope: AZURE_AD_SCOPE
})
});
@@ -57,7 +61,7 @@ export default async function Avatar(req: NextApiRequest, res: NextApiResponse)
updated: new Date()
},
where: {
- id: session.user.id
+ activeDirectoryId: session.user.activeDirectoryId
}
});
diff --git a/pages/api/user/feedback.ts b/pages/api/user/feedback.ts
index 889217c..5bd38f1 100644
--- a/pages/api/user/feedback.ts
+++ b/pages/api/user/feedback.ts
@@ -1,6 +1,5 @@
import prisma from "@/lib/prisma";
-import { getFeedbackEmailTemplate } from "@/utils/emailUtils";
-import { getSessionUserActiveDirectoryId } from "@/utils/sessionUtils";
+import { getFeedbackEmailTemplate } from "@/utils/email-utils";
import sendGridMail from "@sendgrid/mail";
import { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "next-auth/react";
@@ -12,9 +11,8 @@ export default async function Feedback(req: NextApiRequest, res: NextApiResponse
return res.status(401).end();
}
- const sessionUserActiveDirectoryId = getSessionUserActiveDirectoryId(session);
- if (sessionUserActiveDirectoryId === null || sessionUserActiveDirectoryId === undefined) {
+ if (!session.user.activeDirectoryId) {
return res.status(400).end();
}
@@ -42,14 +40,14 @@ export default async function Feedback(req: NextApiRequest, res: NextApiResponse
);
}
- const {
- id: userId,
- email,
- name
- } = await prisma.user.findUnique({
- where: { activeDirectoryId: sessionUserActiveDirectoryId }
+ const user = await prisma.user.findUnique({
+ where: { activeDirectoryId: session.user.activeDirectoryId }
});
+ if (!user) {
+ return res.status(404).end();
+ }
+
if (req.method === "POST") {
const message: string = req.body.message;
const reaction: number = +req.body.reaction;
@@ -62,7 +60,7 @@ export default async function Feedback(req: NextApiRequest, res: NextApiResponse
data: {
feedback: message,
reaction: reaction,
- userId
+ userId: user.id
}
});
@@ -74,8 +72,8 @@ export default async function Feedback(req: NextApiRequest, res: NextApiResponse
await sendGridMail.send({
to: process.env.FEEDBACK_RECIPIENT_EMAIL,
from: "tommy.barvag@knowit.no",
- subject: `kxb.app feedback from ${name}`,
- html: getFeedbackEmailTemplate(name, message, email)
+ subject: `kxb.app feedback from ${user.name ?? user.email}`,
+ html: getFeedbackEmailTemplate(user.name ?? user.email, message, user.email)
});
}
diff --git a/pages/api/user/index.ts b/pages/api/user/index.ts
index 32364e8..07acd25 100644
--- a/pages/api/user/index.ts
+++ b/pages/api/user/index.ts
@@ -1,5 +1,4 @@
-import prismaUser from "@/lib/prismaUser";
-import { sessionUserIsAdmin } from "@/utils/sessionUtils";
+import prisma from "@/lib/prisma";
import { NextApiRequest, NextApiResponse } from "next";
import { getSession } from "next-auth/react";
@@ -10,12 +9,12 @@ export default async function Users(req: NextApiRequest, res: NextApiResponse) {
return res.status(401).end();
}
- if (!sessionUserIsAdmin(session)) {
+ if (!session.user.isAdmin) {
return res.status(403).end();
}
if (req.method === "GET") {
- return res.status(200).json(await prismaUser.get());
+ return res.status(200).json(await prisma.user.findMany());
}
return res.send("Method not allowed.");
diff --git a/pages/api/user/work-day-detail/index.ts b/pages/api/user/work-day-detail/index.ts
new file mode 100644
index 0000000..e4017e5
--- /dev/null
+++ b/pages/api/user/work-day-detail/index.ts
@@ -0,0 +1,59 @@
+import prisma from "@/lib/prisma";
+import { userWorkDayDetailSchema } from "@/lib/validations/user";
+import { NextApiRequest, NextApiResponse } from "next";
+import { getSession } from "next-auth/react";
+
+export default async function User(req: NextApiRequest, res: NextApiResponse) {
+ const session = await getSession({ req });
+
+ if (!session) {
+ return res.status(401).end();
+ }
+
+ const user = await prisma.user.findUnique({
+ where: {
+ activeDirectoryId: session.user.activeDirectoryId
+ }
+ });
+
+ if (!user) {
+ return res.status(404).end();
+ }
+
+ if (!session.user.isAdmin && user.activeDirectoryId !== session.user.activeDirectoryId) {
+ return res.status(403).end();
+ }
+
+ if (user === null || user === undefined) {
+ return res.status(404).end();
+ }
+
+ if (req.method === "PATCH" && req.body) {
+ const body = req.body ? req.body : {};
+
+ const { id, date, extraHours, nonCommissionedHours, sickDay } =
+ userWorkDayDetailSchema.parse(body);
+
+ await prisma.userWorkDayDetail.upsert({
+ where: {
+ id: id
+ },
+ create: {
+ userId: user.id,
+ sickDay: sickDay,
+ extraHours: extraHours,
+ nonCommissionedHours: nonCommissionedHours,
+ date: date
+ },
+ update: {
+ nonCommissionedHours: nonCommissionedHours,
+ extraHours: extraHours,
+ sickDay: sickDay
+ }
+ });
+
+ return res.status(200).end();
+ }
+
+ return res.send("Method not allowed.");
+}
diff --git a/pages/feedback.tsx b/pages/feedback.tsx
deleted file mode 100644
index eecdbec..0000000
--- a/pages/feedback.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { FeedbackForm } from "@/components/feedback";
-import AuthenticatedLayout from "@/components/layouts/authenticatedLayout";
-import { Flex, Heading, Paragraph } from "@/components/ui";
-import { getResultForAuthenticatedPage } from "@/utils/pageUtils";
-import { GetServerSideProps } from "next";
-import * as React from "react";
-
-export default function Feedback() {
- return (
- <>
-
- We appreciate all feedback
-
-
- Good or bad, small or big. We appreciate all feedback.
-
-
-
-
- >
- );
-}
-
-export const getServerSideProps: GetServerSideProps = async context => {
- return getResultForAuthenticatedPage(context);
-};
-
-Feedback.layoutProps = {
- meta: {
- title: "Feedback"
- },
- Layout: AuthenticatedLayout
-};
diff --git a/pages/index.tsx b/pages/index.tsx
deleted file mode 100644
index 7961a21..0000000
--- a/pages/index.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import CompanyBenefits from "@/components/companyBenefits";
-import AuthenticatedLayout from "@/components/layouts/authenticatedLayout";
-import { useSalary, YearlyEarnings, YearStatistic } from "@/components/salary";
-import { Heading } from "@/components/ui";
-import { useUser } from "@/components/user";
-import { getResultForAuthenticatedPage } from "@/utils/pageUtils";
-import { GetServerSideProps } from "next";
-
-export default function Home() {
- const { user } = useUser();
- const { yearSalaryStatistics, nextYearSalaryStatistics, isLoadingSalary } = useSalary();
-
- return (
- <>
- {`Hi ${user?.name}`}
-
-
-
-
- >
- );
-}
-
-export const getServerSideProps: GetServerSideProps = async context => {
- return getResultForAuthenticatedPage(context);
-};
-
-Home.layoutProps = {
- meta: {
- title: "Home"
- },
- Layout: AuthenticatedLayout
-};
diff --git a/pages/login.tsx b/pages/login.tsx
deleted file mode 100644
index 5b0f4b3..0000000
--- a/pages/login.tsx
+++ /dev/null
@@ -1,189 +0,0 @@
-import { LoginButtons } from "@/components/auth";
-import { Box, Flex, Heading, Image, Paragraph, Svg, Text } from "@/components/ui";
-import { GetServerSideProps } from "next";
-import { getProviders, getSession } from "next-auth/react";
-import { useRouter } from "next/router";
-import * as React from "react";
-
-export default function Login({ session }) {
- const router = useRouter();
- const [isLoading, setIsLoading] = React.useState(!!session);
-
- React.useEffect(() => {
- if (session) {
- setIsLoading(true);
- router.push("/");
- }
- }, [session, router]);
-
- return isLoading ? (
-
-
-
-
-
-
-
-
- Logging you in
- This may take a few seconds, please don't close this page.
-
-
- ) : (
-
-
-
-
-
-
-
-
- KXB Specialist
- Login to see your estimated salary.
- setIsLoading(true)} />
-
-
-
-
-
-
-
- );
-}
-
-export const getServerSideProps: GetServerSideProps = async context => {
- const session = await getSession(context);
-
- return {
- props: {
- session,
- loginProviders: await getProviders()
- }
- };
-};
diff --git a/pages/profile.tsx b/pages/profile.tsx
deleted file mode 100644
index edc5881..0000000
--- a/pages/profile.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import AuthenticatedLayout from "@/components/layouts/authenticatedLayout";
-import { Flex, Heading, Paragraph } from "@/components/ui";
-import { UserProfile, useUser } from "@/components/user";
-import { getResultForAuthenticatedPage } from "@/utils/pageUtils";
-import { GetServerSideProps } from "next";
-import * as React from "react";
-
-export default function Profile() {
- const { user } = useUser();
-
- return (
- <>
- Hi {user?.name}
- To edit your settings, use the form below.
-
-
-
- >
- );
-}
-
-export const getServerSideProps: GetServerSideProps = async context => {
- return getResultForAuthenticatedPage(context);
-};
-
-Profile.layoutProps = {
- meta: {
- title: "Profile"
- },
- Layout: AuthenticatedLayout
-};
diff --git a/pages/salary-calculator.tsx b/pages/salary-calculator.tsx
deleted file mode 100644
index 11c4bbb..0000000
--- a/pages/salary-calculator.tsx
+++ /dev/null
@@ -1,133 +0,0 @@
-import { Calendar, useCalendar } from "@/components/calendar";
-import AuthenticatedLayout from "@/components/layouts/authenticatedLayout";
-import { YearStatistic } from "@/components/salary";
-import SalaryStatistics from "@/components/salary/components/salaryStatistics";
-import { Box, Flex, Heading, Paragraph, Text, TextField } from "@/components/ui";
-import DEFAULT_USER_SALARY from "@/constants/defaultUserSalary";
-import { getEarningsForMonth, getEarningsForYear } from "@/logic/earningsLogic";
-import { getResultForAuthenticatedPage } from "@/utils/pageUtils";
-import { GetServerSideProps } from "next";
-import * as React from "react";
-
-export default function SalaryCalculator() {
- const { year, nextYear, activeCalendarMonthDetail, isLoadingCalendar } = useCalendar();
-
- const [hourlyRate, setHourlyRate] = React.useState(DEFAULT_USER_SALARY.hourlyRate);
- const [commission, setCommission] = React.useState(DEFAULT_USER_SALARY.commission);
- const [tax, setTax] = React.useState(DEFAULT_USER_SALARY.tax);
- const workHours = DEFAULT_USER_SALARY.workHours;
-
- const salaryStatistics = React.useMemo(
- () =>
- getEarningsForMonth(activeCalendarMonthDetail, hourlyRate, commission, tax, workHours, []),
- [activeCalendarMonthDetail, hourlyRate, commission, tax, workHours]
- );
-
- const yearSalaryStatistics = React.useMemo(
- () => getEarningsForYear(year, hourlyRate, commission, tax, workHours, []),
- [year, hourlyRate, commission, tax, workHours]
- );
-
- const nextYearSalaryStatistics = React.useMemo(
- () => getEarningsForYear(nextYear, hourlyRate, commission, tax, workHours, []),
- [nextYear, hourlyRate, commission, tax, workHours]
- );
-
- return (
- <>
- Salary Calculator
- We provide you with a simple salary model.
-
- You get paid by commission or guaranteed salary. Knowit covers both employer's national
- insurance contributions (14.10%) and holyday payment (12%). This means you can calculate
- your next payment by the following formulae{" "}
-
- work hours in month x hourly rate x commission = your salary.
-
-
- Monthly salary example
-
-
- setHourlyRate(+e.target.value)}
- type="number"
- />
- setCommission(+e.target.value)}
- type="number"
- step="0.01"
- />
- setTax(+e.target.value)}
- type="number"
- step="0.01"
- />
-
-
-
-
-
-
- Yearly overview
-
- Holyday payment is paid in June. For this month you get holiday payment and commissioned
- pay. It's usually a good practice to set aside some money for the month after your
- vacation as the holyday pay is usually paid before the actual vacation.
-
-
-
- >
- );
-}
-
-export const getServerSideProps: GetServerSideProps = async context => {
- return getResultForAuthenticatedPage(context);
-};
-
-SalaryCalculator.layoutProps = {
- meta: {
- title: "Salary calculator"
- },
- Layout: AuthenticatedLayout
-};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c297bb8..c4e4fc2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,105 +1,343 @@
lockfileVersion: 5.4
specifiers:
- '@next/font': ^13.1.6
- '@prisma/client': ^4.9.0
- '@radix-ui/react-checkbox': ^1.0.1
- '@radix-ui/react-dialog': ^1.0.2
- '@radix-ui/react-label': ^2.0.0
- '@radix-ui/react-popover': ^1.0.3
+ '@hookform/resolvers': ^2.9.11
+ '@ianvs/prettier-plugin-sort-imports': ^3.7.1
+ '@prisma/client': ^4.11.0
+ '@radix-ui/react-checkbox': ^1.0.3
+ '@radix-ui/react-dialog': ^1.0.3
+ '@radix-ui/react-hover-card': ^1.0.5
+ '@radix-ui/react-icons': ^1.2.0
+ '@radix-ui/react-label': ^2.0.1
+ '@radix-ui/react-popover': ^1.0.5
'@radix-ui/react-presence': ^1.0.0
- '@radix-ui/react-radio-group': ^1.1.1
- '@radix-ui/react-select': ^1.2.0
- '@radix-ui/react-separator': ^1.0.1
+ '@radix-ui/react-radio-group': ^1.1.2
+ '@radix-ui/react-select': ^1.2.1
+ '@radix-ui/react-separator': ^1.0.2
+ '@radix-ui/react-toast': ^1.1.3
'@sendgrid/mail': ^7.7.0
- '@stitches/react': ^1.2.8
- '@types/lodash.merge': ^4.6.7
- '@types/node': ^18.11.18
- '@types/react': ^18.0.27
+ '@tailwindcss/line-clamp': ^0.4.2
+ '@types/node': ^18.15.3
+ '@types/react': ^18.0.28
+ '@types/react-dom': ^18.0.11
+ autoprefixer: ^10.4.14
+ class-variance-authority: ^0.4.0
+ clsx: ^1.2.1
date-fns: ^2.29.3
- eslint: 8.33.0
- eslint-config-next: ^13.1.6
- framer-motion: ^8.5.5
+ eslint: 8.36.0
+ eslint-config-next: ^13.2.4
+ eslint-config-prettier: ^8.7.0
+ eslint-plugin-react: ^7.32.2
+ eslint-plugin-tailwindcss: ^3.10.1
jwt-decode: ^3.1.2
- lodash.merge: ^4.6.2
- next: ^13.1.6
- next-auth: ^4.19.0
+ next: ^13.2.4
+ next-auth: ^4.20.1
next-themes: ^0.2.1
- prettier: ^2.8.3
- prisma: ^4.9.0
+ postcss: ^8.4.21
+ prettier: ^2.8.4
+ prettier-plugin-tailwindcss: ^0.2.4
+ prisma: ^4.11.0
react: ^18.2.0
react-dom: ^18.2.0
- react-hook-form: ^7.43.0
- react-icons: ^4.7.1
- react-use: ^17.4.0
- swr: 2.0.3
+ react-hook-form: ^7.43.5
+ server-only: ^0.0.1
+ swr: 2.1.0
+ tailwind-merge: ^1.10.0
+ tailwindcss: ^3.2.7
+ tailwindcss-animate: ^1.0.5
typescript: ^4.9.5
+ zod: ^3.21.4
dependencies:
- '@next/font': 13.1.6
- '@prisma/client': 4.9.0_prisma@4.9.0
- '@radix-ui/react-checkbox': 1.0.1_biqbaboplfbrettd7655fr4n2y
- '@radix-ui/react-dialog': 1.0.2_5ndqzdd6t4rivxsukjv3i3ak2q
- '@radix-ui/react-label': 2.0.0_biqbaboplfbrettd7655fr4n2y
- '@radix-ui/react-popover': 1.0.3_5ndqzdd6t4rivxsukjv3i3ak2q
+ '@hookform/resolvers': 2.9.11_react-hook-form@7.43.5
+ '@prisma/client': 4.11.0_prisma@4.11.0
+ '@radix-ui/react-checkbox': 1.0.3_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-dialog': 1.0.3_zula6vjvt3wdocc4mwcxqa6nzi
+ '@radix-ui/react-hover-card': 1.0.5_zula6vjvt3wdocc4mwcxqa6nzi
+ '@radix-ui/react-icons': 1.2.0_react@18.2.0
+ '@radix-ui/react-label': 2.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-popover': 1.0.5_zula6vjvt3wdocc4mwcxqa6nzi
'@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y
- '@radix-ui/react-radio-group': 1.1.1_biqbaboplfbrettd7655fr4n2y
- '@radix-ui/react-select': 1.2.0_5ndqzdd6t4rivxsukjv3i3ak2q
- '@radix-ui/react-separator': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-radio-group': 1.1.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-select': 1.2.1_zula6vjvt3wdocc4mwcxqa6nzi
+ '@radix-ui/react-separator': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-toast': 1.1.3_biqbaboplfbrettd7655fr4n2y
'@sendgrid/mail': 7.7.0
- '@stitches/react': 1.2.8_react@18.2.0
+ class-variance-authority: 0.4.0_typescript@4.9.5
+ clsx: 1.2.1
date-fns: 2.29.3
- framer-motion: 8.5.5_biqbaboplfbrettd7655fr4n2y
jwt-decode: 3.1.2
- lodash.merge: 4.6.2
- next: 13.1.6_biqbaboplfbrettd7655fr4n2y
- next-auth: 4.19.0_3vryta7zmbcsw4rrqf4axjqggm
- next-themes: 0.2.1_3vryta7zmbcsw4rrqf4axjqggm
+ next: 13.2.4_biqbaboplfbrettd7655fr4n2y
+ next-auth: 4.20.1_ld2jel3hspngo3u5lti2kgl2sq
+ next-themes: 0.2.1_ld2jel3hspngo3u5lti2kgl2sq
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
- react-hook-form: 7.43.0_react@18.2.0
- react-icons: 4.7.1_react@18.2.0
- react-use: 17.4.0_biqbaboplfbrettd7655fr4n2y
- swr: 2.0.3_react@18.2.0
+ react-hook-form: 7.43.5_react@18.2.0
+ server-only: 0.0.1
+ swr: 2.1.0_react@18.2.0
+ tailwind-merge: 1.10.0
+ zod: 3.21.4
devDependencies:
- '@types/lodash.merge': 4.6.7
- '@types/node': 18.11.18
- '@types/react': 18.0.27
- eslint: 8.33.0
- eslint-config-next: 13.1.6_4vsywjlpuriuw3tl5oq6zy5a64
- prettier: 2.8.3
- prisma: 4.9.0
+ '@ianvs/prettier-plugin-sort-imports': 3.7.1_prettier@2.8.4
+ '@tailwindcss/line-clamp': 0.4.2_tailwindcss@3.2.7
+ '@types/node': 18.15.3
+ '@types/react': 18.0.28
+ '@types/react-dom': 18.0.11
+ autoprefixer: 10.4.14_postcss@8.4.21
+ eslint: 8.36.0
+ eslint-config-next: 13.2.4_vgl77cfdswitgr47lm5swmv43m
+ eslint-config-prettier: 8.7.0_eslint@8.36.0
+ eslint-plugin-react: 7.32.2_eslint@8.36.0
+ eslint-plugin-tailwindcss: 3.10.1_tailwindcss@3.2.7
+ postcss: 8.4.21
+ prettier: 2.8.4
+ prettier-plugin-tailwindcss: 0.2.4_2m34gfnsqvzqy5bj5eci3wt3by
+ prisma: 4.11.0
+ tailwindcss: 3.2.7_postcss@8.4.21
+ tailwindcss-animate: 1.0.5_tailwindcss@3.2.7
typescript: 4.9.5
packages:
+ /@ampproject/remapping/2.2.0:
+ resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
+ engines: {node: '>=6.0.0'}
+ dependencies:
+ '@jridgewell/gen-mapping': 0.1.1
+ '@jridgewell/trace-mapping': 0.3.17
+ dev: true
+
+ /@babel/code-frame/7.18.6:
+ resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/highlight': 7.18.6
+ dev: true
+
+ /@babel/compat-data/7.21.0:
+ resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/core/7.21.3:
+ resolution: {integrity: sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@ampproject/remapping': 2.2.0
+ '@babel/code-frame': 7.18.6
+ '@babel/generator': 7.21.3
+ '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.21.3
+ '@babel/helper-module-transforms': 7.21.2
+ '@babel/helpers': 7.21.0
+ '@babel/parser': 7.21.3
+ '@babel/template': 7.20.7
+ '@babel/traverse': 7.21.3
+ '@babel/types': 7.21.3
+ convert-source-map: 1.9.0
+ debug: 4.3.4
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/generator/7.21.3:
+ resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.21.3
+ '@jridgewell/gen-mapping': 0.3.2
+ '@jridgewell/trace-mapping': 0.3.17
+ jsesc: 2.5.2
+ dev: true
+
+ /@babel/helper-compilation-targets/7.20.7_@babel+core@7.21.3:
+ resolution: {integrity: sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/compat-data': 7.21.0
+ '@babel/core': 7.21.3
+ '@babel/helper-validator-option': 7.21.0
+ browserslist: 4.21.5
+ lru-cache: 5.1.1
+ semver: 6.3.0
+ dev: true
+
+ /@babel/helper-environment-visitor/7.18.9:
+ resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/helper-function-name/7.21.0:
+ resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/template': 7.20.7
+ '@babel/types': 7.21.3
+ dev: true
+
+ /@babel/helper-hoist-variables/7.18.6:
+ resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.21.3
+ dev: true
+
+ /@babel/helper-module-imports/7.18.6:
+ resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.21.3
+ dev: true
+
+ /@babel/helper-module-transforms/7.21.2:
+ resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-environment-visitor': 7.18.9
+ '@babel/helper-module-imports': 7.18.6
+ '@babel/helper-simple-access': 7.20.2
+ '@babel/helper-split-export-declaration': 7.18.6
+ '@babel/helper-validator-identifier': 7.19.1
+ '@babel/template': 7.20.7
+ '@babel/traverse': 7.21.3
+ '@babel/types': 7.21.3
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/helper-simple-access/7.20.2:
+ resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.21.3
+ dev: true
+
+ /@babel/helper-split-export-declaration/7.18.6:
+ resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.21.3
+ dev: true
+
+ /@babel/helper-string-parser/7.19.4:
+ resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/helper-validator-identifier/7.19.1:
+ resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/helper-validator-option/7.21.0:
+ resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/helpers/7.21.0:
+ resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/template': 7.20.7
+ '@babel/traverse': 7.21.3
+ '@babel/types': 7.21.3
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/highlight/7.18.6:
+ resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-validator-identifier': 7.19.1
+ chalk: 2.4.2
+ js-tokens: 4.0.0
+ dev: true
+
+ /@babel/parser/7.21.3:
+ resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+ dependencies:
+ '@babel/types': 7.21.3
+ dev: true
+
/@babel/runtime/7.20.13:
resolution: {integrity: sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.11
+ dev: false
- /@emotion/is-prop-valid/0.8.8:
- resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==}
- requiresBuild: true
+ /@babel/runtime/7.21.0:
+ resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==}
+ engines: {node: '>=6.9.0'}
dependencies:
- '@emotion/memoize': 0.7.4
- dev: false
- optional: true
+ regenerator-runtime: 0.13.11
- /@emotion/memoize/0.7.4:
- resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
- dev: false
- optional: true
+ /@babel/template/7.20.7:
+ resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.18.6
+ '@babel/parser': 7.21.3
+ '@babel/types': 7.21.3
+ dev: true
- /@eslint/eslintrc/1.4.1:
- resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==}
+ /@babel/traverse/7.21.3:
+ resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.18.6
+ '@babel/generator': 7.21.3
+ '@babel/helper-environment-visitor': 7.18.9
+ '@babel/helper-function-name': 7.21.0
+ '@babel/helper-hoist-variables': 7.18.6
+ '@babel/helper-split-export-declaration': 7.18.6
+ '@babel/parser': 7.21.3
+ '@babel/types': 7.21.3
+ debug: 4.3.4
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@babel/types/7.21.3:
+ resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-string-parser': 7.19.4
+ '@babel/helper-validator-identifier': 7.19.1
+ to-fast-properties: 2.0.0
+ dev: true
+
+ /@eslint-community/eslint-utils/4.2.0_eslint@8.36.0:
+ resolution: {integrity: sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+ dependencies:
+ eslint: 8.36.0
+ eslint-visitor-keys: 3.3.0
+ dev: true
+
+ /@eslint-community/regexpp/4.4.0:
+ resolution: {integrity: sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+ dev: true
+
+ /@eslint/eslintrc/2.0.1:
+ resolution: {integrity: sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4
- espree: 9.4.1
+ espree: 9.5.0
globals: 13.20.0
ignore: 5.2.4
import-fresh: 3.3.0
@@ -110,6 +348,11 @@ packages:
- supports-color
dev: true
+ /@eslint/js/8.36.0:
+ resolution: {integrity: sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dev: true
+
/@floating-ui/core/0.7.3:
resolution: {integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==}
dev: false
@@ -120,7 +363,7 @@ packages:
'@floating-ui/core': 0.7.3
dev: false
- /@floating-ui/react-dom/0.7.2_5ndqzdd6t4rivxsukjv3i3ak2q:
+ /@floating-ui/react-dom/0.7.2_zula6vjvt3wdocc4mwcxqa6nzi:
resolution: {integrity: sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==}
peerDependencies:
react: '>=16.8.0'
@@ -129,11 +372,19 @@ packages:
'@floating-ui/dom': 0.5.4
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
- use-isomorphic-layout-effect: 1.1.2_3stiutgnnbnfnf3uowm5cip22i
+ use-isomorphic-layout-effect: 1.1.2_pmekkgnqduwlme35zpnqhenc34
transitivePeerDependencies:
- '@types/react'
dev: false
+ /@hookform/resolvers/2.9.11_react-hook-form@7.43.5:
+ resolution: {integrity: sha512-bA3aZ79UgcHj7tFV7RlgThzwSSHZgvfbt2wprldRkYBcMopdMvHyO17Wwp/twcJasNFischFfS7oz8Katz8DdQ==}
+ peerDependencies:
+ react-hook-form: ^7.0.0
+ dependencies:
+ react-hook-form: 7.43.5_react@18.2.0
+ dev: false
+
/@humanwhocodes/config-array/0.11.8:
resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==}
engines: {node: '>=10.10.0'}
@@ -154,69 +405,78 @@ packages:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
dev: true
- /@motionone/animation/10.15.1:
- resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==}
+ /@ianvs/prettier-plugin-sort-imports/3.7.1_prettier@2.8.4:
+ resolution: {integrity: sha512-XDnBUUruJY9KgNd7T2ZHnVPWo5B9NzVDCLEMm7HjXTA3rTtMg5Q46gYRjLvampDXSmN8+icu54aRE3IIT8U+1w==}
+ peerDependencies:
+ '@vue/compiler-sfc': '>=3.0.0'
+ prettier: 2.x
+ peerDependenciesMeta:
+ '@vue/compiler-sfc':
+ optional: true
dependencies:
- '@motionone/easing': 10.15.1
- '@motionone/types': 10.15.1
- '@motionone/utils': 10.15.1
- tslib: 2.5.0
- dev: false
+ '@babel/core': 7.21.3
+ '@babel/generator': 7.21.3
+ '@babel/parser': 7.21.3
+ '@babel/traverse': 7.21.3
+ '@babel/types': 7.21.3
+ javascript-natural-sort: 0.7.1
+ lodash.clone: 4.5.0
+ lodash.isequal: 4.5.0
+ prettier: 2.8.4
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
- /@motionone/dom/10.15.5:
- resolution: {integrity: sha512-Xc5avlgyh3xukU9tydh9+8mB8+2zAq+WlLsC3eEIp7Ax7DnXgY7Bj/iv0a4X2R9z9ZFZiaXK3BO0xMYHKbAAdA==}
+ /@jridgewell/gen-mapping/0.1.1:
+ resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
+ engines: {node: '>=6.0.0'}
dependencies:
- '@motionone/animation': 10.15.1
- '@motionone/generators': 10.15.1
- '@motionone/types': 10.15.1
- '@motionone/utils': 10.15.1
- hey-listen: 1.0.8
- tslib: 2.5.0
- dev: false
+ '@jridgewell/set-array': 1.1.2
+ '@jridgewell/sourcemap-codec': 1.4.14
+ dev: true
- /@motionone/easing/10.15.1:
- resolution: {integrity: sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw==}
+ /@jridgewell/gen-mapping/0.3.2:
+ resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
+ engines: {node: '>=6.0.0'}
dependencies:
- '@motionone/utils': 10.15.1
- tslib: 2.5.0
- dev: false
+ '@jridgewell/set-array': 1.1.2
+ '@jridgewell/sourcemap-codec': 1.4.14
+ '@jridgewell/trace-mapping': 0.3.17
+ dev: true
- /@motionone/generators/10.15.1:
- resolution: {integrity: sha512-67HLsvHJbw6cIbLA/o+gsm7h+6D4Sn7AUrB/GPxvujse1cGZ38F5H7DzoH7PhX+sjvtDnt2IhFYF2Zp1QTMKWQ==}
- dependencies:
- '@motionone/types': 10.15.1
- '@motionone/utils': 10.15.1
- tslib: 2.5.0
- dev: false
+ /@jridgewell/resolve-uri/3.1.0:
+ resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
+ engines: {node: '>=6.0.0'}
+ dev: true
- /@motionone/types/10.15.1:
- resolution: {integrity: sha512-iIUd/EgUsRZGrvW0jqdst8st7zKTzS9EsKkP+6c6n4MPZoQHwiHuVtTQLD6Kp0bsBLhNzKIBlHXponn/SDT4hA==}
- dev: false
+ /@jridgewell/set-array/1.1.2:
+ resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
+ engines: {node: '>=6.0.0'}
+ dev: true
- /@motionone/utils/10.15.1:
- resolution: {integrity: sha512-p0YncgU+iklvYr/Dq4NobTRdAPv9PveRDUXabPEeOjBLSO/1FNB2phNTZxOxpi1/GZwYpAoECEa0Wam+nsmhSw==}
+ /@jridgewell/sourcemap-codec/1.4.14:
+ resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
+ dev: true
+
+ /@jridgewell/trace-mapping/0.3.17:
+ resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
dependencies:
- '@motionone/types': 10.15.1
- hey-listen: 1.0.8
- tslib: 2.5.0
- dev: false
+ '@jridgewell/resolve-uri': 3.1.0
+ '@jridgewell/sourcemap-codec': 1.4.14
+ dev: true
- /@next/env/13.1.6:
- resolution: {integrity: sha512-s+W9Fdqh5MFk6ECrbnVmmAOwxKQuhGMT7xXHrkYIBMBcTiOqNWhv5KbJIboKR5STXxNXl32hllnvKaffzFaWQg==}
+ /@next/env/13.2.4:
+ resolution: {integrity: sha512-+Mq3TtpkeeKFZanPturjcXt+KHfKYnLlX6jMLyCrmpq6OOs4i1GqBOAauSkii9QeKCMTYzGppar21JU57b/GEA==}
dev: false
- /@next/eslint-plugin-next/13.1.6:
- resolution: {integrity: sha512-o7cauUYsXjzSJkay8wKjpKJf2uLzlggCsGUkPu3lP09Pv97jYlekTC20KJrjQKmSv5DXV0R/uks2ZXhqjNkqAw==}
+ /@next/eslint-plugin-next/13.2.4:
+ resolution: {integrity: sha512-ck1lI+7r1mMJpqLNa3LJ5pxCfOB1lfJncKmRJeJxcJqcngaFwylreLP7da6Rrjr6u2gVRTfmnkSkjc80IiQCwQ==}
dependencies:
glob: 7.1.7
dev: true
- /@next/font/13.1.6:
- resolution: {integrity: sha512-AITjmeb1RgX1HKMCiA39ztx2mxeAyxl4ljv2UoSBUGAbFFMg8MO7YAvjHCgFhD39hL7YTbFjol04e/BPBH5RzQ==}
- dev: false
-
- /@next/swc-android-arm-eabi/13.1.6:
- resolution: {integrity: sha512-F3/6Z8LH/pGlPzR1AcjPFxx35mPqjE5xZcf+IL+KgbW9tMkp7CYi1y7qKrEWU7W4AumxX/8OINnDQWLiwLasLQ==}
+ /@next/swc-android-arm-eabi/13.2.4:
+ resolution: {integrity: sha512-DWlalTSkLjDU11MY11jg17O1gGQzpRccM9Oes2yTqj2DpHndajrXHGxj9HGtJ+idq2k7ImUdJVWS2h2l/EDJOw==}
engines: {node: '>= 10'}
cpu: [arm]
os: [android]
@@ -224,8 +484,8 @@ packages:
dev: false
optional: true
- /@next/swc-android-arm64/13.1.6:
- resolution: {integrity: sha512-cMwQjnB8vrYkWyK/H0Rf2c2pKIH4RGjpKUDvbjVAit6SbwPDpmaijLio0LWFV3/tOnY6kvzbL62lndVA0mkYpw==}
+ /@next/swc-android-arm64/13.2.4:
+ resolution: {integrity: sha512-sRavmUImUCf332Gy+PjIfLkMhiRX1Ez4SI+3vFDRs1N5eXp+uNzjFUK/oLMMOzk6KFSkbiK/3Wt8+dHQR/flNg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
@@ -233,8 +493,8 @@ packages:
dev: false
optional: true
- /@next/swc-darwin-arm64/13.1.6:
- resolution: {integrity: sha512-KKRQH4DDE4kONXCvFMNBZGDb499Hs+xcFAwvj+rfSUssIDrZOlyfJNy55rH5t2Qxed1e4K80KEJgsxKQN1/fyw==}
+ /@next/swc-darwin-arm64/13.2.4:
+ resolution: {integrity: sha512-S6vBl+OrInP47TM3LlYx65betocKUUlTZDDKzTiRDbsRESeyIkBtZ6Qi5uT2zQs4imqllJznVjFd1bXLx3Aa6A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
@@ -242,8 +502,8 @@ packages:
dev: false
optional: true
- /@next/swc-darwin-x64/13.1.6:
- resolution: {integrity: sha512-/uOky5PaZDoaU99ohjtNcDTJ6ks/gZ5ykTQDvNZDjIoCxFe3+t06bxsTPY6tAO6uEAw5f6vVFX5H5KLwhrkZCA==}
+ /@next/swc-darwin-x64/13.2.4:
+ resolution: {integrity: sha512-a6LBuoYGcFOPGd4o8TPo7wmv5FnMr+Prz+vYHopEDuhDoMSHOnC+v+Ab4D7F0NMZkvQjEJQdJS3rqgFhlZmKlw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
@@ -251,8 +511,8 @@ packages:
dev: false
optional: true
- /@next/swc-freebsd-x64/13.1.6:
- resolution: {integrity: sha512-qaEALZeV7to6weSXk3Br80wtFQ7cFTpos/q+m9XVRFggu+8Ib895XhMWdJBzew6aaOcMvYR6KQ6JmHA2/eMzWw==}
+ /@next/swc-freebsd-x64/13.2.4:
+ resolution: {integrity: sha512-kkbzKVZGPaXRBPisoAQkh3xh22r+TD+5HwoC5bOkALraJ0dsOQgSMAvzMXKsN3tMzJUPS0tjtRf1cTzrQ0I5vQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
@@ -260,8 +520,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-arm-gnueabihf/13.1.6:
- resolution: {integrity: sha512-OybkbC58A1wJ+JrJSOjGDvZzrVEQA4sprJejGqMwiZyLqhr9Eo8FXF0y6HL+m1CPCpPhXEHz/2xKoYsl16kNqw==}
+ /@next/swc-linux-arm-gnueabihf/13.2.4:
+ resolution: {integrity: sha512-7qA1++UY0fjprqtjBZaOA6cas/7GekpjVsZn/0uHvquuITFCdKGFCsKNBx3S0Rpxmx6WYo0GcmhNRM9ru08BGg==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
@@ -269,8 +529,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-arm64-gnu/13.1.6:
- resolution: {integrity: sha512-yCH+yDr7/4FDuWv6+GiYrPI9kcTAO3y48UmaIbrKy8ZJpi7RehJe3vIBRUmLrLaNDH3rY1rwoHi471NvR5J5NQ==}
+ /@next/swc-linux-arm64-gnu/13.2.4:
+ resolution: {integrity: sha512-xzYZdAeq883MwXgcwc72hqo/F/dwUxCukpDOkx/j1HTq/J0wJthMGjinN9wH5bPR98Mfeh1MZJ91WWPnZOedOg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -278,8 +538,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-arm64-musl/13.1.6:
- resolution: {integrity: sha512-ECagB8LGX25P9Mrmlc7Q/TQBb9rGScxHbv/kLqqIWs2fIXy6Y/EiBBiM72NTwuXUFCNrWR4sjUPSooVBJJ3ESQ==}
+ /@next/swc-linux-arm64-musl/13.2.4:
+ resolution: {integrity: sha512-8rXr3WfmqSiYkb71qzuDP6I6R2T2tpkmf83elDN8z783N9nvTJf2E7eLx86wu2OJCi4T05nuxCsh4IOU3LQ5xw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -287,8 +547,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-x64-gnu/13.1.6:
- resolution: {integrity: sha512-GT5w2mruk90V/I5g6ScuueE7fqj/d8Bui2qxdw6lFxmuTgMeol5rnzAv4uAoVQgClOUO/MULilzlODg9Ib3Y4Q==}
+ /@next/swc-linux-x64-gnu/13.2.4:
+ resolution: {integrity: sha512-Ngxh51zGSlYJ4EfpKG4LI6WfquulNdtmHg1yuOYlaAr33KyPJp4HeN/tivBnAHcZkoNy0hh/SbwDyCnz5PFJQQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -296,8 +556,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-x64-musl/13.1.6:
- resolution: {integrity: sha512-keFD6KvwOPzmat4TCnlnuxJCQepPN+8j3Nw876FtULxo8005Y9Ghcl7ACcR8GoiKoddAq8gxNBrpjoxjQRHeAQ==}
+ /@next/swc-linux-x64-musl/13.2.4:
+ resolution: {integrity: sha512-gOvwIYoSxd+j14LOcvJr+ekd9fwYT1RyMAHOp7znA10+l40wkFiMONPLWiZuHxfRk+Dy7YdNdDh3ImumvL6VwA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -305,8 +565,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-arm64-msvc/13.1.6:
- resolution: {integrity: sha512-OwertslIiGQluFvHyRDzBCIB07qJjqabAmINlXUYt7/sY7Q7QPE8xVi5beBxX/rxTGPIbtyIe3faBE6Z2KywhQ==}
+ /@next/swc-win32-arm64-msvc/13.2.4:
+ resolution: {integrity: sha512-q3NJzcfClgBm4HvdcnoEncmztxrA5GXqKeiZ/hADvC56pwNALt3ngDC6t6qr1YW9V/EPDxCYeaX4zYxHciW4Dw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
@@ -314,8 +574,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-ia32-msvc/13.1.6:
- resolution: {integrity: sha512-g8zowiuP8FxUR9zslPmlju7qYbs2XBtTLVSxVikPtUDQedhcls39uKYLvOOd1JZg0ehyhopobRoH1q+MHlIN/w==}
+ /@next/swc-win32-ia32-msvc/13.2.4:
+ resolution: {integrity: sha512-/eZ5ncmHUYtD2fc6EUmAIZlAJnVT2YmxDsKs1Ourx0ttTtvtma/WKlMV5NoUsyOez0f9ExLyOpeCoz5aj+MPXw==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
@@ -323,8 +583,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-x64-msvc/13.1.6:
- resolution: {integrity: sha512-Ls2OL9hi3YlJKGNdKv8k3X/lLgc3VmLG3a/DeTkAd+lAituJp8ZHmRmm9f9SL84fT3CotlzcgbdaCDfFwFA6bA==}
+ /@next/swc-win32-x64-msvc/13.2.4:
+ resolution: {integrity: sha512-0MffFmyv7tBLlji01qc0IaPP/LVExzvj7/R5x1Jph1bTAIj4Vu81yFQWHHQAP6r4ff9Ukj1mBK6MDNVXm7Tcvw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -353,8 +613,8 @@ packages:
fastq: 1.15.0
dev: true
- /@panva/hkdf/1.0.2:
- resolution: {integrity: sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==}
+ /@panva/hkdf/1.0.4:
+ resolution: {integrity: sha512-003xWiCuvePbLaPHT+CRuaV4GlyCAVm6XYSbBZDHoWZGn1mNkVKFaDbGJjjxmEFvizUwlCoM6O18FCBMMky2zQ==}
dev: false
/@pkgr/utils/2.3.1:
@@ -363,14 +623,14 @@ packages:
dependencies:
cross-spawn: 7.0.3
is-glob: 4.0.3
- open: 8.4.0
+ open: 8.4.2
picocolors: 1.0.0
tiny-glob: 0.2.9
tslib: 2.5.0
dev: true
- /@prisma/client/4.9.0_prisma@4.9.0:
- resolution: {integrity: sha512-bz6QARw54sWcbyR1lLnF2QHvRW5R/Jxnbbmwh3u+969vUKXtBkXgSgjDA85nji31ZBlf7+FrHDy5x+5ydGyQDg==}
+ /@prisma/client/4.11.0_prisma@4.11.0:
+ resolution: {integrity: sha512-0INHYkQIqgAjrt7NzhYpeDQi8x3Nvylc2uDngKyFDDj1tTRQ4uV1HnVmd1sQEraeVAN63SOK0dgCKQHlvjL0KA==}
engines: {node: '>=14.17'}
requiresBuild: true
peerDependencies:
@@ -379,54 +639,54 @@ packages:
prisma:
optional: true
dependencies:
- '@prisma/engines-version': 4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5
- prisma: 4.9.0
+ '@prisma/engines-version': 4.11.0-57.8fde8fef4033376662cad983758335009d522acb
+ prisma: 4.11.0
dev: false
- /@prisma/engines-version/4.9.0-42.ceb5c99003b99c9ee2c1d2e618e359c14aef2ea5:
- resolution: {integrity: sha512-M16aibbxi/FhW7z1sJCX8u+0DriyQYY5AyeTH7plQm9MLnURoiyn3CZBqAyIoQ+Z1pS77usCIibYJWSgleBMBA==}
+ /@prisma/engines-version/4.11.0-57.8fde8fef4033376662cad983758335009d522acb:
+ resolution: {integrity: sha512-3Vd8Qq06d5xD8Ch5WauWcUUrsVPdMC6Ge8ILji8RFfyhUpqon6qSyGM0apvr1O8n8qH8cKkEFqRPsYjuz5r83g==}
dev: false
- /@prisma/engines/4.9.0:
- resolution: {integrity: sha512-t1pt0Gsp+HcgPJrHFc+d/ZSAaKKWar2G/iakrE07yeKPNavDP3iVKPpfXP22OTCHZUWf7OelwKJxQgKAm5hkgw==}
+ /@prisma/engines/4.11.0:
+ resolution: {integrity: sha512-0AEBi2HXGV02cf6ASsBPhfsVIbVSDC9nbQed4iiY5eHttW9ZtMxHThuKZE1pnESbr8HRdgmFSa/Kn4OSNYuibg==}
requiresBuild: true
/@radix-ui/number/1.0.0:
resolution: {integrity: sha512-Ofwh/1HX69ZfJRiRBMTy7rgjAzHmwe4kW9C9Y99HTRUcYLUuVT0KESFj15rPjRgKJs20GPq8Bm5aEDJ8DuA3vA==}
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
dev: false
/@radix-ui/primitive/1.0.0:
resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==}
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
dev: false
- /@radix-ui/react-arrow/1.0.1_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-1yientwXqXcErDHEv8av9ZVNEBldH8L9scVR3is20lL+jOCfcJyMFZFEY5cgIrgexsq1qggSXqiEL/d/4f+QXA==}
+ /@radix-ui/react-arrow/1.0.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@babel/runtime': 7.21.0
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
- /@radix-ui/react-checkbox/1.0.1_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-TisH0B8hWmYP3ONRduYCyN04rR9yLPIw/Rwyn1RoC1suSoGCa8Wn+YPdSSSarSszeIbcg3p2lBkDp2XXit4sZw==}
+ /@radix-ui/react-checkbox/1.0.3_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-55B8/vKzTuzxllH5sGJO4zaBf9gYpJuJRRzaOKm+0oAefRnMvbf+Kgww7IOANVN0w3z7agFJgtnXaZl8Uj95AA==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
'@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0
'@radix-ui/react-use-previous': 1.0.0_react@18.2.0
'@radix-ui/react-use-size': 1.0.0_react@18.2.0
@@ -434,16 +694,16 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
- /@radix-ui/react-collection/1.0.1_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==}
+ /@radix-ui/react-collection/1.0.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-s8WdQQ6wNXpaxdZ308KSr8fEWGrg4un8i4r/w7fhiS4ElRNjk5rRcl0/C6TANG2LvLOGIxtzo/jAg6Qf73TEBw==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-slot': 1.0.1_react@18.2.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
@@ -454,7 +714,7 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
react: 18.2.0
dev: false
@@ -463,33 +723,33 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
react: 18.2.0
dev: false
- /@radix-ui/react-dialog/1.0.2_5ndqzdd6t4rivxsukjv3i3ak2q:
- resolution: {integrity: sha512-EKxxp2WNSmUPkx4trtWNmZ4/vAYEg7JkAfa1HKBUnaubw9eHzf1Orr9B472lJYaYz327RHDrd4R95fsw7VR8DA==}
+ /@radix-ui/react-dialog/1.0.3_zula6vjvt3wdocc4mwcxqa6nzi:
+ resolution: {integrity: sha512-owNhq36kNPqC2/a+zJRioPg6HHnTn5B/sh/NjTY8r4W9g1L5VJlrzZIVcBr7R9Mg8iLjVmh6MGgMlfoVf/WO/A==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
- '@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-dismissable-layer': 1.0.3_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-focus-guards': 1.0.0_react@18.2.0
- '@radix-ui/react-focus-scope': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-focus-scope': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-id': 1.0.0_react@18.2.0
- '@radix-ui/react-portal': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-portal': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-slot': 1.0.1_react@18.2.0
'@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0
- aria-hidden: 1.2.2_3stiutgnnbnfnf3uowm5cip22i
+ aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
- react-remove-scroll: 2.5.5_3stiutgnnbnfnf3uowm5cip22i
+ react-remove-scroll: 2.5.5_pmekkgnqduwlme35zpnqhenc34
transitivePeerDependencies:
- '@types/react'
dev: false
@@ -499,20 +759,20 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
react: 18.2.0
dev: false
- /@radix-ui/react-dismissable-layer/1.0.2_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-WjJzMrTWROozDqLB0uRWYvj4UuXsM/2L19EmQ3Au+IJWqwvwq9Bwd+P8ivo0Deg9JDPArR1I6MbWNi1CmXsskg==}
+ /@radix-ui/react-dismissable-layer/1.0.3_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-nXZOvFjOuHS1ovumntGV7NNoLaEp9JEvTht3MBjP44NSW5hUKj/8OnfN3+8WmB+CEhN44XaGhpHoSsUIEl5P7Q==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0
'@radix-ui/react-use-escape-keydown': 1.0.2_react@18.2.0
react: 18.2.0
@@ -524,86 +784,116 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
react: 18.2.0
dev: false
- /@radix-ui/react-focus-scope/1.0.1_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-Ej2MQTit8IWJiS2uuujGUmxXjF/y5xZptIIQnyd2JHLwtV0R2j9NRVoRj/1j/gJ7e3REdaBw4Hjf4a1ImhkZcQ==}
+ /@radix-ui/react-focus-scope/1.0.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
+ /@radix-ui/react-hover-card/1.0.5_zula6vjvt3wdocc4mwcxqa6nzi:
+ resolution: {integrity: sha512-jXRuZEkxSWdHZbVyL0J46cm7pQjmOMpwJEFKY+VqAJnV+FxS+zIZExI1OCeIiDwCBzUy6If1FfouOsfqBxr86g==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@radix-ui/primitive': 1.0.0
+ '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
+ '@radix-ui/react-context': 1.0.0_react@18.2.0
+ '@radix-ui/react-dismissable-layer': 1.0.3_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-popper': 1.1.1_zula6vjvt3wdocc4mwcxqa6nzi
+ '@radix-ui/react-portal': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ transitivePeerDependencies:
+ - '@types/react'
+ dev: false
+
+ /@radix-ui/react-icons/1.2.0_react@18.2.0:
+ resolution: {integrity: sha512-NqrZxn+Ig6c6MypUt84/Nab9WBFXH75T1mhEhFjPlIYaNkp113pqlo6QdK5r7zb3b7RckDAPgUQACes0aKwcFA==}
+ peerDependencies:
+ react: ^16.x || ^17.x || ^18.x
+ dependencies:
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-id/1.0.0_react@18.2.0:
resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0
react: 18.2.0
dev: false
- /@radix-ui/react-label/2.0.0_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-7qCcZ3j2VQspWjy+gKR4W+V/z0XueQjeiZnlPOtsyiP9HaS8bfSU7ECoI3bvvdYntQj7NElW7OAYsYRW4MQvCg==}
+ /@radix-ui/react-label/2.0.1_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-qcfbS3B8hTYmEO44RNcXB6pegkxRsJIbdxTMu0PEX0Luv5O2DvTIwwVYxQfUwLpM88EL84QRPLOLgwUSApMsLQ==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@babel/runtime': 7.21.0
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
- /@radix-ui/react-popover/1.0.3_5ndqzdd6t4rivxsukjv3i3ak2q:
- resolution: {integrity: sha512-YwedSukfWsyJs3/yP3yXUq44k4/JBe3jqU63Z8v2i19qZZ3dsx32oma17ztgclWPNuqp3A+Xa9UiDlZHyVX8Vg==}
+ /@radix-ui/react-popover/1.0.5_zula6vjvt3wdocc4mwcxqa6nzi:
+ resolution: {integrity: sha512-GRHZ8yD12MrN2NLobHPE8Rb5uHTxd9x372DE9PPNnBjpczAQHcZ5ne0KXG4xpf+RDdXSzdLv9ym6mYJCDTaUZg==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
- '@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-dismissable-layer': 1.0.3_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-focus-guards': 1.0.0_react@18.2.0
- '@radix-ui/react-focus-scope': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-focus-scope': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-id': 1.0.0_react@18.2.0
- '@radix-ui/react-popper': 1.1.0_5ndqzdd6t4rivxsukjv3i3ak2q
- '@radix-ui/react-portal': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-popper': 1.1.1_zula6vjvt3wdocc4mwcxqa6nzi
+ '@radix-ui/react-portal': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-slot': 1.0.1_react@18.2.0
'@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0
- aria-hidden: 1.2.2_3stiutgnnbnfnf3uowm5cip22i
+ aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
- react-remove-scroll: 2.5.5_3stiutgnnbnfnf3uowm5cip22i
+ react-remove-scroll: 2.5.5_pmekkgnqduwlme35zpnqhenc34
transitivePeerDependencies:
- '@types/react'
dev: false
- /@radix-ui/react-popper/1.1.0_5ndqzdd6t4rivxsukjv3i3ak2q:
- resolution: {integrity: sha512-07U7jpI0dZcLRAxT7L9qs6HecSoPhDSJybF7mEGHJDBDv+ZoGCvIlva0s+WxMXwJEav+ckX3hAlXBtnHmuvlCQ==}
+ /@radix-ui/react-popper/1.1.1_zula6vjvt3wdocc4mwcxqa6nzi:
+ resolution: {integrity: sha512-keYDcdMPNMjSC8zTsZ8wezUMiWM9Yj14wtF3s0PTIs9srnEPC9Kt2Gny1T3T81mmSeyDjZxsD9N5WCwNNb712w==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
- '@floating-ui/react-dom': 0.7.2_5ndqzdd6t4rivxsukjv3i3ak2q
- '@radix-ui/react-arrow': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@babel/runtime': 7.21.0
+ '@floating-ui/react-dom': 0.7.2_zula6vjvt3wdocc4mwcxqa6nzi
+ '@radix-ui/react-arrow': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0
'@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0
'@radix-ui/react-use-rect': 1.0.0_react@18.2.0
@@ -615,14 +905,14 @@ packages:
- '@types/react'
dev: false
- /@radix-ui/react-portal/1.0.1_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-NY2vUWI5WENgAT1nfC6JS7RU5xRYBfjZVLq0HmgEN1Ezy3rk/UruMV4+Rd0F40PEaFC5SrLS1ixYvcYIQrb4Ig==}
+ /@radix-ui/react-portal/1.0.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@babel/runtime': 7.21.0
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
@@ -640,32 +930,32 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
- /@radix-ui/react-primitive/1.0.1_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==}
+ /@radix-ui/react-primitive/1.0.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/react-slot': 1.0.1_react@18.2.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
- /@radix-ui/react-radio-group/1.1.1_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-fmg1CuDKt3GAkL3YnHekmdOicyrXlbp/s/D0MrHa+YB2Un+umpJGheiRowlQtxSpb1eeehKNTINgNESi8WK5rA==}
+ /@radix-ui/react-radio-group/1.1.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-S7K8upMjOkx1fTUzEugbfCYPwI9Yw4m2h2ZfJP+ZWP/Mzc/LE2T6QgiAMaSaC3vZSxU5Kk5Eb377zMklWeaaCQ==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
'@radix-ui/react-direction': 1.0.0_react@18.2.0
'@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
- '@radix-ui/react-roving-focus': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-roving-focus': 1.0.3_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0
'@radix-ui/react-use-previous': 1.0.0_react@18.2.0
'@radix-ui/react-use-size': 1.0.0_react@18.2.0
@@ -673,68 +963,68 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
- /@radix-ui/react-roving-focus/1.0.2_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-HLK+CqD/8pN6GfJm3U+cqpqhSKYAWiOJDe+A+8MfxBnOue39QEeMa43csUn2CXCHQT0/mewh1LrrG4tfkM9DMA==}
+ /@radix-ui/react-roving-focus/1.0.3_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-stjCkIoMe6h+1fWtXlA6cRfikdBzCLp3SnVk7c48cv/uy3DTGoXhN76YaOYUJuy3aEDvDIKwKR5KSmvrtPvQPQ==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/primitive': 1.0.0
- '@radix-ui/react-collection': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-collection': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
'@radix-ui/react-direction': 1.0.0_react@18.2.0
'@radix-ui/react-id': 1.0.0_react@18.2.0
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0
'@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
- /@radix-ui/react-select/1.2.0_5ndqzdd6t4rivxsukjv3i3ak2q:
- resolution: {integrity: sha512-MmXKsIBrG9GKxt8JKIn75LEPiX/zejBmj/Z36Hxtm9cdmCFzTo78QJ0Q3buLGzr0c3lzXdfgeKntmgCzaGxgkw==}
+ /@radix-ui/react-select/1.2.1_zula6vjvt3wdocc4mwcxqa6nzi:
+ resolution: {integrity: sha512-GULRMITaOHNj79BZvQs3iZO0+f2IgI8g5HDhMi7Bnc13t7IlG86NFtOCfTLme4PNZdEtU+no+oGgcl6IFiphpQ==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/number': 1.0.0
'@radix-ui/primitive': 1.0.0
- '@radix-ui/react-collection': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-collection': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
'@radix-ui/react-direction': 1.0.0_react@18.2.0
- '@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-dismissable-layer': 1.0.3_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-focus-guards': 1.0.0_react@18.2.0
- '@radix-ui/react-focus-scope': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-focus-scope': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-id': 1.0.0_react@18.2.0
- '@radix-ui/react-popper': 1.1.0_5ndqzdd6t4rivxsukjv3i3ak2q
- '@radix-ui/react-portal': 1.0.1_biqbaboplfbrettd7655fr4n2y
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-popper': 1.1.1_zula6vjvt3wdocc4mwcxqa6nzi
+ '@radix-ui/react-portal': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-slot': 1.0.1_react@18.2.0
'@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0
'@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0
'@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0
'@radix-ui/react-use-previous': 1.0.0_react@18.2.0
- '@radix-ui/react-visually-hidden': 1.0.1_biqbaboplfbrettd7655fr4n2y
- aria-hidden: 1.2.2_3stiutgnnbnfnf3uowm5cip22i
+ '@radix-ui/react-visually-hidden': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ aria-hidden: 1.2.3
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
- react-remove-scroll: 2.5.5_3stiutgnnbnfnf3uowm5cip22i
+ react-remove-scroll: 2.5.5_pmekkgnqduwlme35zpnqhenc34
transitivePeerDependencies:
- '@types/react'
dev: false
- /@radix-ui/react-separator/1.0.1_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-uc6Izot0D8uVz6T2nSb/HI7OaxkeaD50GgKr3W6HORnbfGVrG7LWuy+g6Fd58n8wHbrRblSYJZEfcjgymMlJjw==}
+ /@radix-ui/react-separator/1.0.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-lZoAG/rS2jzb/OSvyBrpN3dmikw20ewmWx1GkM1VldbDyD0DACCbH9LIXSrqyS/2mE1VYKOHmyq5W90Dx4ryqA==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@babel/runtime': 7.21.0
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
@@ -744,9 +1034,32 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
+ '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-toast/1.1.3_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-yHFgpxi9wjbfPvpSPdYAzivCqw48eA1ofT8m/WqYOVTxKPdmQMuVKRYPlMmj4C1d6tJdFj/LBa1J4iY3fL4OwQ==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.21.0
+ '@radix-ui/primitive': 1.0.0
+ '@radix-ui/react-collection': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
+ '@radix-ui/react-context': 1.0.0_react@18.2.0
+ '@radix-ui/react-dismissable-layer': 1.0.3_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-portal': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
+ '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0
+ '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0
+ '@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0
+ '@radix-ui/react-visually-hidden': 1.0.2_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
dev: false
/@radix-ui/react-use-callback-ref/1.0.0_react@18.2.0:
@@ -754,7 +1067,7 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
react: 18.2.0
dev: false
@@ -763,7 +1076,7 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0
react: 18.2.0
dev: false
@@ -773,7 +1086,7 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0
react: 18.2.0
dev: false
@@ -783,7 +1096,7 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
react: 18.2.0
dev: false
@@ -792,7 +1105,7 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
react: 18.2.0
dev: false
@@ -801,7 +1114,7 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/rect': 1.0.0
react: 18.2.0
dev: false
@@ -811,19 +1124,19 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
'@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0
react: 18.2.0
dev: false
- /@radix-ui/react-visually-hidden/1.0.1_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-K1hJcCMfWfiYUibRqf3V8r5Drpyf7rh44jnrwAbdvI5iCCijilBBeyQv9SKidYNZIopMdCyR9FnIjkHxHN0FcQ==}
+ /@radix-ui/react-visually-hidden/1.0.2_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-qirnJxtYn73HEk1rXL12/mXnu2rwsNHDID10th2JGtdK25T9wX+mxRmGt7iPSahw512GbZOc0syZX1nLQGoEOg==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
- '@babel/runtime': 7.20.13
- '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
+ '@babel/runtime': 7.21.0
+ '@radix-ui/react-primitive': 1.0.2_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
@@ -831,7 +1144,7 @@ packages:
/@radix-ui/rect/1.0.0:
resolution: {integrity: sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==}
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
dev: false
/@rushstack/eslint-patch/1.2.0:
@@ -865,47 +1178,39 @@ packages:
- debug
dev: false
- /@stitches/react/1.2.8_react@18.2.0:
- resolution: {integrity: sha512-9g9dWI4gsSVe8bNLlb+lMkBYsnIKCZTmvqvDG+Avnn69XfmHZKiaMrx7cgTaddq7aTPPmXiTsbFcUy0xgI4+wA==}
- peerDependencies:
- react: '>= 16.3.0'
- dependencies:
- react: 18.2.0
- dev: false
-
/@swc/helpers/0.4.14:
resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==}
dependencies:
tslib: 2.5.0
dev: false
- /@types/js-cookie/2.2.7:
- resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==}
- dev: false
-
- /@types/json5/0.0.29:
- resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
- dev: true
-
- /@types/lodash.merge/4.6.7:
- resolution: {integrity: sha512-OwxUJ9E50gw3LnAefSHJPHaBLGEKmQBQ7CZe/xflHkyy/wH2zVyEIAKReHvVrrn7zKdF58p16We9kMfh7v0RRQ==}
+ /@tailwindcss/line-clamp/0.4.2_tailwindcss@3.2.7:
+ resolution: {integrity: sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw==}
+ peerDependencies:
+ tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1'
dependencies:
- '@types/lodash': 4.14.191
+ tailwindcss: 3.2.7_postcss@8.4.21
dev: true
- /@types/lodash/4.14.191:
- resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
+ /@types/json5/0.0.29:
+ resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
dev: true
- /@types/node/18.11.18:
- resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
+ /@types/node/18.15.3:
+ resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==}
dev: true
/@types/prop-types/15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
- /@types/react/18.0.27:
- resolution: {integrity: sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==}
+ /@types/react-dom/18.0.11:
+ resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==}
+ dependencies:
+ '@types/react': 18.0.28
+ dev: true
+
+ /@types/react/18.0.28:
+ resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==}
dependencies:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.2
@@ -914,8 +1219,8 @@ packages:
/@types/scheduler/0.16.2:
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==}
- /@typescript-eslint/parser/5.50.0_4vsywjlpuriuw3tl5oq6zy5a64:
- resolution: {integrity: sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ==}
+ /@typescript-eslint/parser/5.55.0_vgl77cfdswitgr47lm5swmv43m:
+ resolution: {integrity: sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -924,31 +1229,31 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/scope-manager': 5.50.0
- '@typescript-eslint/types': 5.50.0
- '@typescript-eslint/typescript-estree': 5.50.0_typescript@4.9.5
+ '@typescript-eslint/scope-manager': 5.55.0
+ '@typescript-eslint/types': 5.55.0
+ '@typescript-eslint/typescript-estree': 5.55.0_typescript@4.9.5
debug: 4.3.4
- eslint: 8.33.0
+ eslint: 8.36.0
typescript: 4.9.5
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/scope-manager/5.50.0:
- resolution: {integrity: sha512-rt03kaX+iZrhssaT974BCmoUikYtZI24Vp/kwTSy841XhiYShlqoshRFDvN1FKKvU2S3gK+kcBW1EA7kNUrogg==}
+ /@typescript-eslint/scope-manager/5.55.0:
+ resolution: {integrity: sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- '@typescript-eslint/types': 5.50.0
- '@typescript-eslint/visitor-keys': 5.50.0
+ '@typescript-eslint/types': 5.55.0
+ '@typescript-eslint/visitor-keys': 5.55.0
dev: true
- /@typescript-eslint/types/5.50.0:
- resolution: {integrity: sha512-atruOuJpir4OtyNdKahiHZobPKFvZnBnfDiyEaBf6d9vy9visE7gDjlmhl+y29uxZ2ZDgvXijcungGFjGGex7w==}
+ /@typescript-eslint/types/5.55.0:
+ resolution: {integrity: sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /@typescript-eslint/typescript-estree/5.50.0_typescript@4.9.5:
- resolution: {integrity: sha512-Gq4zapso+OtIZlv8YNAStFtT6d05zyVCK7Fx3h5inlLBx2hWuc/0465C2mg/EQDDU2LKe52+/jN4f0g9bd+kow==}
+ /@typescript-eslint/typescript-estree/5.55.0_typescript@4.9.5:
+ resolution: {integrity: sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
typescript: '*'
@@ -956,8 +1261,8 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/types': 5.50.0
- '@typescript-eslint/visitor-keys': 5.50.0
+ '@typescript-eslint/types': 5.55.0
+ '@typescript-eslint/visitor-keys': 5.55.0
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
@@ -968,18 +1273,14 @@ packages:
- supports-color
dev: true
- /@typescript-eslint/visitor-keys/5.50.0:
- resolution: {integrity: sha512-cdMeD9HGu6EXIeGOh2yVW6oGf9wq8asBgZx7nsR/D36gTfQ0odE5kcRYe5M81vjEFAcPeugXrHg78Imu55F6gg==}
+ /@typescript-eslint/visitor-keys/5.55.0:
+ resolution: {integrity: sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- '@typescript-eslint/types': 5.50.0
+ '@typescript-eslint/types': 5.55.0
eslint-visitor-keys: 3.3.0
dev: true
- /@xobotyi/scrollbar-width/1.9.5:
- resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==}
- dev: false
-
/acorn-jsx/5.3.2_acorn@8.8.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -988,6 +1289,25 @@ packages:
acorn: 8.8.2
dev: true
+ /acorn-node/1.8.2:
+ resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==}
+ dependencies:
+ acorn: 7.4.1
+ acorn-walk: 7.2.0
+ xtend: 4.0.2
+ dev: true
+
+ /acorn-walk/7.2.0:
+ resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
+ engines: {node: '>=0.4.0'}
+ dev: true
+
+ /acorn/7.4.1:
+ resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+ dev: true
+
/acorn/8.8.2:
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
engines: {node: '>=0.4.0'}
@@ -1008,6 +1328,13 @@ packages:
engines: {node: '>=8'}
dev: true
+ /ansi-styles/3.2.1:
+ resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+ engines: {node: '>=4'}
+ dependencies:
+ color-convert: 1.9.3
+ dev: true
+
/ansi-styles/4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
@@ -1015,22 +1342,26 @@ packages:
color-convert: 2.0.1
dev: true
+ /anymatch/3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+ dev: true
+
+ /arg/5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+ dev: true
+
/argparse/2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
dev: true
- /aria-hidden/1.2.2_3stiutgnnbnfnf3uowm5cip22i:
- resolution: {integrity: sha512-6y/ogyDTk/7YAe91T3E2PR1ALVKyM2QbTio5HwM+N1Q6CMlCKhvClyIjkckBswa0f2xJhjsfzIGa1yVSe1UMVA==}
+ /aria-hidden/1.2.3:
+ resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==}
engines: {node: '>=10'}
- peerDependencies:
- '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0
- react: ^16.9.0 || ^17.0.0 || ^18.0.0
- peerDependenciesMeta:
- '@types/react':
- optional: true
dependencies:
- '@types/react': 18.0.27
- react: 18.2.0
tslib: 2.5.0
dev: false
@@ -1040,13 +1371,20 @@ packages:
deep-equal: 2.2.0
dev: true
+ /array-buffer-byte-length/1.0.0:
+ resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
+ dependencies:
+ call-bind: 1.0.2
+ is-array-buffer: 3.0.2
+ dev: true
+
/array-includes/3.1.6:
resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==}
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
get-intrinsic: 1.2.0
is-string: 1.0.7
dev: true
@@ -1061,8 +1399,8 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
es-shim-unscopables: 1.0.0
dev: true
@@ -1071,8 +1409,8 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
es-shim-unscopables: 1.0.0
dev: true
@@ -1080,8 +1418,8 @@ packages:
resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
es-shim-unscopables: 1.0.0
get-intrinsic: 1.2.0
dev: true
@@ -1090,6 +1428,22 @@ packages:
resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==}
dev: true
+ /autoprefixer/10.4.14_postcss@8.4.21:
+ resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==}
+ engines: {node: ^10 || ^12 || >=14}
+ hasBin: true
+ peerDependencies:
+ postcss: ^8.1.0
+ dependencies:
+ browserslist: 4.21.5
+ caniuse-lite: 1.0.30001466
+ fraction.js: 4.2.0
+ normalize-range: 0.1.2
+ picocolors: 1.0.0
+ postcss: 8.4.21
+ postcss-value-parser: 4.2.0
+ dev: true
+
/available-typed-arrays/1.0.5:
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
engines: {node: '>= 0.4'}
@@ -1118,6 +1472,11 @@ packages:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
+ /binary-extensions/2.2.0:
+ resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+ engines: {node: '>=8'}
+ dev: true
+
/brace-expansion/1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
@@ -1132,6 +1491,17 @@ packages:
fill-range: 7.0.1
dev: true
+ /browserslist/4.21.5:
+ resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+ dependencies:
+ caniuse-lite: 1.0.30001466
+ electron-to-chromium: 1.4.330
+ node-releases: 2.0.10
+ update-browserslist-db: 1.0.10_browserslist@4.21.5
+ dev: true
+
/call-bind/1.0.2:
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
dependencies:
@@ -1144,9 +1514,22 @@ packages:
engines: {node: '>=6'}
dev: true
- /caniuse-lite/1.0.30001449:
- resolution: {integrity: sha512-CPB+UL9XMT/Av+pJxCKGhdx+yg1hzplvFJQlJ2n68PyQGMz9L/E2zCyLdOL8uasbouTUgnPl+y0tccI/se+BEw==}
- dev: false
+ /camelcase-css/2.0.1:
+ resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+ engines: {node: '>= 6'}
+ dev: true
+
+ /caniuse-lite/1.0.30001466:
+ resolution: {integrity: sha512-ewtFBSfWjEmxUgNBSZItFSmVtvk9zkwkl1OfRZlKA8slltRN+/C/tuGVrF9styXkN36Yu3+SeJ1qkXxDEyNZ5w==}
+
+ /chalk/2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+ dev: true
/chalk/4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
@@ -1156,10 +1539,47 @@ packages:
supports-color: 7.2.0
dev: true
+ /chokidar/3.5.3:
+ resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+ engines: {node: '>= 8.10.0'}
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.2
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
+ /class-variance-authority/0.4.0_typescript@4.9.5:
+ resolution: {integrity: sha512-74enNN8O9ZNieycac/y8FxqgyzZhZbxmCitAtAeUrLPlxjSd5zA7LfpprmxEcOmQBnaGs5hYhiSGnJ0mqrtBLQ==}
+ peerDependencies:
+ typescript: '>= 4.5.5 < 5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ typescript: 4.9.5
+ dev: false
+
/client-only/0.0.1:
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
dev: false
+ /clsx/1.2.1:
+ resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
+ engines: {node: '>=6'}
+ dev: false
+
+ /color-convert/1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+ dependencies:
+ color-name: 1.1.3
+ dev: true
+
/color-convert/2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@@ -1167,6 +1587,10 @@ packages:
color-name: 1.1.4
dev: true
+ /color-name/1.1.3:
+ resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+ dev: true
+
/color-name/1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: true
@@ -1175,17 +1599,15 @@ packages:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
+ /convert-source-map/1.9.0:
+ resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
+ dev: true
+
/cookie/0.5.0:
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
engines: {node: '>= 0.6'}
dev: false
- /copy-to-clipboard/3.3.3:
- resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
- dependencies:
- toggle-selection: 1.0.6
- dev: false
-
/cross-spawn/7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -1195,19 +1617,11 @@ packages:
which: 2.0.2
dev: true
- /css-in-js-utils/3.1.0:
- resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==}
- dependencies:
- hyphenate-style-name: 1.0.4
- dev: false
-
- /css-tree/1.1.3:
- resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==}
- engines: {node: '>=8.0.0'}
- dependencies:
- mdn-data: 2.0.14
- source-map: 0.6.1
- dev: false
+ /cssesc/3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+ dev: true
/csstype/3.1.1:
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
@@ -1251,7 +1665,7 @@ packages:
es-get-iterator: 1.1.3
get-intrinsic: 1.2.0
is-arguments: 1.1.1
- is-array-buffer: 3.0.1
+ is-array-buffer: 3.0.2
is-date-object: 1.0.5
is-regex: 1.1.4
is-shared-array-buffer: 1.0.2
@@ -1280,18 +1694,36 @@ packages:
engines: {node: '>=8'}
dev: true
- /define-properties/1.1.4:
- resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
+ /define-properties/1.2.0:
+ resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
engines: {node: '>= 0.4'}
dependencies:
has-property-descriptors: 1.0.0
object-keys: 1.1.1
dev: true
+ /defined/1.0.1:
+ resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==}
+ dev: true
+
/detect-node-es/1.1.0:
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
dev: false
+ /detective/5.2.1:
+ resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==}
+ engines: {node: '>=0.8.0'}
+ hasBin: true
+ dependencies:
+ acorn-node: 1.8.2
+ defined: 1.0.1
+ minimist: 1.2.8
+ dev: true
+
+ /didyoumean/1.2.2:
+ resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+ dev: true
+
/dir-glob/3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
@@ -1299,6 +1731,10 @@ packages:
path-type: 4.0.0
dev: true
+ /dlv/1.1.3:
+ resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+ dev: true
+
/doctrine/2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
@@ -1313,6 +1749,10 @@ packages:
esutils: 2.0.3
dev: true
+ /electron-to-chromium/1.4.330:
+ resolution: {integrity: sha512-PqyefhybrVdjAJ45HaPLtuVaehiSw7C3ya0aad+rvmV53IVyXmYRk3pwIOb2TxTDTnmgQdn46NjMMaysx79/6Q==}
+ dev: true
+
/emoji-regex/9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
dev: true
@@ -1325,21 +1765,15 @@ packages:
tapable: 2.2.1
dev: true
- /error-stack-parser/2.1.4:
- resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==}
- dependencies:
- stackframe: 1.3.4
- dev: false
-
- /es-abstract/1.21.1:
- resolution: {integrity: sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==}
+ /es-abstract/1.21.2:
+ resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==}
engines: {node: '>= 0.4'}
dependencies:
+ array-buffer-byte-length: 1.0.0
available-typed-arrays: 1.0.5
call-bind: 1.0.2
es-set-tostringtag: 2.0.1
es-to-primitive: 1.2.1
- function-bind: 1.1.1
function.prototype.name: 1.1.5
get-intrinsic: 1.2.0
get-symbol-description: 1.0.0
@@ -1349,8 +1783,8 @@ packages:
has-property-descriptors: 1.0.0
has-proto: 1.0.1
has-symbols: 1.0.3
- internal-slot: 1.0.4
- is-array-buffer: 3.0.1
+ internal-slot: 1.0.5
+ is-array-buffer: 3.0.2
is-callable: 1.2.7
is-negative-zero: 2.0.2
is-regex: 1.1.4
@@ -1363,6 +1797,7 @@ packages:
object.assign: 4.1.4
regexp.prototype.flags: 1.4.3
safe-regex-test: 1.0.0
+ string.prototype.trim: 1.2.7
string.prototype.trimend: 1.0.6
string.prototype.trimstart: 1.0.6
typed-array-length: 1.0.4
@@ -1408,13 +1843,23 @@ packages:
is-symbol: 1.0.4
dev: true
+ /escalade/3.1.1:
+ resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /escape-string-regexp/1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+ dev: true
+
/escape-string-regexp/4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
dev: true
- /eslint-config-next/13.1.6_4vsywjlpuriuw3tl5oq6zy5a64:
- resolution: {integrity: sha512-0cg7h5wztg/SoLAlxljZ0ZPUQ7i6QKqRiP4M2+MgTZtxWwNKb2JSwNc18nJ6/kXBI6xYvPraTbQSIhAuVw6czw==}
+ /eslint-config-next/13.2.4_vgl77cfdswitgr47lm5swmv43m:
+ resolution: {integrity: sha512-lunIBhsoeqw6/Lfkd6zPt25w1bn0znLA/JCL+au1HoEpSb4/PpsOYsYtgV/q+YPsoKIOzFyU5xnb04iZnXjUvg==}
peerDependencies:
eslint: ^7.23.0 || ^8.0.0
typescript: '>=3.3.1'
@@ -1422,22 +1867,31 @@ packages:
typescript:
optional: true
dependencies:
- '@next/eslint-plugin-next': 13.1.6
+ '@next/eslint-plugin-next': 13.2.4
'@rushstack/eslint-patch': 1.2.0
- '@typescript-eslint/parser': 5.50.0_4vsywjlpuriuw3tl5oq6zy5a64
- eslint: 8.33.0
+ '@typescript-eslint/parser': 5.55.0_vgl77cfdswitgr47lm5swmv43m
+ eslint: 8.36.0
eslint-import-resolver-node: 0.3.7
- eslint-import-resolver-typescript: 3.5.3_ohdts44xlqyeyrlje4qnefqeay
- eslint-plugin-import: 2.27.5_nowqz4jutkd4a233czbfk7jsgu
- eslint-plugin-jsx-a11y: 6.7.1_eslint@8.33.0
- eslint-plugin-react: 7.32.2_eslint@8.33.0
- eslint-plugin-react-hooks: 4.6.0_eslint@8.33.0
+ eslint-import-resolver-typescript: 3.5.3_eakrjjutlgqjxe5ydhtnd4qdmy
+ eslint-plugin-import: 2.27.5_v7jo3sddp7aqau7pajjy572cju
+ eslint-plugin-jsx-a11y: 6.7.1_eslint@8.36.0
+ eslint-plugin-react: 7.32.2_eslint@8.36.0
+ eslint-plugin-react-hooks: 4.6.0_eslint@8.36.0
typescript: 4.9.5
transitivePeerDependencies:
- eslint-import-resolver-webpack
- supports-color
dev: true
+ /eslint-config-prettier/8.7.0_eslint@8.36.0:
+ resolution: {integrity: sha512-HHVXLSlVUhMSmyW4ZzEuvjpwqamgmlfkutD53cYXLikh4pt/modINRcCIApJ84czDxM4GZInwUrromsDdTImTA==}
+ hasBin: true
+ peerDependencies:
+ eslint: '>=7.0.0'
+ dependencies:
+ eslint: 8.36.0
+ dev: true
+
/eslint-import-resolver-node/0.3.7:
resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==}
dependencies:
@@ -1448,7 +1902,7 @@ packages:
- supports-color
dev: true
- /eslint-import-resolver-typescript/3.5.3_ohdts44xlqyeyrlje4qnefqeay:
+ /eslint-import-resolver-typescript/3.5.3_eakrjjutlgqjxe5ydhtnd4qdmy:
resolution: {integrity: sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
@@ -1457,9 +1911,9 @@ packages:
dependencies:
debug: 4.3.4
enhanced-resolve: 5.12.0
- eslint: 8.33.0
- eslint-plugin-import: 2.27.5_nowqz4jutkd4a233czbfk7jsgu
- get-tsconfig: 4.3.0
+ eslint: 8.36.0
+ eslint-plugin-import: 2.27.5_v7jo3sddp7aqau7pajjy572cju
+ get-tsconfig: 4.4.0
globby: 13.1.3
is-core-module: 2.11.0
is-glob: 4.0.3
@@ -1468,7 +1922,7 @@ packages:
- supports-color
dev: true
- /eslint-module-utils/2.7.4_4lq3tljpmtdh3elqaianviuctu:
+ /eslint-module-utils/2.7.4_wgltsi2kbncdpzwytonvdf2oba:
resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==}
engines: {node: '>=4'}
peerDependencies:
@@ -1489,16 +1943,16 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
- '@typescript-eslint/parser': 5.50.0_4vsywjlpuriuw3tl5oq6zy5a64
+ '@typescript-eslint/parser': 5.55.0_vgl77cfdswitgr47lm5swmv43m
debug: 3.2.7
- eslint: 8.33.0
+ eslint: 8.36.0
eslint-import-resolver-node: 0.3.7
- eslint-import-resolver-typescript: 3.5.3_ohdts44xlqyeyrlje4qnefqeay
+ eslint-import-resolver-typescript: 3.5.3_eakrjjutlgqjxe5ydhtnd4qdmy
transitivePeerDependencies:
- supports-color
dev: true
- /eslint-plugin-import/2.27.5_nowqz4jutkd4a233czbfk7jsgu:
+ /eslint-plugin-import/2.27.5_v7jo3sddp7aqau7pajjy572cju:
resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==}
engines: {node: '>=4'}
peerDependencies:
@@ -1508,15 +1962,15 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
- '@typescript-eslint/parser': 5.50.0_4vsywjlpuriuw3tl5oq6zy5a64
+ '@typescript-eslint/parser': 5.55.0_vgl77cfdswitgr47lm5swmv43m
array-includes: 3.1.6
array.prototype.flat: 1.3.1
array.prototype.flatmap: 1.3.1
debug: 3.2.7
doctrine: 2.1.0
- eslint: 8.33.0
+ eslint: 8.36.0
eslint-import-resolver-node: 0.3.7
- eslint-module-utils: 2.7.4_4lq3tljpmtdh3elqaianviuctu
+ eslint-module-utils: 2.7.4_wgltsi2kbncdpzwytonvdf2oba
has: 1.0.3
is-core-module: 2.11.0
is-glob: 4.0.3
@@ -1524,20 +1978,20 @@ packages:
object.values: 1.1.6
resolve: 1.22.1
semver: 6.3.0
- tsconfig-paths: 3.14.1
+ tsconfig-paths: 3.14.2
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
dev: true
- /eslint-plugin-jsx-a11y/6.7.1_eslint@8.33.0:
+ /eslint-plugin-jsx-a11y/6.7.1_eslint@8.36.0:
resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==}
engines: {node: '>=4.0'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies:
- '@babel/runtime': 7.20.13
+ '@babel/runtime': 7.21.0
aria-query: 5.1.3
array-includes: 3.1.6
array.prototype.flatmap: 1.3.1
@@ -1546,7 +2000,7 @@ packages:
axobject-query: 3.1.1
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
- eslint: 8.33.0
+ eslint: 8.36.0
has: 1.0.3
jsx-ast-utils: 3.3.3
language-tags: 1.0.5
@@ -1556,16 +2010,16 @@ packages:
semver: 6.3.0
dev: true
- /eslint-plugin-react-hooks/4.6.0_eslint@8.33.0:
+ /eslint-plugin-react-hooks/4.6.0_eslint@8.36.0:
resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
engines: {node: '>=10'}
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
dependencies:
- eslint: 8.33.0
+ eslint: 8.36.0
dev: true
- /eslint-plugin-react/7.32.2_eslint@8.33.0:
+ /eslint-plugin-react/7.32.2_eslint@8.36.0:
resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==}
engines: {node: '>=4'}
peerDependencies:
@@ -1575,7 +2029,7 @@ packages:
array.prototype.flatmap: 1.3.1
array.prototype.tosorted: 1.1.1
doctrine: 2.1.0
- eslint: 8.33.0
+ eslint: 8.36.0
estraverse: 5.3.0
jsx-ast-utils: 3.3.3
minimatch: 3.1.2
@@ -1589,6 +2043,17 @@ packages:
string.prototype.matchall: 4.0.8
dev: true
+ /eslint-plugin-tailwindcss/3.10.1_tailwindcss@3.2.7:
+ resolution: {integrity: sha512-NLPZ6b6nd/8CgGNMQ6NDiPUfBLQpSGu/u9RyX3MCZOwzNs2dFt1OamNAiRuo3Ixh7Gv4t5UcAcdNt8z74UDJkA==}
+ engines: {node: '>=12.13.0'}
+ peerDependencies:
+ tailwindcss: ^3.2.2
+ dependencies:
+ fast-glob: 3.2.12
+ postcss: 8.4.21
+ tailwindcss: 3.2.7_postcss@8.4.21
+ dev: true
+
/eslint-scope/7.1.1:
resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -1597,32 +2062,20 @@ packages:
estraverse: 5.3.0
dev: true
- /eslint-utils/3.0.0_eslint@8.33.0:
- resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
- engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
- peerDependencies:
- eslint: '>=5'
- dependencies:
- eslint: 8.33.0
- eslint-visitor-keys: 2.1.0
- dev: true
-
- /eslint-visitor-keys/2.1.0:
- resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
- engines: {node: '>=10'}
- dev: true
-
/eslint-visitor-keys/3.3.0:
resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /eslint/8.33.0:
- resolution: {integrity: sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==}
+ /eslint/8.36.0:
+ resolution: {integrity: sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
- '@eslint/eslintrc': 1.4.1
+ '@eslint-community/eslint-utils': 4.2.0_eslint@8.36.0
+ '@eslint-community/regexpp': 4.4.0
+ '@eslint/eslintrc': 2.0.1
+ '@eslint/js': 8.36.0
'@humanwhocodes/config-array': 0.11.8
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
@@ -1633,10 +2086,9 @@ packages:
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.1.1
- eslint-utils: 3.0.0_eslint@8.33.0
eslint-visitor-keys: 3.3.0
- espree: 9.4.1
- esquery: 1.4.0
+ espree: 9.5.0
+ esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 6.0.1
@@ -1657,7 +2109,6 @@ packages:
minimatch: 3.1.2
natural-compare: 1.4.0
optionator: 0.9.1
- regexpp: 3.2.0
strip-ansi: 6.0.1
strip-json-comments: 3.1.1
text-table: 0.2.0
@@ -1665,8 +2116,8 @@ packages:
- supports-color
dev: true
- /espree/9.4.1:
- resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==}
+ /espree/9.5.0:
+ resolution: {integrity: sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
acorn: 8.8.2
@@ -1674,8 +2125,8 @@ packages:
eslint-visitor-keys: 3.3.0
dev: true
- /esquery/1.4.0:
- resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
+ /esquery/1.5.0:
+ resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
engines: {node: '>=0.10'}
dependencies:
estraverse: 5.3.0
@@ -1700,6 +2151,7 @@ packages:
/fast-deep-equal/3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+ dev: true
/fast-glob/3.2.12:
resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}
@@ -1717,20 +2169,8 @@ packages:
dev: true
/fast-levenshtein/2.0.6:
- resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
- dev: true
-
- /fast-loops/1.1.3:
- resolution: {integrity: sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==}
- dev: false
-
- /fast-shallow-equal/1.0.0:
- resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==}
- dev: false
-
- /fastest-stable-stringify/2.0.2:
- resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==}
- dev: false
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+ dev: true
/fastq/1.15.0:
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
@@ -1788,25 +2228,22 @@ packages:
is-callable: 1.2.7
dev: true
- /framer-motion/8.5.5_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-5IDx5bxkjWHWUF3CVJoSyUVOtrbAxtzYBBowRE2uYI/6VYhkEBD+rbTHEGuUmbGHRj6YqqSfoG7Aa1cLyWCrBA==}
- peerDependencies:
- react: ^18.0.0
- react-dom: ^18.0.0
- dependencies:
- '@motionone/dom': 10.15.5
- hey-listen: 1.0.8
- react: 18.2.0
- react-dom: 18.2.0_react@18.2.0
- tslib: 2.5.0
- optionalDependencies:
- '@emotion/is-prop-valid': 0.8.8
- dev: false
+ /fraction.js/4.2.0:
+ resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
+ dev: true
/fs.realpath/1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
dev: true
+ /fsevents/2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/function-bind/1.1.1:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
dev: true
@@ -1816,8 +2253,8 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
functions-have-names: 1.2.3
dev: true
@@ -1825,6 +2262,11 @@ packages:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
dev: true
+ /gensync/1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
/get-intrinsic/1.2.0:
resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==}
dependencies:
@@ -1846,8 +2288,8 @@ packages:
get-intrinsic: 1.2.0
dev: true
- /get-tsconfig/4.3.0:
- resolution: {integrity: sha512-YCcF28IqSay3fqpIu5y3Krg/utCBHBeoflkZyHj/QcqI2nrLPC3ZegS9CmIo+hJb8K7aiGsuUl7PwWVjNG2HQQ==}
+ /get-tsconfig/4.4.0:
+ resolution: {integrity: sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==}
dev: true
/glob-parent/5.1.2:
@@ -1886,6 +2328,11 @@ packages:
path-is-absolute: 1.0.1
dev: true
+ /globals/11.12.0:
+ resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+ engines: {node: '>=4'}
+ dev: true
+
/globals/13.20.0:
resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==}
engines: {node: '>=8'}
@@ -1897,7 +2344,7 @@ packages:
resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
engines: {node: '>= 0.4'}
dependencies:
- define-properties: 1.1.4
+ define-properties: 1.2.0
dev: true
/globalyzer/0.1.0:
@@ -1949,6 +2396,11 @@ packages:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
dev: true
+ /has-flag/3.0.0:
+ resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+ engines: {node: '>=4'}
+ dev: true
+
/has-flag/4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
@@ -1984,14 +2436,6 @@ packages:
function-bind: 1.1.1
dev: true
- /hey-listen/1.0.8:
- resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==}
- dev: false
-
- /hyphenate-style-name/1.0.4:
- resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==}
- dev: false
-
/ignore/5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'}
@@ -2021,15 +2465,8 @@ packages:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
dev: true
- /inline-style-prefixer/6.0.4:
- resolution: {integrity: sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg==}
- dependencies:
- css-in-js-utils: 3.1.0
- fast-loops: 1.1.3
- dev: false
-
- /internal-slot/1.0.4:
- resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==}
+ /internal-slot/1.0.5:
+ resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
engines: {node: '>= 0.4'}
dependencies:
get-intrinsic: 1.2.0
@@ -2051,8 +2488,8 @@ packages:
has-tostringtag: 1.0.0
dev: true
- /is-array-buffer/3.0.1:
- resolution: {integrity: sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==}
+ /is-array-buffer/3.0.2:
+ resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
dependencies:
call-bind: 1.0.2
get-intrinsic: 1.2.0
@@ -2065,6 +2502,13 @@ packages:
has-bigints: 1.0.2
dev: true
+ /is-binary-path/2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+ dependencies:
+ binary-extensions: 2.2.0
+ dev: true
+
/is-boolean-object/1.1.2:
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
engines: {node: '>= 0.4'}
@@ -2210,12 +2654,12 @@ packages:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
- /jose/4.11.2:
- resolution: {integrity: sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A==}
- dev: false
+ /javascript-natural-sort/0.7.1:
+ resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==}
+ dev: true
- /js-cookie/2.2.1:
- resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==}
+ /jose/4.13.1:
+ resolution: {integrity: sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ==}
dev: false
/js-sdsl/4.3.0:
@@ -2232,6 +2676,12 @@ packages:
argparse: 2.0.1
dev: true
+ /jsesc/2.5.2:
+ resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
+ engines: {node: '>=4'}
+ hasBin: true
+ dev: true
+
/json-schema-traverse/0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
dev: true
@@ -2244,7 +2694,13 @@ packages:
resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
hasBin: true
dependencies:
- minimist: 1.2.7
+ minimist: 1.2.8
+ dev: true
+
+ /json5/2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
dev: true
/jsx-ast-utils/3.3.3:
@@ -2277,6 +2733,11 @@ packages:
type-check: 0.4.0
dev: true
+ /lilconfig/2.1.0:
+ resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
+ engines: {node: '>=10'}
+ dev: true
+
/locate-path/6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
@@ -2284,8 +2745,17 @@ packages:
p-locate: 5.0.0
dev: true
+ /lodash.clone/4.5.0:
+ resolution: {integrity: sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==}
+ dev: true
+
+ /lodash.isequal/4.5.0:
+ resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
+ dev: true
+
/lodash.merge/4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+ dev: true
/loose-envify/1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
@@ -2293,16 +2763,18 @@ packages:
dependencies:
js-tokens: 4.0.0
+ /lru-cache/5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+ dependencies:
+ yallist: 3.1.1
+ dev: true
+
/lru-cache/6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
dependencies:
yallist: 4.0.0
- /mdn-data/2.0.14:
- resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
- dev: false
-
/merge2/1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -2322,8 +2794,8 @@ packages:
brace-expansion: 1.1.11
dev: true
- /minimist/1.2.7:
- resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
+ /minimist/1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
dev: true
/ms/2.1.2:
@@ -2334,36 +2806,17 @@ packages:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
dev: true
- /nano-css/5.3.5_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg==}
- peerDependencies:
- react: '*'
- react-dom: '*'
- dependencies:
- css-tree: 1.1.3
- csstype: 3.1.1
- fastest-stable-stringify: 2.0.2
- inline-style-prefixer: 6.0.4
- react: 18.2.0
- react-dom: 18.2.0_react@18.2.0
- rtl-css-js: 1.16.1
- sourcemap-codec: 1.4.8
- stacktrace-js: 2.0.2
- stylis: 4.1.3
- dev: false
-
/nanoid/3.3.4:
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
- dev: false
/natural-compare/1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true
- /next-auth/4.19.0_3vryta7zmbcsw4rrqf4axjqggm:
- resolution: {integrity: sha512-56Pc/Ni0cxfrVDAz2KeWdj+VXvABbHF1grgFR4+ktXbRmKQOiMU9uyMwhasBQkbL+pRrWujO0Ib/bwkP0oZS4g==}
+ /next-auth/4.20.1_ld2jel3hspngo3u5lti2kgl2sq:
+ resolution: {integrity: sha512-ZcTUN4qzzZ/zJYgOW0hMXccpheWtAol8QOMdMts+LYRcsPGsqf2hEityyaKyECQVw1cWInb9dF3wYwI5GZdEmQ==}
peerDependencies:
next: ^12.2.5 || ^13
nodemailer: ^6.6.5
@@ -2373,43 +2826,46 @@ packages:
nodemailer:
optional: true
dependencies:
- '@babel/runtime': 7.20.13
- '@panva/hkdf': 1.0.2
+ '@babel/runtime': 7.21.0
+ '@panva/hkdf': 1.0.4
cookie: 0.5.0
- jose: 4.11.2
- next: 13.1.6_biqbaboplfbrettd7655fr4n2y
+ jose: 4.13.1
+ next: 13.2.4_biqbaboplfbrettd7655fr4n2y
oauth: 0.9.15
- openid-client: 5.3.2
- preact: 10.11.3
- preact-render-to-string: 5.2.6_preact@10.11.3
+ openid-client: 5.4.0
+ preact: 10.13.1
+ preact-render-to-string: 5.2.6_preact@10.13.1
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
uuid: 8.3.2
dev: false
- /next-themes/0.2.1_3vryta7zmbcsw4rrqf4axjqggm:
+ /next-themes/0.2.1_ld2jel3hspngo3u5lti2kgl2sq:
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
peerDependencies:
next: '*'
react: '*'
react-dom: '*'
dependencies:
- next: 13.1.6_biqbaboplfbrettd7655fr4n2y
+ next: 13.2.4_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
dev: false
- /next/13.1.6_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-hHlbhKPj9pW+Cymvfzc15lvhaOZ54l+8sXDXJWm3OBNBzgrVj6hwGPmqqsXg40xO1Leq+kXpllzRPuncpC0Phw==}
+ /next/13.2.4_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-g1I30317cThkEpvzfXujf0O4wtaQHtDCLhlivwlTJ885Ld+eOgcz7r3TGQzeU+cSRoNHtD8tsJgzxVdYojFssw==}
engines: {node: '>=14.6.0'}
hasBin: true
peerDependencies:
+ '@opentelemetry/api': ^1.4.0
fibers: '>= 3.1.0'
node-sass: ^6.0.0 || ^7.0.0
react: ^18.2.0
react-dom: ^18.2.0
sass: ^1.3.0
peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
fibers:
optional: true
node-sass:
@@ -2417,32 +2873,46 @@ packages:
sass:
optional: true
dependencies:
- '@next/env': 13.1.6
+ '@next/env': 13.2.4
'@swc/helpers': 0.4.14
- caniuse-lite: 1.0.30001449
+ caniuse-lite: 1.0.30001466
postcss: 8.4.14
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
styled-jsx: 5.1.1_react@18.2.0
optionalDependencies:
- '@next/swc-android-arm-eabi': 13.1.6
- '@next/swc-android-arm64': 13.1.6
- '@next/swc-darwin-arm64': 13.1.6
- '@next/swc-darwin-x64': 13.1.6
- '@next/swc-freebsd-x64': 13.1.6
- '@next/swc-linux-arm-gnueabihf': 13.1.6
- '@next/swc-linux-arm64-gnu': 13.1.6
- '@next/swc-linux-arm64-musl': 13.1.6
- '@next/swc-linux-x64-gnu': 13.1.6
- '@next/swc-linux-x64-musl': 13.1.6
- '@next/swc-win32-arm64-msvc': 13.1.6
- '@next/swc-win32-ia32-msvc': 13.1.6
- '@next/swc-win32-x64-msvc': 13.1.6
+ '@next/swc-android-arm-eabi': 13.2.4
+ '@next/swc-android-arm64': 13.2.4
+ '@next/swc-darwin-arm64': 13.2.4
+ '@next/swc-darwin-x64': 13.2.4
+ '@next/swc-freebsd-x64': 13.2.4
+ '@next/swc-linux-arm-gnueabihf': 13.2.4
+ '@next/swc-linux-arm64-gnu': 13.2.4
+ '@next/swc-linux-arm64-musl': 13.2.4
+ '@next/swc-linux-x64-gnu': 13.2.4
+ '@next/swc-linux-x64-musl': 13.2.4
+ '@next/swc-win32-arm64-msvc': 13.2.4
+ '@next/swc-win32-ia32-msvc': 13.2.4
+ '@next/swc-win32-x64-msvc': 13.2.4
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
dev: false
+ /node-releases/2.0.10:
+ resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
+ dev: true
+
+ /normalize-path/3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /normalize-range/0.1.2:
+ resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/oauth/0.9.15:
resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==}
dev: false
@@ -2457,6 +2927,11 @@ packages:
engines: {node: '>= 6'}
dev: false
+ /object-hash/3.0.0:
+ resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+ engines: {node: '>= 6'}
+ dev: true
+
/object-inspect/1.12.3:
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
dev: true
@@ -2466,7 +2941,7 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
+ define-properties: 1.2.0
dev: true
/object-keys/1.1.1:
@@ -2479,7 +2954,7 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
+ define-properties: 1.2.0
has-symbols: 1.0.3
object-keys: 1.1.1
dev: true
@@ -2489,8 +2964,8 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
dev: true
/object.fromentries/2.0.6:
@@ -2498,15 +2973,15 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
dev: true
/object.hasown/1.1.2:
resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==}
dependencies:
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
dev: true
/object.values/1.1.6:
@@ -2514,8 +2989,8 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
dev: true
/oidc-token-hash/5.0.1:
@@ -2529,8 +3004,8 @@ packages:
wrappy: 1.0.2
dev: true
- /open/8.4.0:
- resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==}
+ /open/8.4.2:
+ resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
engines: {node: '>=12'}
dependencies:
define-lazy-prop: 2.0.0
@@ -2538,10 +3013,10 @@ packages:
is-wsl: 2.2.0
dev: true
- /openid-client/5.3.2:
- resolution: {integrity: sha512-nXXt+cna0XHOw+WqjMZOmuXw/YZEMwfWD2lD7tCsFtsBjMQGVXA+NZABA3upYBET1suhIsmfd7GnxG4jCAnvYQ==}
+ /openid-client/5.4.0:
+ resolution: {integrity: sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ==}
dependencies:
- jose: 4.11.2
+ jose: 4.13.1
lru-cache: 6.0.0
object-hash: 2.2.0
oidc-token-hash: 5.0.1
@@ -2612,6 +3087,72 @@ packages:
engines: {node: '>=8.6'}
dev: true
+ /pify/2.3.0:
+ resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /postcss-import/14.1.0_postcss@8.4.21:
+ resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ postcss: ^8.0.0
+ dependencies:
+ postcss: 8.4.21
+ postcss-value-parser: 4.2.0
+ read-cache: 1.0.0
+ resolve: 1.22.1
+ dev: true
+
+ /postcss-js/4.0.1_postcss@8.4.21:
+ resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
+ engines: {node: ^12 || ^14 || >= 16}
+ peerDependencies:
+ postcss: ^8.4.21
+ dependencies:
+ camelcase-css: 2.0.1
+ postcss: 8.4.21
+ dev: true
+
+ /postcss-load-config/3.1.4_postcss@8.4.21:
+ resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
+ engines: {node: '>= 10'}
+ peerDependencies:
+ postcss: '>=8.0.9'
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+ ts-node:
+ optional: true
+ dependencies:
+ lilconfig: 2.1.0
+ postcss: 8.4.21
+ yaml: 1.10.2
+ dev: true
+
+ /postcss-nested/6.0.0_postcss@8.4.21:
+ resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.2.14
+ dependencies:
+ postcss: 8.4.21
+ postcss-selector-parser: 6.0.11
+ dev: true
+
+ /postcss-selector-parser/6.0.11:
+ resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==}
+ engines: {node: '>=4'}
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+ dev: true
+
+ /postcss-value-parser/4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+ dev: true
+
/postcss/8.4.14:
resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==}
engines: {node: ^10 || ^12 || >=14}
@@ -2621,17 +3162,26 @@ packages:
source-map-js: 1.0.2
dev: false
- /preact-render-to-string/5.2.6_preact@10.11.3:
+ /postcss/8.4.21:
+ resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.4
+ picocolors: 1.0.0
+ source-map-js: 1.0.2
+ dev: true
+
+ /preact-render-to-string/5.2.6_preact@10.13.1:
resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==}
peerDependencies:
preact: '>=10'
dependencies:
- preact: 10.11.3
+ preact: 10.13.1
pretty-format: 3.8.0
dev: false
- /preact/10.11.3:
- resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==}
+ /preact/10.13.1:
+ resolution: {integrity: sha512-KyoXVDU5OqTpG9LXlB3+y639JAGzl8JSBXLn1J9HTSB3gbKcuInga7bZnXLlxmK94ntTs1EFeZp0lrja2AuBYQ==}
dev: false
/prelude-ls/1.2.1:
@@ -2639,8 +3189,64 @@ packages:
engines: {node: '>= 0.8.0'}
dev: true
- /prettier/2.8.3:
- resolution: {integrity: sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==}
+ /prettier-plugin-tailwindcss/0.2.4_2m34gfnsqvzqy5bj5eci3wt3by:
+ resolution: {integrity: sha512-wMyugRI2yD8gqmMpZSS8kTA0gGeKozX/R+w8iWE+yiCZL09zY0SvfiHfHabNhjGhzxlQ2S2VuTxPE3T72vppCQ==}
+ engines: {node: '>=12.17.0'}
+ peerDependencies:
+ '@ianvs/prettier-plugin-sort-imports': '*'
+ '@prettier/plugin-php': '*'
+ '@prettier/plugin-pug': '*'
+ '@shopify/prettier-plugin-liquid': '*'
+ '@shufo/prettier-plugin-blade': '*'
+ '@trivago/prettier-plugin-sort-imports': '*'
+ prettier: '>=2.2.0'
+ prettier-plugin-astro: '*'
+ prettier-plugin-css-order: '*'
+ prettier-plugin-import-sort: '*'
+ prettier-plugin-jsdoc: '*'
+ prettier-plugin-organize-attributes: '*'
+ prettier-plugin-organize-imports: '*'
+ prettier-plugin-style-order: '*'
+ prettier-plugin-svelte: '*'
+ prettier-plugin-twig-melody: '*'
+ peerDependenciesMeta:
+ '@ianvs/prettier-plugin-sort-imports':
+ optional: true
+ '@prettier/plugin-php':
+ optional: true
+ '@prettier/plugin-pug':
+ optional: true
+ '@shopify/prettier-plugin-liquid':
+ optional: true
+ '@shufo/prettier-plugin-blade':
+ optional: true
+ '@trivago/prettier-plugin-sort-imports':
+ optional: true
+ prettier-plugin-astro:
+ optional: true
+ prettier-plugin-css-order:
+ optional: true
+ prettier-plugin-import-sort:
+ optional: true
+ prettier-plugin-jsdoc:
+ optional: true
+ prettier-plugin-organize-attributes:
+ optional: true
+ prettier-plugin-organize-imports:
+ optional: true
+ prettier-plugin-style-order:
+ optional: true
+ prettier-plugin-svelte:
+ optional: true
+ prettier-plugin-twig-melody:
+ optional: true
+ dependencies:
+ '@ianvs/prettier-plugin-sort-imports': 3.7.1_prettier@2.8.4
+ prettier: 2.8.4
+ dev: true
+
+ /prettier/2.8.4:
+ resolution: {integrity: sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==}
engines: {node: '>=10.13.0'}
hasBin: true
dev: true
@@ -2649,13 +3255,13 @@ packages:
resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
dev: false
- /prisma/4.9.0:
- resolution: {integrity: sha512-bS96oZ5oDFXYgoF2l7PJ3Mp1wWWfLOo8B/jAfbA2Pn0Wm5Z/owBHzaMQKS3i1CzVBDWWPVnOohmbJmjvkcHS5w==}
+ /prisma/4.11.0:
+ resolution: {integrity: sha512-4zZmBXssPUEiX+GeL0MUq/Yyie4ltiKmGu7jCJFnYMamNrrulTBc+D+QwAQSJ01tyzeGHlD13kOnqPwRipnlNw==}
engines: {node: '>=14.17'}
hasBin: true
requiresBuild: true
dependencies:
- '@prisma/engines': 4.9.0
+ '@prisma/engines': 4.11.0
/prop-types/15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
@@ -2674,6 +3280,11 @@ packages:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
+ /quick-lru/5.1.1:
+ resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
+ engines: {node: '>=10'}
+ dev: true
+
/react-dom/18.2.0_react@18.2.0:
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
peerDependencies:
@@ -2684,8 +3295,8 @@ packages:
scheduler: 0.23.0
dev: false
- /react-hook-form/7.43.0_react@18.2.0:
- resolution: {integrity: sha512-/rVEz7T0gLdSFwPqutJ1kn2e0sQNyb9ci/hmwEYr2YG0KF/LSuRLvNrf9QWJM+gj88CjDpDW5Bh/1AD7B2+z9Q==}
+ /react-hook-form/7.43.5_react@18.2.0:
+ resolution: {integrity: sha512-YcaXhuFHoOPipu5pC7ckxrLrialiOcU91pKu8P+isAcXZyMgByUK9PkI9j5fENO4+6XU5PwWXRGMIFlk9u9UBQ==}
engines: {node: '>=12.22.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18
@@ -2693,19 +3304,11 @@ packages:
react: 18.2.0
dev: false
- /react-icons/4.7.1_react@18.2.0:
- resolution: {integrity: sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==}
- peerDependencies:
- react: '*'
- dependencies:
- react: 18.2.0
- dev: false
-
/react-is/16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
dev: true
- /react-remove-scroll-bar/2.3.4_3stiutgnnbnfnf3uowm5cip22i:
+ /react-remove-scroll-bar/2.3.4_pmekkgnqduwlme35zpnqhenc34:
resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==}
engines: {node: '>=10'}
peerDependencies:
@@ -2715,13 +3318,13 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.0.27
+ '@types/react': 18.0.28
react: 18.2.0
- react-style-singleton: 2.2.1_3stiutgnnbnfnf3uowm5cip22i
+ react-style-singleton: 2.2.1_pmekkgnqduwlme35zpnqhenc34
tslib: 2.5.0
dev: false
- /react-remove-scroll/2.5.5_3stiutgnnbnfnf3uowm5cip22i:
+ /react-remove-scroll/2.5.5_pmekkgnqduwlme35zpnqhenc34:
resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==}
engines: {node: '>=10'}
peerDependencies:
@@ -2731,16 +3334,16 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.0.27
+ '@types/react': 18.0.28
react: 18.2.0
- react-remove-scroll-bar: 2.3.4_3stiutgnnbnfnf3uowm5cip22i
- react-style-singleton: 2.2.1_3stiutgnnbnfnf3uowm5cip22i
+ react-remove-scroll-bar: 2.3.4_pmekkgnqduwlme35zpnqhenc34
+ react-style-singleton: 2.2.1_pmekkgnqduwlme35zpnqhenc34
tslib: 2.5.0
- use-callback-ref: 1.3.0_3stiutgnnbnfnf3uowm5cip22i
- use-sidecar: 1.1.2_3stiutgnnbnfnf3uowm5cip22i
+ use-callback-ref: 1.3.0_pmekkgnqduwlme35zpnqhenc34
+ use-sidecar: 1.1.2_pmekkgnqduwlme35zpnqhenc34
dev: false
- /react-style-singleton/2.2.1_3stiutgnnbnfnf3uowm5cip22i:
+ /react-style-singleton/2.2.1_pmekkgnqduwlme35zpnqhenc34:
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
engines: {node: '>=10'}
peerDependencies:
@@ -2750,47 +3353,13 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.0.27
+ '@types/react': 18.0.28
get-nonce: 1.0.1
invariant: 2.2.4
react: 18.2.0
tslib: 2.5.0
dev: false
- /react-universal-interface/0.6.2_react@18.2.0+tslib@2.5.0:
- resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==}
- peerDependencies:
- react: '*'
- tslib: '*'
- dependencies:
- react: 18.2.0
- tslib: 2.5.0
- dev: false
-
- /react-use/17.4.0_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q==}
- peerDependencies:
- react: ^16.8.0 || ^17.0.0 || ^18.0.0
- react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
- dependencies:
- '@types/js-cookie': 2.2.7
- '@xobotyi/scrollbar-width': 1.9.5
- copy-to-clipboard: 3.3.3
- fast-deep-equal: 3.1.3
- fast-shallow-equal: 1.0.0
- js-cookie: 2.2.1
- nano-css: 5.3.5_biqbaboplfbrettd7655fr4n2y
- react: 18.2.0
- react-dom: 18.2.0_react@18.2.0
- react-universal-interface: 0.6.2_react@18.2.0+tslib@2.5.0
- resize-observer-polyfill: 1.5.1
- screenfull: 5.2.0
- set-harmonic-interval: 1.0.1
- throttle-debounce: 3.0.1
- ts-easing: 0.2.0
- tslib: 2.5.0
- dev: false
-
/react/18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
@@ -2798,6 +3367,19 @@ packages:
loose-envify: 1.4.0
dev: false
+ /read-cache/1.0.0:
+ resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+ dependencies:
+ pify: 2.3.0
+ dev: true
+
+ /readdirp/3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+ dependencies:
+ picomatch: 2.3.1
+ dev: true
+
/regenerator-runtime/0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
@@ -2806,19 +3388,10 @@ packages:
engines: {node: '>= 0.4'}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
+ define-properties: 1.2.0
functions-have-names: 1.2.3
dev: true
- /regexpp/3.2.0:
- resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
- engines: {node: '>=8'}
- dev: true
-
- /resize-observer-polyfill/1.5.1:
- resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
- dev: false
-
/resolve-from/4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -2854,12 +3427,6 @@ packages:
glob: 7.2.3
dev: true
- /rtl-css-js/1.16.1:
- resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==}
- dependencies:
- '@babel/runtime': 7.20.13
- dev: false
-
/run-parallel/1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
dependencies:
@@ -2880,11 +3447,6 @@ packages:
loose-envify: 1.4.0
dev: false
- /screenfull/5.2.0:
- resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==}
- engines: {node: '>=0.10.0'}
- dev: false
-
/semver/6.3.0:
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
hasBin: true
@@ -2898,9 +3460,8 @@ packages:
lru-cache: 6.0.0
dev: true
- /set-harmonic-interval/1.0.1:
- resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==}
- engines: {node: '>=6.9'}
+ /server-only/0.0.1:
+ resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
dev: false
/shebang-command/2.0.0:
@@ -2936,82 +3497,50 @@ packages:
/source-map-js/1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
- dev: false
-
- /source-map/0.5.6:
- resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==}
- engines: {node: '>=0.10.0'}
- dev: false
-
- /source-map/0.6.1:
- resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
- engines: {node: '>=0.10.0'}
- dev: false
-
- /sourcemap-codec/1.4.8:
- resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
- deprecated: Please use @jridgewell/sourcemap-codec instead
- dev: false
-
- /stack-generator/2.0.10:
- resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==}
- dependencies:
- stackframe: 1.3.4
- dev: false
-
- /stackframe/1.3.4:
- resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==}
- dev: false
-
- /stacktrace-gps/3.1.2:
- resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==}
- dependencies:
- source-map: 0.5.6
- stackframe: 1.3.4
- dev: false
-
- /stacktrace-js/2.0.2:
- resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==}
- dependencies:
- error-stack-parser: 2.1.4
- stack-generator: 2.0.10
- stacktrace-gps: 3.1.2
- dev: false
/stop-iteration-iterator/1.0.0:
resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
engines: {node: '>= 0.4'}
dependencies:
- internal-slot: 1.0.4
+ internal-slot: 1.0.5
dev: true
/string.prototype.matchall/4.0.8:
resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
get-intrinsic: 1.2.0
has-symbols: 1.0.3
- internal-slot: 1.0.4
+ internal-slot: 1.0.5
regexp.prototype.flags: 1.4.3
side-channel: 1.0.4
dev: true
+ /string.prototype.trim/1.2.7:
+ resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
+ dev: true
+
/string.prototype.trimend/1.0.6:
resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
dev: true
/string.prototype.trimstart/1.0.6:
resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==}
dependencies:
call-bind: 1.0.2
- define-properties: 1.1.4
- es-abstract: 1.21.1
+ define-properties: 1.2.0
+ es-abstract: 1.21.2
dev: true
/strip-ansi/6.0.1:
@@ -3048,9 +3577,12 @@ packages:
react: 18.2.0
dev: false
- /stylis/4.1.3:
- resolution: {integrity: sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==}
- dev: false
+ /supports-color/5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+ dependencies:
+ has-flag: 3.0.0
+ dev: true
/supports-color/7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
@@ -3064,8 +3596,8 @@ packages:
engines: {node: '>= 0.4'}
dev: true
- /swr/2.0.3_react@18.2.0:
- resolution: {integrity: sha512-sGvQDok/AHEWTPfhUWXEHBVEXmgGnuahyhmRQbjl9XBYxT/MSlAzvXEKQpyM++bMPaI52vcWS2HiKNaW7+9OFw==}
+ /swr/2.1.0_react@18.2.0:
+ resolution: {integrity: sha512-4hYl5p3/KalQ1MORealM79g/DtLohmud6yyfXw5l4wjtFksYUnocRFudvyaoUtgj3FrVNK9lS25Av9dsZYvz0g==}
engines: {pnpm: '7'}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0
@@ -3082,6 +3614,52 @@ packages:
tslib: 2.5.0
dev: true
+ /tailwind-merge/1.10.0:
+ resolution: {integrity: sha512-WFnDXSS4kFTZwjKg5/oZSGzBRU/l+qcbv5NVTzLUQvJ9yovDAP05h0F2+ZFW0Lw9EcgRoc2AfURUdZvnEFrXKg==}
+ dev: false
+
+ /tailwindcss-animate/1.0.5_tailwindcss@3.2.7:
+ resolution: {integrity: sha512-UU3qrOJ4lFQABY+MVADmBm+0KW3xZyhMdRvejwtXqYOL7YjHYxmuREFAZdmVG5LPe5E9CAst846SLC4j5I3dcw==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || insiders'
+ dependencies:
+ tailwindcss: 3.2.7_postcss@8.4.21
+ dev: true
+
+ /tailwindcss/3.2.7_postcss@8.4.21:
+ resolution: {integrity: sha512-B6DLqJzc21x7wntlH/GsZwEXTBttVSl1FtCzC8WP4oBc/NKef7kaax5jeihkkCEWc831/5NDJ9gRNDK6NEioQQ==}
+ engines: {node: '>=12.13.0'}
+ hasBin: true
+ peerDependencies:
+ postcss: ^8.0.9
+ dependencies:
+ arg: 5.0.2
+ chokidar: 3.5.3
+ color-name: 1.1.4
+ detective: 5.2.1
+ didyoumean: 1.2.2
+ dlv: 1.1.3
+ fast-glob: 3.2.12
+ glob-parent: 6.0.2
+ is-glob: 4.0.3
+ lilconfig: 2.1.0
+ micromatch: 4.0.5
+ normalize-path: 3.0.0
+ object-hash: 3.0.0
+ picocolors: 1.0.0
+ postcss: 8.4.21
+ postcss-import: 14.1.0_postcss@8.4.21
+ postcss-js: 4.0.1_postcss@8.4.21
+ postcss-load-config: 3.1.4_postcss@8.4.21
+ postcss-nested: 6.0.0_postcss@8.4.21
+ postcss-selector-parser: 6.0.11
+ postcss-value-parser: 4.2.0
+ quick-lru: 5.1.1
+ resolve: 1.22.1
+ transitivePeerDependencies:
+ - ts-node
+ dev: true
+
/tapable/2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}
@@ -3091,11 +3669,6 @@ packages:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true
- /throttle-debounce/3.0.1:
- resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==}
- engines: {node: '>=10'}
- dev: false
-
/tiny-glob/0.2.9:
resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==}
dependencies:
@@ -3103,6 +3676,11 @@ packages:
globrex: 0.1.2
dev: true
+ /to-fast-properties/2.0.0:
+ resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+ engines: {node: '>=4'}
+ dev: true
+
/to-regex-range/5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@@ -3110,20 +3688,12 @@ packages:
is-number: 7.0.0
dev: true
- /toggle-selection/1.0.6:
- resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
- dev: false
-
- /ts-easing/0.2.0:
- resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==}
- dev: false
-
- /tsconfig-paths/3.14.1:
- resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==}
+ /tsconfig-paths/3.14.2:
+ resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==}
dependencies:
'@types/json5': 0.0.29
json5: 1.0.2
- minimist: 1.2.7
+ minimist: 1.2.8
strip-bom: 3.0.0
dev: true
@@ -3168,7 +3738,6 @@ packages:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
hasBin: true
- dev: true
/unbox-primitive/1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
@@ -3179,13 +3748,24 @@ packages:
which-boxed-primitive: 1.0.2
dev: true
+ /update-browserslist-db/1.0.10_browserslist@4.21.5:
+ resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+ dependencies:
+ browserslist: 4.21.5
+ escalade: 3.1.1
+ picocolors: 1.0.0
+ dev: true
+
/uri-js/4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
dependencies:
punycode: 2.3.0
dev: true
- /use-callback-ref/1.3.0_3stiutgnnbnfnf3uowm5cip22i:
+ /use-callback-ref/1.3.0_pmekkgnqduwlme35zpnqhenc34:
resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==}
engines: {node: '>=10'}
peerDependencies:
@@ -3195,12 +3775,12 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.0.27
+ '@types/react': 18.0.28
react: 18.2.0
tslib: 2.5.0
dev: false
- /use-isomorphic-layout-effect/1.1.2_3stiutgnnbnfnf3uowm5cip22i:
+ /use-isomorphic-layout-effect/1.1.2_pmekkgnqduwlme35zpnqhenc34:
resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==}
peerDependencies:
'@types/react': '*'
@@ -3209,11 +3789,11 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.0.27
+ '@types/react': 18.0.28
react: 18.2.0
dev: false
- /use-sidecar/1.1.2_3stiutgnnbnfnf3uowm5cip22i:
+ /use-sidecar/1.1.2_pmekkgnqduwlme35zpnqhenc34:
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
engines: {node: '>=10'}
peerDependencies:
@@ -3223,7 +3803,7 @@ packages:
'@types/react':
optional: true
dependencies:
- '@types/react': 18.0.27
+ '@types/react': 18.0.28
detect-node-es: 1.1.0
react: 18.2.0
tslib: 2.5.0
@@ -3237,6 +3817,10 @@ packages:
react: 18.2.0
dev: false
+ /util-deprecate/1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+ dev: true
+
/uuid/8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
@@ -3290,10 +3874,28 @@ packages:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: true
+ /xtend/4.0.2:
+ resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+ engines: {node: '>=0.4'}
+ dev: true
+
+ /yallist/3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+ dev: true
+
/yallist/4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+ /yaml/1.10.2:
+ resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+ engines: {node: '>= 6'}
+ dev: true
+
/yocto-queue/0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
dev: true
+
+ /zod/3.21.4:
+ resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==}
+ dev: false
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..88a3558
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+ }
\ No newline at end of file
diff --git a/prettier.config.js b/prettier.config.js
new file mode 100644
index 0000000..b2c639a
--- /dev/null
+++ b/prettier.config.js
@@ -0,0 +1,33 @@
+/** @type {import('prettier').Config} */
+module.exports = {
+ endOfLine: "lf",
+ semi: true,
+ singleQuote: false,
+ tabWidth: 2,
+ trailingComma: "es5",
+ importOrder: [
+ "^use client$",
+ "",
+ "^(react/(.*)$)|^(react$)",
+ "^(next/(.*)$)|^(next$)",
+ "",
+ "",
+ "^types$",
+ "^@/types/(.*)$",
+ "^@/config/(.*)$",
+ "^@/lib/(.*)$",
+ "^@/components/(.*)$",
+ "^@/styles/(.*)$",
+ "^[./]",
+ ],
+ importOrderSeparation: false,
+ importOrderSortSpecifiers: true,
+ importOrderBuiltinModulesToTop: true,
+ importOrderParserPlugins: ["typescript", "jsx", "decorators-legacy"],
+ importOrderMergeDuplicateImports: true,
+ importOrderCombineTypeAndValueImports: true,
+ plugins: [
+ "@ianvs/prettier-plugin-sort-imports",
+ "prettier-plugin-tailwindcss",
+ ],
+ };
\ No newline at end of file
diff --git a/stitches.config.ts b/stitches.config.ts
deleted file mode 100644
index e6a9ca9..0000000
--- a/stitches.config.ts
+++ /dev/null
@@ -1,300 +0,0 @@
-import type * as Stitches from "@stitches/react";
-import { createStitches } from "@stitches/react";
-
-export type { VariantProps } from "@stitches/react";
-export type CSS = Stitches.CSS;
-
-export const { styled, css, theme, createTheme, getCssText, globalCss, keyframes, config } =
- createStitches({
- theme: {
- colors: {
- white: "hsl(360, 100%, 100%)",
- black: "hsl(0, 0%, 0%)",
- background: "hsl(0, 0%, 7%)",
- main: "$black",
- mainDark: "hsl(0, 0%, 0%)",
- secondary: "$white",
- secondaryDark: "hsl(0, 0%, 63%)",
- link: "$green",
- text: "$secondary",
- textDark: "$secondaryDark",
- gray: "hsl(0, 0%, 20%)",
- grayDark: "hsl(0, 0%, 7%)",
- grayLight: "hsl(0, 0%, 26%)",
- grayLighter: "hsl(0, 0%, 31%)",
- grayLightest: "hsl(0, 0%, 37%)",
- green: "hsl(158, 64%, 52%)",
- greenDark: "hsl(163, 94%, 24%)",
- greenLight: "hsl(156, 72%, 67%)",
- greenLighter: "hsl(152, 76%, 80%)",
- greenLightest: "hsl(149, 80%, 90%)",
- red: "hsl(0, 91%, 71%)",
- redDark: "hsl(0, 74%, 42%)",
- redLight: "hsl(0, 94%, 82%)",
- redLighter: "hsl(0, 96%, 89%)",
- redLightest: "hsl(0, 93%, 94%)",
- // Semantic colors
- hiContrast: "$slate12",
- // loContrast: '$slate1',
- loContrast: "white",
- canvas: "hsl(0 0% 93%)",
- panel: "$main",
- avatarImageFallback: "$gray",
- transparentPanel: "hsl(0 0% 0% / 97%)",
- shadowLight: "hsl(206 22% 7% / 35%)",
- shadowDark: "hsl(206 22% 7% / 20%)",
- overlay: "rgba(0, 0, 0, 0.8)"
- },
- fonts: {
- default: "-apple-system, system-ui, sans-serif"
- },
- space: {
- 1: "4px",
- 2: "8px",
- 3: "16px",
- 4: "24px",
- 5: "32px",
- 6: "40px",
- 7: "48px",
- 8: "56px",
- 9: "64px",
- 10: "62px",
- 11: "80px",
- 12: "96px",
- 13: "120px",
- 14: "140px",
- 15: "180px",
- 16: "200px",
- 17: "300px"
- },
- sizes: {
- 1: "4px",
- 2: "8px",
- 3: "16px",
- 4: "24px",
- 5: "32px",
- 6: "40px",
- 7: "48px",
- 8: "56px",
- 9: "64px",
- 10: "62px",
- 11: "80px",
- 12: "96px",
- 13: "120px",
- 14: "140px",
- 15: "180px",
- 16: "200px",
- 17: "300px",
- logoFull: "170px",
- smContainer: "384px",
- mdContainer: "672px",
- lgContainer: "1024px",
- xlContainer: "1280px",
- scrollBar: "10px",
- calendarContainer: "512px",
- themeSelect: "88px"
- },
- fontSizes: {
- tiny: "10px",
- 1: "12px",
- 2: "14px",
- 3: "16px",
- 4: "18px",
- 5: "20px",
- 6: "26px",
- 7: "32px",
- 8: "40px",
- 9: "56px",
- 10: "64px",
- 11: "72px",
- 12: "96px",
- 13: "120px"
- },
- radii: {
- 1: "2px",
- 2: "4px",
- 3: "6px",
- 4: "8px",
- 5: "12px",
- 6: "24px",
- round: "50%",
- pill: "9999px"
- },
- zIndices: {
- 1: "100",
- 2: "200",
- 3: "300",
- 4: "400",
- max: "999"
- },
- shadows: {
- card: "0 0 0 1px $colors$gray",
- input: "0 0 0 2px $colors$gray",
- select: "0 0 0 1px $colors$gray",
- selectHover: "0 0 0 1px $colors$secondary",
- inputHover: "0 0 0 2px $colors$secondary",
- inputActive: "0 0 0 2px $colors$secondary",
- inputFocus: "0 0 0 2px $colors$secondary",
- alertInfo: "0 0 0 1px $colors$gray",
- alertSuccess: "0 0 0 1px $colors$green",
- alertWarning: "0 0 0 1px $colors$red",
- alertError: "0 0 0 1px $colors$red"
- },
- letterSpacings: {
- 1: "1x",
- 2: "2px",
- 3: "3px",
- 4: "4px",
- normal: "normal",
- caps: "4px",
- avatarImageFallback: "$2"
- }
- },
- media: {
- bp1: "(min-width: 600px)",
- bp2: "(min-width: 900px)",
- bp3: "(min-width: 1200px)",
- bp4: "(min-width: 1800px)",
- motion: "(prefers-reduced-motion)",
- hover: "(any-hover: hover)",
- dark: "(prefers-color-scheme: dark)",
- light: "(prefers-color-scheme: light)"
- },
- utils: {
- p: (value: Stitches.PropertyValue<"padding">) => ({
- padding: value
- }),
- pt: (value: Stitches.PropertyValue<"paddingTop">) => ({
- paddingTop: value
- }),
- pr: (value: Stitches.PropertyValue<"paddingRight">) => ({
- paddingRight: value
- }),
- pb: (value: Stitches.PropertyValue<"paddingBottom">) => ({
- paddingBottom: value
- }),
- pl: (value: Stitches.PropertyValue<"paddingLeft">) => ({
- paddingLeft: value
- }),
- px: (value: Stitches.PropertyValue<"paddingLeft">) => ({
- paddingLeft: value,
- paddingRight: value
- }),
- py: (value: Stitches.PropertyValue<"paddingTop">) => ({
- paddingTop: value,
- paddingBottom: value
- }),
-
- m: (value: Stitches.PropertyValue<"margin">) => ({
- margin: value
- }),
- mt: (value: Stitches.PropertyValue<"marginTop">) => ({
- marginTop: value
- }),
- mr: (value: Stitches.PropertyValue<"marginRight">) => ({
- marginRight: value
- }),
- mb: (value: Stitches.PropertyValue<"marginBottom">) => ({
- marginBottom: value
- }),
- ml: (value: Stitches.PropertyValue<"marginLeft">) => ({
- marginLeft: value
- }),
- mx: (value: Stitches.PropertyValue<"marginLeft">) => ({
- marginLeft: value,
- marginRight: value
- }),
- my: (value: Stitches.PropertyValue<"marginTop">) => ({
- marginTop: value,
- marginBottom: value
- }),
-
- ta: (value: Stitches.PropertyValue<"textAlign">) => ({ textAlign: value }),
-
- fd: (value: Stitches.PropertyValue<"flexDirection">) => ({ flexDirection: value }),
- fw: (value: Stitches.PropertyValue<"flexWrap">) => ({ flexWrap: value }),
-
- ai: (value: Stitches.PropertyValue<"alignItems">) => ({ alignItems: value }),
- ac: (value: Stitches.PropertyValue<"alignContent">) => ({ alignContent: value }),
- jc: (value: Stitches.PropertyValue<"justifyContent">) => ({ justifyContent: value }),
- as: (value: Stitches.PropertyValue<"alignSelf">) => ({ alignSelf: value }),
- fg: (value: Stitches.PropertyValue<"flexGrow">) => ({ flexGrow: value }),
- fs: (value: Stitches.PropertyValue<"flexShrink">) => ({ flexShrink: value }),
- fb: (value: Stitches.PropertyValue<"flexBasis">) => ({ flexBasis: value }),
-
- bc: (value: Stitches.PropertyValue<"backgroundColor">) => ({
- backgroundColor: value
- }),
-
- br: (value: Stitches.PropertyValue<"borderRadius">) => ({
- borderRadius: value
- }),
- btrr: (value: Stitches.PropertyValue<"borderTopRightRadius">) => ({
- borderTopRightRadius: value
- }),
- bbrr: (value: Stitches.PropertyValue<"borderBottomRightRadius">) => ({
- borderBottomRightRadius: value
- }),
- bblr: (value: Stitches.PropertyValue<"borderBottomLeftRadius">) => ({
- borderBottomLeftRadius: value
- }),
- btlr: (value: Stitches.PropertyValue<"borderTopLeftRadius">) => ({
- borderTopLeftRadius: value
- }),
-
- bs: (value: Stitches.PropertyValue<"boxShadow">) => ({ boxShadow: value }),
-
- lh: (value: Stitches.PropertyValue<"lineHeight">) => ({ lineHeight: value }),
-
- ox: (value: Stitches.PropertyValue<"overflowX">) => ({ overflowX: value }),
- oy: (value: Stitches.PropertyValue<"overflowY">) => ({ overflowY: value }),
-
- pe: (value: Stitches.PropertyValue<"pointerEvents">) => ({ pointerEvents: value }),
- us: (value: Stitches.PropertyValue<"userSelect">) => ({
- WebkitUserSelect: value,
- userSelect: value
- }),
-
- userSelect: (value: Stitches.PropertyValue<"userSelect">) => ({
- WebkitUserSelect: value,
- userSelect: value
- }),
-
- size: (value: Stitches.PropertyValue<"width">) => ({
- width: value,
- height: value
- }),
-
- appearance: (value: Stitches.PropertyValue<"appearance">) => ({
- WebkitAppearance: value,
- appearance: value
- }),
- backgroundClip: (value: Stitches.PropertyValue<"backgroundClip">) => ({
- WebkitBackgroundClip: value,
- backgroundClip: value
- })
- }
- });
-
-export const lightTheme = createTheme("light-theme", {
- colors: {
- main: "$white",
- secondary: "$black",
- secondaryDark: "hsl(0, 0%, 33%)",
- background: "hsl(45, 12%, 94%)",
- gray: "hsl(0, 0%, 40%)",
- grayDark: "hsl(45, 12%, 94%)",
- grayLight: "hsl(0, 0%, 52%)",
- grayLighter: "hsl(0, 0%, 62%)",
- grayLightest: "hsl(0, 0%, 74%)",
- overlay: "rgba(0, 0, 0, 0.35)",
- avatarImageFallback: "$grayLightest"
- },
- shadows: {
- card: "0 2px 4px hsla(0, 0%, 0%, 0.1)",
- input: "0 0 0 1px $colors$grayLightest",
- inputHover: "0 0 0 1px $colors$secondary",
- inputActive: "0 0 0 1px $colors$secondary",
- inputFocus: "0 0 0 1px $colors$secondary"
- }
-});
diff --git a/styles/globals.css b/styles/globals.css
new file mode 100644
index 0000000..b5c61c9
--- /dev/null
+++ b/styles/globals.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..557c954
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,36 @@
+const { fontFamily } = require("tailwindcss/defaultTheme")
+
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ darkMode: ["class"],
+ content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"],
+ theme: {
+ container: {
+ center: true,
+ padding: "1.5rem",
+ screens: {
+ "2xl": "1360px",
+ },
+ },
+ extend: {
+ fontFamily: {
+ sans: ["var(--font-sans)", ...fontFamily.sans],
+ },
+ keyframes: {
+ "accordion-down": {
+ from: { height: 0 },
+ to: { height: "var(--radix-accordion-content-height)" },
+ },
+ "accordion-up": {
+ from: { height: "var(--radix-accordion-content-height)" },
+ to: { height: 0 },
+ },
+ },
+ animation: {
+ "accordion-down": "accordion-down 0.2s ease-out",
+ "accordion-up": "accordion-up 0.2s ease-out",
+ },
+ },
+ },
+ plugins: [require("tailwindcss-animate"), require("@tailwindcss/line-clamp")],
+};
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index b19d089..6396cfc 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
- "lib": ["dom", "dom.iterable", "esnext"],
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
@@ -13,18 +17,27 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
+ "incremental": true,
"baseUrl": "./",
"paths": {
- "@/components/*": ["components/*"],
- "@/constants/*": ["constants/*"],
- "@/lib/*": ["lib/*"],
- "@/logic/*": ["logic/*"],
- "@/styles/*": ["styles/*"],
- "@/types": ["types/index"],
- "@/utils/*": ["utils/*"]
+ "@/*": [
+ "./*"
+ ]
},
- "incremental": true
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "strictNullChecks": true
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],
- "exclude": ["node_modules"]
-}
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
\ No newline at end of file
diff --git a/types/index.d.ts b/types/index.d.ts
index 865a31a..7a815fa 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1,8 +1,5 @@
///
-import { Prisma } from ".prisma/client";
-import { Session, TokenSet } from "next-auth";
-
export type WithChildren = T & { children?: React.ReactNode };
export type Unpacked = T extends (infer U)[] ? U : T;
@@ -74,13 +71,9 @@ export type UserEarningsDetails = {
nextYearSalaryStatistics: CalendarYearEarnings;
};
-export type PrismaUserUserWorkDayDetail = {
- id: number;
- date: string;
- extraHours: Prisma.Decimal;
- nonCommissionedHours: Prisma.Decimal;
- sickDay: boolean;
- userId: number;
+export type CalendarHolidayInformation = {
+ name: string;
+ shortDate: string;
};
export type CalendarYear = {
@@ -91,9 +84,11 @@ export type CalendarYear = {
export type CalendarMonth = {
month: string;
- halfTax: boolean;
+ monthNumber: number;
+ year: number;
days: CalendarDay[];
payDay?: CalendarDay;
+ halfTax: boolean;
};
export type CalendarDay = {
@@ -104,9 +99,11 @@ export type CalendarDay = {
formattedDate: string;
formattedShortDate: string;
formattedLongDate: string;
+ holidayInformation?: CalendarHolidayInformation;
isHoliday: boolean;
isWorkDay?: boolean;
- isKnowitClosed: boolean;
+ isSunday?: boolean;
+ isKnowitClosed?: boolean;
};
export type Holiday = {
@@ -116,24 +113,22 @@ export type Holiday = {
formattedLongDate: string;
};
-export type NextAuthSessionUser = {
- name?: string | null;
- email?: string | null;
- image?: string | null;
- id?: string | null;
- isAdmin?: boolean;
- isSpecialist?: boolean;
- activeDirectoryId?: string;
+type CalendarEntries = {
+ type: "header" | "week" | "spacing" | "day";
+ value: string | number | null;
+ date: string;
+ formattedDate: string;
+ week?: number;
+ isToday?: boolean;
+ isOdd?: boolean;
+ isHoliday?: boolean;
+ isSunday?: boolean;
+ isStartOfWeek?: boolean;
+ isWorkDay?: boolean;
+ isNonCommissionedWorkDay?: boolean;
+ workDayDetails?: UserWorkDayDetail;
};
-export type NextAuthSession = {
- user?: NextAuthSessionUser;
-} & Session;
-
-export type NextAuthToken = {
- sub: string;
-} & TokenSet;
-
export type AzureAdTokenClaims = {
aud: string;
iss: string;
diff --git a/types/next-auth.d.ts b/types/next-auth.d.ts
index 8ed8e1b..8fd59ad 100644
--- a/types/next-auth.d.ts
+++ b/types/next-auth.d.ts
@@ -1,32 +1,32 @@
-import { DefaultSession } from "next-auth";
+import { User } from "next-auth";
import "next-auth/jwt";
-import { User } from ".";
-declare module "next-auth" {
- /**
- * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
- */
- interface Session {
- user: DefaultSession["user"] &
- User & {
- image: string;
- };
- accessToken: string;
- }
-}
+type UserId = string
declare module "next-auth/jwt" {
- /** Returned by the `jwt` callback and `getToken`, when using JWT sessions */
interface JWT {
- /** OpenID ID Token */
- name: string;
- email: string;
- picture: string;
- sub: string;
- iat: number;
- exp: number;
- jti: string;
- dbUser?: Omit;
- accessToken?: string;
+ id: UserId;
+ isAdmin: boolean;
+ isSpecialist: boolean;
+ activeDirectoryId: string;
+ commission: number;
+ hourlyRate: number;
+ tax: number;
+ workHours: number;
+ }
+}
+
+declare module "next-auth" {
+ interface Session {
+ user: User & {
+ id: UserId;
+ isAdmin: boolean;
+ isSpecialist: boolean;
+ activeDirectoryId: string;
+ commission: number;
+ hourlyRate: number;
+ tax: number;
+ workHours: number;
+ }
}
}
diff --git a/utils/calendar-utils.ts b/utils/calendar-utils.ts
new file mode 100644
index 0000000..0e8a2b2
--- /dev/null
+++ b/utils/calendar-utils.ts
@@ -0,0 +1,425 @@
+import { getPayDay } from "@/logic/calendar-logic";
+import { CalendarDay, CalendarEntries, CalendarMonth, Holiday, UserWorkDayDetail } from "@/types";
+import { isString, memoize, range } from "@/utils/common-utils";
+import {
+ getFormattedDate,
+ getFormattedDay,
+ getFormattedDayAndMonth,
+ getFormattedLongDate,
+ getFormattedMonth,
+ getFormattedShortDate,
+ getWeek
+} from "@/utils/date-utils";
+import {
+ addDays,
+ addMonths,
+ eachDayOfInterval,
+ eachMonthOfInterval,
+ eachWeekOfInterval,
+ endOfISOWeek,
+ endOfMonth,
+ endOfYear,
+ getDate,
+ getDay,
+ getMonth,
+ getYear,
+ isLeapYear,
+ startOfISOWeek,
+ startOfMonth,
+ startOfYear,
+ subDays
+} from "date-fns";
+import { enUS, nb } from "date-fns/locale";
+
+export const isWorkDay = (day: CalendarDay): boolean =>
+ (!day?.isHoliday ?? false) &&
+ day?.name?.toUpperCase() !== "SATURDAY" &&
+ day?.name?.toUpperCase() !== "SUNDAY";
+
+const isKnowitClosed = (date: Date): boolean => {
+ const year = getYear(date);
+ const knowitClosedDays = {
+ [getFormattedDate(new Date(year, 11, 24))]: true,
+ [getFormattedDate(new Date(year, 11, 31))]: true
+ };
+
+ return getFormattedDate(date) in knowitClosedDays;
+};
+
+export const getLocale = (locale: string | undefined): Locale => {
+ if (!locale || !isString(locale)) {
+ return enUS;
+ }
+
+ switch (locale.toLowerCase()) {
+ case "nb":
+ case "nn":
+ case "nb-no":
+ case "nn-no":
+ return nb;
+ case "en":
+ return enUS;
+ case "en-us":
+ return enUS;
+ case "en-gb":
+ return enUS;
+
+ default:
+ return enUS;
+ }
+};
+
+export const getWeekStarsOn = (locale: string | undefined): 0 | 1 => {
+ if (!isString(locale)) {
+ return 1;
+ }
+
+ switch (locale) {
+ case "nb":
+ case "nn":
+ case "nb-no":
+ case "nn-no":
+ return 1;
+
+ default:
+ return 0;
+ }
+};
+
+const getEaster = (year: number): Date => {
+ const a = year % 19;
+ const b = Math.floor(year / 100);
+ const c = year % 100;
+ const d = Math.floor(b / 4);
+ const e = b % 4;
+ const f = Math.floor((b + 8) / 25);
+ const g = Math.floor((b - f + 1) / 3);
+ const h = (19 * a + b - d - g + 15) % 30;
+ const i = Math.floor(c / 4);
+ const k = c % 4;
+ const l = (32 + 2 * e + 2 * i - h - k) % 7;
+ const m = Math.floor((a + 11 * h + 22 * l) / 451);
+ const n0 = h + l + 7 * m + 114;
+ const n = Math.floor(n0 / 31) - 1;
+ const p = (n0 % 31) + 1;
+ return new Date(year, n, p);
+};
+
+export const getFirstDayOfYear = (year: number): Date => new Date(year, 0, 1);
+
+export const yearIsLeapYear = (year: number): boolean => isLeapYear(getFirstDayOfYear(year));
+
+export const getAllWeeksInYear = (date: Date, locale: string): Date[] => {
+ const l = getLocale(locale);
+
+ return eachWeekOfInterval(
+ {
+ start: startOfYear(date),
+ end: endOfYear(date)
+ },
+ { locale: l, weekStartsOn: getWeekStarsOn(l?.code) }
+ );
+};
+
+export const getAllDaysInWeek = (date: Date): Date[] =>
+ eachDayOfInterval({
+ start: startOfISOWeek(date),
+ end: endOfISOWeek(date)
+ });
+
+export const getAllDaysInMonth = (date: Date): Date[] =>
+ eachDayOfInterval({
+ start: startOfMonth(date),
+ end: endOfMonth(date)
+ });
+
+export const getAllMonthsInYear = (date: Date): Date[] =>
+ eachMonthOfInterval({
+ start: startOfYear(date),
+ end: endOfYear(date)
+ });
+
+const getNorwegianHolidays = memoize(function (year: number): Holiday[] {
+ const newYearsDay = startOfYear(getFirstDayOfYear(year));
+ const easter = getEaster(year);
+ const palmSunday = subDays(easter, 7);
+ const maundyThursday = subDays(easter, 3);
+ const goodFriday = subDays(easter, 2);
+ const easterMonday = addDays(easter, 1);
+ const labourDay = new Date(year, 4, 1);
+ const constitutionDay = new Date(year, 4, 17);
+ const ascensionDay = addDays(easter, 39);
+ const whitsun = addDays(easter, 49);
+ const whitMonday = addDays(easter, 50);
+ const christmasDay = new Date(year, 11, 25);
+ const stStephensDay = new Date(year, 11, 26);
+
+ // TODO: Handle i18n
+ return [
+ // 1. nyttårsdag
+ createHoliday("New Year's Day", newYearsDay),
+ // Palmesøndag
+ createHoliday("Palm Sunday", palmSunday),
+ // Skjærtorsdag
+ createHoliday("Maundy Thursday", maundyThursday),
+ // Langfredag
+ createHoliday("Good Friday", goodFriday),
+ // 1. påskedag
+ createHoliday("Easter", easter),
+ // 2. påskedag
+ createHoliday("Easter Monday", easterMonday),
+ // 1. mai / Arbeidernes dag
+ createHoliday("Labour Day", labourDay),
+ // 17. mai / Grunnlovsdag
+ createHoliday("Constitution Day", constitutionDay),
+ // Kristi himmelfartsdag
+ createHoliday("Ascension Day", ascensionDay),
+ // 1. pinsedag
+ createHoliday("Whitsun", whitsun),
+ // 2. pinsedag
+ createHoliday("Whit Monday", whitMonday),
+ // 1. juledag
+ createHoliday("Christmas Day", christmasDay),
+ // 2. juledag
+ createHoliday("St. Stephen's Day", stStephensDay)
+ ];
+});
+
+const getNorwegianHolidaysDictionary = memoize(function (year: number): Record {
+ return getNorwegianHolidays(year).reduce(
+ (result, norwegianHoliday) => ({
+ ...result,
+ [getFormattedDate(norwegianHoliday.date)]: norwegianHoliday
+ }),
+ {}
+ );
+});
+
+const getHolidayInformation = (date: Date) => {
+ const norwegianHolidays = getNorwegianHolidaysDictionary(getYear(date));
+
+ const formattedDate = getFormattedDate(date);
+ const isHoliday = formattedDate in norwegianHolidays;
+
+ return {
+ isHoliday,
+ holidayInformation: isHoliday
+ ? {
+ name: norwegianHolidays[formattedDate].name,
+ shortDate: getFormattedDayAndMonth(date)
+ }
+ : undefined
+ };
+};
+
+const createHoliday = (name: string, date: Date): Holiday => ({
+ name,
+ date,
+ formattedShortDate: getFormattedShortDate(date),
+ formattedLongDate: getFormattedLongDate(date)
+});
+
+export const createDate = (date: Date, locale?: string): CalendarDay => {
+ const { isHoliday, holidayInformation } = getHolidayInformation(date);
+
+ const modifiedDate = {
+ date,
+ day: getDate(date),
+ name: getFormattedDay(date, locale),
+ weekNumber: getWeek(date, locale),
+ formattedDate: getFormattedDate(date, locale),
+ formattedShortDate: getFormattedShortDate(date, locale),
+ formattedLongDate: getFormattedLongDate(date, locale),
+ isHoliday,
+ holidayInformation,
+ isSunday: getDay(date) === 0 && !isHoliday,
+ isKnowitClosed: isKnowitClosed(date)
+ };
+
+ return {
+ ...modifiedDate,
+ isWorkDay: isWorkDay(modifiedDate)
+ };
+};
+
+export const getCalendarMonth = (date: Date, locale?: string): CalendarMonth => {
+ const month = getFormattedMonth(date, locale);
+
+ const calendarMonth: CalendarMonth = {
+ month,
+ monthNumber: getMonth(date),
+ year: getYear(date),
+ days: getAllDaysInMonth(date).map(date => createDate(date, locale)),
+ halfTax: month?.toUpperCase() === "NOVEMBER"
+ };
+
+ return {
+ ...calendarMonth,
+ payDay: getPayDay(calendarMonth)
+ };
+};
+
+export const getCalendarYear = (year: number, locale?: string) => {
+ const date = getFirstDayOfYear(year);
+ const allMonthsInYear = getAllMonthsInYear(date);
+
+ return {
+ year,
+ isLeapYear: yearIsLeapYear(year),
+ months: allMonthsInYear.map(date => getCalendarMonth(date, locale))
+ };
+};
+
+export const getStaticYearRange = () => {
+ const date = new Date();
+ return range(date.getFullYear() - 10, date.getFullYear() + 50);
+};
+
+const isStriped = (index: number, showWeeks = false) => {
+ if (showWeeks) {
+ // the first 8 should not be striped
+ // the next 8 should be striped
+ // the next 8 should not be striped
+ // the next 8 should be striped
+ // etc.
+ return index % 16 >= 8;
+ }
+
+ // the first 7 should not be striped
+ // the next 7 should be striped
+ // the next 7 should not be striped
+ // the next 7 should be striped
+ // etc.
+ return index % 14 >= 7;
+};
+
+export const getCalendarMonthEntries = (
+ month: CalendarMonth,
+ currentDate: Date,
+ showWeeks = true,
+ workDayDetails?: UserWorkDayDetail[]
+) => {
+ const lastMonth = getAllDaysInMonth(addMonths(month.days[0].date, -1));
+
+ const calendarEntries = month.days.reduce((acc, day, index) => {
+ // if index is 0, we are on the first day of the month
+ // first we need to add header days
+ // then we need to add week number
+ // then we need to add spacing days
+ if (index === 0) {
+ (showWeeks
+ ? ["week", "mon.", "tue.", "wed.", "thu.", "fri.", "sat.", "sun."]
+ : ["mon.", "tue.", "wed.", "thu.", "fri.", "sat.", "sun."]
+ ).forEach(value => {
+ acc.push({
+ type: "header",
+ value,
+ date: day.date.toISOString(),
+ formattedDate: day.formattedDate,
+ isOdd: isStriped(acc.length, showWeeks)
+ });
+ });
+ }
+
+ // render week number
+ if (showWeeks && acc.length % (showWeeks ? 8 : 7) === 0) {
+ acc.push({
+ type: "week",
+ value: getWeek(day.date),
+ date: day.date.toISOString(),
+ formattedDate: day.formattedDate,
+ isOdd: isStriped(acc.length, showWeeks)
+ });
+ }
+
+ const workDayDetail = workDayDetails?.find(
+ userWorkDayDetail => userWorkDayDetail.date === day.formattedDate
+ );
+
+ // render spacing days for month
+ // week starts on monday
+ // so if first date is a wednesday, we need to render two days of spacing
+ if (index === 0) {
+ const spacingDays =
+ {
+ 0: 6,
+ 1: 0,
+ 2: 1,
+ 3: 2,
+ 4: 3,
+ 5: 4,
+ 6: 5,
+ 7: 6
+ }[day.date.getDay()] ?? 0;
+
+ for (let i = 0; i < spacingDays; i++) {
+ acc.push({
+ type: "spacing",
+ // we use date from last month to render spacing days
+ value: lastMonth[lastMonth.length - (spacingDays - i)].getDate(),
+ date: day.date.toISOString(),
+ formattedDate: day.formattedDate,
+ week: getWeek(day.date),
+ isOdd: isStriped(acc.length, showWeeks),
+ isStartOfWeek: acc.length % (showWeeks ? 8 : 7) === 0,
+ isWorkDay: day.isWorkDay,
+ isNonCommissionedWorkDay: (workDayDetail?.nonCommissionedHours ?? 0) > 0,
+ workDayDetails: workDayDetail
+ });
+ }
+ }
+
+ // render day
+ acc.push({
+ type: "day",
+ value: day.date.getDate(),
+ date: day.date.toISOString(),
+ formattedDate: day.formattedDate,
+ week: getWeek(day.date),
+ isToday:
+ day.date.getDate() === currentDate.getDate() &&
+ day.date.getMonth() === currentDate.getMonth() &&
+ day.date.getFullYear() === currentDate.getFullYear(),
+ isOdd: isStriped(acc.length, showWeeks),
+ isHoliday: day.isHoliday,
+ isSunday: day.isSunday,
+ isStartOfWeek: acc.length % (showWeeks ? 8 : 7) === 0,
+ isWorkDay: day.isWorkDay,
+ isNonCommissionedWorkDay: (workDayDetail?.nonCommissionedHours ?? 0) > 0,
+ workDayDetails: workDayDetail
+ });
+
+ // if index is last day of month, we need to add spacing days
+ if (index === month.days.length - 1) {
+ const spacingDays =
+ {
+ 0: 0,
+ 1: 6,
+ 2: 5,
+ 3: 4,
+ 4: 3,
+ 5: 2,
+ 6: 1,
+ 7: 0
+ }[day.date.getDay()] ?? 0;
+ for (let i = 0; i < spacingDays; i++) {
+ acc.push({
+ type: "spacing",
+ value: i + 1,
+ date: day.date.toISOString(),
+ formattedDate: day.formattedDate,
+ week: getWeek(day.date),
+ isOdd: isStriped(acc.length, showWeeks),
+ isStartOfWeek: acc.length % (showWeeks ? 8 : 7) === 0,
+ isWorkDay: day.isWorkDay,
+ isNonCommissionedWorkDay: (workDayDetail?.nonCommissionedHours ?? 0) > 0,
+ workDayDetails: workDayDetail
+ });
+ }
+ }
+
+ return acc;
+ }, [] as CalendarEntries[]);
+
+ return calendarEntries;
+};
diff --git a/utils/calendar/calendarUtils.ts b/utils/calendar/calendarUtils.ts
deleted file mode 100644
index 6ed450a..0000000
--- a/utils/calendar/calendarUtils.ts
+++ /dev/null
@@ -1,217 +0,0 @@
-import { isWorkDay } from "@/logic/calendarLogic";
-import { CalendarDay, CalendarMonth, Holiday } from "@/types";
-import { isString, memoize } from "@/utils/commonUtils";
-import {
- getFormattedDate,
- getFormattedDay,
- getFormattedLongDate,
- getFormattedMonth,
- getFormattedShortDate
-} from "@/utils/dateUtils";
-import {
- addDays,
- eachDayOfInterval,
- eachMonthOfInterval,
- eachWeekOfInterval,
- endOfISOWeek,
- endOfMonth,
- endOfYear,
- getDate,
- getWeek,
- getYear,
- isLeapYear,
- startOfISOWeek,
- startOfMonth,
- startOfYear,
- subDays
-} from "date-fns";
-import { enUS, nb } from "date-fns/locale";
-
-export const getLocale = (locale: string): Locale => {
- if (!isString(locale)) {
- return enUS;
- }
-
- switch (locale.toLowerCase()) {
- case "nb":
- case "nn":
- case "nb-no":
- case "nn-no":
- return nb;
-
- default:
- return enUS;
- }
-};
-
-export const getWeekStarsOn = (locale: string): 0 | 1 => {
- if (!isString(locale)) {
- return 0;
- }
-
- switch (locale) {
- case "nb":
- case "nn":
- case "nb-no":
- case "nn-no":
- return 1;
-
- default:
- return 0;
- }
-};
-
-const getEaster = (year: number): Date => {
- const a = year % 19;
- const b = Math.floor(year / 100);
- const c = year % 100;
- const d = Math.floor(b / 4);
- const e = b % 4;
- const f = Math.floor((b + 8) / 25);
- const g = Math.floor((b - f + 1) / 3);
- const h = (19 * a + b - d - g + 15) % 30;
- const i = Math.floor(c / 4);
- const k = c % 4;
- const l = (32 + 2 * e + 2 * i - h - k) % 7;
- const m = Math.floor((a + 11 * h + 22 * l) / 451);
- const n0 = h + l + 7 * m + 114;
- const n = Math.floor(n0 / 31) - 1;
- const p = (n0 % 31) + 1;
- return new Date(year, n, p);
-};
-
-export const getFirstDayOfYear = (year: number): Date => new Date(year, 0, 1);
-
-export const yearIsLeapYear = (year: number): boolean => isLeapYear(getFirstDayOfYear(year));
-
-export const getAllWeeksInYear = (date: Date, locale: string): Date[] => {
- const l = getLocale(locale);
-
- return eachWeekOfInterval(
- {
- start: startOfYear(date),
- end: endOfYear(date)
- },
- { locale: l, weekStartsOn: getWeekStarsOn(l?.code) }
- );
-};
-
-export const getAllDaysInWeek = (date: Date): Date[] =>
- eachDayOfInterval({
- start: startOfISOWeek(date),
- end: endOfISOWeek(date)
- });
-
-export const getAllDaysInMonth = (date: Date): Date[] =>
- eachDayOfInterval({
- start: startOfMonth(date),
- end: endOfMonth(date)
- });
-
-export const getAllMonthsInYear = (date: Date): Date[] =>
- eachMonthOfInterval({
- start: startOfYear(date),
- end: endOfYear(date)
- });
-
-const getNorwegianHolidays = memoize(function (year: number): Holiday[] {
- const newYearsDay = startOfYear(getFirstDayOfYear(year));
- const easter = getEaster(year);
- const palmSunday = subDays(easter, 7);
- const maundyThursday = subDays(easter, 3);
- const goodFriday = subDays(easter, 2);
- const easterMonday = addDays(easter, 1);
- const labourDay = new Date(year, 4, 1);
- const constitutionDay = new Date(year, 4, 17);
- const ascensionDay = addDays(easter, 39);
- const whitsun = addDays(easter, 49);
- const whitMonday = addDays(easter, 50);
- const christmasDay = new Date(year, 11, 25);
- const stStephensDay = new Date(year, 11, 26);
-
- return [
- createHoliday("Nyttårsdag", newYearsDay),
- createHoliday("Palmesøndag", palmSunday),
- createHoliday("Skjærtorsdag", maundyThursday),
- createHoliday("Langfredag", goodFriday),
- createHoliday("1. påskedag", easter),
- createHoliday("2. påskedag", easterMonday),
- createHoliday("1. mai", labourDay),
- createHoliday("17. mai", constitutionDay),
- createHoliday("Kristi Himmelsprettsdag", ascensionDay),
- createHoliday("1. pinsedag", whitsun),
- createHoliday("2. pinsedag", whitMonday),
- createHoliday("1. juledag", christmasDay),
- createHoliday("2. juledag", stStephensDay)
- ];
-});
-
-const getNorwegianHolidaysDictionary = memoize(function (year: number): Record {
- return getNorwegianHolidays(year).reduce(
- (result, norwegianHoliday) => ({
- ...result,
- [getFormattedDate(norwegianHoliday.date)]: norwegianHoliday
- }),
- {}
- );
-});
-
-const isHoliday = (date: Date): boolean => {
- const norwegianHolidays = getNorwegianHolidaysDictionary(getYear(date));
-
- return getFormattedDate(date) in norwegianHolidays;
-};
-
-const isKnowitClosed = (date: Date): boolean => {
- const year = getYear(date);
- const knowitClosedDays = {
- [getFormattedDate(new Date(year, 11, 24))]: true,
- [getFormattedDate(new Date(year, 11, 31))]: true
- };
-
- return getFormattedDate(date) in knowitClosedDays;
-};
-
-const createHoliday = (name: string, date: Date): Holiday => ({
- name,
- date,
- formattedShortDate: getFormattedShortDate(date),
- formattedLongDate: getFormattedLongDate(date)
-});
-
-export const createDate = (date: Date, locale?: string): CalendarDay => {
- const modifiedDate = {
- date,
- day: getDate(date),
- name: getFormattedDay(date, locale),
- weekNumber: getWeek(date),
- formattedDate: getFormattedDate(date, locale),
- formattedShortDate: getFormattedShortDate(date, locale),
- formattedLongDate: getFormattedLongDate(date, locale),
- isHoliday: isHoliday(date),
- isKnowitClosed: isKnowitClosed(date)
- };
-
- return {
- ...modifiedDate,
- isWorkDay: isWorkDay(modifiedDate)
- };
-};
-
-export const getCalendarYear = (year: number) => {
- const date = getFirstDayOfYear(year);
- const allMonthsInYear = getAllMonthsInYear(date);
-
- return {
- year,
- isLeapYear: yearIsLeapYear(year),
- months: allMonthsInYear.map(monthDate => {
- const month = getFormattedMonth(monthDate);
- return {
- month,
- halfTax: month?.toUpperCase() === "NOVEMBER",
- days: getAllDaysInMonth(monthDate).map(date => createDate(date))
- };
- })
- };
-};
diff --git a/utils/common-utils.ts b/utils/common-utils.ts
new file mode 100644
index 0000000..34cc209
--- /dev/null
+++ b/utils/common-utils.ts
@@ -0,0 +1,31 @@
+export const isString = (value: any) =>
+ Object.prototype.toString.call(value) === "[object String]";
+
+export const memoize = (func) => {
+ const cache = {};
+ return (...args) => {
+ const key = JSON.stringify(args);
+ return key in cache ? cache[key] : (cache[key] = func(...args));
+ };
+};
+
+export const range = (min: number, max: number) =>
+ Array.from({ length: max - min + 1 }, (_, i) => min + i);
+
+export const omit = (obj: {}, keys: string[]) =>
+ Object.keys(obj)
+ .filter((k) => !keys.includes(k))
+ .reduce((res, k) => Object.assign(res, { [k]: obj[k] }), {});
+
+export const capitalize = (str: string): string =>
+ str.length === 0 ? str : `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
+
+export function getAbsoluteUrl(path?: string) {
+ const base = process.env.NEXT_PUBLIC_APP_URL ?? "https://kxb.app";
+
+ if (path) {
+ return `${base}${path}`;
+ }
+
+ return base;
+}
diff --git a/utils/commonUtils.ts b/utils/commonUtils.ts
deleted file mode 100644
index c403124..0000000
--- a/utils/commonUtils.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-export const isString = (value: any) => Object.prototype.toString.call(value) === "[object String]";
-
-export const memoize = func => {
- const cache = {};
- return (...args) => {
- const key = JSON.stringify(args);
- return key in cache ? cache[key] : (cache[key] = func(...args));
- };
-};
-
-export const range = (min: number, max: number) =>
- Array.from({ length: max - min + 1 }, (_, i) => min + i);
-
-export const omit = (obj: {}, keys: string[]) =>
- Object.keys(obj)
- .filter(k => !keys.includes(k))
- .reduce((res, k) => Object.assign(res, { [k]: obj[k] }), {});
-
-export const stringToTitleCase = (str: string) =>
- str.length === 0 ? str : str.charAt(0).toUpperCase() + str.slice(1);
diff --git a/utils/currencyFormat.ts b/utils/currency-format.ts
similarity index 100%
rename from utils/currencyFormat.ts
rename to utils/currency-format.ts
diff --git a/utils/dateUtils.ts b/utils/date-utils.ts
similarity index 55%
rename from utils/dateUtils.ts
rename to utils/date-utils.ts
index bee4a82..fcb30d8 100644
--- a/utils/dateUtils.ts
+++ b/utils/date-utils.ts
@@ -1,35 +1,56 @@
-import { MONTH_NAMES, MONTH_VALUES } from "@/constants/dateConstants";
-import { range } from "@/utils/commonUtils";
-import { format } from "date-fns";
-import { getLocale } from "./calendar/calendarUtils";
+import { MONTH_NAMES, MONTH_VALUES } from "@/constants/date-constants";
+import { range } from "@/utils/common-utils";
+import {
+ format,
+ getDate,
+ getHours,
+ getMinutes,
+ getMonth,
+ getSeconds,
+ getWeek as getWeekDateFns,
+ getYear
+} from "date-fns";
-export const getFormattedDate = (date: Date, locale?: string): string =>
+import { getLocale, getWeekStarsOn } from "./calendar-utils";
+
+export const getFormattedDate = (date: Date, locale?: string) =>
format(date, "dd-MM-yyyy", { locale: getLocale(locale) });
-export const getFormattedShortDate = (date: Date, locale?: string): string =>
+export const getFormattedShortDate = (date: Date, locale?: string) =>
format(date, "EE d. MMM yyyy", { locale: getLocale(locale) });
-export const getFormattedLongDate = (date: Date, locale?: string): string =>
+export const getFormattedLongDate = (date: Date, locale?: string) =>
format(date, "EEEE d. MMMM yyyy", { locale: getLocale(locale) });
-export const getFormattedDay = (date: Date, locale?: string): string =>
+export const getFormattedDay = (date: Date, locale?: string) =>
format(date, "EEEE", { locale: getLocale(locale) });
-export const getFormattedMonth = (date: Date, locale?: string): string =>
+export const getFormattedMonth = (date: Date, locale?: string) =>
format(date, "MMMM", { locale: getLocale(locale) });
-export const getFormattedMicrosoftSqlDate = (date: Date, locale?: string): string =>
+export const getFormattedDayAndMonth = (date: Date, locale?: string) =>
+ format(date, "dd.MM", { locale: getLocale(locale) });
+
+export const getFormattedMicrosoftSqlDate = (date: Date, locale?: string) =>
format(date, "yyyy-MM-dd hh-mm-ss", { locale: getLocale(locale) });
export const getFormattedTime = (date: Date, locale?: string): string =>
format(date, "HH:mm:ss", { locale: getLocale(locale) });
-export const getFormattedDateAndTime = (date: Date, locale?: string): string =>
+export const getFormattedDateAndTime = (date: Date, locale?: string) =>
`${getFormattedDate(date, locale)} ${getFormattedTime(date, locale)}`;
-export const getFormattedIsoDateAndTime = (date: Date, locale?: string): string =>
+export const getFormattedIsoDateAndTime = (date: Date, locale?: string) =>
format(date, "yyyy-MM-dd'T'HH:mm:ssXX", { locale: getLocale(locale) });
+export const getWeek = (date: Date, locale?: string) =>
+ getWeekDateFns(date, {
+ locale: getLocale(locale),
+ weekStartsOn: getWeekStarsOn(locale),
+ });
+export const getDayName = (date: Date, locale?: string) =>
+ format(date, "EE", { locale: getLocale(locale) });
+
export const getMonthFromName = (monthName: string) => {
switch (monthName?.toUpperCase()) {
case MONTH_NAMES.JANUARY:
@@ -69,3 +90,26 @@ export const getThisYearAndTwoYearsIntoTheFuture = () => {
let year = new Date().getFullYear();
return range(year, year + 8);
};
+
+export const extractDate = (input: Date) => {
+ const month = getMonth(input) + 1;
+ const date = getDate(input);
+ const hours = getHours(input);
+ const minutes = getMinutes(input);
+ const seconds = getSeconds(input);
+
+ return {
+ year: getYear(input).toString(),
+ month: month < 10 ? `0${month}` : month.toString(),
+ day: date < 10 ? `0${date}` : date.toString(),
+ hour: hours < 10 ? `0${hours}` : hours.toString(),
+ minute: minutes < 10 ? `0${minutes}` : minutes.toString(),
+ second: seconds < 10 ? `0${seconds}` : seconds.toString(),
+ };
+};
+
+export const getHourAndMinutes = (date: Date) => {
+ const extractedDate = extractDate(date);
+
+ return `${extractedDate.hour}:${extractedDate.minute}`;
+};
diff --git a/utils/emailUtils.ts b/utils/email-utils.ts
similarity index 100%
rename from utils/emailUtils.ts
rename to utils/email-utils.ts
diff --git a/utils/pageUtils.ts b/utils/pageUtils.ts
deleted file mode 100644
index 55e01d0..0000000
--- a/utils/pageUtils.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import prismaUser from "@/lib/prismaUser";
-import { sessionUserIsSpecialist } from "@/utils/sessionUtils";
-import { GetServerSidePropsContext } from "next";
-import { getSession } from "next-auth/react";
-import { ParsedUrlQuery } from "querystring";
-
-export const getResultForAuthenticatedPage = async (
- context: GetServerSidePropsContext
-) => {
- let session = await getSession(context);
-
- // Session user is specialist and is granted access to the app
- // or
- // session is valid and specialist only mode is disabled
- if (
- sessionUserIsSpecialist(session) ||
- (session &&
- !(process.env.SHOW_ME_THE_MONEY_SPECIALISTS_ONLY_MODE?.toLowerCase() === "true" ?? false))
- ) {
- const { refreshToken, ...user } = session.user?.activeDirectoryId
- ? session.user
- : await prismaUser.getByEmail(session.user.email);
-
- return {
- props: {
- session,
- user
- }
- };
- }
-
- // We have a session user but the session user is not
- // a specialist. Redirect to access denied
- if (session) {
- return {
- redirect: {
- destination: "/access-denied",
- permanent: false
- }
- };
- }
-
- // No session. Redirect to login
- return {
- redirect: {
- destination: "/login",
- permanent: false
- }
- };
-};
diff --git a/utils/sessionUtils.ts b/utils/sessionUtils.ts
deleted file mode 100644
index 543b411..0000000
--- a/utils/sessionUtils.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Session } from "next-auth";
-
-const userIsAdmin = (user: Session["user"]): boolean => user?.isAdmin ?? false;
-const userIsSpecialist = (user: Session["user"]): boolean => user?.isSpecialist ?? false;
-
-export const sessionUserIsAdmin = (session: Session) => userIsAdmin(session?.user);
-export const sessionUserIsSpecialist = (session: Session) => userIsSpecialist(session?.user);
-export const getSessionUserActiveDirectoryId = (session: Session) =>
- session?.user?.activeDirectoryId;
diff --git a/utils/userUtils.ts b/utils/user-utils.ts
similarity index 96%
rename from utils/userUtils.ts
rename to utils/user-utils.ts
index 84d1cb7..f02d9b3 100644
--- a/utils/userUtils.ts
+++ b/utils/user-utils.ts
@@ -1,5 +1,5 @@
-import DEFAULT_USER_SALARY from "@/constants/defaultUserSalary";
-import { getEarningsForMonth, getEarningsForYear } from "@/logic/earningsLogic";
+import DEFAULT_USER_SALARY from "@/constants/default-user-salary";
+import { getEarningsForMonth, getEarningsForYear } from "@/logic/earnings-logic";
import {
CalendarMonth,
CalendarYear,