Skip to content

Commit d66dd8d

Browse files
authored
Merge pull request #3530 from Northeastern-Electric-Racing/#3529-onboarding-markdown
#3529 onboarding markdown
2 parents 8321338 + 96d99f1 commit d66dd8d

10 files changed

Lines changed: 348 additions & 95 deletions

File tree

src/frontend/src/components/NERFormModal.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ interface NERFormModalProps<T extends FieldValues> extends NERModalProps {
88
onFormSubmit: (data: T) => void;
99
formId: string;
1010
children?: ReactNode;
11+
paperProps?: any;
1112
}
1213

1314
const NERFormModal = ({
@@ -23,7 +24,8 @@ const NERFormModal = ({
2324
disabled,
2425
children,
2526
showCloseButton,
26-
hideBackDrop = false
27+
hideBackDrop = false,
28+
paperProps
2729
}: NERFormModalProps<any>) => {
2830
/**
2931
* Wrapper function for onSubmit so that form data is reset after submit
@@ -53,6 +55,7 @@ const NERFormModal = ({
5355
disabled={disabled}
5456
showCloseButton={showCloseButton}
5557
hideBackDrop={hideBackDrop}
58+
paperProps={paperProps}
5659
>
5760
<form id={formId} onSubmit={handleFormSubmit} noValidate>
5861
{children}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import React from 'react';
2+
import ReactMarkdown from 'react-markdown';
3+
import { Box } from '@mui/material';
4+
5+
interface NERMarkdownProps {
6+
markdown: string;
7+
}
8+
9+
const NERMarkdown = ({ markdown }: NERMarkdownProps) => {
10+
return (
11+
<Box
12+
sx={{
13+
fontSize: '16px',
14+
lineHeight: 1.3,
15+
'& h1': {
16+
fontSize: '1.4em',
17+
margin: '0.8em 0 0.4em 0',
18+
fontWeight: 600
19+
},
20+
'& h2': {
21+
fontSize: '1.2em',
22+
margin: '0.7em 0 0.3em 0',
23+
fontWeight: 600
24+
},
25+
'& h3': {
26+
fontSize: '1.1em',
27+
margin: '0.6em 0 0.2em 0',
28+
fontWeight: 600
29+
},
30+
'& h4, & h5, & h6': {
31+
fontSize: '1em',
32+
margin: '0.5em 0 0.2em 0',
33+
fontWeight: 600
34+
},
35+
'& p': {
36+
margin: '0.3em 0'
37+
},
38+
'& ul, & ol': {
39+
margin: '0.4em 0',
40+
paddingLeft: '1.5em'
41+
},
42+
'& li': {
43+
margin: '0.1em 0'
44+
},
45+
'& li > ul, & li > ol': {
46+
margin: '0.2em 0'
47+
},
48+
'& a': {
49+
color: '#dc2626',
50+
textDecoration: 'underline',
51+
'&:hover': {
52+
color: '#b91c1c',
53+
textDecoration: 'none'
54+
}
55+
},
56+
'& code': {
57+
backgroundColor: '#f3f4f6',
58+
color: '#374151',
59+
padding: '0.1em 0.3em',
60+
borderRadius: '3px',
61+
fontSize: '0.9em'
62+
},
63+
'& pre': {
64+
backgroundColor: '#f3f4f6',
65+
color: '#374151',
66+
padding: '0.5em',
67+
borderRadius: '4px',
68+
overflowX: 'auto',
69+
margin: '0.4em 0'
70+
},
71+
'& pre code': {
72+
background: 'none',
73+
color: 'inherit',
74+
padding: 0
75+
},
76+
'& blockquote': {
77+
borderLeft: '3px solid #d1d5db',
78+
paddingLeft: '0.8em',
79+
margin: '0.4em 0',
80+
fontStyle: 'italic',
81+
color: '#6b7280'
82+
},
83+
'& table': {
84+
borderCollapse: 'collapse',
85+
margin: '0.4em 0',
86+
fontSize: '0.9em'
87+
},
88+
'& th, & td': {
89+
border: '1px solid #d1d5db',
90+
padding: '0.3em 0.5em',
91+
textAlign: 'left'
92+
},
93+
'& th': {
94+
backgroundColor: '#f9fafb',
95+
fontWeight: 600
96+
},
97+
'& hr': {
98+
border: 'none',
99+
borderTop: '1px solid #e5e7eb',
100+
margin: '0.6em 0'
101+
},
102+
'& img': {
103+
maxWidth: '100%',
104+
height: 'auto',
105+
margin: '0.3em 0'
106+
}
107+
}}
108+
>
109+
<ReactMarkdown>{markdown}</ReactMarkdown>
110+
</Box>
111+
);
112+
};
113+
114+
export default NERMarkdown;

src/frontend/src/components/NERModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const NERModal = ({
4949
onClose={onHide}
5050
PaperProps={{
5151
style: paperProps
52-
? { ...paperProps, borderRadius: '10px', maxWidth: '700px' }
52+
? { borderRadius: '10px', maxWidth: '700px', ...paperProps }
5353
: { borderRadius: '10px', maxWidth: '700px' }
5454
}}
5555
>

src/frontend/src/pages/AdminToolsPage/OnboardingConfig/Checklists/AdminSubtaskSection.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import NERDeleteModal from '../../../../components/NERDeleteModal';
1111
import { useToast } from '../../../../hooks/toasts.hooks';
1212
import { useDeleteChecklist } from '../../../../hooks/onboarding.hook';
1313
import EditSubtaskModal from './EditSubtaskModal';
14+
import NERMarkdown from '../../../../components/NERMarkdown';
1415

1516
interface AdminSubtaskSectionProps {
1617
parentTask: Checklist;
@@ -88,7 +89,7 @@ const AdminSubtaskSection: React.FC<AdminSubtaskSectionProps> = ({ parentTask })
8889
marginBottom: 1
8990
}}
9091
>
91-
<Typography color={theme.palette.text.primary}>{description}</Typography>
92+
<NERMarkdown markdown={description} />
9293
</Box>
9394
))}
9495
<Box sx={{ display: 'flex', justifyContent: 'flex-start', mb: -1 }}>

src/frontend/src/pages/AdminToolsPage/OnboardingConfig/Checklists/CreateChecklistModal.tsx

Lines changed: 113 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,25 @@ import LoadingIndicator from '../../../../components/LoadingIndicator';
33
import { ChecklistCreateArgs, useCreateChecklist } from '../../../../hooks/onboarding.hook';
44
import { useToast } from '../../../../hooks/toasts.hooks';
55
import { yupResolver } from '@hookform/resolvers/yup';
6-
import { FormControl, FormLabel, TextField, InputAdornment, Checkbox, IconButton, useTheme } from '@mui/material';
7-
import { Box } from '@mui/system';
6+
import {
7+
FormControl,
8+
FormLabel,
9+
TextField,
10+
InputAdornment,
11+
Checkbox,
12+
IconButton,
13+
useTheme,
14+
Typography
15+
} from '@mui/material';
16+
import { Box, Stack } from '@mui/system';
817
import React, { useState } from 'react';
918
import { useForm, useFieldArray, Controller } from 'react-hook-form';
1019
import { CreateChecklistPreview } from 'shared';
1120
import NERFormModal from '../../../../components/NERFormModal';
1221
import * as yup from 'yup';
1322
import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline';
1423
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
24+
import NERMarkdown from '../../../../components/NERMarkdown';
1525

1626
interface CreateChecklistModalProps {
1727
open: boolean;
@@ -64,6 +74,7 @@ const CreateChecklistModal = ({ open, handleClose, teamId, teamTypeId }: CreateC
6474
const {
6575
handleSubmit,
6676
control,
77+
watch,
6778
reset,
6879
formState: { errors }
6980
} = useForm<ChecklistFormValues>({
@@ -136,6 +147,7 @@ const CreateChecklistModal = ({ open, handleClose, teamId, teamTypeId }: CreateC
136147
onFormSubmit={onFormSubmit}
137148
formId={'create-task-form'}
138149
showCloseButton
150+
paperProps={{ maxWidth: '80vw' }}
139151
>
140152
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
141153
<FormControl fullWidth>
@@ -255,53 +267,113 @@ const CreateChecklistModal = ({ open, handleClose, teamId, teamTypeId }: CreateC
255267
color: theme.palette.error.main,
256268
fontWeight: 'bold',
257269
fontSize: '1.5rem',
258-
textDecoration: 'underline'
270+
textDecoration: 'underline',
271+
mb: 2
259272
}}
260273
>
261274
Descriptions*
262275
</FormLabel>
263276
{fields.map((item, index) => (
264-
<Box key={item.id} sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
265-
<Controller
266-
name={`descriptions.${index}.name`}
267-
control={control}
268-
defaultValue={item.name || ''}
269-
render={({ field }) => (
270-
<TextField
271-
{...field}
272-
placeholder="Enter description..."
273-
fullWidth
274-
multiline
275-
variant="outlined"
276-
InputProps={{
277-
endAdornment: (
278-
<InputAdornment position="end">
279-
{index !== 0 && (
277+
<Stack key={item.id} direction={'row'} spacing={3} sx={{ mb: 3 }}>
278+
<Box
279+
sx={{
280+
display: 'flex',
281+
width: '30vw',
282+
alignItems: 'stretch'
283+
}}
284+
>
285+
<Controller
286+
name={`descriptions.${index}.name`}
287+
control={control}
288+
defaultValue={item.name || ''}
289+
render={({ field }) => (
290+
<TextField
291+
{...field}
292+
placeholder="Enter description markdown..."
293+
fullWidth
294+
multiline
295+
variant="outlined"
296+
minRows={12}
297+
maxRows={20}
298+
InputProps={{
299+
endAdornment: index !== 0 && (
300+
<InputAdornment position="end" sx={{ alignSelf: 'flex-start', mt: 1 }}>
280301
<IconButton onClick={() => remove(index)}>
281302
<RemoveCircleOutlineIcon sx={{ color: 'white' }} />
282303
</IconButton>
283-
)}
284-
</InputAdornment>
285-
),
286-
disableUnderline: true,
287-
sx: { '& fieldset': { border: 'none' } }
288-
}}
289-
sx={{
290-
backgroundColor: theme.palette.background.paper,
291-
borderRadius: 5,
292-
mt: 1,
293-
width: '100%',
294-
...(index === 0 && {
295-
minHeight: '150px',
296-
fontSize: '1.25rem'
297-
})
298-
}}
299-
error={!!errors.descriptions?.[index]?.name}
300-
helperText={errors.descriptions?.[index]?.name?.message}
301-
/>
302-
)}
303-
/>
304-
</Box>
304+
</InputAdornment>
305+
),
306+
disableUnderline: true,
307+
sx: {
308+
'& fieldset': { border: 'none' },
309+
fontSize: '1.1rem',
310+
lineHeight: 1.6,
311+
padding: 2
312+
}
313+
}}
314+
sx={{
315+
backgroundColor: theme.palette.background.paper,
316+
borderRadius: 3,
317+
width: '100%',
318+
'& .MuiInputBase-root': {
319+
alignItems: 'flex-start'
320+
}
321+
}}
322+
error={!!errors.descriptions?.[index]?.name}
323+
helperText={errors.descriptions?.[index]?.name?.message}
324+
/>
325+
)}
326+
/>
327+
</Box>
328+
<Box
329+
sx={{
330+
backgroundColor: theme.palette.background.paper,
331+
borderRadius: 3,
332+
width: '30vw',
333+
padding: 2,
334+
display: 'flex',
335+
flexDirection: 'column',
336+
overflow: 'hidden',
337+
wordWrap: 'break-word',
338+
border: `1px solid ${theme.palette.divider}`,
339+
'& *': {
340+
wordWrap: 'break-word',
341+
overflowWrap: 'break-word'
342+
}
343+
}}
344+
>
345+
<Typography
346+
variant="caption"
347+
sx={{
348+
color: theme.palette.text.secondary,
349+
mb: 1,
350+
fontStyle: 'italic'
351+
}}
352+
>
353+
Preview:
354+
</Typography>
355+
<Box
356+
sx={{
357+
flex: 1,
358+
overflow: 'auto'
359+
}}
360+
>
361+
{watch(`descriptions.${index}.name`) ? (
362+
<NERMarkdown markdown={watch(`descriptions.${index}.name`)} />
363+
) : (
364+
<Typography
365+
variant="body2"
366+
sx={{
367+
color: theme.palette.text.disabled,
368+
fontStyle: 'italic'
369+
}}
370+
>
371+
Start typing to see formatted markdown...
372+
</Typography>
373+
)}
374+
</Box>
375+
</Box>
376+
</Stack>
305377
))}
306378

307379
<IconButton

0 commit comments

Comments
 (0)