Skip to content

Commit

Permalink
label crud done
Browse files Browse the repository at this point in the history
  • Loading branch information
Yogesh070 committed Aug 21, 2023
1 parent be80f49 commit 185615c
Show file tree
Hide file tree
Showing 8 changed files with 563 additions and 33 deletions.
11 changes: 11 additions & 0 deletions src/components/BorderedContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {theme} from 'antd';
import React from 'react';

const {useToken} = theme;

const BorderedContainer = (props:React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>) => {
const {token} = useToken();
return <div {...props} style={{backgroundColor: token.colorBgElevated,borderRadius:token.borderRadius,border:`1px solid ${token.colorBorder}`}} >{props.children}</div>;
};

export default BorderedContainer;
4 changes: 2 additions & 2 deletions src/components/Item/ItemDetailsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {api} from '../../utils/api';
import Checklist from '../Checklist/Checklist';
import {useProjectStore} from '../../store/project.store';
import {useState} from 'react';
import LabelSelect from '../LabelDropdown/LabelSelect';
import LabelSelect from '../Label/LabelDropdown/LabelSelect';
import dayjs from 'dayjs';

import React from 'react';
Expand Down Expand Up @@ -247,7 +247,7 @@ const ItemDetailsModal: React.FC<DetailsModalProps> = (
}
options={[
{
value: null,
value: undefined,
label: 'Unassigned',
},
...userOptions,
Expand Down
177 changes: 177 additions & 0 deletions src/components/Label/LabelForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import {Tag, Form, Input, Button, ColorPicker} from 'antd';
import type {FormInstance} from 'antd/es/form';
import {useRouter} from 'next/router';
import React, {useEffect} from 'react';
import {useProjectStore} from '../../store/project.store';
import {api} from '../../utils/api';

import {ReloadOutlined} from '@ant-design/icons';
import type {Label} from '@prisma/client';

type FieldType = {
title: string;
description?: string;
color: string;
};

interface Props {
form: FormInstance;
forEdit?: boolean;
onCancel?: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void;
layout?: 'vertical' | 'horizontal';
onFinish?: (value: Label) => void;
}

const LabelForm = (props: Props) => {
const router = useRouter();
const {projectId} = router.query;
const {addLabel, editLabel} = useProjectStore();

useEffect(() => {
if (props.forEdit) {
props.form.setFieldsValue({
title: props.form.getFieldValue('title'),
description: props.form.getFieldValue('description'),
color: props.form.getFieldValue('color'),
});
}
}, [props.forEdit, props.form]);

const {mutate: createLabel, isLoading: isCreating} =
api.project.createProjectLabels.useMutation({
onSuccess: (data) => {
addLabel(data);
props.form.resetFields();
props.onFinish?.(data);
},
});

const {mutate: editLabelAPI, isLoading: isEditing} =
api.project.updateProjectLabel.useMutation({
onSuccess: (data) => {
editLabel(data);
props.onFinish?.(data);
},
});

const handleSubmit = (values: FieldType) => {
if (props.forEdit) {
editLabelAPI({
id: props.form.getFieldValue('id'),
title: values.title,
color: values.color,
description: values.description,
});
return;
}
createLabel({
projectId: projectId as string,
title: values.title,
color: values.color,
description: values.description,
});
};

const getRandomColor = () => {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; ++i) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
const color: string = Form.useWatch('color', props.form);
const titleValue: string = Form.useWatch('name', props.form);
const getNameValue = () => {
if (titleValue === undefined || titleValue.length === 0)
return 'Label preview';

return titleValue ?? 'Label preview';
};

return (
<>
<Tag color={color} className="mb-3">
{getNameValue()}
</Tag>
<Form
form={props.form}
name={props.forEdit ? 'edit-label' : 'create-label'}
layout="vertical"
autoComplete="off"
className={`flex ${
props.layout === 'vertical' ? 'flex-col' : 'items-end gap-1'
} jusify-between `}
onFinish={handleSubmit}
initialValues={{
color: getRandomColor(),
}}
>
<Form.Item<FieldType>
name="title"
label="Title"
rules={[{required: true}, {min: 4}]}
>
<Input />
</Form.Item>
<Form.Item<FieldType> name="description" label="Description">
<Input />
</Form.Item>
<div className="flex gap-1-2 items-center">
<Form.Item<FieldType> name="color" label="Color" required>
<ColorPicker
showText
disabledAlpha
value={color}
onChange={(_, hex) => {
props.form.setFieldsValue({color: hex});
}}
presets={[
{
label: 'Recommended',
colors: [
'#F5222D',
'#FA8C16',
'#FADB14',
'#8BBB11',
'#52C41A',
'#13A8A8',
'#1677FF',
'#2F54EB',
'#722ED1',
'#EB2F96',
],
},
]}
/>
</Form.Item>
<Button
type="primary"
style={{backgroundColor: color}}
icon={<ReloadOutlined color={color} />}
onClick={() => {
props.form.setFieldsValue({color: getRandomColor()});
}}
/>
</div>

<Form.Item className="flex-1 flex justify-end">
<div className="flex gap-1-2">
<Button type="default" onClick={props.onCancel}>
Cancel
</Button>
<Button
type="primary"
onClick={props.form.submit}
loading={isCreating || isEditing}
>
{props.forEdit ? 'Edit Label' : ' Create Label'}
</Button>
</div>
</Form.Item>
</Form>
</>
);
};

export default LabelForm;
64 changes: 64 additions & 0 deletions src/layout/SettingsLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import Board from './Board';

import type {ReactNode} from 'react';
import {Button, Divider, Typography} from 'antd';
import {useRouter} from 'next/router';

import {InfoCircleOutlined, TagsOutlined} from '@ant-design/icons';

const {Title} = Typography;

const SettingsLayout = ({children}: {children: ReactNode}) => {
const settingsOptions = [
{
icon: <InfoCircleOutlined />,
label: 'Overview',
route: 'settings',
href: '',
},
{
icon: <TagsOutlined />,
label: 'Labels',
route: 'labels',
href: 'labels',
},
];
const router = useRouter();
return (
<>
<Board>
<Title level={4} className='mb-0'>Settings</Title>
<div className="flex gap-1-2">
{settingsOptions.map((option, index) => {
const key = String(index + 1);
return (
<Button
key={key}
type={
router.pathname.split('/')[
router.pathname.split('/').length - 1
] == option.route
? 'default'
: 'text'
}
icon={option.icon}
onClick={() =>
router.push({
pathname: `/w/${router.query.workspaceId}/projects/${router.query.projectId}/settings/${option.href}`,
})
}
>
{option.label}
</Button>
);
})}
</div>
<Divider dashed className="my-1" />
{children}
</Board>
</>
);
};

export default SettingsLayout;
42 changes: 12 additions & 30 deletions src/pages/w/[workspaceId]/projects/[projectId]/settings/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import React from 'react';
import Board from '../../../../../../layout/Board';
import { Button, Input, Form, Select, Badge, Breadcrumb, message, Skeleton } from 'antd';
import { Button, Input, Form, Select, Badge, message, Skeleton, Typography } from 'antd';
import { api } from '../../../../../../utils/api';
import { useRouter } from 'next/router';
import Image from 'next/image';
import AddUserPopUp from '../../../../../../components/AddUserPopUp.tsx/AddUserPopUp';
import CustomDivider from '../../../../../../components/CustomDivider/CustomDivider';
import Link from 'next/link';
import { ProjectStatus } from '@prisma/client'
import { HomeOutlined } from '@ant-design/icons';

import type { FormInstance } from 'antd/lib/form/Form';
import type { User } from '@prisma/client';
import SettingsLayout from '../../../../../../layout/SettingsLayout';

type FormInitialValues = {
name: string | undefined;
projectLead: string | undefined;
defaultAssignee: string | undefined | null;
};

const { Text, Title } = Typography;

const Settings = () => {

const router = useRouter();
Expand Down Expand Up @@ -86,30 +86,13 @@ const Settings = () => {
return (
<>
<Skeleton active loading={projectDetails.isLoading} avatar>
<Breadcrumb
items={[
{
title: <Link href='/w/home'><HomeOutlined rev={undefined} /></Link>,
},
{
title: (
<Link href={`/w/${workspaceId}/projects/${projectId}`}>
<span>{projectDetails.data?.name}</span>
</Link>
),
},
{
title: 'Settings',
},
]}
/>
<div className="flex justify-between my-3">
<div className="flex justify-between">
<div className="flex gap-1 ">
<Image src="/logo.svg" width={64} height={64} alt={'dp'} priority />
<div className='flex flex-col gap-1-2 justify-between'>
<h1>{projectDetails.data?.name} {projectDetails.data?.status == ProjectStatus.ACTIVE ? <Badge status="success" /> :
<Title level={5} >{projectDetails.data?.name} {projectDetails.data?.status == ProjectStatus.ACTIVE ? <Badge status="success" /> :
<Badge status="error" />}
</h1>
</Title>
<Button type="default" size='middle'>Change Icon</Button>
</div>
</div>
Expand Down Expand Up @@ -157,12 +140,12 @@ const Settings = () => {
/>
</Form.Item>
</Form>
<CustomDivider className='my-4' />
<CustomDivider />
<div className="flex items-center justify-between flex-wrap gap-1-2">
<div>
<h1>Danger Zone</h1>
<p>When deleting a project, all of the data and resources within that project will be permanently removed and cannot be recovered.
</p>
<Title level={5}>Danger Zone</Title>
<Text>When deleting a project, all of the data and resources within that project will be permanently removed and cannot be recovered.
</Text>
</div>
<Button type="primary" danger onClick={handleProjectDelete} loading={isDeleting}>
Delete Project
Expand All @@ -173,10 +156,9 @@ const Settings = () => {
);
};


Settings.getLayout = function getLayout(page: React.ReactElement) {
return (
<Board>{page}</Board>
<SettingsLayout>{page}</SettingsLayout>
)
}
export default Settings
Expand Down
Loading

0 comments on commit 185615c

Please sign in to comment.