update build ok,
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { LoadingButton } from '@mui/lab';
|
import { LoadingButton } from '@mui/lab';
|
||||||
@@ -12,6 +14,10 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { paths } from '@/paths';
|
import { paths } from '@/paths';
|
||||||
import { logger } from '@/lib/default-logger';
|
import { logger } from '@/lib/default-logger';
|
||||||
import { toast } from '@/components/core/toaster';
|
import { toast } from '@/components/core/toaster';
|
||||||
|
import type { LessonType } from '@/components/dashboard/lesson_type/ILessonType';
|
||||||
|
|
||||||
|
import type { Filters } from '@/components/dashboard/lesson_type/lesson-types-filters';
|
||||||
|
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
searchParams: {
|
searchParams: {
|
||||||
@@ -27,7 +33,22 @@ interface PageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Page({ searchParams }: PageProps): React.JSX.Element {
|
export default function Page({ searchParams }: PageProps): React.JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { email, phone, sortDir, status, name, visible, type } = searchParams;
|
const { email, phone, sortDir, status, name, visible, type } = searchParams;
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const [isLoadingAddPage, setIsLoadingAddPage] = React.useState<boolean>(false);
|
||||||
|
const [lessonTypesData, setLessonTypesData] = React.useState<LessonType[]>([]);
|
||||||
|
const sortedLessonTypes = applySort(lessonTypesData, sortDir);
|
||||||
|
const filteredLessonTypes = applyFilters(sortedLessonTypes, {
|
||||||
|
email,
|
||||||
|
phone,
|
||||||
|
status,
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
visible,
|
||||||
|
//
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -38,7 +59,75 @@ export default function Page({ searchParams }: PageProps): React.JSX.Element {
|
|||||||
width: 'var(--Content-width)',
|
width: 'var(--Content-width)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
hello lesson_type
|
<Stack spacing={4}>
|
||||||
|
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={3} sx={{ alignItems: 'flex-start' }}>
|
||||||
|
<Box sx={{ flex: '1 1 auto' }}>
|
||||||
|
<Typography variant="h4">{t('Lesson Types')}</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||||
|
<LoadingButton
|
||||||
|
loading={isLoadingAddPage}
|
||||||
|
onClick={(): void => {
|
||||||
|
setIsLoadingAddPage(true);
|
||||||
|
router.push(paths.dashboard.lesson_types.create);
|
||||||
|
}}
|
||||||
|
startIcon={<PlusIcon />}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
{/* add new lesson type */}
|
||||||
|
{t('dashboard.lessonTypes.add')}
|
||||||
|
</LoadingButton>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sorting and filtering has to be done on the server.
|
||||||
|
|
||||||
|
function applySort(row: LessonType[], sortDir: 'asc' | 'desc' | undefined): LessonType[] {
|
||||||
|
return row.sort((a, b) => {
|
||||||
|
if (sortDir === 'asc') {
|
||||||
|
return a.createdAt.getTime() - b.createdAt.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.createdAt.getTime() - a.createdAt.getTime();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyFilters(row: LessonType[], { email, phone, status, name, visible }: Filters): LessonType[] {
|
||||||
|
return row.filter((item) => {
|
||||||
|
if (email) {
|
||||||
|
if (!item.email?.toLowerCase().includes(email.toLowerCase())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phone) {
|
||||||
|
if (!item.phone?.toLowerCase().includes(phone.toLowerCase())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
if (item.status !== status) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
if (!item.name?.toLowerCase().includes(name.toLowerCase())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
if (!item.visible?.toLowerCase().includes(visible.toLowerCase())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
const helloworld = 'helloworld';
|
|
||||||
|
|
||||||
export { helloworld };
|
|
@@ -0,0 +1,57 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { dayjs } from '@/lib/dayjs';
|
||||||
|
|
||||||
|
export interface LessonType {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
pos: number;
|
||||||
|
visible: 'visible' | 'hidden';
|
||||||
|
createdAt: Date;
|
||||||
|
//
|
||||||
|
// original
|
||||||
|
// id: string;
|
||||||
|
// name: string;
|
||||||
|
//
|
||||||
|
avatar?: string;
|
||||||
|
email: string;
|
||||||
|
phone?: string;
|
||||||
|
quota: number;
|
||||||
|
status: 'pending' | 'active' | 'blocked';
|
||||||
|
// createdAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LessonTypeDefaultValue: LessonType = {
|
||||||
|
id: 'string',
|
||||||
|
name: 'string',
|
||||||
|
type: 'string',
|
||||||
|
pos: 1,
|
||||||
|
visible: 'visible',
|
||||||
|
createdAt: dayjs().toDate(),
|
||||||
|
//
|
||||||
|
// original
|
||||||
|
// id: 'string',
|
||||||
|
// name: 'string',
|
||||||
|
//
|
||||||
|
avatar: 'string',
|
||||||
|
email: 'string',
|
||||||
|
phone: 'string',
|
||||||
|
quota: 1,
|
||||||
|
status: 'pending',
|
||||||
|
// createdAt: Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface DBLessonType {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
pos: number;
|
||||||
|
visible: 'visible' | 'hidden';
|
||||||
|
createdAt: Date;
|
||||||
|
created: 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Helloworld {
|
||||||
|
id: string;
|
||||||
|
}
|
@@ -1,3 +0,0 @@
|
|||||||
const helloworld = 'helloworld';
|
|
||||||
|
|
||||||
export { helloworld };
|
|
@@ -0,0 +1,403 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Chip from '@mui/material/Chip';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import FormControl from '@mui/material/FormControl';
|
||||||
|
import OutlinedInput from '@mui/material/OutlinedInput';
|
||||||
|
import Select from '@mui/material/Select';
|
||||||
|
import type { SelectChangeEvent } from '@mui/material/Select';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
|
import Tab from '@mui/material/Tab';
|
||||||
|
import Tabs from '@mui/material/Tabs';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { paths } from '@/paths';
|
||||||
|
import { FilterButton, FilterPopover, useFilterContext } from '@/components/core/filter-button';
|
||||||
|
import { Option } from '@/components/core/option';
|
||||||
|
|
||||||
|
import { LessonType } from './ILessonType';
|
||||||
|
import { useLessonTypesSelection } from './lesson-types-selection-context';
|
||||||
|
|
||||||
|
export interface Filters {
|
||||||
|
email?: string;
|
||||||
|
phone?: string;
|
||||||
|
status?: string;
|
||||||
|
name?: string;
|
||||||
|
visible?: string;
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SortDir = 'asc' | 'desc';
|
||||||
|
|
||||||
|
export interface LessonTypesFiltersProps {
|
||||||
|
filters?: Filters;
|
||||||
|
sortDir?: SortDir;
|
||||||
|
fullData: LessonType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LessonTypesFilters({
|
||||||
|
filters = {},
|
||||||
|
sortDir = 'desc',
|
||||||
|
fullData,
|
||||||
|
}: LessonTypesFiltersProps): React.JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { email, phone, status, name, visible, type } = filters;
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const selection = useLessonTypesSelection();
|
||||||
|
|
||||||
|
function getVisible(): number {
|
||||||
|
return fullData.reduce((count, item: LessonType) => {
|
||||||
|
return item.visible === 'visible' ? count + 1 : count;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHidden(): number {
|
||||||
|
return fullData.reduce((count, item: LessonType) => {
|
||||||
|
return item.visible === 'hidden' ? count + 1 : count;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The tabs should be generated using API data.
|
||||||
|
const tabs = [
|
||||||
|
{ label: 'All', value: '', count: fullData.length },
|
||||||
|
// { label: 'Active', value: 'active', count: 3 },
|
||||||
|
// { label: 'Pending', value: 'pending', count: 1 },
|
||||||
|
// { label: 'Blocked', value: 'blocked', count: 1 },
|
||||||
|
{ label: t('visible'), value: 'visible', count: getVisible() },
|
||||||
|
{ label: t('hidden'), value: 'hidden', count: getHidden() },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const updateSearchParams = React.useCallback(
|
||||||
|
(newFilters: Filters, newSortDir: SortDir): void => {
|
||||||
|
const searchParams = new URLSearchParams();
|
||||||
|
|
||||||
|
if (newSortDir === 'asc') {
|
||||||
|
searchParams.set('sortDir', newSortDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newFilters.status) {
|
||||||
|
searchParams.set('status', newFilters.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newFilters.email) {
|
||||||
|
searchParams.set('email', newFilters.email);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newFilters.phone) {
|
||||||
|
searchParams.set('phone', newFilters.phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newFilters.name) {
|
||||||
|
searchParams.set('name', newFilters.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newFilters.type) {
|
||||||
|
searchParams.set('type', newFilters.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newFilters.visible) {
|
||||||
|
searchParams.set('visible', newFilters.visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
router.push(`${paths.dashboard.lesson_types.list}?${searchParams.toString()}`);
|
||||||
|
},
|
||||||
|
[router]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClearFilters = React.useCallback(() => {
|
||||||
|
updateSearchParams({}, sortDir);
|
||||||
|
}, [updateSearchParams, sortDir]);
|
||||||
|
|
||||||
|
const handleStatusChange = React.useCallback(
|
||||||
|
(_: React.SyntheticEvent, value: string) => {
|
||||||
|
updateSearchParams({ ...filters, status: value }, sortDir);
|
||||||
|
},
|
||||||
|
[updateSearchParams, filters, sortDir]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleVisibleChange = React.useCallback(
|
||||||
|
(_: React.SyntheticEvent, value: string) => {
|
||||||
|
updateSearchParams({ ...filters, visible: value }, sortDir);
|
||||||
|
},
|
||||||
|
[updateSearchParams, filters, sortDir]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleNameChange = React.useCallback(
|
||||||
|
(value?: string) => {
|
||||||
|
updateSearchParams({ ...filters, name: value }, sortDir);
|
||||||
|
},
|
||||||
|
[updateSearchParams, filters, sortDir]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTypeChange = React.useCallback(
|
||||||
|
(value?: string) => {
|
||||||
|
updateSearchParams({ ...filters, type: value }, sortDir);
|
||||||
|
},
|
||||||
|
[updateSearchParams, filters, sortDir]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleEmailChange = React.useCallback(
|
||||||
|
(value?: string) => {
|
||||||
|
updateSearchParams({ ...filters, email: value }, sortDir);
|
||||||
|
},
|
||||||
|
[updateSearchParams, filters, sortDir]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlePhoneChange = React.useCallback(
|
||||||
|
(value?: string) => {
|
||||||
|
updateSearchParams({ ...filters, phone: value }, sortDir);
|
||||||
|
},
|
||||||
|
[updateSearchParams, filters, sortDir]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSortChange = React.useCallback(
|
||||||
|
(event: SelectChangeEvent) => {
|
||||||
|
updateSearchParams(filters, event.target.value as SortDir);
|
||||||
|
},
|
||||||
|
[updateSearchParams, filters]
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasFilters = status || email || phone || visible || name || type;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Tabs onChange={handleVisibleChange} sx={{ px: 3 }} value={visible ?? ''} variant="scrollable">
|
||||||
|
{tabs.map((tab) => (
|
||||||
|
<Tab
|
||||||
|
icon={<Chip label={tab.count} size="small" variant="soft" />}
|
||||||
|
iconPosition="end"
|
||||||
|
key={tab.value}
|
||||||
|
label={tab.label}
|
||||||
|
sx={{ minHeight: 'auto' }}
|
||||||
|
tabIndex={0}
|
||||||
|
value={tab.value}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
<Divider />
|
||||||
|
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flexWrap: 'wrap', px: 3, py: 2 }}>
|
||||||
|
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flex: '1 1 auto', flexWrap: 'wrap' }}>
|
||||||
|
<FilterButton
|
||||||
|
displayValue={name}
|
||||||
|
label={t('Name')}
|
||||||
|
onFilterApply={(value) => {
|
||||||
|
handleNameChange(value as string);
|
||||||
|
}}
|
||||||
|
onFilterDelete={() => {
|
||||||
|
handleNameChange();
|
||||||
|
}}
|
||||||
|
popover={<NameFilterPopover />}
|
||||||
|
value={name}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FilterButton
|
||||||
|
displayValue={type}
|
||||||
|
label={t('Type')}
|
||||||
|
onFilterApply={(value) => {
|
||||||
|
handleTypeChange(value as string);
|
||||||
|
}}
|
||||||
|
onFilterDelete={() => {
|
||||||
|
handleTypeChange();
|
||||||
|
}}
|
||||||
|
popover={<TypeFilterPopover />}
|
||||||
|
value={type}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
<FilterButton
|
||||||
|
displayValue={email}
|
||||||
|
label="Email"
|
||||||
|
onFilterApply={(value) => {
|
||||||
|
handleEmailChange(value as string);
|
||||||
|
}}
|
||||||
|
onFilterDelete={() => {
|
||||||
|
handleEmailChange();
|
||||||
|
}}
|
||||||
|
popover={<EmailFilterPopover />}
|
||||||
|
value={email}
|
||||||
|
/>
|
||||||
|
*/}
|
||||||
|
|
||||||
|
{/*
|
||||||
|
<FilterButton
|
||||||
|
displayValue={phone}
|
||||||
|
label="Phone number"
|
||||||
|
onFilterApply={(value) => {
|
||||||
|
handlePhoneChange(value as string);
|
||||||
|
}}
|
||||||
|
onFilterDelete={() => {
|
||||||
|
handlePhoneChange();
|
||||||
|
}}
|
||||||
|
popover={<PhoneFilterPopover />}
|
||||||
|
value={phone}
|
||||||
|
/>
|
||||||
|
*/}
|
||||||
|
|
||||||
|
{hasFilters ? <Button onClick={handleClearFilters}>{t('Clear filters')}</Button> : null}
|
||||||
|
</Stack>
|
||||||
|
{selection.selectedAny ? (
|
||||||
|
<Stack direction="row" spacing={2} sx={{ alignItems: 'center' }}>
|
||||||
|
<Typography color="text.secondary" variant="body2">
|
||||||
|
{selection.selected.size} {t('selected')}
|
||||||
|
</Typography>
|
||||||
|
<Button color="error" variant="contained">
|
||||||
|
{t('Delete')}
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
) : null}
|
||||||
|
<Select name="sort" onChange={handleSortChange} sx={{ maxWidth: '100%', width: '120px' }} value={sortDir}>
|
||||||
|
<Option value="desc">{t('Newest')}</Option>
|
||||||
|
<Option value="asc">{t('Oldest')}</Option>
|
||||||
|
</Select>
|
||||||
|
</Stack>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TypeFilterPopover(): React.JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { anchorEl, onApply, onClose, open, value: initialValue } = useFilterContext();
|
||||||
|
const [value, setValue] = React.useState<string>('');
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setValue((initialValue as string | undefined) ?? '');
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FilterPopover anchorEl={anchorEl} onClose={onClose} open={open} title={t('Filter by type')}>
|
||||||
|
<FormControl>
|
||||||
|
<OutlinedInput
|
||||||
|
onChange={(event) => {
|
||||||
|
setValue(event.target.value);
|
||||||
|
}}
|
||||||
|
onKeyUp={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
onApply(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
onApply(value);
|
||||||
|
}}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
{t('Apply')}
|
||||||
|
</Button>
|
||||||
|
</FilterPopover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function NameFilterPopover(): React.JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { anchorEl, onApply, onClose, open, value: initialValue } = useFilterContext();
|
||||||
|
const [value, setValue] = React.useState<string>('');
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setValue((initialValue as string | undefined) ?? '');
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FilterPopover anchorEl={anchorEl} onClose={onClose} open={open} title={t('Filter by name')}>
|
||||||
|
<FormControl>
|
||||||
|
<OutlinedInput
|
||||||
|
onChange={(event) => {
|
||||||
|
setValue(event.target.value);
|
||||||
|
}}
|
||||||
|
onKeyUp={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
onApply(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
onApply(value);
|
||||||
|
}}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
{t('Apply')}
|
||||||
|
</Button>
|
||||||
|
</FilterPopover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EmailFilterPopover(): React.JSX.Element {
|
||||||
|
const { anchorEl, onApply, onClose, open, value: initialValue } = useFilterContext();
|
||||||
|
const [value, setValue] = React.useState<string>('');
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setValue((initialValue as string | undefined) ?? '');
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FilterPopover anchorEl={anchorEl} onClose={onClose} open={open} title="Filter by email">
|
||||||
|
<FormControl>
|
||||||
|
<OutlinedInput
|
||||||
|
onChange={(event) => {
|
||||||
|
setValue(event.target.value);
|
||||||
|
}}
|
||||||
|
onKeyUp={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
onApply(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
onApply(value);
|
||||||
|
}}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Apply
|
||||||
|
</Button>
|
||||||
|
</FilterPopover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PhoneFilterPopover(): React.JSX.Element {
|
||||||
|
const { anchorEl, onApply, onClose, open, value: initialValue } = useFilterContext();
|
||||||
|
const [value, setValue] = React.useState<string>('');
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setValue((initialValue as string | undefined) ?? '');
|
||||||
|
}, [initialValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FilterPopover anchorEl={anchorEl} onClose={onClose} open={open} title="Filter by phone number">
|
||||||
|
<FormControl>
|
||||||
|
<OutlinedInput
|
||||||
|
onChange={(event) => {
|
||||||
|
setValue(event.target.value);
|
||||||
|
}}
|
||||||
|
onKeyUp={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
onApply(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
onApply(value);
|
||||||
|
}}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Apply
|
||||||
|
</Button>
|
||||||
|
</FilterPopover>
|
||||||
|
);
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { useSelection } from '@/hooks/use-selection';
|
||||||
|
import type { Selection } from '@/hooks/use-selection';
|
||||||
|
|
||||||
|
import { LessonType } from './ILessonType';
|
||||||
|
|
||||||
|
function noop(): void {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LessonTypesSelectionContextValue extends Selection {}
|
||||||
|
|
||||||
|
export const LessonTypesSelectionContext = React.createContext<LessonTypesSelectionContextValue>({
|
||||||
|
deselectAll: noop,
|
||||||
|
deselectOne: noop,
|
||||||
|
selectAll: noop,
|
||||||
|
selectOne: noop,
|
||||||
|
selected: new Set(),
|
||||||
|
selectedAny: false,
|
||||||
|
selectedAll: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
interface LessonTypesSelectionProviderProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
lessonTypes: LessonType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LessonTypesSelectionProvider({
|
||||||
|
children,
|
||||||
|
lessonTypes = [],
|
||||||
|
}: LessonTypesSelectionProviderProps): React.JSX.Element {
|
||||||
|
const lessonTypeIds = React.useMemo(() => lessonTypes.map((lessonType) => lessonType.id), [lessonTypes]);
|
||||||
|
const selection = useSelection(lessonTypeIds);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LessonTypesSelectionContext.Provider value={{ ...selection }}>{children}</LessonTypesSelectionContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useLessonTypesSelection(): LessonTypesSelectionContextValue {
|
||||||
|
return React.useContext(LessonTypesSelectionContext);
|
||||||
|
}
|
Reference in New Issue
Block a user