Compare commits
6 Commits
chore/upgr
...
fix/api-ke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
902a539d73 | ||
|
|
7fef9be638 | ||
|
|
c7473062aa | ||
|
|
920e9b18a2 | ||
|
|
bcd20b58c6 | ||
|
|
36675604f9 |
@@ -4,8 +4,9 @@ ARG PROJECT=@kan/web
|
|||||||
|
|
||||||
# 1. Alpine image
|
# 1. Alpine image
|
||||||
FROM node:${NODE_VERSION}-alpine AS alpine
|
FROM node:${NODE_VERSION}-alpine AS alpine
|
||||||
RUN apk update
|
RUN apk update && \
|
||||||
RUN apk add --no-cache libc6-compat python3 make g++
|
apk add --no-cache --virtual .build-deps libc6-compat python3 make g++ && \
|
||||||
|
rm -rf /var/cache/apk/* /tmp/* || true
|
||||||
|
|
||||||
# Setup pnpm and turbo on the alpine base
|
# Setup pnpm and turbo on the alpine base
|
||||||
FROM alpine AS base
|
FROM alpine AS base
|
||||||
|
|||||||
@@ -26,9 +26,11 @@ const config = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
/** We already do linting and typechecking as separate tasks in CI */
|
/** We already do linting and typechecking as separate tasks in CI */
|
||||||
eslint: { ignoreDuringBuilds: true },
|
|
||||||
typescript: { ignoreBuildErrors: true },
|
typescript: { ignoreBuildErrors: true },
|
||||||
|
|
||||||
|
// temporarily ignore eslint errors during build until we fix all the errors sigh
|
||||||
|
eslint: { ignoreDuringBuilds: true },
|
||||||
|
|
||||||
images: {
|
images: {
|
||||||
remotePatterns: (() => {
|
remotePatterns: (() => {
|
||||||
/** @type {Array<{protocol: "http" | "https", hostname: string}>} */
|
/** @type {Array<{protocol: "http" | "https", hostname: string}>} */
|
||||||
@@ -75,12 +77,13 @@ const config = {
|
|||||||
return patterns;
|
return patterns;
|
||||||
})(),
|
})(),
|
||||||
},
|
},
|
||||||
webpack(config) {
|
turbopack: {
|
||||||
config.module.rules.push({
|
rules: {
|
||||||
test: /\.svg$/,
|
"*.svg": {
|
||||||
use: ["@svgr/webpack"],
|
loaders: ["@svgr/webpack"],
|
||||||
});
|
as: "*.js",
|
||||||
return config;
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
experimental: {
|
experimental: {
|
||||||
// instrumentationHook: true,
|
// instrumentationHook: true,
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm with-env next build",
|
"build": "pnpm with-env next build --turbo",
|
||||||
"clean": "git clean -xdf .cache .next .turbo node_modules",
|
"clean": "git clean -xdf .cache .next .turbo node_modules",
|
||||||
"dev": "pnpm with-env next dev",
|
"dev": "pnpm with-env next dev --turbo",
|
||||||
"format": "prettier --check . --ignore-path ../../.gitignore",
|
"format": "prettier --check . --ignore-path ../../.gitignore",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"start": "pnpm with-env next start",
|
"start": "pnpm with-env next start",
|
||||||
@@ -47,12 +47,10 @@
|
|||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"geist": "^1.3.1",
|
"geist": "^1.3.1",
|
||||||
"jose": "^6.1.2",
|
"jose": "^6.1.2",
|
||||||
"next": "^15.3.4",
|
"next": "15.5.9",
|
||||||
"next-logger": "^5.0.1",
|
|
||||||
"next-runtime-env": "^1.7.2",
|
"next-runtime-env": "^1.7.2",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"nextjs-cors": "^2.2.0",
|
"nextjs-cors": "^2.2.0",
|
||||||
"pino": "^9.6.0",
|
|
||||||
"posthog-js": "^1.254.0",
|
"posthog-js": "^1.254.0",
|
||||||
"react": "catalog:react18",
|
"react": "catalog:react18",
|
||||||
"react-beautiful-dnd": "^13.1.1",
|
"react-beautiful-dnd": "^13.1.1",
|
||||||
@@ -86,7 +84,6 @@
|
|||||||
"dotenv-cli": "^7.4.4",
|
"dotenv-cli": "^7.4.4",
|
||||||
"eslint": "catalog:",
|
"eslint": "catalog:",
|
||||||
"jiti": "^1.21.6",
|
"jiti": "^1.21.6",
|
||||||
"pino-pretty": "^13.0.0",
|
|
||||||
"prettier": "catalog:",
|
"prettier": "catalog:",
|
||||||
"tailwindcss": "catalog:",
|
"tailwindcss": "catalog:",
|
||||||
"typescript": "catalog:"
|
"typescript": "catalog:"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -1,6 +0,0 @@
|
|||||||
export async function register() {
|
|
||||||
if (process.env.NEXT_RUNTIME === "nodejs") {
|
|
||||||
await require("pino");
|
|
||||||
await require("next-logger");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,7 +29,6 @@ import { useModal } from "~/providers/modal";
|
|||||||
import { usePopup } from "~/providers/popup";
|
import { usePopup } from "~/providers/popup";
|
||||||
import { useWorkspace } from "~/providers/workspace";
|
import { useWorkspace } from "~/providers/workspace";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { convertDueDateFiltersToRanges } from "~/utils/dueDateFilters";
|
|
||||||
import { formatToArray } from "~/utils/helpers";
|
import { formatToArray } from "~/utils/helpers";
|
||||||
import BoardDropdown from "./components/BoardDropdown";
|
import BoardDropdown from "./components/BoardDropdown";
|
||||||
import Card from "./components/Card";
|
import Card from "./components/Card";
|
||||||
@@ -97,20 +96,17 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) {
|
|||||||
| "no-due-date"
|
| "no-due-date"
|
||||||
)[];
|
)[];
|
||||||
|
|
||||||
const queryParams: {
|
const boardType: "regular" | "template" = isTemplate ? "template" : "regular";
|
||||||
boardPublicId: string;
|
|
||||||
members: string[];
|
const queryParams = {
|
||||||
labels: string[];
|
|
||||||
lists: string[];
|
|
||||||
dueDate: ReturnType<typeof convertDueDateFiltersToRanges>;
|
|
||||||
type: "regular" | "template";
|
|
||||||
} = {
|
|
||||||
boardPublicId: boardId ?? "",
|
boardPublicId: boardId ?? "",
|
||||||
members: formatToArray(router.query.members),
|
members: formatToArray(router.query.members),
|
||||||
labels: formatToArray(router.query.labels),
|
labels: formatToArray(router.query.labels),
|
||||||
lists: formatToArray(router.query.lists),
|
lists: formatToArray(router.query.lists),
|
||||||
dueDate: convertDueDateFiltersToRanges(semanticFilters),
|
...(semanticFilters.length > 0 && {
|
||||||
type: isTemplate ? "template" : "regular",
|
dueDateFilters: semanticFilters,
|
||||||
|
}),
|
||||||
|
type: boardType,
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import type { FC, SVGProps } from "react";
|
import type { FC, SVGProps } from "react";
|
||||||
import { Trans } from "@lingui/react/macro";
|
import { Trans } from "@lingui/react/macro";
|
||||||
|
|
||||||
import AirbusLogo from "/public/logos/airbus.svg";
|
import AirbusLogo from "~/assets/logos/airbus.svg";
|
||||||
import BitwardenLogo from "/public/logos/bitwarden.svg";
|
import BitwardenLogo from "~/assets/logos/bitwarden.svg";
|
||||||
import CouchbaseLogo from "/public/logos/couchbase.svg";
|
import CouchbaseLogo from "~/assets/logos/couchbase.svg";
|
||||||
import DeloitteLogo from "/public/logos/deloitte.svg";
|
import DeloitteLogo from "~/assets/logos/deloitte.svg";
|
||||||
import FastCompanyLogo from "/public/logos/fast_company.svg";
|
import FastCompanyLogo from "~/assets/logos/fast_company.svg";
|
||||||
import LegoLogo from "/public/logos/lego.svg";
|
import LegoLogo from "~/assets/logos/lego.svg";
|
||||||
import LinkedinLogo from "/public/logos/linkedin.svg";
|
import LinkedinLogo from "~/assets/logos/linkedin.svg";
|
||||||
import SanaLogo from "/public/logos/sana.svg";
|
import SanaLogo from "~/assets/logos/sana.svg";
|
||||||
import WakamLogo from "/public/logos/wakam.svg";
|
import WakamLogo from "~/assets/logos/wakam.svg";
|
||||||
|
|
||||||
type LogoComponent = FC<SVGProps<SVGSVGElement>>;
|
type LogoComponent = FC<SVGProps<SVGSVGElement>>;
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ export default function Logos() {
|
|||||||
<div className="absolute left-0 top-0 z-10 h-full w-8 bg-gradient-to-r from-white/60 to-transparent dark:from-dark-50" />
|
<div className="absolute left-0 top-0 z-10 h-full w-8 bg-gradient-to-r from-white/60 to-transparent dark:from-dark-50" />
|
||||||
<div className="absolute right-0 top-0 z-10 h-full w-8 bg-gradient-to-l from-white/60 to-transparent dark:from-dark-50" />
|
<div className="absolute right-0 top-0 z-10 h-full w-8 bg-gradient-to-l from-white/60 to-transparent dark:from-dark-50" />
|
||||||
|
|
||||||
<div className="animate-scroll flex" style={{ width: "max-content" }}>
|
<div className="flex animate-scroll" style={{ width: "max-content" }}>
|
||||||
<div className="flex flex-shrink-0 items-center space-x-12">
|
<div className="flex flex-shrink-0 items-center space-x-12">
|
||||||
{logos.map((logo) => {
|
{logos.map((logo) => {
|
||||||
const LogoComponent: LogoComponent = logo.component;
|
const LogoComponent: LogoComponent = logo.component;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import ThemeToggle from "~/components/ThemeToggle";
|
|||||||
import { useModal } from "~/providers/modal";
|
import { useModal } from "~/providers/modal";
|
||||||
import { usePopup } from "~/providers/popup";
|
import { usePopup } from "~/providers/popup";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { convertDueDateFiltersToRanges } from "~/utils/dueDateFilters";
|
|
||||||
import { formatToArray } from "~/utils/helpers";
|
import { formatToArray } from "~/utils/helpers";
|
||||||
import Card from "~/views/board/components/Card";
|
import Card from "~/views/board/components/Card";
|
||||||
import Filters from "~/views/board/components/Filters";
|
import Filters from "~/views/board/components/Filters";
|
||||||
@@ -55,7 +54,9 @@ export default function PublicBoardView() {
|
|||||||
members: formatToArray(router.query.members),
|
members: formatToArray(router.query.members),
|
||||||
labels: formatToArray(router.query.labels),
|
labels: formatToArray(router.query.labels),
|
||||||
lists: formatToArray(router.query.lists),
|
lists: formatToArray(router.query.lists),
|
||||||
dueDate: convertDueDateFiltersToRanges(dueDateFilters),
|
...(dueDateFilters.length > 0 && {
|
||||||
|
dueDateFilters: dueDateFilters,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: router.isReady && !!boardSlug,
|
enabled: router.isReady && !!boardSlug,
|
||||||
|
|||||||
@@ -8,17 +8,15 @@ import * as labelRepo from "@kan/db/repository/label.repo";
|
|||||||
import * as listRepo from "@kan/db/repository/list.repo";
|
import * as listRepo from "@kan/db/repository/list.repo";
|
||||||
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
||||||
import { colours } from "@kan/shared/constants";
|
import { colours } from "@kan/shared/constants";
|
||||||
import { generateSlug, generateUID } from "@kan/shared/utils";
|
import {
|
||||||
|
convertDueDateFiltersToRanges,
|
||||||
|
generateSlug,
|
||||||
|
generateUID,
|
||||||
|
} from "@kan/shared/utils";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||||
import { assertUserInWorkspace } from "../utils/auth";
|
import { assertUserInWorkspace } from "../utils/auth";
|
||||||
|
|
||||||
const dueDateFilterSchema = z.object({
|
|
||||||
startDate: z.string().datetime().optional(),
|
|
||||||
endDate: z.string().datetime().optional(),
|
|
||||||
hasNoDueDate: z.boolean().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const boardRouter = createTRPCRouter({
|
export const boardRouter = createTRPCRouter({
|
||||||
all: protectedProcedure
|
all: protectedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
@@ -85,7 +83,18 @@ export const boardRouter = createTRPCRouter({
|
|||||||
members: z.array(z.string().min(12)).optional(),
|
members: z.array(z.string().min(12)).optional(),
|
||||||
labels: z.array(z.string().min(12)).optional(),
|
labels: z.array(z.string().min(12)).optional(),
|
||||||
lists: z.array(z.string().min(12)).optional(),
|
lists: z.array(z.string().min(12)).optional(),
|
||||||
dueDate: z.array(dueDateFilterSchema).optional(),
|
dueDateFilters: z
|
||||||
|
.array(
|
||||||
|
z.enum([
|
||||||
|
"overdue",
|
||||||
|
"today",
|
||||||
|
"tomorrow",
|
||||||
|
"next-week",
|
||||||
|
"next-month",
|
||||||
|
"no-due-date",
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
type: z.enum(["regular", "template"]).optional(),
|
type: z.enum(["regular", "template"]).optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -112,6 +121,11 @@ export const boardRouter = createTRPCRouter({
|
|||||||
|
|
||||||
await assertUserInWorkspace(ctx.db, userId, board.workspaceId);
|
await assertUserInWorkspace(ctx.db, userId, board.workspaceId);
|
||||||
|
|
||||||
|
// Convert semantic string filters to date ranges expected by the repo
|
||||||
|
const dueDateFilters = input.dueDateFilters
|
||||||
|
? convertDueDateFiltersToRanges(input.dueDateFilters)
|
||||||
|
: [];
|
||||||
|
|
||||||
const result = await boardRepo.getByPublicId(
|
const result = await boardRepo.getByPublicId(
|
||||||
ctx.db,
|
ctx.db,
|
||||||
input.boardPublicId,
|
input.boardPublicId,
|
||||||
@@ -119,14 +133,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
members: input.members ?? [],
|
members: input.members ?? [],
|
||||||
labels: input.labels ?? [],
|
labels: input.labels ?? [],
|
||||||
lists: input.lists ?? [],
|
lists: input.lists ?? [],
|
||||||
dueDate:
|
dueDate: dueDateFilters,
|
||||||
input.dueDate?.map((filter) => ({
|
|
||||||
startDate: filter.startDate
|
|
||||||
? new Date(filter.startDate)
|
|
||||||
: undefined,
|
|
||||||
endDate: filter.endDate ? new Date(filter.endDate) : undefined,
|
|
||||||
hasNoDueDate: filter.hasNoDueDate,
|
|
||||||
})) ?? [],
|
|
||||||
type: input.type,
|
type: input.type,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -160,7 +167,18 @@ export const boardRouter = createTRPCRouter({
|
|||||||
members: z.array(z.string().min(12)).optional(),
|
members: z.array(z.string().min(12)).optional(),
|
||||||
labels: z.array(z.string().min(12)).optional(),
|
labels: z.array(z.string().min(12)).optional(),
|
||||||
lists: z.array(z.string().min(12)).optional(),
|
lists: z.array(z.string().min(12)).optional(),
|
||||||
dueDate: z.array(dueDateFilterSchema).optional(),
|
dueDateFilters: z
|
||||||
|
.array(
|
||||||
|
z.enum([
|
||||||
|
"overdue",
|
||||||
|
"today",
|
||||||
|
"tomorrow",
|
||||||
|
"next-week",
|
||||||
|
"next-month",
|
||||||
|
"no-due-date",
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof boardRepo.getBySlug>>>())
|
.output(z.custom<Awaited<ReturnType<typeof boardRepo.getBySlug>>>())
|
||||||
@@ -176,6 +194,11 @@ export const boardRouter = createTRPCRouter({
|
|||||||
code: "NOT_FOUND",
|
code: "NOT_FOUND",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Convert semantic string filters to date ranges expected by the repo
|
||||||
|
const dueDateFilters = input.dueDateFilters
|
||||||
|
? convertDueDateFiltersToRanges(input.dueDateFilters)
|
||||||
|
: [];
|
||||||
|
|
||||||
const result = await boardRepo.getBySlug(
|
const result = await boardRepo.getBySlug(
|
||||||
ctx.db,
|
ctx.db,
|
||||||
input.boardSlug,
|
input.boardSlug,
|
||||||
@@ -184,14 +207,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
members: input.members ?? [],
|
members: input.members ?? [],
|
||||||
labels: input.labels ?? [],
|
labels: input.labels ?? [],
|
||||||
lists: input.lists ?? [],
|
lists: input.lists ?? [],
|
||||||
dueDate:
|
dueDate: dueDateFilters,
|
||||||
input.dueDate?.map((filter) => ({
|
|
||||||
startDate: filter.startDate
|
|
||||||
? new Date(filter.startDate)
|
|
||||||
: undefined,
|
|
||||||
endDate: filter.endDate ? new Date(filter.endDate) : undefined,
|
|
||||||
hasNoDueDate: filter.hasNoDueDate,
|
|
||||||
})) ?? [],
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -306,12 +322,6 @@ export const boardRouter = createTRPCRouter({
|
|||||||
sourceBoardId: sourceBoardInfo.id,
|
sourceBoardId: sourceBoardInfo.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result)
|
|
||||||
throw new TRPCError({
|
|
||||||
message: `Failed to create board from source`,
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
},
|
},
|
||||||
"prettier": "@kan/prettier-config",
|
"prettier": "@kan/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@better-auth/stripe": "^1.3.7",
|
"@better-auth/stripe": "^1.4.6",
|
||||||
"better-auth": "^1.3.7"
|
"better-auth": "^1.4.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,8 +78,7 @@ export const configuredProviders = socialProviderList.reduce<
|
|||||||
Object.keys(acc).includes("google") &&
|
Object.keys(acc).includes("google") &&
|
||||||
acc[provider]
|
acc[provider]
|
||||||
) {
|
) {
|
||||||
const allowed = process.env.BETTER_AUTH_ALLOWED_DOMAINS
|
const allowed = process.env.BETTER_AUTH_ALLOWED_DOMAINS?.split(",")
|
||||||
?.split(",")
|
|
||||||
.map((d) => d.trim().toLowerCase())
|
.map((d) => d.trim().toLowerCase())
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
if (allowed && allowed.length > 0) {
|
if (allowed && allowed.length > 0) {
|
||||||
@@ -299,9 +298,8 @@ export const initAuth = (db: dbClient) => {
|
|||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
// @todo: hasing is disabled due to a bug in the api key plugin
|
|
||||||
apiKey({
|
apiKey({
|
||||||
disableKeyHashing: true,
|
enableSessionForAPIKeys: true,
|
||||||
rateLimit: {
|
rateLimit: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
timeWindow: 1000 * 60, // 1 minute
|
timeWindow: 1000 * 60, // 1 minute
|
||||||
@@ -365,8 +363,7 @@ export const initAuth = (db: dbClient) => {
|
|||||||
// Fall through to any additional checks below
|
// Fall through to any additional checks below
|
||||||
}
|
}
|
||||||
// Enforce allowed domains (OIDC/social) if configured
|
// Enforce allowed domains (OIDC/social) if configured
|
||||||
const allowed = process.env.BETTER_AUTH_ALLOWED_DOMAINS
|
const allowed = process.env.BETTER_AUTH_ALLOWED_DOMAINS?.split(",")
|
||||||
?.split(",")
|
|
||||||
.map((d) => d.trim().toLowerCase())
|
.map((d) => d.trim().toLowerCase())
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
if (allowed && allowed.length > 0) {
|
if (allowed && allowed.length > 0) {
|
||||||
|
|||||||
@@ -24,9 +24,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@novu/api": "^3.11.0",
|
"@novu/api": "^3.11.0",
|
||||||
"@react-email/components": "^0.0.25",
|
"@react-email/components": "^1.0.1",
|
||||||
"nodemailer": "^7.0.3",
|
"nodemailer": "^7.0.3",
|
||||||
"react-email": "^3.0.1"
|
"react-email": "^5.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kan/eslint-config": "workspace:*",
|
"@kan/eslint-config": "workspace:*",
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
},
|
},
|
||||||
"prettier": "@kan/prettier-config",
|
"prettier": "@kan/prettier-config",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"jose": "^6.1.2",
|
"jose": "^6.1.2",
|
||||||
"nanoid": "^5.0.9",
|
"nanoid": "^5.0.9",
|
||||||
"next-runtime-env": "^1.7.2"
|
"next-runtime-env": "^1.7.2"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { addDays, endOfDay, startOfDay } from "date-fns";
|
import { addDays, endOfDay, startOfDay } from "date-fns";
|
||||||
|
|
||||||
type DueDateFilterKey =
|
export type DueDateFilterKey =
|
||||||
| "overdue"
|
| "overdue"
|
||||||
| "today"
|
| "today"
|
||||||
| "tomorrow"
|
| "tomorrow"
|
||||||
@@ -9,8 +9,8 @@ type DueDateFilterKey =
|
|||||||
| "no-due-date";
|
| "no-due-date";
|
||||||
|
|
||||||
export interface DueDateFilter {
|
export interface DueDateFilter {
|
||||||
startDate?: string;
|
startDate?: Date;
|
||||||
endDate?: string;
|
endDate?: Date;
|
||||||
hasNoDueDate?: boolean;
|
hasNoDueDate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,35 +21,35 @@ export const convertDueDateFiltersToRanges = (
|
|||||||
|
|
||||||
const today = startOfDay(new Date());
|
const today = startOfDay(new Date());
|
||||||
const tomorrow = addDays(today, 1);
|
const tomorrow = addDays(today, 1);
|
||||||
const nextWeekEnd = addDays(today, 8); // 7 days ahead
|
const nextWeekEnd = addDays(today, 8);
|
||||||
const nextMonthEnd = addDays(today, 31); // 30 days ahead
|
const nextMonthEnd = addDays(today, 31);
|
||||||
|
|
||||||
return filters.map((filter) => {
|
return filters.map((filter) => {
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
case "overdue":
|
case "overdue":
|
||||||
return {
|
return {
|
||||||
endDate: today.toISOString(),
|
endDate: today,
|
||||||
};
|
};
|
||||||
case "today":
|
case "today":
|
||||||
return {
|
return {
|
||||||
startDate: today.toISOString(),
|
startDate: today,
|
||||||
endDate: endOfDay(today).toISOString(),
|
endDate: endOfDay(today),
|
||||||
};
|
};
|
||||||
case "tomorrow":
|
case "tomorrow":
|
||||||
return {
|
return {
|
||||||
startDate: tomorrow.toISOString(),
|
startDate: tomorrow,
|
||||||
endDate: endOfDay(tomorrow).toISOString(),
|
endDate: endOfDay(tomorrow),
|
||||||
};
|
};
|
||||||
case "next-week": {
|
case "next-week": {
|
||||||
return {
|
return {
|
||||||
startDate: today.toISOString(),
|
startDate: today,
|
||||||
endDate: nextWeekEnd.toISOString(),
|
endDate: nextWeekEnd,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "next-month": {
|
case "next-month": {
|
||||||
return {
|
return {
|
||||||
startDate: nextWeekEnd.toISOString(),
|
startDate: nextWeekEnd,
|
||||||
endDate: nextMonthEnd.toISOString(),
|
endDate: nextMonthEnd,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "no-due-date":
|
case "no-due-date":
|
||||||
@@ -2,3 +2,4 @@ export * from "./generateUID";
|
|||||||
export * from "./generateSlug";
|
export * from "./generateSlug";
|
||||||
export * from "./subscriptions";
|
export * from "./subscriptions";
|
||||||
export * from "./email";
|
export * from "./email";
|
||||||
|
export * from "./dueDateFilters";
|
||||||
|
|||||||