Skip to content

Commit

Permalink
Add comment box
Browse files Browse the repository at this point in the history
  • Loading branch information
dv297 committed Oct 21, 2022
1 parent 8097aeb commit 3ea974f
Show file tree
Hide file tree
Showing 14 changed files with 313 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ export interface EditableMarkdownDisplayProps {
onBlurSubmission: (data: string) => Promise<void>;
initialValue: string;
label: string;
editorHeight?: number;
}

const EditableMarkdownDisplay = (props: EditableMarkdownDisplayProps) => {
const { onBlurSubmission, initialValue } = props;
const { onBlurSubmission, initialValue, editorHeight = 200 } = props;
const {
textValue,
handleBlurSubmission,
Expand Down Expand Up @@ -50,6 +51,7 @@ const EditableMarkdownDisplay = (props: EditableMarkdownDisplayProps) => {
name={keys.text}
onBlur={handleBlurSubmission}
shouldAutoFocus
height={editorHeight}
/>
</div>
{isLoading ? (
Expand Down
18 changes: 14 additions & 4 deletions src/components/common/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import {
useForm,
} from 'react-hook-form';

interface OnSubmitHelpers {
clearFormData: () => void;
}

interface FormProps<TFieldValues extends FieldValues = FieldValues> {
children: ({
keys,
Expand All @@ -17,7 +21,7 @@ interface FormProps<TFieldValues extends FieldValues = FieldValues> {
formState: FormState<TFieldValues>;
}) => ReactNode;
defaultValues: DefaultValues<TFieldValues>;
onSubmit?: (data: TFieldValues) => void;
onSubmit?: (data: TFieldValues, helpers: OnSubmitHelpers) => void;
resolver?: Resolver<TFieldValues>;
onBlur?: (data: TFieldValues, event: FocusEvent<HTMLFormElement>) => void;
}
Expand All @@ -34,9 +38,15 @@ function Form<FormStructure extends Record<string, any>>(
resolver,
});

const submissionHandler = methods.handleSubmit(
onSubmit as (data: Record<string, any>) => void
);
const helpers: OnSubmitHelpers = {
clearFormData: () => {
methods.reset();
},
};

const submissionHandler = methods.handleSubmit((data) => {
return onSubmit?.(data as FormStructure, helpers);
});

const { formState } = methods;

Expand Down
4 changes: 3 additions & 1 deletion src/components/common/FormMarkdownEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ interface FormMarkdownEditorProps {
event: FocusEvent<HTMLFormElement | HTMLDivElement> | null
) => void;
shouldAutoFocus?: boolean;
height?: number;
}

const FormMarkdownEditor = (props: FormMarkdownEditorProps) => {
const { name, onBlur, shouldAutoFocus } = props;
const { name, onBlur, shouldAutoFocus, height = 200 } = props;
const { control } = useFormContext(); // retrieve all hook methods
const { theme } = useTheme();

Expand All @@ -44,6 +45,7 @@ const FormMarkdownEditor = (props: FormMarkdownEditorProps) => {
}}
>
<MDEditor
height={height}
value={value}
onChange={onChange}
autoFocus={shouldAutoFocus}
Expand Down
5 changes: 3 additions & 2 deletions src/components/common/FormSubmitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import Button from '@src/components/common/Button';

interface FormSubmitButtonProps {
label: string;
isDisabled?: boolean;
}

const FormSubmitButton = (props: FormSubmitButtonProps) => {
const { label } = props;
const { label, isDisabled } = props;

return (
<Button type="submit" variant="contained">
<Button type="submit" variant="contained" isDisabled={isDisabled}>
{label}
</Button>
);
Expand Down
60 changes: 60 additions & 0 deletions src/components/common/SimpleMarkdownField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import clsx from 'clsx';
import dynamic from 'next/dynamic';

const FormMarkdownEditor = dynamic(
() => import('@src/components/common/FormMarkdownEditor')
);

export interface SimpleMarkdownFieldProps {
name: string;
}

const SimpleMarkdownField = (props: SimpleMarkdownFieldProps) => {
const { name } = props;
const [isAdvancedEditorOpen, setIsAdvancedEditorOpen] = useState(false);
const { control } = useFormContext();

let inputElement = (
<Controller
name={name}
control={control}
render={({ field: { onChange, value } }) => {
return (
<textarea
rows={4}
className={clsx(
'w-full h-[100px] bg-transparent border border-gray-300 dark:border-gray-700 text-lg pl-4 py-1 rounded-lg focus:outline-accent-blue-500'
)}
value={value}
onChange={onChange}
/>
);
}}
/>
);

if (isAdvancedEditorOpen) {
inputElement = <FormMarkdownEditor name={name} height={100} />;
}

return (
<div>
<div className="w-full flex items-center">
<span className="text-xs flex-1 text-slate-600 dark:text-gray-300">
Supports Markdown
</span>
<button
className="text-sm mb-2 underline"
onClick={() => setIsAdvancedEditorOpen((value) => !value)}
>
Switch to {isAdvancedEditorOpen ? 'simple' : 'full'} editor
</button>
</div>
<div className="h-28">{inputElement}</div>
</div>
);
};

export default SimpleMarkdownField;
6 changes: 5 additions & 1 deletion src/components/pages/issue/IssueAuditEntryListingFactory.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { ReactNode } from 'react';
import dynamic from 'next/dynamic';
import { z } from 'zod';

import { IssueAuditEntrySchema } from '@src/schemas/IssueSchema';
const MarkdownPreview = dynamic(
() => import('@src/components/common/EditableDisplays/MarkdownPreview')
);

const formatDate = (dateString: Date) => {
return new Date(dateString).toLocaleDateString('en-us', {
Expand Down Expand Up @@ -41,7 +45,7 @@ const CommentListing = (props: IssueAuditEntryListingProps) => {
</p>
</div>
<div className="mt-2 text-sm text-gray-700">
<p>{issueAuditEntry.newValue}</p>
<MarkdownPreview value={issueAuditEntry.newValue} />
</div>
</div>
</>
Expand Down
130 changes: 92 additions & 38 deletions src/components/pages/issue/IssueAuditEntryTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { ReactNode, useState } from 'react';
import { ReactNode, SyntheticEvent, useState } from 'react';
import { CircularProgress } from '@mui/material';
import Box from '@mui/material/Box';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import { useQuery } from '@tanstack/react-query';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { z } from 'zod';

import Form from '@src/components/common/Form';
import FormSubmitButton from '@src/components/common/FormSubmitButton';
import SimpleMarkdownField from '@src/components/common/SimpleMarkdownField';
import IssueAuditEntryListingFactory from '@src/components/pages/issue/IssueAuditEntryListingFactory';
import { IssueAuditEntryCreateBodySchema } from '@src/schemas/IssueSchema';
import IssueService from '@src/services/IssueService';
import QueryKeys from '@src/services/QueryKeys';

Expand All @@ -17,16 +22,17 @@ interface AuditEntryListProps {
const AuditEntryList = (props: AuditEntryListProps) => {
const { filter, issueTag } = props;

const { data: issueAuditEntryResponse, isLoading } = useQuery(
const { data: issueAuditEntryResponse, isFetching } = useQuery(
[QueryKeys.ISSUE_AUDIT_ENTRY, { tag: issueTag }],
() => IssueService.getIssueAuditEntries(issueTag, { filter }),
{
cacheTime: 0,
keepPreviousData: false,
refetchOnWindowFocus: false,
}
);

if (isLoading) {
if (isFetching) {
return (
<div className="w-full h-32 flex justify-center items-center">
<CircularProgress />
Expand All @@ -39,14 +45,23 @@ const AuditEntryList = (props: AuditEntryListProps) => {
}

if (issueAuditEntryResponse.length === 0) {
let placeholderMessage = '';

if (!filter) {
placeholderMessage = 'No changes or comments made yet.';
} else if (filter === 'comment') {
placeholderMessage = 'No comments made yet';
} else if (filter === 'change') {
placeholderMessage = 'No changes made yet';
}

return (
<div className="w-full h-32 flex">
<p>No changes or comments made yet.</p>
<p>{placeholderMessage}</p>
</div>
);
}

// return <div>AuditEntryList</div>;
return (
<div>
<ul role="list" className="-mb-8">
Expand Down Expand Up @@ -76,8 +91,8 @@ function TabPanel(props: TabPanelProps) {
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
id={`issue-tabpanel-${index}`}
aria-labelledby={`ìssue-tab-${index}`}
{...other}
>
{value === index && <Box sx={{ p: 3 }}>{children}</Box>}
Expand All @@ -87,8 +102,8 @@ function TabPanel(props: TabPanelProps) {

function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
id: `issue-tab-${index}`,
'aria-controls': `issue-tabpanel-${index}`,
};
}

Expand All @@ -100,39 +115,78 @@ const IssueAuditEntryTabs = (props: IssueAuditEntryTabsProps) => {
const { issueTag } = props;
const [value, setValue] = useState(0);

const handleChange = (event: React.SyntheticEvent, newValue: number) => {
const queryClient = useQueryClient();
const mutation = useMutation(
[QueryKeys.ISSUE_AUDIT_ENTRY],
(input: z.infer<typeof IssueAuditEntryCreateBodySchema>) =>
IssueService.createIssueAuditEntry(issueTag, input),
{
onSuccess: () => {
queryClient.invalidateQueries([QueryKeys.ISSUE_AUDIT_ENTRY]);
},
}
);

const handleChange = (event: SyntheticEvent, newValue: number) => {
setValue(newValue);
};

return (
<Box sx={{ width: '100%' }}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs
value={value}
onChange={handleChange}
aria-label="basic tabs example"
>
<Tab label="All" {...a11yProps(0)} />
<Tab label="Comments" {...a11yProps(1)} />
<Tab label="History" {...a11yProps(2)} />
</Tabs>
<div className="relative">
<Form
defaultValues={{ comment: '' }}
onSubmit={async (data, helpers) => {
if (data.comment !== '') {
await mutation.mutate({
type: 'COMMENT',
oldValue: null,
newValue: data.comment,
});
helpers.clearFormData();
}
}}
>
{({ keys }) => {
return (
<div>
<SimpleMarkdownField name={keys.comment} />
<FormSubmitButton
label="Submit"
isDisabled={mutation.isLoading}
/>
</div>
);
}}
</Form>
<Box sx={{ width: '100%' }}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs
value={value}
onChange={handleChange}
aria-label="Issue History"
>
<Tab label="All" {...a11yProps(0)} />
<Tab label="Comments" {...a11yProps(1)} />
<Tab label="History" {...a11yProps(2)} />
</Tabs>
</Box>
{value === 0 && (
<TabPanel value={value} index={0}>
<AuditEntryList issueTag={issueTag} />
</TabPanel>
)}
{value === 1 && (
<TabPanel value={value} index={1}>
<AuditEntryList filter="comment" issueTag={issueTag} />
</TabPanel>
)}
{value === 2 && (
<TabPanel value={value} index={2}>
<AuditEntryList filter="change" issueTag={issueTag} />
</TabPanel>
)}
</Box>
{value === 0 && (
<TabPanel value={value} index={0}>
<AuditEntryList issueTag={issueTag} />
</TabPanel>
)}
{value === 1 && (
<TabPanel value={value} index={1}>
<AuditEntryList filter="comment" issueTag={issueTag} />
</TabPanel>
)}
{value === 2 && (
<TabPanel value={value} index={2}>
<AuditEntryList filter="change" issueTag={issueTag} />
</TabPanel>
)}
</Box>
</div>
);
};

Expand Down
Loading

0 comments on commit 3ea974f

Please sign in to comment.