chore: UI/UX improvements (#3319)

* chore: add proper message for cycle/ module having start & end date but isn't active yet.

* fix: infinite loader after updating workspace settings.

* fix: user profile icon dropdown doesn't closes automatically.

* style: fix inconsistent padding in cycle empty state.

* chore: remove multiple `empty state` in labels settings and improve add label logic.

* style: fix inconsistent padding in project label, integration and estimates empty state.

* style: fix integrations settings breadcrumb title.

* style: add proper `disabled` styles for email field in profile settings.

* style: fix cycle layout height.
This commit is contained in:
Prateek Shourya 2024-01-05 14:13:04 +05:30 committed by GitHub
parent d98b688342
commit 9dd8c8ba14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 48 additions and 55 deletions

View File

@ -43,7 +43,7 @@ export const NewEmptyState: React.FC<Props> = ({
return (
<div className="flex items-center justify-center overflow-y-auto">
<div className=" flex h-full w-full flex-col items-center justify-center ">
<div className="m-5 flex max-w-6xl flex-col gap-5 rounded-xl border border-custom-border-200 px-10 py-7 shadow-sm md:m-16">
<div className="m-5 flex max-w-6xl flex-col gap-5 rounded-xl border border-custom-border-200 px-10 py-7 shadow-sm md:m-8">
<h3 className="text-2xl font-semibold">{title}</h3>
{description && <p className=" text-lg">{description}</p>}
<div className="relative w-full max-w-6xl">

View File

@ -539,7 +539,9 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
<div className="flex items-center gap-1">
<AlertCircle height={14} width={14} className="text-custom-text-200" />
<span className="text-xs italic text-custom-text-200">
Invalid date. Please enter valid date.
{cycleDetails?.start_date && cycleDetails?.end_date
? "This cycle isn't active yet."
: "Invalid date. Please enter valid date."}
</span>
</div>
)}

View File

@ -102,7 +102,7 @@ export const EstimatesList: React.FC = observer(() => {
))}
</section>
) : (
<div className="h-full w-full overflow-y-auto">
<div className="w-full py-8">
<EmptyState
title="No estimates yet"
description="Estimates help you communicate the complexity of an issue."

View File

@ -96,9 +96,9 @@ export const ProjectSettingsLabelList: React.FC = observer(() => {
Add label
</Button>
</div>
<div className="w-full">
<div className="w-full py-8">
{showLabelForm && (
<div className="w-full rounded border border-custom-border-200 px-3.5 py-2">
<div className="w-full rounded border border-custom-border-200 px-3.5 py-2 my-2">
<CreateUpdateLabelInline
labelForm={showLabelForm}
setLabelForm={setLabelForm}
@ -112,7 +112,7 @@ export const ProjectSettingsLabelList: React.FC = observer(() => {
</div>
)}
{projectLabels ? (
projectLabels.length === 0 ? (
projectLabels.length === 0 && !showLabelForm ? (
<EmptyState
title="No labels yet"
description="Create labels to help organize and filter issues in you project"
@ -203,25 +203,14 @@ export const ProjectSettingsLabelList: React.FC = observer(() => {
)
)
) : (
!showLabelForm && (
<Loader className="space-y-5">
<Loader.Item height="42px" />
<Loader.Item height="42px" />
<Loader.Item height="42px" />
<Loader.Item height="42px" />
</Loader>
)}
{/* empty state */}
{projectLabels && projectLabels.length === 0 && (
<EmptyState
title="No labels yet"
description="Create labels to help organize and filter issues in your project."
image={emptyLabel}
primaryButton={{
text: "Add label",
onClick: () => newLabel(),
}}
/>
)
)}
</div>
</>

View File

@ -550,7 +550,9 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
<div className="flex items-center gap-1">
<AlertCircle height={14} width={14} className="text-custom-text-200" />
<span className="text-xs italic text-custom-text-200">
Invalid date. Please enter valid date.
{moduleDetails?.start_date && moduleDetails?.target_date
? "This module isn't active yet."
: "Invalid date. Please enter valid date."}
</span>
</div>
)}

View File

@ -278,14 +278,14 @@ export const WorkspaceSidebarDropdown = observer(() => {
<div className="flex flex-col gap-2.5 pb-2">
<span className="px-2 text-custom-sidebar-text-200">{currentUser?.email}</span>
{profileLinks(workspaceSlug?.toString() ?? "", currentUser?.id ?? "").map((link, index) => (
<Menu.Item key={index} as="button" type="button">
<Link href={link.link}>
<Link key={index} href={link.link}>
<Menu.Item key={index} as="div">
<span className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80">
<link.icon className="h-4 w-4 stroke-[1.5]" />
{link.name}
</span>
</Link>
</Menu.Item>
</Link>
))}
</div>
<div className={`pt-2 ${isUserInstanceAdmin ? "pb-2" : ""}`}>
@ -301,13 +301,13 @@ export const WorkspaceSidebarDropdown = observer(() => {
</div>
{isUserInstanceAdmin && (
<div className="p-2 pb-0">
<Menu.Item as="button" type="button" className="w-full">
<Link href="/god-mode">
<Menu.Item as="button" type="button" className="w-full">
<span className="flex w-full items-center justify-center rounded bg-custom-primary-100/20 px-2 py-1 text-sm font-medium text-custom-primary-100 hover:bg-custom-primary-100/30 hover:text-custom-primary-200">
Enter God Mode
</span>
</Link>
</Menu.Item>
</Link>
</div>
)}
</Menu.Items>

View File

@ -59,7 +59,7 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
if (!workspaceSlug || !projectId) return null;
return (
<>
<div className="w-full h-full">
<CycleCreateUpdateModal
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
@ -67,7 +67,7 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
handleClose={() => setCreateModal(false)}
/>
{totalCycles === 0 ? (
<div className="grid h-full place-items-center">
<div className="h-full place-items-center">
<NewEmptyState
title="Group and timebox your work in Cycles."
description="Break work down by timeboxed chunks, work backwards from your project deadline to set dates, and make tangible progress as a team."
@ -194,7 +194,7 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
</Tab.Panels>
</Tab.Group>
)}
</>
</div>
);
});

View File

@ -54,6 +54,7 @@ const ProjectIntegrationsPage: NextPageWithLayout = () => {
))}
</div>
) : (
<div className="w-full py-8">
<EmptyState
title="You haven't configured integrations"
description="Configure GitHub and other integrations to sync your project issues."
@ -64,6 +65,7 @@ const ProjectIntegrationsPage: NextPageWithLayout = () => {
}}
disabled={!isAdmin}
/>
</div>
)
) : (
<Loader className="space-y-5">

View File

@ -65,7 +65,7 @@ const WorkspaceIntegrationsPage: NextPageWithLayout = observer(() => {
WorkspaceIntegrationsPage.getLayout = function getLayout(page: ReactElement) {
return (
<AppLayout header={<WorkspaceSettingHeader title="Export Settings" />}>
<AppLayout header={<WorkspaceSettingHeader title="Integrations Settings" />}>
<WorkspaceSettingLayout>{page}</WorkspaceSettingLayout>
</AppLayout>
);

View File

@ -218,7 +218,7 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
<div className="grid grid-cols-1 gap-6 px-8 lg:grid-cols-2 2xl:grid-cols-3">
<div className="flex flex-col gap-1">
<h4 className="text-sm">
First name <span className="text-red-500">*</span>
First name<span className="text-red-500">*</span>
</h4>
<Controller
control={control}
@ -275,17 +275,16 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
rules={{
required: "Email is required.",
}}
render={({ field: { value, onChange, ref } }) => (
render={({ field: { value, ref } }) => (
<Input
id="email"
name="email"
type="email"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.email)}
placeholder="Enter your email"
className={`w-full rounded-md ${errors.email ? "border-red-500" : ""}`}
className={`w-full rounded-md cursor-not-allowed !bg-custom-background-80 ${errors.email ? "border-red-500" : ""}`}
disabled
/>
)}

View File

@ -14,10 +14,9 @@ import {
IWorkspaceBulkInviteFormData,
IWorkspaceViewProps,
IUserProjectsRole,
TIssueMap,
TIssue,
IWorkspaceView,
} from "@plane/types";
import { IWorkspaceView } from "@plane/types";
export class WorkspaceService extends APIService {
constructor() {

View File

@ -135,7 +135,7 @@ export class WorkspaceRootStore implements IWorkspaceRootStore {
updateWorkspace = async (workspaceSlug: string, data: Partial<IWorkspace>) =>
await this.workspaceService.updateWorkspace(workspaceSlug, data).then((response) => {
runInAction(() => {
set(this.workspaces, response.id, data);
set(this.workspaces, response.id, response);
});
return response;
});