feat: loading states update (#3639)

* dev: implement layout skeleton loader and helper function

* chore: implemented layout loader

* chore: settings loader added

* chore: cycle, module, view, pages, notification and projects loader added

* chore: kanban loader improvement

* chore: loader utils updated
This commit is contained in:
Anmol Singh Bhatia 2024-02-13 19:12:10 +05:30 committed by GitHub
parent 83139989c2
commit b1989bae1b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 951 additions and 507 deletions

View file

@ -13,7 +13,8 @@ import { CyclesHeader } from "components/headers";
import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// ui
import { Spinner, Tooltip } from "@plane/ui";
import { Tooltip } from "@plane/ui";
import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "components/ui";
// types
import { TCycleView, TCycleLayout } from "@plane/types";
import { NextPageWithLayout } from "lib/types";
@ -66,9 +67,11 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
if (loader)
return (
<div className="flex items-center justify-center h-full w-full">
<Spinner />
</div>
<>
{cycleLayout === "list" && <CycleModuleListLayout />}
{cycleLayout === "board" && <CycleModuleBoardLayout />}
{cycleLayout === "gantt" && <GanttLayoutLoader />}
</>
);
return (

View file

@ -16,7 +16,7 @@ import { AppLayout } from "layouts/app-layout";
import { RecentPagesList, CreateUpdatePageModal } from "components/pages";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
import { PagesHeader } from "components/headers";
import { Spinner } from "@plane/ui";
import { PagesLoader } from "components/ui";
// types
import { NextPageWithLayout } from "lib/types";
// constants
@ -125,12 +125,7 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
</Tab.List>
);
if (loader || archivedPageLoader)
return (
<div className="flex items-center justify-center h-full w-full">
<Spinner />
</div>
);
if (loader || archivedPageLoader) return <PagesLoader />;
return (
<>

View file

@ -15,7 +15,7 @@ import { IntegrationCard } from "components/project";
import { ProjectSettingHeader } from "components/headers";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// ui
import { Loader } from "@plane/ui";
import { IntegrationsSettingsLoader } from "components/ui";
// types
import { IProject } from "@plane/types";
import { NextPageWithLayout } from "lib/types";
@ -79,12 +79,7 @@ const ProjectIntegrationsPage: NextPageWithLayout = () => {
</div>
)
) : (
<Loader className="space-y-5">
<Loader.Item height="40px" />
<Loader.Item height="40px" />
<Loader.Item height="40px" />
<Loader.Item height="40px" />
</Loader>
<IntegrationsSettingsLoader />
)}
</div>
);

View file

@ -13,7 +13,8 @@ import { WorkspaceSettingHeader } from "components/headers";
import { ApiTokenListItem, CreateApiTokenModal } from "components/api-token";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// ui
import { Button, Spinner } from "@plane/ui";
import { Button } from "@plane/ui";
import { APITokenSettingsLoader } from "components/ui";
// services
import { APITokenService } from "services/api_token.service";
// types
@ -56,49 +57,47 @@ const ApiTokensPage: NextPageWithLayout = observer(() => {
</div>
);
if (!tokens) {
return <APITokenSettingsLoader />;
}
return (
<>
<CreateApiTokenModal isOpen={isCreateTokenModalOpen} onClose={() => setIsCreateTokenModalOpen(false)} />
{tokens ? (
<section className="h-full w-full overflow-y-auto py-8 pr-9">
{tokens.length > 0 ? (
<>
<div className="flex items-center justify-between border-b border-custom-border-200 py-3.5">
<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="flex h-full w-full flex-col">
<div className="flex items-center justify-between gap-4 border-b border-custom-border-200 pb-3.5">
<h3 className="text-xl font-medium">API tokens</h3>
<Button variant="primary" onClick={() => setIsCreateTokenModalOpen(true)}>
Add API token
</Button>
</div>
<div className="h-full w-full flex items-center justify-center">
<EmptyState
title={emptyStateDetail.title}
description={emptyStateDetail.description}
image={emptyStateImage}
size="lg"
/>
</div>
<section className="h-full w-full overflow-y-auto py-8 pr-9">
{tokens.length > 0 ? (
<>
<div className="flex items-center justify-between border-b border-custom-border-200 py-3.5">
<h3 className="text-xl font-medium">API tokens</h3>
<Button variant="primary" onClick={() => setIsCreateTokenModalOpen(true)}>
Add API token
</Button>
</div>
)}
</section>
) : (
<div className="grid h-full w-full place-items-center p-4">
<Spinner />
</div>
)}
<div>
{tokens.map((token) => (
<ApiTokenListItem key={token.id} token={token} />
))}
</div>
</>
) : (
<div className="flex h-full w-full flex-col">
<div className="flex items-center justify-between gap-4 border-b border-custom-border-200 pb-3.5">
<h3 className="text-xl font-medium">API tokens</h3>
<Button variant="primary" onClick={() => setIsCreateTokenModalOpen(true)}>
Add API token
</Button>
</div>
<div className="h-full w-full flex items-center justify-center">
<EmptyState
title={emptyStateDetail.title}
description={emptyStateDetail.description}
image={emptyStateImage}
size="lg"
/>
</div>
</div>
)}
</section>
</>
);
});

View file

@ -13,8 +13,7 @@ import { WorkspaceSettingLayout } from "layouts/settings-layout";
import { SingleIntegrationCard } from "components/integration";
import { WorkspaceSettingHeader } from "components/headers";
// ui
import { IntegrationAndImportExportBanner } from "components/ui";
import { Loader } from "@plane/ui";
import { IntegrationAndImportExportBanner, IntegrationsSettingsLoader } from "components/ui";
// types
import { NextPageWithLayout } from "lib/types";
// fetch-keys
@ -53,10 +52,7 @@ const WorkspaceIntegrationsPage: NextPageWithLayout = observer(() => {
{appIntegrations ? (
appIntegrations.map((integration) => <SingleIntegrationCard key={integration.id} integration={integration} />)
) : (
<Loader className="mt-4 space-y-2.5">
<Loader.Item height="89px" />
<Loader.Item height="89px" />
</Loader>
<IntegrationsSettingsLoader />
)}
</div>
</section>

View file

@ -13,7 +13,8 @@ import { WorkspaceSettingHeader } from "components/headers";
import { WebhooksList, CreateWebhookModal } from "components/web-hooks";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
// ui
import { Button, Spinner } from "@plane/ui";
import { Button } from "@plane/ui";
import { WebhookSettingsLoader } from "components/ui";
// types
import { NextPageWithLayout } from "lib/types";
// constants
@ -59,12 +60,7 @@ const WebhooksListPage: NextPageWithLayout = observer(() => {
</div>
);
if (!webhooks)
return (
<div className="grid h-full w-full place-items-center p-4">
<Spinner />
</div>
);
if (!webhooks) return <WebhookSettingsLoader />;
return (
<div className="h-full w-full overflow-hidden py-8 pr-9">