Fix: bug fixes and UI / UX improvements (#2906)

* Fix: issue with project publish modal data not updating immediately.

* fix: issue with workspace list not scrollable in profile settings.

* fix: update redirect workspace slug logic to redirect to prev workspace instead of `/`.

* style: update API tokens and webhooks empty state designs.
This commit is contained in:
Prateek Shourya 2023-11-28 11:29:01 +05:30 committed by sriram veeraghanta
parent d84e043c93
commit c4fb543372
6 changed files with 83 additions and 33 deletions

View File

@ -14,7 +14,7 @@ export const ApiTokenEmptyState: React.FC<Props> = (props) => {
return (
<div
className={`flex items-center justify-center mx-auto rounded-sm border border-custom-border-200 bg-custom-background-90 py-10 px-16 w-full`}
className={`flex items-center justify-center mx-auto rounded-sm border border-custom-border-200 bg-custom-background-90 py-10 px-16 w-full lg:w-3/4`}
>
<div className="text-center flex flex-col items-center w-full">
<Image src={emptyApiTokens} className="w-52 sm:w-60" alt="empty" />

View File

@ -119,7 +119,7 @@ export const PublishProjectModal: React.FC<Props> = observer((props) => {
reset({ ...updatedData });
}
}, [reset, projectPublishStore.projectPublishSettings]);
}, [reset, projectPublishStore.projectPublishSettings, isOpen]);
// fetch publish settings
useEffect(() => {

View File

@ -11,7 +11,7 @@ export const WebhooksEmptyState = () => {
return (
<div
className={`flex items-center justify-center mx-auto rounded-sm border border-custom-border-200 bg-custom-background-90 py-10 px-16 w-full`}
className={`flex items-center justify-center mx-auto rounded-sm border border-custom-border-200 bg-custom-background-90 py-10 px-16 w-full lg:w-3/4`}
>
<div className="text-center flex flex-col items-center w-full">
<Image src={EmptyWebhook} className="w-52 sm:w-60" alt="empty" />

View File

@ -1,16 +1,18 @@
import { Fragment, useEffect, useRef, useState } from "react";
import { mutate } from "swr";
import Link from "next/link";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { useTheme } from "next-themes";
import { Menu, Transition } from "@headlessui/react";
// icons
import { LogIn, LogOut, MoveLeft, Plus, User, UserPlus } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// ui
import { Avatar, Tooltip } from "@plane/ui";
import { Fragment } from "react";
import { mutate } from "swr";
import { useRouter } from "next/router";
// hooks
import useToast from "hooks/use-toast";
import { useTheme } from "next-themes";
const SIDEBAR_LINKS = [
{
@ -28,6 +30,11 @@ const SIDEBAR_LINKS = [
];
export const ProfileLayoutSidebar = observer(() => {
// states
const [isScrolled, setIsScrolled] = useState(false); // scroll animation state
// refs
const containerRef = useRef<HTMLDivElement | null>(null);
const router = useRouter();
const { setTheme } = useTheme();
@ -62,6 +69,27 @@ export const ProfileLayoutSidebar = observer(() => {
);
};
/**
* Implementing scroll animation styles based on the scroll length of the container
*/
useEffect(() => {
const handleScroll = () => {
if (containerRef.current) {
const scrollTop = containerRef.current.scrollTop;
setIsScrolled(scrollTop > 0);
}
};
const currentContainerRef = containerRef.current;
if (currentContainerRef) {
currentContainerRef.addEventListener("scroll", handleScroll);
}
return () => {
if (currentContainerRef) {
currentContainerRef.removeEventListener("scroll", handleScroll);
}
};
}, []);
return (
<div
className={`fixed md:relative inset-y-0 flex flex-col bg-custom-sidebar-background-100 h-full flex-shrink-0 flex-grow-0 border-r border-custom-sidebar-border-200 z-20 duration-300 ${
@ -166,11 +194,16 @@ export const ProfileLayoutSidebar = observer(() => {
))}
</div>
{workspaces && workspaces.length > 0 && (
<div className="flex flex-col px-4 flex-shrink-0">
<div className="flex flex-col h-full overflow-x-hidden px-4">
{!sidebarCollapsed && (
<div className="rounded text-custom-sidebar-text-400 px-1.5 text-sm font-semibold">Your workspaces</div>
)}
<div className="space-y-2 mt-2">
<div
ref={containerRef}
className={`space-y-2 mt-2 pt-2 h-full overflow-y-auto ${
isScrolled ? "border-t border-custom-sidebar-border-300" : ""
}`}
>
{workspaces.map((workspace) => (
<Link
key={workspace.id}
@ -208,7 +241,7 @@ export const ProfileLayoutSidebar = observer(() => {
</div>
</div>
)}
<div className="flex-grow flex items-end px-4 py-2">
<div className="flex-grow flex items-end px-4 py-2 border-t border-custom-border-200">
<button
type="button"
className="grid place-items-center rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none md:hidden"

View File

@ -48,25 +48,27 @@ const ApiTokensPage: NextPageWithLayout = observer(() => {
<>
<CreateApiTokenModal isOpen={isCreateTokenModalOpen} onClose={() => setIsCreateTokenModalOpen(false)} />
{tokens ? (
tokens.length > 0 ? (
<section className="pr-9 py-8 w-full overflow-y-auto">
<div className="flex items-center justify-between py-3.5 border-b border-custom-border-200 mb-2">
<h3 className="text-xl font-medium">API tokens</h3>
<Button variant="primary" onClick={() => setIsCreateTokenModalOpen(true)}>
Add API token
</Button>
<section className="pr-9 py-8 w-full overflow-y-auto">
{tokens.length > 0 ? (
<>
<div className="flex items-center justify-between py-3.5 border-b border-custom-border-200 mb-2">
<h3 className="text-xl font-medium">API tokens</h3>
<Button variant="primary" onClick={() => setIsCreateTokenModalOpen(true)}>
Add API token
</Button>
</div>
<div>
{tokens.map((token) => (
<ApiTokenListItem key={token.id} token={token} />
))}
</div>
</>
) : (
<div className="mx-auto">
<ApiTokenEmptyState onClick={() => setIsCreateTokenModalOpen(true)} />
</div>
<div>
{tokens.map((token) => (
<ApiTokenListItem key={token.id} token={token} />
))}
</div>
</section>
) : (
<div className="mx-auto py-8">
<ApiTokenEmptyState onClick={() => setIsCreateTokenModalOpen(true)} />
</div>
)
)}
</section>
) : (
<div className="h-full w-full grid place-items-center p-4">
<Spinner />

View File

@ -4,6 +4,7 @@ import Image from "next/image";
import { useRouter } from "next/router";
import useSWR, { mutate } from "swr";
import { useTheme } from "next-themes";
import { observer } from "mobx-react-lite";
// services
import { WorkspaceService } from "services/workspace.service";
import { UserService } from "services/user.service";
@ -30,15 +31,23 @@ import type { IWorkspaceMemberInvitation } from "types";
import { ROLE } from "constants/workspace";
// components
import { EmptyState } from "components/common";
// mobx-store
import { useMobxStore } from "lib/mobx/store-provider";
// services
const workspaceService = new WorkspaceService();
const userService = new UserService();
const UserInvitationsPage: NextPageWithLayout = () => {
const UserInvitationsPage: NextPageWithLayout = observer(() => {
const [invitationsRespond, setInvitationsRespond] = useState<string[]>([]);
const [isJoiningWorkspaces, setIsJoiningWorkspaces] = useState(false);
// store
const {
workspace: { workspaceSlug },
user: { currentUserSettings },
} = useMobxStore();
const router = useRouter();
const { theme } = useTheme();
@ -51,6 +60,12 @@ const UserInvitationsPage: NextPageWithLayout = () => {
workspaceService.userWorkspaceInvitations()
);
const redirectWorkspaceSlug =
workspaceSlug ||
currentUserSettings?.workspace?.last_workspace_slug ||
currentUserSettings?.workspace?.fallback_workspace_slug ||
"";
const handleInvitation = (workspace_invitation: IWorkspaceMemberInvitation, action: "accepted" | "withdraw") => {
if (action === "accepted") {
setInvitationsRespond((prevData) => [...prevData, workspace_invitation.id]);
@ -180,7 +195,7 @@ const UserInvitationsPage: NextPageWithLayout = () => {
>
Accept & Join
</Button>
<Link href="/">
<Link href={`/${redirectWorkspaceSlug}`}>
<a>
<Button variant="neutral-primary" size="md">
Go Home
@ -197,8 +212,8 @@ const UserInvitationsPage: NextPageWithLayout = () => {
description="You can see here if someone invites you to a workspace."
image={emptyInvitation}
primaryButton={{
text: "Back to Dashboard",
onClick: () => router.push("/"),
text: "Back to dashboard",
onClick: () => router.push(`/${redirectWorkspaceSlug}`),
}}
/>
</div>
@ -206,7 +221,7 @@ const UserInvitationsPage: NextPageWithLayout = () => {
) : null}
</div>
);
};
});
UserInvitationsPage.getLayout = function getLayout(page: ReactElement) {
return (