[6/n][project settings ui] collaborators ui (#28)

This commit is contained in:
Vivian Phung 2024-05-14 15:57:47 -04:00 committed by GitHub
commit 68f1a265f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 159 additions and 58 deletions

View File

@ -12,7 +12,7 @@ import { Button } from 'components/shared/Button';
import { import {
BranchIcon, BranchIcon,
ClockIcon, ClockIcon,
GitHubLogo, GithubLogoIcon,
HorizontalDotIcon, HorizontalDotIcon,
WarningDiamondIcon, WarningDiamondIcon,
} from 'components/shared/CustomIcon'; } from 'components/shared/CustomIcon';
@ -118,7 +118,7 @@ export const ProjectCard = ({
<div className={theme.deploymentText()}> <div className={theme.deploymentText()}>
{hasDeployment ? ( {hasDeployment ? (
<> <>
<GitHubLogo /> <GithubLogoIcon />
<span>{relativeTimeMs(project.deployments[0].createdAt)} on</span> <span>{relativeTimeMs(project.deployments[0].createdAt)} on</span>
<BranchIcon /> <BranchIcon />
<span>{project.deployments[0].branch}</span> <span>{project.deployments[0].branch}</span>

View File

@ -3,16 +3,15 @@ import { CustomIcon, CustomIconProps } from './CustomIcon';
export const BranchStrokeIcon = (props: CustomIconProps) => { export const BranchStrokeIcon = (props: CustomIconProps) => {
return ( return (
<CustomIcon <CustomIcon
width="16" width="20"
height="16" height="20"
viewBox="0 0 16 16" viewBox="0 0 20 20"
fill="none" fill="none"
{...props} {...props}
> >
<path <path
d="M4.66667 4.99984C5.58714 4.99984 6.33333 4.25365 6.33333 3.33317C6.33333 2.4127 5.58714 1.6665 4.66667 1.6665C3.74619 1.6665 3 2.4127 3 3.33317C3 4.25365 3.74619 4.99984 4.66667 4.99984ZM4.66667 4.99984V10.9998M4.66667 10.9998C3.74619 10.9998 3 11.746 3 12.6665C3 13.587 3.74619 14.3332 4.66667 14.3332C5.58714 14.3332 6.33333 13.587 6.33333 12.6665C6.33333 11.746 5.58714 10.9998 4.66667 10.9998ZM4.66667 10.9998V9.33317C4.66667 8.59679 5.26362 7.99984 6 7.99984H10C10.7364 7.99984 11.3333 7.40288 11.3333 6.6665V4.99984M11.3333 4.99984C12.2538 4.99984 13 4.25365 13 3.33317C13 2.4127 12.2538 1.6665 11.3333 1.6665C10.4129 1.6665 9.66667 2.4127 9.66667 3.33317C9.66667 4.25365 10.4129 4.99984 11.3333 4.99984Z" d="M14.1667 9.99967V10.4997H14.6667V9.99967H14.1667ZM5.83333 9.99967V9.49967H5.33333V9.99967H5.83333ZM7.41667 4.16634C7.41667 5.04079 6.70778 5.74967 5.83333 5.74967V6.74967C7.26007 6.74967 8.41667 5.59308 8.41667 4.16634H7.41667ZM5.83333 5.74967C4.95888 5.74967 4.25 5.04079 4.25 4.16634H3.25C3.25 5.59308 4.4066 6.74967 5.83333 6.74967V5.74967ZM4.25 4.16634C4.25 3.29189 4.95888 2.58301 5.83333 2.58301V1.58301C4.4066 1.58301 3.25 2.73961 3.25 4.16634H4.25ZM5.83333 2.58301C6.70778 2.58301 7.41667 3.29189 7.41667 4.16634H8.41667C8.41667 2.73961 7.26007 1.58301 5.83333 1.58301V2.58301ZM7.41667 15.833C7.41667 16.7075 6.70778 17.4163 5.83333 17.4163V18.4163C7.26007 18.4163 8.41667 17.2597 8.41667 15.833H7.41667ZM5.83333 17.4163C4.95888 17.4163 4.25 16.7075 4.25 15.833H3.25C3.25 17.2597 4.4066 18.4163 5.83333 18.4163V17.4163ZM4.25 15.833C4.25 14.9586 4.95888 14.2497 5.83333 14.2497V13.2497C4.4066 13.2497 3.25 14.4063 3.25 15.833H4.25ZM5.83333 14.2497C6.70778 14.2497 7.41667 14.9586 7.41667 15.833H8.41667C8.41667 14.4063 7.26007 13.2497 5.83333 13.2497V14.2497ZM5.33333 6.24967V13.7497H6.33333V6.24967H5.33333ZM13.6667 6.24967V9.99967H14.6667V6.24967H13.6667ZM14.1667 9.49967H5.83333V10.4997H14.1667V9.49967ZM5.33333 9.99967V13.7497H6.33333V9.99967H5.33333ZM15.75 4.16634C15.75 5.04079 15.0411 5.74967 14.1667 5.74967V6.74967C15.5934 6.74967 16.75 5.59308 16.75 4.16634H15.75ZM14.1667 5.74967C13.2922 5.74967 12.5833 5.04079 12.5833 4.16634H11.5833C11.5833 5.59308 12.7399 6.74967 14.1667 6.74967V5.74967ZM12.5833 4.16634C12.5833 3.29189 13.2922 2.58301 14.1667 2.58301V1.58301C12.7399 1.58301 11.5833 2.73961 11.5833 4.16634H12.5833ZM14.1667 2.58301C15.0411 2.58301 15.75 3.29189 15.75 4.16634H16.75C16.75 2.73961 15.5934 1.58301 14.1667 1.58301V2.58301Z"
stroke="currentColor" fill="currentColor"
strokeLinejoin="round"
/> />
</CustomIcon> </CustomIcon>
); );

View File

@ -0,0 +1,19 @@
import { CustomIcon, CustomIconProps } from './CustomIcon';
export const CollaboratorsIcon = (props: CustomIconProps) => {
return (
<CustomIcon
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
{...props}
>
<path
d="M12.5002 2.08301C14.3411 2.08301 15.8335 3.57539 15.8335 5.41634C15.8335 7.25729 14.3411 8.74968 12.5002 8.74968M14.5835 11.1659C17.2451 12.0374 19.1668 14.498 19.1668 17.083H16.6668M7.50016 8.74968C5.65921 8.74968 4.16683 7.25729 4.16683 5.41634C4.16683 3.57539 5.65921 2.08301 7.50016 2.08301C9.34111 2.08301 10.8335 3.57539 10.8335 5.41634C10.8335 7.25729 9.34111 8.74968 7.50016 8.74968ZM0.833496 17.083C0.833496 13.8613 3.81826 10.833 7.50016 10.833C11.1821 10.833 14.1668 13.8613 14.1668 17.083H0.833496Z"
stroke="currentColor"
stroke-linecap="square"
/>
</CustomIcon>
);
};

View File

@ -1,6 +1,6 @@
import { CustomIcon, CustomIconProps } from './CustomIcon'; import { CustomIcon, CustomIconProps } from './CustomIcon';
export const GitHubLogo = (props: CustomIconProps) => { export const GithubLogoIcon = (props: CustomIconProps) => {
return ( return (
<CustomIcon <CustomIcon
width="12" width="12"

View File

@ -0,0 +1,22 @@
import { CustomIcon, CustomIconProps } from './CustomIcon';
export const SwitchIcon = (props: CustomIconProps) => {
return (
<CustomIcon
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
{...props}
>
<path
d="M1.25 9.99967C1.25 7.00813 3.67512 4.58301 6.66667 4.58301H13.3333C16.3249 4.58301 18.75 7.00813 18.75 9.99967C18.75 12.9912 16.3249 15.4163 13.3333 15.4163H6.66667C3.67512 15.4163 1.25 12.9912 1.25 9.99967Z"
stroke="currentColor"
/>
<path
d="M10.4167 9.99967C10.4167 8.38884 11.7225 7.08301 13.3333 7.08301C14.9442 7.08301 16.25 8.38884 16.25 9.99967C16.25 11.6105 14.9442 12.9163 13.3333 12.9163C11.7225 12.9163 10.4167 11.6105 10.4167 9.99967Z"
stroke="currentColor"
/>
</CustomIcon>
);
};

View File

@ -32,7 +32,7 @@ export * from './BuildingIcon';
export * from './CheckRadioIcon'; export * from './CheckRadioIcon';
export * from './ChevronDownIcon'; export * from './ChevronDownIcon';
export * from './BranchIcon'; export * from './BranchIcon';
export * from './GitHubLogo'; export * from './GitHubLogoIcon';
export * from './ClockIcon'; export * from './ClockIcon';
export * from './HorizontalDotIcon'; export * from './HorizontalDotIcon';
export * from './WarningDiamondIcon'; export * from './WarningDiamondIcon';
@ -69,6 +69,8 @@ export * from './GoogleIcon';
export * from './KeyIcon'; export * from './KeyIcon';
export * from './TrashIcon'; export * from './TrashIcon';
export * from './CopyUnfilledIcon'; export * from './CopyUnfilledIcon';
export * from './SwitchIcon';
export * from './CollaboratorsIcon';
// Templates // Templates
export * from './templates'; export * from './templates';

View File

@ -1 +1,4 @@
export * from './Tabs'; export * from './Tabs';
export { default as TabsList } from './TabsList';
export { default as TabsTrigger } from './TabsTrigger';
export { default as TabsContent } from './TabsContent';

View File

@ -7,7 +7,7 @@ import FilterForm, {
FilterValue, FilterValue,
StatusOptions, StatusOptions,
} from 'components/projects/project/deployments/FilterForm'; } from 'components/projects/project/deployments/FilterForm';
import { OutletContextType } from '../../../../types/types'; import { OutletContextType } from '../../../../types';
import { useGQLClient } from 'context/GQLClientContext'; import { useGQLClient } from 'context/GQLClientContext';
import { Button } from 'components/shared/Button'; import { Button } from 'components/shared/Button';
import { RefreshIcon } from 'components/shared/CustomIcon'; import { RefreshIcon } from 'components/shared/CustomIcon';

View File

@ -4,10 +4,7 @@ import { Link, useNavigate, useOutletContext } from 'react-router-dom';
import { RequestError } from 'octokit'; import { RequestError } from 'octokit';
import { useOctokit } from '../../../../context/OctokitContext'; import { useOctokit } from '../../../../context/OctokitContext';
import { import { GitCommitWithBranch, OutletContextType } from '../../../../types';
GitCommitWithBranch,
OutletContextType,
} from '../../../../types/types';
import { useGQLClient } from '../../../../context/GQLClientContext'; import { useGQLClient } from '../../../../context/GQLClientContext';
import { Button } from 'components/shared/Button'; import { Button } from 'components/shared/Button';
import { Heading } from 'components/shared/Heading'; import { Heading } from 'components/shared/Heading';

View File

@ -1,40 +1,46 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { Link, Outlet, useLocation, useOutletContext } from 'react-router-dom'; import { Link, Outlet, useLocation, useOutletContext } from 'react-router-dom';
import { OutletContextType } from '../../../../types';
import { import {
Tabs, Tabs,
TabsHeader, TabsContent,
TabsBody, TabsList,
Tab, TabsTrigger,
} from '@snowballtools/material-tailwind-react-fork'; } from 'components/shared/Tabs';
import {
import { OutletContextType } from '../../../../types/types'; BranchStrokeIcon,
CollaboratorsIcon,
GearIcon,
GlobeIcon,
SwitchIcon,
} from 'components/shared/CustomIcon';
const tabsData = [ const tabsData = [
{ {
label: 'General', label: 'General',
icon: '', icon: <GearIcon />,
value: 'general', value: 'general',
}, },
{ {
label: 'Domains', label: 'Domains',
icon: '', icon: <GlobeIcon />,
value: 'domains', value: 'domains',
}, },
{ {
label: 'Git', label: 'Git',
icon: '', icon: <BranchStrokeIcon />,
value: 'git', value: 'git',
}, },
{ {
label: 'Environment variables', label: 'Environment variables',
icon: '', icon: <SwitchIcon />,
value: 'environment-variables', value: 'environment-variables',
}, },
{ {
label: 'Members', label: 'Collaborators',
icon: '', icon: <CollaboratorsIcon />,
value: 'members', value: 'collaborators',
}, },
]; ];
@ -57,31 +63,26 @@ const SettingsTabPanel = () => {
<Tabs <Tabs
value={currentTab} value={currentTab}
orientation="vertical" orientation="vertical"
className="grid grid-cols-4" className="grid grid-cols-5"
> >
<TabsHeader <TabsList className="col-span-1">
className="bg-transparent col-span-1"
indicatorProps={{
className: 'bg-gray-900/10 shadow-none !text-gray-900',
}}
>
{tabsData.map(({ label, value, icon }) => ( {tabsData.map(({ label, value, icon }) => (
<Link key={value} to={value === 'general' ? '' : value}> <Link key={value} to={value === 'general' ? '' : value}>
<Tab <TabsTrigger
value={value === 'general' ? '' : `/${value}`} value={value === 'general' ? '' : `/${value}`}
className="flex justify-start" className="col-span-1"
> >
<div className="flex gap-2"> <div className="items-center gap-2 inline-flex">
<div>{icon}</div> <div className="items-center">{icon}</div>
<div>{label}</div> <div className="items-center">{label}</div>
</div> </div>
</Tab> </TabsTrigger>
</Link> </Link>
))} ))}
</TabsHeader> </TabsList>
<TabsBody className="col-span-2"> <TabsContent value={currentTab ?? ''} className="col-span-3">
<Outlet context={{ project, onUpdate }} /> <Outlet context={{ project, onUpdate }} />
</TabsBody> </TabsContent>
</Tabs> </Tabs>
</> </>
); );

View File

@ -4,7 +4,7 @@ import SettingsTabPanel from './Settings';
import GeneralTabPanel from './settings/General'; import GeneralTabPanel from './settings/General';
import GitTabPanel from './settings/Git'; import GitTabPanel from './settings/Git';
import { EnvironmentVariablesTabPanel } from './settings/EnvironmentVariables'; import { EnvironmentVariablesTabPanel } from './settings/EnvironmentVariables';
import MembersTabPanel from './settings/Members'; import CollaboratorsTabPanel from './settings/Collaborators';
import Domains from './settings/Domains'; import Domains from './settings/Domains';
const Integrations = () => ( const Integrations = () => (
@ -34,8 +34,8 @@ export const settingsTabRoutes = [
element: <EnvironmentVariablesTabPanel />, element: <EnvironmentVariablesTabPanel />,
}, },
{ {
path: 'members', path: 'collaborators',
element: <MembersTabPanel />, element: <CollaboratorsTabPanel />,
}, },
]; ];

View File

@ -2,11 +2,11 @@ import { useCallback, useEffect, useState } from 'react';
import { useOutletContext } from 'react-router-dom'; import { useOutletContext } from 'react-router-dom';
import { Permission, AddProjectMemberInput, ProjectMember } from 'gql-client'; import { Permission, AddProjectMemberInput, ProjectMember } from 'gql-client';
import MemberCard from '../../../../../components/projects/project/settings/MemberCard'; 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';
@ -14,7 +14,7 @@ import { Heading } from 'components/shared/Heading';
const FIRST_MEMBER_CARD = 0; const FIRST_MEMBER_CARD = 0;
const MembersTabPanel = () => { const CollaboratorsTabPanel = () => {
const client = useGQLClient(); const client = useGQLClient();
const { toast } = useToast(); const { toast } = useToast();
const { project } = useOutletContext<OutletContextType>(); const { project } = useOutletContext<OutletContextType>();
@ -160,4 +160,4 @@ const MembersTabPanel = () => {
); );
}; };
export default MembersTabPanel; export default CollaboratorsTabPanel;

View File

@ -0,0 +1,29 @@
import { Meta, StoryObj } from '@storybook/react';
import { CollaboratorsIcon } from 'components/shared/CustomIcon';
const meta: Meta<typeof CollaboratorsIcon> = {
title: 'Components/CollaboratorsIcon',
component: CollaboratorsIcon,
tags: ['autodocs'],
argTypes: {
size: {
control: 'text',
},
name: {
control: 'text',
},
},
};
export default meta;
type Story = StoryObj<typeof CollaboratorsIcon>;
export const Default: Story = {
render: ({ size, name }) => <CollaboratorsIcon size={size} name={name} />,
args: {
size: '24px',
name: 'plus',
},
};

View File

@ -0,0 +1,29 @@
import { Meta, StoryObj } from '@storybook/react';
import { GithubLogoIcon } from 'components/shared/CustomIcon';
const meta: Meta<typeof GithubLogoIcon> = {
title: 'Icons/GithubLogoIcon',
component: GithubLogoIcon,
tags: ['autodocs'],
argTypes: {
size: {
control: 'text',
},
name: {
control: 'text',
},
},
};
export default meta;
type Story = StoryObj<typeof GithubLogoIcon>;
export const Default: Story = {
render: ({ size, name }) => <GithubLogoIcon size={size} name={name} />,
args: {
size: '24px',
name: 'plus',
},
};

View File

@ -1,10 +1,10 @@
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react';
import { GitHubLogo } from 'components/shared/CustomIcon'; import { SwitchIcon } from 'components/shared/CustomIcon';
const meta: Meta<typeof GitHubLogo> = { const meta: Meta<typeof SwitchIcon> = {
title: 'Icons/GitHubLogo', title: 'Icons/SwitchIcon',
component: GitHubLogo, component: SwitchIcon,
tags: ['autodocs'], tags: ['autodocs'],
argTypes: { argTypes: {
size: { size: {
@ -18,10 +18,10 @@ const meta: Meta<typeof GitHubLogo> = {
export default meta; export default meta;
type Story = StoryObj<typeof GitHubLogo>; type Story = StoryObj<typeof SwitchIcon>;
export const Default: Story = { export const Default: Story = {
render: ({ size, name }) => <GitHubLogo size={size} name={name} />, render: ({ size, name }) => <SwitchIcon size={size} name={name} />,
args: { args: {
size: '24px', size: '24px',
name: 'plus', name: 'plus',