feat(ui): added table skeleton and loading for settings page,

fix(ui): resolved missing loading state in add new member and whitespace in project settings page
This commit is contained in:
akhilmhdh
2023-02-22 23:29:26 +05:30
parent 621b640af4
commit 86a2647134
8 changed files with 186 additions and 147 deletions
@@ -45,11 +45,9 @@ import {
export const ProjectSettingsPage = () => {
const { t } = useTranslation();
const { currentWorkspace, workspaces } = useWorkspace();
const { currentWorkspace, workspaces, isLoading: isWorkspaceLoading } = useWorkspace();
const router = useRouter();
const { data: serviceTokens } = useGetUserWsServiceTokens({
workspaceID: currentWorkspace?._id || ''
});
const workspaceID = currentWorkspace?._id || '';
const { createNotification } = useNotificationContext();
// delete action worksapce
@@ -66,12 +64,15 @@ export const ProjectSettingsPage = () => {
const deleteWsEnv = useDeleteWsEnvironment();
// service token
const { data: serviceTokens, isLoading: isServiceTokenLoading } = useGetUserWsServiceTokens({
workspaceID: currentWorkspace?._id || ''
});
const { data: latestFileKey } = useGetUserWsKey(workspaceID);
const createServiceToken = useCreateServiceToken();
const deleteServiceToken = useDeleteServiceToken();
// tag
const { data: wsTags } = useGetWsTags(workspaceID);
const { data: wsTags, isLoading: isTagLoading } = useGetWsTags(workspaceID);
const createWsTag = useCreateWsTag();
const deleteWsTag = useDeleteWsTag();
@@ -300,7 +301,7 @@ export const ProjectSettingsPage = () => {
};
return (
<div className="container mx-auto flex flex-col px-8 text-mineshaft-50 dark dark:[color-scheme:dark]">
<div className="dark container mx-auto flex flex-col px-8 text-mineshaft-50 dark:[color-scheme:dark]">
{/* TODO(akhilmhdh): Remove this right when layout is refactored */}
<div className="relative right-5">
<NavHeader pageName={t('settings-project:title')} isProjectRelated />
@@ -319,6 +320,7 @@ export const ProjectSettingsPage = () => {
/>
<CopyProjectIDSection workspaceID={currentWorkspace?._id || ''} />
<EnvironmentSection
isLoading={isWorkspaceLoading}
environments={currentWorkspace?.environments || []}
onCreate={onCreateWsEnv}
onDelete={onDeleteWsEnv}
@@ -326,6 +328,7 @@ export const ProjectSettingsPage = () => {
isEnvServiceAllowed={isEnvServiceAllowed}
/>
<ServiceTokenSection
isLoading={isServiceTokenLoading}
tokens={serviceTokens || []}
environments={currentWorkspace?.environments || []}
onDeleteToken={onDeleteServiceToken}
@@ -333,6 +336,7 @@ export const ProjectSettingsPage = () => {
onCreateToken={onCreateServiceToken}
/>
<SecretTagsSection
isLoading={isTagLoading}
tags={wsTags || []}
onDeleteTag={onDeleteTag}
workspaceName={currentWorkspace?.name || ''}
@@ -13,22 +13,18 @@ export const AutoCapitalizationSection = ({
}: Props) => {
const { t } = useTranslation();
return (
<form>
<div className="mb-6 mt-4 flex w-full flex-col items-start rounded-md bg-white/5 px-6 pb-6 pt-2">
<p className="mb-4 mt-2 text-xl font-semibold">
{t('settings-project:auto-capitalization')}
</p>
<Checkbox
className="data-[state=checked]:bg-primary"
id="autoCapitalization"
isChecked={workspaceAutoCapitalization}
onCheckedChange={(state) => {
onAutoCapitalizationChange(state as boolean);
}}
>
{t('settings-project:auto-capitalization-description')}
</Checkbox>
</div>
</form>
<div className="mb-6 mt-4 flex w-full flex-col items-start rounded-md bg-white/5 px-6 pb-6 pt-2">
<p className="mb-4 mt-2 text-xl font-semibold">{t('settings-project:auto-capitalization')}</p>
<Checkbox
className="data-[state=checked]:bg-primary"
id="autoCapitalization"
isChecked={workspaceAutoCapitalization}
onCheckedChange={(state) => {
onAutoCapitalizationChange(state as boolean);
}}
>
{t('settings-project:auto-capitalization-description')}
</Checkbox>
</div>
);
};
@@ -7,6 +7,7 @@ import * as yup from 'yup';
import {
Button,
DeleteActionModal,
EmptyState,
FormControl,
IconButton,
Input,
@@ -14,6 +15,7 @@ import {
ModalContent,
Table,
TableContainer,
TableSkeleton,
TBody,
Td,
Th,
@@ -25,6 +27,7 @@ import { usePopUp } from '@app/hooks/usePopUp';
type Props = {
environments: Array<{ name: string; slug: string }>;
isLoading?: boolean;
isEnvServiceAllowed: boolean;
onCreate: (data: CreateUpdateEnvFormData) => Promise<void>;
onUpdate: (oldEnvSlug: string, data: CreateUpdateEnvFormData) => Promise<void>;
@@ -43,6 +46,7 @@ export const EnvironmentSection = ({
isEnvServiceAllowed,
onCreate,
onDelete,
isLoading,
onUpdate
}: Props): JSX.Element => {
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
@@ -116,7 +120,8 @@ export const EnvironmentSection = ({
</Tr>
</THead>
<TBody>
{environments?.length > 0 ? (
{isLoading && <TableSkeleton columns={3} key="project-envs" />}
{!isLoading &&
environments.map(({ name, slug }) => (
<Tr key={name}>
<Td>{name}</Td>
@@ -152,11 +157,11 @@ export const EnvironmentSection = ({
</IconButton>
</Td>
</Tr>
))
) : (
))}
{!isLoading && environments?.length === 0 && (
<Tr>
<Td colSpan={4} className="pt-7 pb-5 text-center text-bunker-400">
No environments found
<Td colSpan={3}>
<EmptyState title="No environments found" />
</Td>
</Tr>
)}
@@ -1,5 +1,5 @@
import { Controller, useForm } from 'react-hook-form';
import { faPlus, faTrashCan } from '@fortawesome/free-solid-svg-icons';
import { faPlus, faTags, faTrashCan } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
@@ -7,6 +7,7 @@ import * as yup from 'yup';
import {
Button,
DeleteActionModal,
EmptyState,
FormControl,
IconButton,
Input,
@@ -16,23 +17,24 @@ import {
ModalTrigger,
Table,
TableContainer,
TableSkeleton,
TBody,
Td,
Th,
THead,
Tr,
} from '@app/components/v2';
Tr} from '@app/components/v2';
import { usePopUp } from '@app/hooks';
import { WorkspaceTag } from '@app/hooks/api/types';
const createTagSchema = yup.object({
name: yup.string().required().label('Tag Name'),
name: yup.string().required().label('Tag Name')
});
export type CreateWsTag = yup.InferType<typeof createTagSchema>;
type Props = {
tags: WorkspaceTag[];
isLoading?: boolean;
workspaceName: string;
onDeleteTag: (tagID: string) => Promise<void>;
onCreateTag: (data: CreateWsTag) => Promise<string>;
@@ -42,6 +44,7 @@ type DeleteModalData = { name: string; id: string };
export const SecretTagsSection = ({
tags = [],
isLoading,
onDeleteTag,
workspaceName,
onCreateTag
@@ -76,7 +79,10 @@ export const SecretTagsSection = ({
<div className="flex w-full flex-row justify-between">
<div className="flex w-full flex-col">
<p className="mb-3 text-xl font-semibold">Secret Tags</p>
<p className="text-sm text-gray-400">Every secret can be assigned to one or more tags. Here you can add and remove tags for the current project.</p>
<p className="text-sm text-gray-400">
Every secret can be assigned to one or more tags. Here you can add and remove tags for
the current project.
</p>
</div>
<div>
<Modal
@@ -92,8 +98,8 @@ export const SecretTagsSection = ({
</Button>
</ModalTrigger>
<ModalContent
title={`Add a tag for ${ workspaceName}`}
subTitle='Specify your tag name, and the slug will be created automatically.'
title={`Add a tag for ${workspaceName}`}
subTitle="Specify your tag name, and the slug will be created automatically."
>
<form onSubmit={handleSubmit(onFormSubmit)}>
<Controller
@@ -102,7 +108,7 @@ export const SecretTagsSection = ({
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label='Tag Name'
label="Tag Name"
isError={Boolean(error)}
errorText={error?.message}
>
@@ -130,7 +136,7 @@ export const SecretTagsSection = ({
</Modal>
</div>
</div>
<TableContainer className='mt-4'>
<TableContainer className="mt-4">
<Table>
<THead>
<Tr>
@@ -140,7 +146,8 @@ export const SecretTagsSection = ({
</Tr>
</THead>
<TBody>
{tags?.length > 0 ? (
{isLoading && <TableSkeleton columns={3} key="secret-tags" />}
{!isLoading &&
tags.map(({ _id, name, slug }) => (
<Tr key={name}>
<Td>{name}</Td>
@@ -149,7 +156,7 @@ export const SecretTagsSection = ({
<IconButton
onClick={() =>
handlePopUpOpen('deleteTagConfirmation', {
name,
name,
id: _id
})
}
@@ -160,11 +167,11 @@ export const SecretTagsSection = ({
</IconButton>
</Td>
</Tr>
))
) : (
))}
{!isLoading && tags?.length === 0 && (
<Tr>
<Td colSpan={4} className="py-6 text-center text-bunker-400">
No tags found for this project
<Td colSpan={3}>
<EmptyState title="No secret tags found" icon={faTags} />
</Td>
</Tr>
)}
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { faCheck, faCopy, faPlus, faTrashCan } from '@fortawesome/free-solid-svg-icons';
import { faCheck, faCopy, faKey, faPlus, faTrashCan } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
@@ -9,6 +9,7 @@ import * as yup from 'yup';
import {
Button,
DeleteActionModal,
EmptyState,
FormControl,
IconButton,
Input,
@@ -20,6 +21,7 @@ import {
SelectItem,
Table,
TableContainer,
TableSkeleton,
TBody,
Td,
Th,
@@ -47,6 +49,7 @@ export type CreateServiceToken = yup.InferType<typeof createServiceTokenSchema>;
type Props = {
tokens: ServiceToken[];
isLoading?: boolean;
workspaceName: string;
environments: WorkspaceEnv[];
onDeleteToken: (serviceTokenID: string) => Promise<void>;
@@ -57,6 +60,7 @@ type DeleteModalData = { name: string; id: string };
export const ServiceTokenSection = ({
tokens = [],
isLoading,
onDeleteToken,
workspaceName,
environments = [],
@@ -269,7 +273,8 @@ export const ServiceTokenSection = ({
</Tr>
</THead>
<TBody>
{tokens?.length > 0 ? (
{isLoading && <TableSkeleton columns={4} key="project-service-tokens" />}
{!isLoading &&
tokens.map((row) => (
<Tr key={row._id}>
<Td>{row.name}</Td>
@@ -290,11 +295,11 @@ export const ServiceTokenSection = ({
</IconButton>
</Td>
</Tr>
))
) : (
))}
{!isLoading && tokens?.length === 0 && (
<Tr>
<Td colSpan={4} className="py-6 text-center text-bunker-400">
No service tokens found
<EmptyState title="No service tokens found" icon={faKey} />
</Td>
</Tr>
)}