Compare commits

..

1 Commits

Author SHA1 Message Date
Henry
8f11eb0085 fix: missing translation for editor placeholder 2025-08-14 21:36:14 +01:00
6 changed files with 116 additions and 150 deletions

View File

@@ -139,7 +139,7 @@ pnpm dev
## Environment Variables 🔐 ## Environment Variables 🔐
| Variable | Description | Required | Example | | Variable | Description | Required | Example |
| ----------------------------------------- | -------------------------------------------------------- | ------------------------ | --------------------------------------------- | | -------------------------------- | -------------------------------------------------------- | ------------------ | --------------------------------------------- |
| `POSTGRES_URL` | PostgreSQL connection URL | To use external database | `postgres://user:pass@localhost:5432/db` | | `POSTGRES_URL` | PostgreSQL connection URL | To use external database | `postgres://user:pass@localhost:5432/db` |
| `EMAIL_FROM` | Sender email address | For Email | `"Kan <hello@mail.kan.bn>"` | | `EMAIL_FROM` | Sender email address | For Email | `"Kan <hello@mail.kan.bn>"` |
| `SMTP_HOST` | SMTP server hostname | For Email | `smtp.resend.com` | | `SMTP_HOST` | SMTP server hostname | For Email | `smtp.resend.com` |
@@ -168,7 +168,6 @@ pnpm dev
| `NEXT_PUBLIC_AVATAR_BUCKET_NAME` | S3 bucket name for avatars | For file uploads | `avatars` | | `NEXT_PUBLIC_AVATAR_BUCKET_NAME` | S3 bucket name for avatars | For file uploads | `avatars` |
| `NEXT_PUBLIC_ALLOW_CREDENTIALS` | Allow email & password login | For authentication | `true` | | `NEXT_PUBLIC_ALLOW_CREDENTIALS` | Allow email & password login | For authentication | `true` |
| `NEXT_PUBLIC_DISABLE_SIGN_UP` | Disable sign up | For authentication | `false` | | `NEXT_PUBLIC_DISABLE_SIGN_UP` | Disable sign up | For authentication | `false` |
| `NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY` | Hide “Powered by kan.bn” on public boards (self-host) | For white labelling | `true` |
See `.env.example` for a complete list of supported environment variables. See `.env.example` for a complete list of supported environment variables.

View File

@@ -24,7 +24,7 @@ export const env = createEnv({
s.split(",").every((l) => z.string().url().safeParse(l).success), s.split(",").every((l) => z.string().url().safeParse(l).success),
) )
.optional(), .optional(),
POSTGRES_URL: z.string().url().optional().or(z.literal("")), POSTGRES_URL: z.string().url().optional().or(z.literal('')),
TRELLO_APP_API_KEY: z.string().optional(), TRELLO_APP_API_KEY: z.string().optional(),
TRELLO_APP_SECRET: z.string().optional(), TRELLO_APP_SECRET: z.string().optional(),
STRIPE_SECRET_KEY: z.string().optional(), STRIPE_SECRET_KEY: z.string().optional(),
@@ -101,13 +101,6 @@ export const env = createEnv({
(s) => !s || s.toLowerCase() === "true" || s.toLowerCase() === "false", (s) => !s || s.toLowerCase() === "true" || s.toLowerCase() === "false",
) )
.optional(), .optional(),
NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY: z
.string()
.transform((s) => (s === "" ? undefined : s))
.refine(
(s) => !s || s.toLowerCase() === "true" || s.toLowerCase() === "false",
)
.optional(),
}, },
/** /**
* Destructure all variables from `process.env` to make sure they aren't tree-shaken away. * Destructure all variables from `process.env` to make sure they aren't tree-shaken away.
@@ -126,8 +119,6 @@ export const env = createEnv({
NEXT_PUBLIC_DISABLE_SIGN_UP: process.env.NEXT_PUBLIC_DISABLE_SIGN_UP, NEXT_PUBLIC_DISABLE_SIGN_UP: process.env.NEXT_PUBLIC_DISABLE_SIGN_UP,
NEXT_PUBLIC_USE_STANDALONE_OUTPUT: NEXT_PUBLIC_USE_STANDALONE_OUTPUT:
process.env.NEXT_PUBLIC_USE_STANDALONE_OUTPUT, process.env.NEXT_PUBLIC_USE_STANDALONE_OUTPUT,
NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY:
process.env.NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY,
}, },
skipValidation: skipValidation:
!!process.env.CI || process.env.npm_lifecycle_event === "lint", !!process.env.CI || process.env.npm_lifecycle_event === "lint",

View File

@@ -29,6 +29,7 @@ import LabelSelector from "./components/LabelSelector";
import ListSelector from "./components/ListSelector"; import ListSelector from "./components/ListSelector";
import MemberSelector from "./components/MemberSelector"; import MemberSelector from "./components/MemberSelector";
import { NewChecklistForm } from "./components/NewChecklistForm"; import { NewChecklistForm } from "./components/NewChecklistForm";
import NewChecklistItemForm from "./components/NewChecklistItemForm";
import NewCommentForm from "./components/NewCommentForm"; import NewCommentForm from "./components/NewCommentForm";
interface FormValues { interface FormValues {
@@ -212,10 +213,9 @@ export default function CardPage() {
<PageHead <PageHead
title={t`${card?.title ?? "Card"} | ${board?.name ?? "Board"}`} title={t`${card?.title ?? "Card"} | ${board?.name ?? "Board"}`}
/> />
<div className="flex h-full flex-1 flex-row overflow-hidden"> <div className="flex h-full flex-1 flex-row">
<div className="scrollbar-thumb-rounded-[4px] scrollbar-track-rounded-[4px] w-full flex-1 overflow-y-auto scrollbar scrollbar-track-light-200 scrollbar-thumb-light-400 hover:scrollbar-thumb-light-400 dark:scrollbar-track-dark-100 dark:scrollbar-thumb-dark-300 dark:hover:scrollbar-thumb-dark-300"> <div className="m-auto flex h-full w-full max-w-[800px] flex-col overflow-hidden">
<div className="p-auto mx-auto flex h-full w-full max-w-[800px] flex-col"> <div className="h-full max-h-[calc(100dvh-3rem)] overflow-y-auto p-6 md:max-h-[calc(100dvh-4rem)] md:p-8">
<div className="p-6 md:p-8">
<div className="mb-8 flex w-full items-center justify-between md:mt-6"> <div className="mb-8 flex w-full items-center justify-between md:mt-6">
{!card && isLoading && ( {!card && isLoading && (
<div className="flex space-x-2"> <div className="flex space-x-2">
@@ -272,7 +272,7 @@ export default function CardPage() {
content={card.description} content={card.description}
onChange={(e) => setValue("description", e)} onChange={(e) => setValue("description", e)}
onBlur={() => handleSubmit(onSubmit)()} onBlur={() => handleSubmit(onSubmit)()}
workspaceMembers={board?.workspace.members ?? []} workspaceMembers={board?.workspace?.members ?? []}
/> />
</div> </div>
</form> </form>
@@ -303,7 +303,6 @@ export default function CardPage() {
)} )}
</div> </div>
</div> </div>
</div>
<Modal modalSize={modalContentType === "NEW_FEEDBACK" ? "md" : "sm"}> <Modal modalSize={modalContentType === "NEW_FEEDBACK" ? "md" : "sm"}>
{modalContentType === "NEW_FEEDBACK" && <FeedbackModal />} {modalContentType === "NEW_FEEDBACK" && <FeedbackModal />}

View File

@@ -2,7 +2,6 @@ import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { t } from "@lingui/core/macro"; import { t } from "@lingui/core/macro";
import { keepPreviousData } from "@tanstack/react-query"; import { keepPreviousData } from "@tanstack/react-query";
import { env } from "next-runtime-env";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { HiLink, HiOutlineLockClosed } from "react-icons/hi2"; import { HiLink, HiOutlineLockClosed } from "react-icons/hi2";
@@ -20,10 +19,6 @@ import Card from "~/views/board/components/Card";
import Filters from "~/views/board/components/Filters"; import Filters from "~/views/board/components/Filters";
import { CardModal } from "./CardModal"; import { CardModal } from "./CardModal";
const IS_CLOUD = env("NEXT_PUBLIC_KAN_ENV") === "cloud";
const HIDE_POWERED_BY =
env("NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY") === "true";
export default function PublicBoardView() { export default function PublicBoardView() {
const router = useRouter(); const router = useRouter();
const { showPopup } = usePopup(); const { showPopup } = usePopup();
@@ -199,26 +194,12 @@ export default function PublicBoardView() {
<CopyBoardLink /> <CopyBoardLink />
</div> </div>
{IS_CLOUD && (
<Link <Link
className="text-lg font-bold tracking-tight text-neutral-900 dark:text-dark-1000" className="text-lg font-bold tracking-tight text-neutral-900 dark:text-dark-1000"
href="/" href="/"
> >
kan.bn kan.bn
</Link> </Link>
)}
{!IS_CLOUD && !HIDE_POWERED_BY && (
<a
href="https://kan.bn"
target="_blank"
rel="noreferrer noopener"
className="absolute right-[1rem] inline-flex items-center gap-[0.175rem] rounded-full border border-light-300 bg-light-50 px-3 py-1 text-[11px] font-medium text-light-950 shadow-sm transition-colors hover:bg-light-100 dark:border-dark-300 dark:bg-dark-50 dark:text-dark-900 dark:hover:bg-dark-100"
>
<span>{`Powered by`}</span>
<span className="font-semibold">kan.bn</span>
</a>
)}
</div> </div>
</div> </div>
<Popup /> <Popup />

View File

@@ -35,9 +35,6 @@ services:
- NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME} - NEXT_PUBLIC_AVATAR_BUCKET_NAME=${NEXT_PUBLIC_AVATAR_BUCKET_NAME}
- NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN} - NEXT_PUBLIC_STORAGE_DOMAIN=${NEXT_PUBLIC_STORAGE_DOMAIN}
# White label
- NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY=${NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY}
# Auth config (optional) # Auth config (optional)
- NEXT_PUBLIC_ALLOW_CREDENTIALS=${NEXT_PUBLIC_ALLOW_CREDENTIALS} - NEXT_PUBLIC_ALLOW_CREDENTIALS=${NEXT_PUBLIC_ALLOW_CREDENTIALS}
- NEXT_PUBLIC_DISABLE_SIGN_UP=${NEXT_PUBLIC_DISABLE_SIGN_UP} - NEXT_PUBLIC_DISABLE_SIGN_UP=${NEXT_PUBLIC_DISABLE_SIGN_UP}

View File

@@ -93,7 +93,6 @@
"SMTP_PASSWORD", "SMTP_PASSWORD",
"SMTP_SECURE", "SMTP_SECURE",
"NEXT_PUBLIC_KAN_ENV", "NEXT_PUBLIC_KAN_ENV",
"NEXT_PUBLIC_WHITE_LABEL_HIDE_POWERED_BY",
"STRIPE_SECRET_KEY", "STRIPE_SECRET_KEY",
"STRIPE_WEBHOOK_SECRET", "STRIPE_WEBHOOK_SECRET",
"STRIPE_PRO_PLAN_PRICE_ID", "STRIPE_PRO_PLAN_PRICE_ID",