feat: add button to view/update board public URL from board page (#107)
* feat: add update board slug button * feat: add edit workspace url button * feat: add loading state to update board slug button * chore: add translations
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import Link from "next/link";
|
||||
import { env } from "next-runtime-env";
|
||||
import { HiLink } from "react-icons/hi";
|
||||
|
||||
const UpdateBoardSlugButton = ({
|
||||
handleOnClick,
|
||||
workspaceSlug,
|
||||
boardSlug,
|
||||
isLoading,
|
||||
}: {
|
||||
handleOnClick: () => void;
|
||||
workspaceSlug: string;
|
||||
boardSlug: string;
|
||||
isLoading: boolean;
|
||||
}) => {
|
||||
if (!isLoading && (!workspaceSlug || !boardSlug)) return <></>;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="hidden h-[36px] w-[225px] animate-pulse rounded-full bg-light-200 dark:bg-dark-100 xl:flex" />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={handleOnClick}
|
||||
className="hidden cursor-pointer items-center gap-2 rounded-full border-[1px] bg-light-50 p-1 pl-4 pr-1 text-sm text-light-950 hover:bg-light-100 dark:border-dark-600 dark:bg-dark-50 dark:text-dark-900 dark:hover:bg-dark-100 xl:flex"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<span>
|
||||
{env("NEXT_PUBLIC_KAN_ENV") === "cloud"
|
||||
? "kan.bn"
|
||||
: env("NEXT_PUBLIC_BASE_URL")}
|
||||
</span>
|
||||
<div className="mx-1.5 h-4 w-px rotate-[20deg] bg-gray-300 dark:bg-dark-600"></div>
|
||||
<span>{workspaceSlug}</span>
|
||||
<div className="mx-1.5 h-4 w-px rotate-[20deg] bg-gray-300 dark:bg-dark-600"></div>
|
||||
<span>{boardSlug}</span>
|
||||
</div>
|
||||
<Link
|
||||
href={`${env("NEXT_PUBLIC_BASE_URL")}/${workspaceSlug}/${boardSlug}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="flex h-7 w-7 items-center justify-center rounded-full hover:bg-light-200 dark:hover:bg-dark-200"
|
||||
>
|
||||
<HiLink className="h-[13px] w-[13px]" />
|
||||
</Link>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpdateBoardSlugButton;
|
||||
@@ -135,7 +135,7 @@ export function UpdateBoardSlugForm({
|
||||
? t`This board URL has already been taken`
|
||||
: undefined)
|
||||
}
|
||||
prefix={`${env("NEXT_PUBLIC_BASE_URL")}/${workspaceSlug}/`}
|
||||
prefix={`${env("NEXT_PUBLIC_KAN_ENV") === "cloud" ? "kan.bn" : env("NEXT_PUBLIC_BASE_URL")}/${workspaceSlug}/`}
|
||||
onKeyDown={async (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
@@ -152,7 +152,14 @@ export function UpdateBoardSlugForm({
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
href="/settings?edit=workspace_url"
|
||||
onClick={closeModal}
|
||||
>
|
||||
{t`Edit workspace URL`}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
isLoading={updateBoardSlug.isPending}
|
||||
|
||||
@@ -32,6 +32,7 @@ import Filters from "./components/Filters";
|
||||
import List from "./components/List";
|
||||
import { NewCardForm } from "./components/NewCardForm";
|
||||
import { NewListForm } from "./components/NewListForm";
|
||||
import UpdateBoardSlugButton from "./components/UpdateBoardSlugButton";
|
||||
import { UpdateBoardSlugForm } from "./components/UpdateBoardSlugForm";
|
||||
import VisibilityButton from "./components/VisibilityButton";
|
||||
|
||||
@@ -267,6 +268,12 @@ export default function BoardPage() {
|
||||
)}
|
||||
|
||||
<div className="order-1 mb-4 flex items-center justify-end space-x-2 md:order-2 md:mb-0">
|
||||
<UpdateBoardSlugButton
|
||||
handleOnClick={() => openModal("UPDATE_BOARD_SLUG")}
|
||||
isLoading={isLoading}
|
||||
workspaceSlug={workspace.slug ?? ""}
|
||||
boardSlug={boardData?.slug ?? ""}
|
||||
/>
|
||||
<VisibilityButton
|
||||
visibility={boardData?.visibility ?? "private"}
|
||||
boardPublicId={boardId ?? ""}
|
||||
|
||||
@@ -105,13 +105,15 @@ export default function PublicBoardView() {
|
||||
{data?.name}
|
||||
</h1>
|
||||
)}
|
||||
<div className="flex items-center space-x-2">
|
||||
<Filters
|
||||
labels={data?.labels ?? []}
|
||||
members={[]}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</div>
|
||||
{data && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Filters
|
||||
labels={data.labels ?? []}
|
||||
members={[]}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="scrollbar-w-none scrollbar-track-rounded-[4px] scrollbar-thumb-rounded-[4px] scrollbar-h-[8px] relative flex-1 overflow-y-hidden overflow-x-scroll overscroll-contain scrollbar scrollbar-track-light-200 scrollbar-thumb-light-400 dark:scrollbar-track-dark-100 dark:scrollbar-thumb-dark-300">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { env } from "next-runtime-env";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { HiMiniArrowTopRightOnSquare } from "react-icons/hi2";
|
||||
|
||||
import Button from "~/components/Button";
|
||||
@@ -27,6 +28,9 @@ export default function SettingsPage() {
|
||||
const { workspace } = useWorkspace();
|
||||
const utils = api.useUtils();
|
||||
const { showPopup } = usePopup();
|
||||
const router = useRouter();
|
||||
const workspaceUrlSectionRef = useRef<HTMLDivElement>(null);
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { data } = api.user.getUser.useQuery();
|
||||
|
||||
@@ -59,6 +63,24 @@ export default function SettingsPage() {
|
||||
};
|
||||
}, [refetchIntegrations]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
router.query.edit === "workspace_url" &&
|
||||
workspaceUrlSectionRef.current &&
|
||||
scrollContainerRef.current
|
||||
) {
|
||||
const element = workspaceUrlSectionRef.current;
|
||||
const container = scrollContainerRef.current;
|
||||
|
||||
container.scrollTop = element.offsetTop - 40;
|
||||
|
||||
const input = element.querySelector('input[type="text"]');
|
||||
if (input instanceof HTMLInputElement) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
}, [router.query.edit]);
|
||||
|
||||
const { mutateAsync: disconnectTrello } =
|
||||
api.integration.disconnect.useMutation({
|
||||
onSuccess: () => {
|
||||
@@ -104,7 +126,10 @@ export default function SettingsPage() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-full w-full flex-col overflow-hidden">
|
||||
<div className="h-full max-h-[calc(100vdh-3rem)] overflow-y-auto md:max-h-[calc(100vdh-4rem)]">
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
className="h-full max-h-[calc(100vdh-3rem)] overflow-y-auto md:max-h-[calc(100vdh-4rem)]"
|
||||
>
|
||||
<PageHead title={t`Settings | ${workspace.name ?? "Workspace"}`} />
|
||||
<div className="m-auto max-w-[1600px] px-5 py-6 md:px-28 md:py-12">
|
||||
<div className="mb-8 flex w-full justify-between">
|
||||
@@ -134,14 +159,16 @@ export default function SettingsPage() {
|
||||
workspaceName={workspace.name}
|
||||
/>
|
||||
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Workspace URL`}
|
||||
</h2>
|
||||
<UpdateWorkspaceUrlForm
|
||||
workspacePublicId={workspace.publicId}
|
||||
workspaceUrl={workspace.slug ?? ""}
|
||||
workspacePlan={workspace.plan ?? "free"}
|
||||
/>
|
||||
<div ref={workspaceUrlSectionRef}>
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Workspace URL`}
|
||||
</h2>
|
||||
<UpdateWorkspaceUrlForm
|
||||
workspacePublicId={workspace.publicId}
|
||||
workspaceUrl={workspace.slug ?? ""}
|
||||
workspacePlan={workspace.plan ?? "free"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h2 className="mb-4 mt-8 text-[14px] text-neutral-900 dark:text-dark-1000">
|
||||
{t`Workspace description`}
|
||||
|
||||
Reference in New Issue
Block a user