[7/n][project settings ui] ProjectSettingContainer (#33)

This commit is contained in:
Vivian Phung 2024-05-14 16:13:28 -04:00 committed by GitHub
commit 690fc8d2cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 116 additions and 66 deletions

View File

@ -0,0 +1,32 @@
import { PropsWithChildren } from 'react';
import { ProjectSettingHeader } from './ProjectSettingHeader';
export interface ProjectSettingContainerProps extends PropsWithChildren {
headingText: string;
className?: string;
button?: React.ReactNode;
badge?: React.ReactNode;
}
const ProjectSettingContainer: React.FC<ProjectSettingContainerProps> = ({
headingText,
className,
button,
children,
badge,
...props
}: ProjectSettingContainerProps) => {
return (
<div className={'flex-col justify-start gap-8 space-y-3 px-2'} {...props}>
<ProjectSettingHeader
headingText={headingText}
button={button}
badge={badge}
/>
{children}
</div>
);
};
export { ProjectSettingContainer };

View File

@ -0,0 +1,30 @@
import { PropsWithChildren } from 'react';
import { Heading } from 'components/shared/Heading';
export interface ProjectSettingHeaderProps extends PropsWithChildren {
headingText: string;
button?: React.ReactNode;
badge?: React.ReactNode;
}
const ProjectSettingHeader: React.FC<ProjectSettingHeaderProps> = ({
headingText,
button,
badge,
...props
}) => {
return (
<div className="flex justify-between items-center" {...props}>
<div className="flex space-x-2 items-center">
<Heading className="text-lg font-medium leading-normal">
{headingText}
</Heading>
{badge}
</div>
{button ?? button}
</div>
);
};
export { ProjectSettingHeader };

View File

@ -6,11 +6,11 @@ import MemberCard from 'components/projects/project/settings/MemberCard';
import AddMemberDialog from 'components/projects/project/settings/AddMemberDialog'; import AddMemberDialog from 'components/projects/project/settings/AddMemberDialog';
import { useGQLClient } from '../../../../../context/GQLClientContext'; import { useGQLClient } from '../../../../../context/GQLClientContext';
import { OutletContextType } from '../../../../../types'; import { OutletContextType } from '../../../../../types';
import { useToast } from 'components/shared/Toast'; import { useToast } from '../../../../../components/shared/Toast';
import { Button } from 'components/shared/Button'; import { Button } from 'components/shared/Button';
import { PlusIcon } from 'components/shared/CustomIcon'; import { PlusIcon } from 'components/shared/CustomIcon';
import { Badge } from 'components/shared/Badge'; import { Badge } from 'components/shared/Badge';
import { Heading } from 'components/shared/Heading'; import { ProjectSettingContainer } from 'components/projects/project/settings/ProjectSettingContainer';
const FIRST_MEMBER_CARD = 0; const FIRST_MEMBER_CARD = 0;
@ -105,16 +105,14 @@ const CollaboratorsTabPanel = () => {
}, [project.id, fetchProjectMembers]); }, [project.id, fetchProjectMembers]);
return ( return (
<div className="space-y-3 px-2"> <ProjectSettingContainer
<div className="flex justify-between"> headingText="Collaborators"
<div className="flex space-x-2"> badge={
<Heading className="text-sky-950 text-lg font-medium leading-normal"> <Badge size="sm" variant="inset">
Collaborators {projectMembers.length + 1}
</Heading> </Badge>
<Badge size="sm" variant="inset"> }
{projectMembers.length + 1} button={
</Badge>
</div>
<Button <Button
size="md" size="md"
onClick={() => setAddMemberDialogOpen((preVal) => !preVal)} onClick={() => setAddMemberDialogOpen((preVal) => !preVal)}
@ -123,7 +121,8 @@ const CollaboratorsTabPanel = () => {
> >
Add member Add member
</Button> </Button>
</div> }
>
<MemberCard <MemberCard
member={project.owner} member={project.owner}
isFirstCard={true} isFirstCard={true}
@ -156,7 +155,7 @@ const CollaboratorsTabPanel = () => {
open={addmemberDialogOpen} open={addmemberDialogOpen}
handleAddMember={addMemberHandler} handleAddMember={addMemberHandler}
/> />
</div> </ProjectSettingContainer>
); );
}; };

View File

@ -7,8 +7,9 @@ import DomainCard from 'components/projects/project/settings/DomainCard';
import { useGQLClient } from '../../../../../context/GQLClientContext'; import { useGQLClient } from '../../../../../context/GQLClientContext';
import { OutletContextType } from '../../../../../types'; import { OutletContextType } from '../../../../../types';
import { useOctokit } from '../../../../../context/OctokitContext'; import { useOctokit } from '../../../../../context/OctokitContext';
import { Heading } from 'components/shared/Heading';
import { Button } from 'components/shared/Button'; import { Button } from 'components/shared/Button';
import { PlusIcon } from 'components/shared/CustomIcon';
import { ProjectSettingContainer } from 'components/projects/project/settings/ProjectSettingContainer';
const Domains = () => { const Domains = () => {
const client = useGQLClient(); const client = useGQLClient();
@ -57,16 +58,20 @@ const Domains = () => {
}, []); }, []);
return ( return (
<> <ProjectSettingContainer
<div className="space-y-3 px-2"> headingText="Domains"
<Heading className="text-sky-950 text-lg font-medium leading-normal"> button={
Domains <Button
</Heading> as="a"
<Button as="a" href="add"> href="add"
variant="secondary"
leftIcon={<PlusIcon />}
size="md"
>
Add domain Add domain
</Button> </Button>
</div> }
>
{domains.map((domain) => { {domains.map((domain) => {
return ( return (
<DomainCard <DomainCard
@ -80,7 +85,7 @@ const Domains = () => {
/> />
); );
})} })}
</> </ProjectSettingContainer>
); );
}; };

View File

@ -16,6 +16,7 @@ import { Button } from 'components/shared/Button';
import { Checkbox } from 'components/shared/Checkbox'; import { Checkbox } from 'components/shared/Checkbox';
import { PlusIcon } from 'components/shared/CustomIcon'; import { PlusIcon } from 'components/shared/CustomIcon';
import { InlineNotification } from 'components/shared/InlineNotification'; import { InlineNotification } from 'components/shared/InlineNotification';
import { ProjectSettingContainer } from 'components/projects/project/settings/ProjectSettingContainer';
export const EnvironmentVariablesTabPanel = () => { export const EnvironmentVariablesTabPanel = () => {
const { id } = useParams(); const { id } = useParams();
@ -130,10 +131,7 @@ export const EnvironmentVariablesTabPanel = () => {
); );
return ( return (
<div className="space-y-3 px-2"> <ProjectSettingContainer headingText="Environment variables">
<Heading className="text-sky-950 text-lg font-medium leading-normal">
Environment variables
</Heading>
<p className="text-slate-600 text-sm font-normal leading-tight"> <p className="text-slate-600 text-sm font-normal leading-tight">
A new deployment is required for your changes to take effect. A new deployment is required for your changes to take effect.
</p> </p>
@ -236,6 +234,6 @@ export const EnvironmentVariablesTabPanel = () => {
}} }}
/> />
</div> </div>
</div> </ProjectSettingContainer>
); );
}; };

View File

@ -12,6 +12,7 @@ import { Button } from 'components/shared/Button';
import { Select, SelectOption } from 'components/shared/Select'; import { Select, SelectOption } from 'components/shared/Select';
import { TrashIcon, CopyUnfilledIcon } from 'components/shared/CustomIcon'; import { TrashIcon, CopyUnfilledIcon } from 'components/shared/CustomIcon';
import { useToast } from 'components/shared/Toast'; import { useToast } from 'components/shared/Toast';
import { ProjectSettingContainer } from 'components/projects/project/settings/ProjectSettingContainer';
const GeneralTabPanel = () => { const GeneralTabPanel = () => {
const client = useGQLClient(); const client = useGQLClient();
@ -109,7 +110,7 @@ const GeneralTabPanel = () => {
}, [project]); }, [project]);
return ( return (
<div className="flex-col justify-start items-start gap-6 inline-flex"> <ProjectSettingContainer headingText="Project Info">
<form <form
onSubmit={handleSubmit(async ({ appName, description }) => { onSubmit={handleSubmit(async ({ appName, description }) => {
const { updateProject } = await client.updateProject(project.id, { const { updateProject } = await client.updateProject(project.id, {
@ -120,11 +121,8 @@ const GeneralTabPanel = () => {
await onUpdate(); await onUpdate();
} }
})} })}
className="self-stretch space-y-3 px-2" className="self-stretch space-y-3"
> >
<Heading className="text-sky-950 text-lg font-medium leading-normal">
Project Info
</Heading>
<Input <Input
// TODO: Debug issue: https://github.com/creativetimofficial/material-tailwind/issues/427 // TODO: Debug issue: https://github.com/creativetimofficial/material-tailwind/issues/427
label="App name" label="App name"
@ -221,7 +219,7 @@ const GeneralTabPanel = () => {
project={project} project={project}
/> />
</div> </div>
</div> </ProjectSettingContainer>
); );
}; };

View File

@ -8,8 +8,9 @@ import { OutletContextType } from '../../../../../types';
import { Button } from 'components/shared/Button'; import { Button } from 'components/shared/Button';
import { Input } from 'components/shared/Input'; import { Input } from 'components/shared/Input';
import { Switch } from 'components/shared/Switch'; import { Switch } from 'components/shared/Switch';
import { Heading } from 'components/shared/Heading';
import { useToast } from 'components/shared/Toast'; import { useToast } from 'components/shared/Toast';
import { ProjectSettingContainer } from 'components/projects/project/settings/ProjectSettingContainer';
import { ProjectSettingHeader } from 'components/projects/project/settings/ProjectSettingHeader';
type UpdateProdBranchValues = { type UpdateProdBranchValues = {
prodBranch: string; prodBranch: string;
@ -134,12 +135,8 @@ const GitTabPanel = () => {
}; };
return ( return (
<div className="flex-col justify-start items-start gap-6 inline-flex"> <ProjectSettingContainer headingText="Git repository">
<div className="self-stretch space-y-3 px-2"> <div className="self-stretch space-y-3">
<Heading className="text-sky-950 text-lg font-medium leading-normal">
Git repository
</Heading>
<div className="flex justify-between mt-4"> <div className="flex justify-between mt-4">
<div> <div>
<p className="text-slate-600 text-sm font-normal leading-tight"> <p className="text-slate-600 text-sm font-normal leading-tight">
@ -171,11 +168,9 @@ const GitTabPanel = () => {
<form <form
onSubmit={handleSubmitProdBranch(updateProdBranchHandler)} onSubmit={handleSubmitProdBranch(updateProdBranchHandler)}
className="space-y-3 px-2" className="space-y-3"
> >
<Heading className="text-sky-950 text-lg font-medium leading-normal"> <ProjectSettingHeader headingText="Production branch" />
Production branch
</Heading>
<p className="text-slate-600 text-sm font-normal leading-tight"> <p className="text-slate-600 text-sm font-normal leading-tight">
By default, each commit pushed to the{' '} By default, each commit pushed to the{' '}
<span className="font-bold">{project.prodBranch}</span> branch <span className="font-bold">{project.prodBranch}</span> branch
@ -193,11 +188,9 @@ const GitTabPanel = () => {
<form <form
onSubmit={handleSubmitWebhooks(updateWebhooksHandler)} onSubmit={handleSubmitWebhooks(updateWebhooksHandler)}
className="space-y-3 px-2" className="space-y-3"
> >
<Heading className="text-sky-950 text-lg font-medium leading-normal"> <ProjectSettingHeader headingText="Deploy webhooks" />
Deploy webhooks
</Heading>
<p className="text-slate-600 text-sm font-normal leading-tight"> <p className="text-slate-600 text-sm font-normal leading-tight">
{' '} {' '}
Webhooks configured to trigger when there is a change in a Webhooks configured to trigger when there is a change in a
@ -228,7 +221,7 @@ const GitTabPanel = () => {
); );
})} })}
</div> </div>
</div> </ProjectSettingContainer>
); );
}; };

View File

@ -2,11 +2,11 @@ import toast from 'react-hot-toast';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { useGQLClient } from '../../../../../../../context/GQLClientContext'; import { useGQLClient } from '../../../../../../../context/GQLClientContext';
import { Heading } from 'components/shared/Heading';
import { Table } from 'components/shared/Table'; import { Table } from 'components/shared/Table';
import { Button } from 'components/shared/Button'; import { Button } from 'components/shared/Button';
import { InlineNotification } from 'components/shared/InlineNotification'; import { InlineNotification } from 'components/shared/InlineNotification';
import { ArrowRightCircleIcon } from 'components/shared/CustomIcon'; import { ArrowRightCircleIcon } from 'components/shared/CustomIcon';
import { ProjectSettingContainer } from 'components/projects/project/settings/ProjectSettingContainer';
const Config = () => { const Config = () => {
const { id, orgSlug } = useParams(); const { id, orgSlug } = useParams();
@ -40,18 +40,13 @@ const Config = () => {
// TODO: Figure out DNS Provider if possible and update appropriatly // TODO: Figure out DNS Provider if possible and update appropriatly
return ( return (
<div className="flex flex-col gap-6 w-full"> <ProjectSettingContainer headingText="Setup domain name">
<div> <p className="text-blue-gray-500">
<Heading className="text-sky-950 text-lg font-medium leading-normal"> Add the following records to your domain.&nbsp;
Setup domain name <a href="https://www.namecheap.com/" target="_blank" rel="noreferrer">
</Heading> <span className="underline">Go to NameCheap</span>
<p className="text-blue-gray-500"> </a>
Add the following records to your domain.&nbsp; </p>
<a href="https://www.namecheap.com/" target="_blank" rel="noreferrer">
<span className="underline">Go to NameCheap</span>
</a>
</p>
</div>
<Table> <Table>
<Table.Header> <Table.Header>
@ -90,7 +85,7 @@ const Config = () => {
> >
Finish Finish
</Button> </Button>
</div> </ProjectSettingContainer>
); );
}; };

View File

@ -5,7 +5,7 @@ import {
IconButton, IconButton,
} from '@snowballtools/material-tailwind-react-fork'; } from '@snowballtools/material-tailwind-react-fork';
import Stepper from '../../../../../../../components/Stepper'; import Stepper from 'components/Stepper';
const AddDomain = () => { const AddDomain = () => {
const { id, orgSlug } = useParams(); const { id, orgSlug } = useParams();