Skip to content

Commit

Permalink
Implemented AddQuestions modal (#1170)
Browse files Browse the repository at this point in the history
* implemented AddQuestions modal

* fix styles

* added category filter

* fix

* added no items text to filter

* refactored types
  • Loading branch information
Tolik170 authored Sep 26, 2023
1 parent ae996ce commit 4b1ca6a
Show file tree
Hide file tree
Showing 31 changed files with 774 additions and 164 deletions.
16 changes: 13 additions & 3 deletions src/components/enhanced-table/EnhancedTable.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useTranslation } from 'react-i18next'

import { SxProps } from '@mui/material'
import ReportIcon from '@mui/icons-material/Report'
import Box from '@mui/material/Box'
import Paper from '@mui/material/Paper'
Expand All @@ -12,6 +13,7 @@ import EnhancedTableRow from '~/components/enhanced-table/enhanced-table-row/Enh
import FilterRow from '~/components/enhanced-table/filter-row/FilterRow'
import Loader from '~/components/loader/Loader'

import { spliceSx } from '~/utils/helper-functions'
import { styles } from '~/components/enhanced-table/EnhancedTable.styles'
import {
TableColumn,
Expand All @@ -23,7 +25,7 @@ import {
TableSort
} from '~/types'

export interface EnhancedTableProps<I, F> extends TableProps {
export interface EnhancedTableProps<I, F> extends Omit<TableProps, 'style'> {
columns: TableColumn<I>[]
isSelection?: boolean
rowActions?: TableRowAction[]
Expand All @@ -35,6 +37,10 @@ export interface EnhancedTableProps<I, F> extends TableProps {
onRowClick?: (item: I) => void
emptyTableKey?: string
selectedRows?: I[]
style?: {
root?: SxProps
tableContainer?: SxProps
}
}

const EnhancedTable = <I extends TableItem, F = undefined>({
Expand All @@ -49,6 +55,7 @@ const EnhancedTable = <I extends TableItem, F = undefined>({
data,
emptyTableKey = 'table.noExactMatches',
selectedRows = [],
style = {},
...props
}: EnhancedTableProps<I, F>) => {
const { t } = useTranslation()
Expand All @@ -69,7 +76,10 @@ const EnhancedTable = <I extends TableItem, F = undefined>({
))

const tableBody = (
<TableContainer data-testid='enhance-table-container'>
<TableContainer
data-testid='enhance-table-container'
sx={style.tableContainer}
>
<Table {...props}>
<EnhancedTableHead
columns={columns}
Expand Down Expand Up @@ -108,7 +118,7 @@ const EnhancedTable = <I extends TableItem, F = undefined>({
tableBody

return (
<Box sx={styles.root}>
<Box sx={spliceSx(styles.root, style.root)}>
<Paper sx={styles.paper}>{tableContent}</Paper>
</Box>
)
Expand Down
2 changes: 1 addition & 1 deletion src/components/enhanced-table/filter-row/FilterRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const FilterRow = ({ columns, filter = {}, isSelection }) => {
/>
))

const emptyCell = isSelection && <TableCell />
const emptyCell = isSelection && filters && <TableCell />

return (
<TableRow>
Expand Down
76 changes: 76 additions & 0 deletions src/components/filter-selector/FilterSelector.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import palette from '~/styles/app-theme/app.pallete'
import { commonHoverShadow } from '~/styles/app-theme/custom-shadows'
import { TypographyVariantEnum } from '~/types'

export const styles = {
root: {
maxWidth: { xs: '200px', md: '300px' },
backgroundColor: 'primary.50',
display: 'flex',
alignItems: 'center',
px: '12px',
borderRadius: '100px'
},
openMenuBtn: { p: 0, ml: '5px' },
text: { typography: TypographyVariantEnum.Subtitle1 },
chosenFilters: {
typography: TypographyVariantEnum.Subtitle1,
ml: '4px',
fontWeight: 500,
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis'
},
arrowIcon: (open: boolean) => ({
color: 'primary.900',
transform: `rotate(${open ? 180 : 0}deg)`,
transition: 'transform 0.3s ease'
}),
menu: (loading: boolean) => ({
'& .simplebar-content': {
...(loading && { height: '216px', display: 'flex' })
}
}),
menuPaperProps: {
style: {
width: '300px',
marginTop: '8px',
borderRadius: '8px',
boxShadow: commonHoverShadow
}
},
inputWrapper: { p: '15px 20px 0px 20px' },
input: {
m: '0 auto',
width: '100%',
borderRadius: '6px',
p: { xs: 0, sm: 0 },
border: `1px solid ${palette.primary[400]}`,
'& > div': { pl: '10px' }
},
clearAll: (isSelected: boolean) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'end',
columnGap: '3px',
typography: TypographyVariantEnum.Subtitle2,
...(!isSelected && { color: 'primary.200' }),
...(isSelected && { cursor: 'pointer' }),
m: '15px 20px 0 0'
}),
clearIcon: { height: '18px', width: '18px' },
divider: {
border: `1px solid ${palette.primary[200]}`,
mt: '8px'
},
noMatches: {
typography: TypographyVariantEnum.Subtitle1,
p: '15px 25px',
display: 'flex',
alignItems: 'center',
columnGap: 1
},
scrollableContent: { maxHeight: '216px' },
loader: { color: 'primary.700' },
noItemsIcon: { color: 'primary.400' }
}
160 changes: 160 additions & 0 deletions src/components/filter-selector/FilterSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { useState, ChangeEvent, useMemo, Dispatch, SetStateAction } from 'react'
import { useTranslation } from 'react-i18next'
import SimpleBar from 'simplebar-react'
import Box from '@mui/material/Box'
import Menu, { MenuProps } from '@mui/material/Menu'
import MenuItem from '@mui/material/MenuItem'
import { PopoverOrigin } from '@mui/material/Popover'
import Divider from '@mui/material/Divider'
import Checkbox from '@mui/material/Checkbox'
import Typography from '@mui/material/Typography'
import IconButton from '@mui/material/IconButton'
import ClearIcon from '@mui/icons-material/Clear'
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'

import useAxios from '~/hooks/use-axios'
import Loader from '~/components/loader/Loader'
import InputWithIcon from '~/components/input-with-icon/InputWithIcon'

import { defaultResponses } from '~/constants'
import { styles } from '~/components/filter-selector/FilterSelector.styles'
import { ServiceFunction } from '~/types'

interface FilterSelectorProps<T> extends Omit<MenuProps, 'open'> {
title: string
service: ServiceFunction<T[]>
selectedItems: string[]
setSelectedItems: Dispatch<SetStateAction<string[]>>
position?: PopoverOrigin['horizontal']
}

const FilterSelector = <T extends string>({
title,
service,
selectedItems,
setSelectedItems,
position = 'left',
...props
}: FilterSelectorProps<T>) => {
const { t } = useTranslation()
const [menuAnchor, setMenuAnchor] = useState<null | HTMLElement>(null)
const [inputValue, setInputValue] = useState<string>('')

const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value)
}

const handleInputReset = () => {
setInputValue('')
}

const handleMenuOpen = () => {
setMenuAnchor(document.getElementById('menu-filter'))
}

const handleMenuClose = () => {
setMenuAnchor(null)
}

const onMenuItemClick = (item: string) => {
if (selectedItems.includes(item)) {
setSelectedItems(selectedItems.filter((selected) => selected !== item))
} else {
setSelectedItems([...selectedItems, item])
}
}

const onClearAll = () => {
selectedItems.length && setSelectedItems([])
}

const { loading, response } = useAxios<T[]>({
service,
defaultResponse: defaultResponses.array
})

const filteredItems = useMemo(
() =>
response.filter((item) =>
item.toLowerCase().includes(inputValue.toLowerCase())
),
[response, inputValue]
)

const menuItems = filteredItems.map((item) => (
<MenuItem key={item} onClick={() => onMenuItemClick(item)} sx={styles.text}>
<Checkbox checked={selectedItems.includes(item)} />
{item}
</MenuItem>
))

const scrollableContent = filteredItems.length ? (
menuItems
) : (
<Box sx={styles.noMatches}>
<ErrorOutlineIcon sx={styles.noItemsIcon} />
{t('common.noItems')}
</Box>
)

const itemsLoad = !response.length && loading
const chosenFiltersText = selectedItems.length
? selectedItems.join(', ')
: t('cooperationsPage.tabs.all')

return (
<Box id='menu-filter' sx={styles.root}>
<Typography sx={styles.text}>{title}:</Typography>
<Typography sx={styles.chosenFilters}>{chosenFiltersText}</Typography>
<IconButton
disableRipple
onClick={handleMenuOpen}
sx={styles.openMenuBtn}
>
<KeyboardArrowDownIcon sx={styles.arrowIcon(!!menuAnchor)} />
</IconButton>

<Menu
PaperProps={styles.menuPaperProps}
anchorEl={menuAnchor}
anchorOrigin={{ vertical: 'bottom', horizontal: position }}
onClose={handleMenuClose}
open={!!menuAnchor}
sx={styles.menu(itemsLoad)}
transformOrigin={{ vertical: 'top', horizontal: position }}
{...props}
>
<Box sx={styles.inputWrapper}>
<InputWithIcon
onChange={handleInputChange}
onClear={handleInputReset}
placeholder={t('common.search')}
sx={styles.input}
value={inputValue}
/>
</Box>

<Typography
onClick={onClearAll}
sx={styles.clearAll(!!selectedItems.length)}
>
<ClearIcon sx={styles.clearIcon} />
{t('header.notifications.clearAll')}
</Typography>

<Divider sx={styles.divider} />

<SimpleBar style={styles.scrollableContent}>
{itemsLoad ? (
<Loader pageLoad size={20} sx={styles.loader} />
) : (
scrollableContent
)}
</SimpleBar>
</Menu>
</Box>
)
}

export default FilterSelector
3 changes: 2 additions & 1 deletion src/components/question/Question.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,14 @@ const Question: FC<QuestionProps> = ({ question, category, sx = {} }) => {
}
}
]

const menuItems = rowActions.map(({ label, func }) => (
<MenuItem key={label} onClick={() => void onAction(func)}>
{label}
</MenuItem>
))

const answersList = question.items.map((answer, i) => (
const answersList = question.answers.map((answer, i) => (
<Box key={answer.text} sx={styles.answer}>
<FormControlLabel
checked={i == 1}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { ReactElement, ReactNode } from 'react'
import { Box, Typography, SxProps } from '@mui/material'
import { SxProps } from '@mui/material'
import Box from '@mui/material/Box'
import Typography from '@mui/material/Typography'

import { styles } from '~/components/title-with-description/TitleWithDescription.styles'
import { ComponentEnum } from '~/types'

interface TitleWithDescriptionProps {
title: string | ReactElement
Expand All @@ -20,7 +24,7 @@ const TitleWithDescription = ({
return (
<Box sx={style.wrapper}>
<Typography sx={style.title}>{title}</Typography>
<Typography component={'span'} sx={style.description}>
<Typography component={ComponentEnum.Span} sx={style.description}>
{description}
</Typography>
</Box>
Expand Down
2 changes: 1 addition & 1 deletion src/components/user-table/UserTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const UserTable = ({
status: 'all',
role
})
const select = useSelect()
const select = useSelect({})
const sort = useSort({ initialSort })
const filter = useFilter({ initialFilters })
const pagination = usePagination({ itemsCount })
Expand Down
3 changes: 3 additions & 0 deletions src/constants/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ export const URLs = {
questions: {
get: '/questions',
delete: '/questions'
},
resourcesCategories: {
getNames: '/resources-categories/names'
}
},
messages: {
Expand Down
1 change: 1 addition & 0 deletions src/constants/translations/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"typeError": "Wrong file type. Only .pdf/.jpg/.jpeg files are allowed.",
"allFilesSizeError": "All files size error",
"fileSizeError": "Розмір одного файлу не може перевищувати 10 Мб.",
"noItems": "No items found",
"labels": {
"email": "Email",
"firstName": "First name",
Expand Down
3 changes: 2 additions & 1 deletion src/constants/translations/en/my-resources-page.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
"attachments": {
"addBtn": "Add Attachment",
"addFromAttachments": "Add files from your Attachments",
"add": "Add files from your Attachments",
"file": "File",
"size": "Size",
"lastUpdate": "Last update",
Expand All @@ -44,6 +44,7 @@
},
"questions": {
"addBtn": "New question",
"add": "Add Questions",
"title": "Title",
"category": "Category",
"updated": "Last updates",
Expand Down
1 change: 1 addition & 0 deletions src/constants/translations/ua/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"typeError": "Неправильний тип файлу. Дозволяються лише файли .pdf/.jpg/.jpeg",
"allFilesSizeError": "Помилка розміру всіх файлів",
"fileSizeError": "Size for one file cannot be more than 10 MB.",
"noItems": "Елементів не знайдено",
"labels": {
"sendMessage": "Надіслати повідомлення",
"viewDetails": "Переглянути деталі",
Expand Down
Loading

0 comments on commit 4b1ca6a

Please sign in to comment.