chore: space ui component revamp and bug fixes (#2980)

* chore: replace space ui component with plane ui component

* fix: space project icon and user pic bug

* chore: code refactor

* fix: profile section navbar fix
This commit is contained in:
Anmol Singh Bhatia 2023-12-05 16:07:25 +05:30 committed by Aaryan Khandelwal
parent 6e834ada66
commit a4d231b39a
18 changed files with 62 additions and 262 deletions

View File

@ -11,7 +11,7 @@ import useToast from "hooks/use-toast";
import useTimer from "hooks/use-timer";
// ui
import { Input, PrimaryButton } from "components/ui";
import { Button, Input } from "@plane/ui";
// types
type EmailCodeFormValues = {
@ -133,7 +133,7 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
id="email"
type="email"
placeholder="Enter your email address..."
className="border-custom-border-300 h-[46px]"
className="border-custom-border-300 h-[46px] w-full"
{...register("email", {
required: "Email address is required",
validate: (value) =>
@ -154,7 +154,7 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
required: "Code is required",
})}
placeholder="Enter code..."
className="border-custom-border-300 h-[46px]"
className="border-custom-border-300 h-[46px] w-full"
/>
{errors.token && <div className="text-sm text-red-500">{errors.token.message}</div>}
<button
@ -185,20 +185,22 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
</>
)}
{codeSent ? (
<PrimaryButton
<Button
variant="primary"
type="submit"
className="w-full text-center h-[46px]"
size="md"
className="w-full"
size="xl"
onClick={handleSubmit(handleSignin)}
disabled={!isValid && isDirty}
loading={isLoading}
>
{isLoading ? "Signing in..." : "Sign in"}
</PrimaryButton>
</Button>
) : (
<PrimaryButton
className="w-full text-center h-[46px]"
size="md"
<Button
variant="primary"
className="w-full"
size="xl"
onClick={() => {
handleSubmit(onSubmit)().then(() => {
setResendCodeTimer(30);
@ -208,7 +210,7 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
loading={isSubmitting}
>
{isSubmitting ? "Sending code..." : "Send sign in code"}
</PrimaryButton>
</Button>
)}
</form>
</>

View File

@ -5,7 +5,8 @@ import { useForm } from "react-hook-form";
// components
import { EmailResetPasswordForm } from "./email-reset-password-form";
// ui
import { Input, PrimaryButton } from "components/ui";
import { Button, Input } from "@plane/ui";
// types
type EmailPasswordFormValues = {
email: string;
@ -58,7 +59,7 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
) || "Email address is not valid",
})}
placeholder="Enter your email address..."
className="border-custom-border-300 h-[46px]"
className="border-custom-border-300 h-[46px] w-full"
/>
{errors.email && <div className="text-sm text-red-500">{errors.email.message}</div>}
</div>
@ -70,7 +71,7 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
required: "Password is required",
})}
placeholder="Enter your password..."
className="border-custom-border-300 h-[46px]"
className="border-custom-border-300 h-[46px] w-full"
/>
{errors.password && <div className="text-sm text-red-500">{errors.password.message}</div>}
</div>
@ -92,14 +93,16 @@ export const EmailPasswordForm: React.FC<Props> = ({ onSubmit }) => {
)}
</div>
<div>
<PrimaryButton
<Button
variant="primary"
type="submit"
className="w-full text-center h-[46px]"
size="xl"
className="w-full"
disabled={!isValid && isDirty}
loading={isSubmitting}
>
{isSignUpPage ? (isSubmitting ? "Signing up..." : "Sign up") : isSubmitting ? "Signing in..." : "Sign in"}
</PrimaryButton>
</Button>
{!isSignUpPage && (
<Link href="/sign-up">
<span className="block text-custom-text-200 hover:text-custom-primary-100 text-xs mt-4">

View File

@ -1,8 +1,7 @@
import React from "react";
import { useForm } from "react-hook-form";
// ui
import { Input } from "components/ui";
import { Button } from "@plane/ui";
import { Button, Input } from "@plane/ui";
// types
type Props = {
setIsResettingPassword: React.Dispatch<React.SetStateAction<boolean>>;
@ -66,15 +65,15 @@ export const EmailResetPasswordForm: React.FC<Props> = ({ setIsResettingPassword
) || "Email address is not valid",
})}
placeholder="Enter registered email address.."
className="h-[46px] border-custom-border-300"
className="h-[46px] border-custom-border-300 w-full"
/>
{errors.email && <div className="text-sm text-red-500">{errors.email.message}</div>}
</div>
<div className="mt-5 flex flex-col-reverse items-center gap-2 sm:flex-row">
<Button variant="neutral-primary" className="w-full" onClick={() => setIsResettingPassword(false)}>
<Button variant="neutral-primary" className="w-full" size="xl" onClick={() => setIsResettingPassword(false)}>
Go Back
</Button>
<Button variant="primary" className="w-full" type="submit" loading={isSubmitting}>
<Button variant="primary" className="w-full" size="xl" type="submit" loading={isSubmitting}>
{isSubmitting ? "Sending link..." : "Send reset link"}
</Button>
</div>

View File

@ -13,7 +13,7 @@ import useToast from "hooks/use-toast";
// services
import UserService from "services/user.service";
// ui
import { Input, PrimaryButton } from "components/ui";
import { Button, Input } from "@plane/ui";
const defaultValues = {
first_name: "",
@ -173,9 +173,9 @@ export const OnBoardingForm: React.FC<Props> = observer(({ user }) => {
</div>
</div>
<PrimaryButton type="submit" size="md" disabled={!isValid} loading={isSubmitting}>
<Button variant="primary" type="submit" className="w-full" size="xl" disabled={!isValid} loading={isSubmitting}>
{isSubmitting ? "Updating..." : "Continue"}
</PrimaryButton>
</Button>
</form>
);
});

View File

@ -1,7 +1,6 @@
import { useEffect } from "react";
import Link from "next/link";
import Image from "next/image";
import { useRouter } from "next/router";
// mobx
@ -11,7 +10,8 @@ import { observer } from "mobx-react-lite";
import { NavbarIssueBoardView } from "./issue-board-view";
import { NavbarTheme } from "./theme";
// ui
import { PrimaryButton } from "components/ui";
import { Avatar, Button } from "@plane/ui";
import { Briefcase } from "lucide-react";
// lib
import { useMobxStore } from "lib/mobx/store-provider";
// store
@ -87,10 +87,24 @@ const IssueNavbar = observer(() => {
{/* project detail */}
<div className="flex flex-shrink-0 items-center gap-2">
<div className="flex h-4 w-4 items-center justify-center">
{projectStore?.project && projectStore?.project?.emoji ? (
renderEmoji(projectStore?.project?.emoji)
{projectStore.project ? (
projectStore.project?.emoji ? (
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
{renderEmoji(projectStore.project.emoji)}
</span>
) : projectStore.project?.icon_prop ? (
<div className="h-7 w-7 flex-shrink-0 grid place-items-center">
{renderEmoji(projectStore.project.icon_prop)}
</div>
) : (
<Image src="/plane-logo.webp" alt="plane logo" className="h-[24px] w-[24px]" height="24" width="24" />
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
{projectStore.project?.name.charAt(0)}
</span>
)
) : (
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
<Briefcase className="h-4 w-4" />
</span>
)}
</div>
<div className="line-clamp-1 max-w-[300px] overflow-hidden text-lg font-medium">
@ -113,26 +127,13 @@ const IssueNavbar = observer(() => {
{user ? (
<div className="flex items-center gap-2 rounded border border-custom-border-200 p-2">
{user.avatar && user.avatar !== "" ? (
<div className="h-5 w-5 rounded-full">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={user.avatar} alt={user.display_name ?? ""} className="rounded-full" />
</div>
) : (
<div className="grid h-5 w-5 place-items-center rounded-full bg-custom-background-80 text-[10px] capitalize">
{(user.display_name ?? "A")[0]}
</div>
)}
<Avatar name={user?.display_name} src={user?.avatar} size={24} shape="square" className="!text-base" />
<h6 className="text-xs font-medium">{user.display_name}</h6>
</div>
) : (
<div className="flex-shrink-0">
<Link href={`/login/?next_path=${router.asPath}`}>
<span>
<PrimaryButton className="flex-shrink-0" outline>
Sign in
</PrimaryButton>
</span>
<Button variant="outline-primary">Sign in</Button>
</Link>
</div>
)}

View File

@ -6,7 +6,8 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
// ui
import { ReactionSelector, Tooltip } from "components/ui";
import { ReactionSelector } from "components/ui";
import { Tooltip } from "@plane/ui";
// helpers
import { groupReactions, renderEmoji } from "helpers/emoji.helper";

View File

@ -7,7 +7,7 @@ import {
PeekOverviewIssueProperties,
} from "components/issues/peek-overview";
// types
import { Loader } from "components/ui/loader";
import { Loader } from "@plane/ui";
import { IIssue } from "types/issue";
type Props = {

View File

@ -10,7 +10,8 @@ import { useMobxStore } from "lib/mobx/store-provider";
// components
import { CommentCard, AddComment } from "components/issues/peek-overview";
// ui
import { Icon, PrimaryButton } from "components/ui";
import { Icon } from "components/ui";
import { Button } from "@plane/ui";
// types
import { IIssue } from "types/issue";
@ -55,9 +56,7 @@ export const PeekOverviewIssueActivity: React.FC<Props> = observer(() => {
Sign in to add your comment
</p>
<Link href={`/?next_path=${router.asPath}`}>
<span>
<PrimaryButton className="flex-shrink-0 !px-7">Sign in</PrimaryButton>
</span>
<Button variant="primary">Sign in</Button>
</Link>
</div>
)}

View File

@ -6,7 +6,8 @@ import { useMobxStore } from "lib/mobx/store-provider";
// helpers
import { groupReactions, renderEmoji } from "helpers/emoji.helper";
// components
import { ReactionSelector, Tooltip } from "components/ui";
import { ReactionSelector } from "components/ui";
import { Tooltip } from "@plane/ui";
export const IssueEmojiReactions: React.FC = observer(() => {
// router

View File

@ -6,7 +6,8 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// lib
import { useMobxStore } from "lib/mobx/store-provider";
import { Tooltip } from "components/ui";
// ui
import { Tooltip } from "@plane/ui";
export const IssueVotes: React.FC = observer(() => {
const [isSubmitting, setIsSubmitting] = useState(false);

View File

@ -7,7 +7,7 @@ import {
PeekOverviewIssueProperties,
} from "components/issues/peek-overview";
import { Loader } from "components/ui/loader";
import { Loader } from "@plane/ui";
import { IIssue } from "types/issue";
type Props = {

View File

@ -1,8 +1,3 @@
export * from "./dropdown";
export * from "./input";
export * from "./loader";
export * from "./primary-button";
export * from "./secondary-button";
export * from "./icon";
export * from "./reaction-selector";
export * from "./tooltip";

View File

@ -1,37 +0,0 @@
import React, { forwardRef, Ref } from "react";
// types
interface Props extends React.InputHTMLAttributes<HTMLInputElement> {
mode?: "primary" | "transparent" | "trueTransparent";
error?: boolean;
inputSize?: "rg" | "lg";
fullWidth?: boolean;
}
export const Input = forwardRef((props: Props, ref: Ref<HTMLInputElement>) => {
const { mode = "primary", error, className = "", type, fullWidth = true, id, inputSize = "rg", ...rest } = props;
return (
<input
id={id}
ref={ref}
type={type}
className={`block rounded-md bg-transparent text-sm focus:outline-none placeholder-custom-text-400 ${
mode === "primary"
? "rounded-md border border-custom-border-200"
: mode === "transparent"
? "rounded border-none bg-transparent ring-0 transition-all focus:ring-1 focus:ring-custom-primary"
: mode === "trueTransparent"
? "rounded border-none bg-transparent ring-0"
: ""
} ${error ? "border-red-500" : ""} ${error && mode === "primary" ? "bg-red-500/20" : ""} ${
fullWidth ? "w-full" : ""
} ${inputSize === "rg" ? "px-3 py-2" : inputSize === "lg" ? "p-3" : ""} ${className}`}
{...rest}
/>
);
});
Input.displayName = "Input";
export default Input;

View File

@ -1,25 +0,0 @@
import React from "react";
type Props = {
children: React.ReactNode;
className?: string;
};
const Loader = ({ children, className = "" }: Props) => (
<div className={`${className} animate-pulse`} role="status">
{children}
</div>
);
type ItemProps = {
height?: string;
width?: string;
};
const Item: React.FC<ItemProps> = ({ height = "auto", width = "auto" }) => (
<div className="rounded-md bg-custom-background-80" style={{ height: height, width: width }} />
);
Loader.Item = Item;
export { Loader };

View File

@ -1,35 +0,0 @@
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
size?: "sm" | "md" | "lg";
outline?: boolean;
loading?: boolean;
}
export const PrimaryButton: React.FC<ButtonProps> = ({
children,
className = "",
onClick,
type = "button",
disabled = false,
loading = false,
size = "sm",
outline = false,
}) => (
<button
type={type}
className={`${className} border border-custom-primary font-medium duration-300 ${
size === "sm"
? "rounded px-3 py-2 text-xs"
: size === "md"
? "rounded-md px-3.5 py-2 text-sm"
: "rounded-lg px-4 py-2 text-base"
} ${disabled ? "cursor-not-allowed opacity-70 hover:opacity-70" : ""} ${
outline
? "bg-transparent text-custom-primary hover:bg-custom-primary hover:text-white"
: "text-white bg-custom-primary hover:border-opacity-90 hover:bg-opacity-90"
} ${loading ? "cursor-wait" : ""}`}
onClick={onClick}
disabled={disabled || loading}
>
{children}
</button>
);

View File

@ -1,35 +0,0 @@
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
size?: "sm" | "md" | "lg";
outline?: boolean;
loading?: boolean;
}
export const SecondaryButton: React.FC<ButtonProps> = ({
children,
className = "",
onClick,
type = "button",
disabled = false,
loading = false,
size = "sm",
outline = false,
}) => (
<button
type={type}
className={`${className} border border-custom-border-200 font-medium duration-300 ${
size === "sm"
? "rounded px-3 py-2 text-xs"
: size === "md"
? "rounded-md px-3.5 py-2 text-sm"
: "rounded-lg px-4 py-2 text-base"
} ${disabled ? "cursor-not-allowed border-custom-border-200 bg-custom-background-90" : ""} ${
outline
? "bg-transparent hover:bg-custom-background-80"
: "bg-custom-background-100 hover:border-opacity-70 hover:bg-opacity-70"
} ${loading ? "cursor-wait" : ""}`}
onClick={onClick}
disabled={disabled || loading}
>
{children}
</button>
);

View File

@ -1,70 +0,0 @@
import React from "react";
// next-themes
import { useTheme } from "next-themes";
// tooltip2
import { Tooltip2 } from "@blueprintjs/popover2";
type Props = {
tooltipHeading?: string;
tooltipContent: string | React.ReactNode;
position?:
| "top"
| "right"
| "bottom"
| "left"
| "auto"
| "auto-end"
| "auto-start"
| "bottom-left"
| "bottom-right"
| "left-bottom"
| "left-top"
| "right-bottom"
| "right-top"
| "top-left"
| "top-right";
children: JSX.Element;
disabled?: boolean;
className?: string;
openDelay?: number;
closeDelay?: number;
};
export const Tooltip: React.FC<Props> = ({
tooltipHeading,
tooltipContent,
position = "top",
children,
disabled = false,
className = "",
openDelay = 200,
closeDelay,
}) => {
const { theme } = useTheme();
return (
<Tooltip2
disabled={disabled}
hoverOpenDelay={openDelay}
hoverCloseDelay={closeDelay}
content={
<div
className={`relative z-50 max-w-xs gap-1 rounded-md border border-custom-border-200 p-2 text-xs shadow-md ${
theme === "custom" ? "bg-custom-background-100 text-custom-text-200" : "bg-black text-gray-400"
} overflow-hidden break-words ${className}`}
>
{tooltipHeading && (
<h5 className={`font-medium ${theme === "custom" ? "text-custom-text-100" : "text-white"}`}>
{tooltipHeading}
</h5>
)}
{tooltipContent}
</div>
}
position={position}
renderTarget={({ isOpen: isTooltipOpen, ref: eleReference, ...tooltipProps }) =>
React.cloneElement(children, { ref: eleReference, ...tooltipProps, ...children.props })
}
/>
);
};

View File

@ -51,7 +51,7 @@ export const ProfileNavbar: React.FC<Props> = (props) => {
{tabsList.map((tab) => (
<Link key={tab.route} href={`/${workspaceSlug}/profile/${userId}/${tab.route}`}>
<span
className={`border-b-2 p-4 text-sm font-medium outline-none whitespace-nowrap ${
className={`flex border-b-2 p-4 text-sm font-medium outline-none whitespace-nowrap ${
router.pathname === tab.selected
? "border-custom-primary-100 text-custom-primary-100"
: "border-transparent"