update,
This commit is contained in:
@@ -63,9 +63,7 @@ export function AccountNotifications({ sx, ...other }: CardProps) {
|
||||
});
|
||||
|
||||
const getSelected = (selectedItems: string[], item: string) =>
|
||||
selectedItems.includes(item)
|
||||
? selectedItems.filter((value) => value !== item)
|
||||
: [...selectedItems, item];
|
||||
selectedItems.includes(item) ? selectedItems.filter((value) => value !== item) : [...selectedItems, item];
|
||||
|
||||
return (
|
||||
<Form methods={methods} onSubmit={onSubmit}>
|
||||
|
@@ -49,23 +49,15 @@ import { InvoiceAnalytic } from '../invoice-analytic';
|
||||
import { InvoiceTableRow } from '../invoice-table-row';
|
||||
import { InvoiceTableToolbar } from '../invoice-table-toolbar';
|
||||
import { InvoiceTableFiltersResult } from '../invoice-table-filters-result';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const TABLE_HEAD: TableHeadCellProps[] = [
|
||||
{ id: 'invoiceNumber', label: 'Customer' },
|
||||
{ id: 'createDate', label: 'Create' },
|
||||
{ id: 'dueDate', label: 'Due' },
|
||||
{ id: 'price', label: 'Amount' },
|
||||
{ id: 'sent', label: 'Sent', align: 'center' },
|
||||
{ id: 'status', label: 'Status' },
|
||||
{ id: '' },
|
||||
];
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function InvoiceListView() {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const table = useTable({ defaultOrderBy: 'createDate' });
|
||||
|
||||
@@ -73,6 +65,16 @@ export function InvoiceListView() {
|
||||
|
||||
const [tableData, setTableData] = useState<IInvoice[]>(_invoices);
|
||||
|
||||
const TABLE_HEAD: TableHeadCellProps[] = [
|
||||
{ id: 'invoiceNumber', label: t('Customer') },
|
||||
{ id: 'createDate', label: t('Create') },
|
||||
{ id: 'dueDate', label: t('Due') },
|
||||
{ id: 'price', label: t('Amount') },
|
||||
{ id: 'sent', label: t('Sent'), align: 'center' },
|
||||
{ id: 'status', label: t('Status') },
|
||||
{ id: '' },
|
||||
];
|
||||
|
||||
const filters = useSetState<IInvoiceTableFilters>({
|
||||
name: '',
|
||||
service: [],
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// src/sections/order/view/order-list-view.tsx
|
||||
import type { IOrderItem } from 'src/types/order';
|
||||
|
||||
import { useBoolean, usePopover } from 'minimal-shared/hooks';
|
||||
@@ -26,6 +27,7 @@ import { Label } from 'src/components/label';
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
import { ConfirmDialog } from 'src/components/custom-dialog';
|
||||
import { CustomPopover } from 'src/components/custom-popover';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -41,6 +43,7 @@ export function OrderTableRow({ row, selected, onSelectRow, onDeleteRow, details
|
||||
const confirmDialog = useBoolean();
|
||||
const menuActions = usePopover();
|
||||
const collapseRow = useBoolean();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const renderPrimaryRow = () => (
|
||||
<TableRow hover selected={selected}>
|
||||
@@ -195,13 +198,13 @@ export function OrderTableRow({ row, selected, onSelectRow, onDeleteRow, details
|
||||
sx={{ color: 'error.main' }}
|
||||
>
|
||||
<Iconify icon="solar:trash-bin-trash-bold" />
|
||||
Delete
|
||||
{t('Delete')}
|
||||
</MenuItem>
|
||||
|
||||
<li>
|
||||
<MenuItem component={RouterLink} href={detailsHref} onClick={() => menuActions.onClose()}>
|
||||
<Iconify icon="solar:eye-bold" />
|
||||
View
|
||||
{t('View')}
|
||||
</MenuItem>
|
||||
</li>
|
||||
</MenuList>
|
||||
|
@@ -16,6 +16,7 @@ import { formHelperTextClasses } from '@mui/material/FormHelperText';
|
||||
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
import { CustomPopover } from 'src/components/custom-popover';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -26,6 +27,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export function OrderTableToolbar({ filters, onResetPage, dateError }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const menuActions = usePopover();
|
||||
|
||||
const { state: currentFilters, setState: updateFilters } = filters;
|
||||
@@ -64,17 +66,17 @@ export function OrderTableToolbar({ filters, onResetPage, dateError }: Props) {
|
||||
<MenuList>
|
||||
<MenuItem onClick={() => menuActions.onClose()}>
|
||||
<Iconify icon="solar:printer-minimalistic-bold" />
|
||||
Print
|
||||
{t('Print')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={() => menuActions.onClose()}>
|
||||
<Iconify icon="solar:import-bold" />
|
||||
Import
|
||||
{t('Import')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={() => menuActions.onClose()}>
|
||||
<Iconify icon="solar:export-bold" />
|
||||
Export
|
||||
{t('Export')}
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</CustomPopover>
|
||||
@@ -93,7 +95,7 @@ export function OrderTableToolbar({ filters, onResetPage, dateError }: Props) {
|
||||
}}
|
||||
>
|
||||
<DatePicker
|
||||
label="Start date"
|
||||
label={t('Start date')}
|
||||
value={currentFilters.startDate}
|
||||
onChange={handleFilterStartDate}
|
||||
slotProps={{ textField: { fullWidth: true } }}
|
||||
@@ -101,7 +103,7 @@ export function OrderTableToolbar({ filters, onResetPage, dateError }: Props) {
|
||||
/>
|
||||
|
||||
<DatePicker
|
||||
label="End date"
|
||||
label={t('End date')}
|
||||
value={currentFilters.endDate}
|
||||
onChange={handleFilterEndDate}
|
||||
slotProps={{
|
||||
@@ -133,7 +135,7 @@ export function OrderTableToolbar({ filters, onResetPage, dateError }: Props) {
|
||||
fullWidth
|
||||
value={currentFilters.name}
|
||||
onChange={handleFilterName}
|
||||
placeholder="Search customer or order number..."
|
||||
placeholder={t('Search customer or order number...')}
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// src/sections/order/view/order-list-view.tsx
|
||||
|
||||
import type { TableHeadCellProps } from 'src/components/table';
|
||||
import type { IOrderItem, IOrderTableFilters } from 'src/types/order';
|
||||
|
||||
@@ -43,30 +45,33 @@ import {
|
||||
import { OrderTableRow } from '../order-table-row';
|
||||
import { OrderTableToolbar } from '../order-table-toolbar';
|
||||
import { OrderTableFiltersResult } from '../order-table-filters-result';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const STATUS_OPTIONS = [{ value: 'all', label: 'All' }, ...ORDER_STATUS_OPTIONS];
|
||||
|
||||
const TABLE_HEAD: TableHeadCellProps[] = [
|
||||
{ id: 'orderNumber', label: 'Order', width: 88 },
|
||||
{ id: 'name', label: 'Customer' },
|
||||
{ id: 'createdAt', label: 'Date', width: 140 },
|
||||
{ id: 'totalQuantity', label: 'Items', width: 120, align: 'center' },
|
||||
{ id: 'totalAmount', label: 'Price', width: 140 },
|
||||
{ id: 'status', label: 'Status', width: 110 },
|
||||
{ id: '', width: 88 },
|
||||
];
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function OrderListView() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const table = useTable({ defaultOrderBy: 'orderNumber' });
|
||||
|
||||
const confirmDialog = useBoolean();
|
||||
|
||||
const [tableData, setTableData] = useState<IOrderItem[]>(_orders);
|
||||
|
||||
const TABLE_HEAD: TableHeadCellProps[] = [
|
||||
{ id: 'orderNumber', label: t('Order'), width: 88 },
|
||||
{ id: 'name', label: t('Customer') },
|
||||
{ id: 'createdAt', label: t('Date'), width: 140 },
|
||||
{ id: 'totalQuantity', label: t('Items'), width: 120, align: 'center' },
|
||||
{ id: 'totalAmount', label: t('Price'), width: 140 },
|
||||
{ id: 'status', label: t('Status'), width: 110 },
|
||||
{ id: '', width: 88 },
|
||||
];
|
||||
|
||||
const filters = useSetState<IOrderTableFilters>({
|
||||
name: '',
|
||||
status: 'all',
|
||||
@@ -143,7 +148,7 @@ export function OrderListView() {
|
||||
confirmDialog.onFalse();
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
{t('Delete')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
@@ -155,9 +160,9 @@ export function OrderListView() {
|
||||
<CustomBreadcrumbs
|
||||
heading="List"
|
||||
links={[
|
||||
{ name: 'Dashboard', href: paths.dashboard.root },
|
||||
{ name: 'Order', href: paths.dashboard.order.root },
|
||||
{ name: 'List' },
|
||||
{ name: t('Dashboard'), href: paths.dashboard.root },
|
||||
{ name: t('Order'), href: paths.dashboard.order.root },
|
||||
{ name: t('List') },
|
||||
]}
|
||||
sx={{ mb: { xs: 3, md: 5 } }}
|
||||
/>
|
||||
@@ -178,7 +183,7 @@ export function OrderListView() {
|
||||
key={tab.value}
|
||||
iconPosition="end"
|
||||
value={tab.value}
|
||||
label={tab.label}
|
||||
label={t(tab.label)}
|
||||
icon={
|
||||
<Label
|
||||
variant={
|
||||
@@ -228,7 +233,7 @@ export function OrderListView() {
|
||||
)
|
||||
}
|
||||
action={
|
||||
<Tooltip title="Delete">
|
||||
<Tooltip title={t('Delete')}>
|
||||
<IconButton color="primary" onClick={confirmDialog.onTrue}>
|
||||
<Iconify icon="solar:trash-bin-trash-bold" />
|
||||
</IconButton>
|
||||
|
@@ -206,7 +206,6 @@ export function ProductNewEditForm({ currentProduct }: Props) {
|
||||
|
||||
if (currentProduct) {
|
||||
// perform save
|
||||
|
||||
await saveProduct(currentProduct.id, values);
|
||||
} else {
|
||||
// perform create
|
||||
@@ -215,7 +214,8 @@ export function ProductNewEditForm({ currentProduct }: Props) {
|
||||
|
||||
toast.success(currentProduct ? 'Update success!' : 'Create success!');
|
||||
|
||||
// router.push(paths.dashboard.product.root);
|
||||
router.push(paths.dashboard.product.root);
|
||||
|
||||
// console.info('DATA', updatedData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// src/sections/product/product-table-row.tsx
|
||||
import type { GridCellParams } from '@mui/x-data-grid';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
@@ -43,7 +44,7 @@ export function RenderCellCreatedAt({ params }: ParamsProps) {
|
||||
}
|
||||
|
||||
export function RenderCellStock({ params }: ParamsProps) {
|
||||
return <>helloworld</>
|
||||
return <>helloworld</>;
|
||||
|
||||
return (
|
||||
<Box sx={{ width: 1, typography: 'caption', color: 'text.secondary' }}>
|
||||
|
@@ -86,7 +86,7 @@ export function ProductDetailsView({ product, error, loading }: Props) {
|
||||
<DashboardContent sx={{ pt: 5 }}>
|
||||
<EmptyContent
|
||||
filled
|
||||
title="Product not found!"
|
||||
title={t('Product not found!')}
|
||||
action={
|
||||
<Button
|
||||
component={RouterLink}
|
||||
@@ -94,7 +94,7 @@ export function ProductDetailsView({ product, error, loading }: Props) {
|
||||
startIcon={<Iconify width={16} icon="eva:arrow-ios-back-fill" />}
|
||||
sx={{ mt: 3 }}
|
||||
>
|
||||
Back to list
|
||||
{t('Back to list')}
|
||||
</Button>
|
||||
}
|
||||
sx={{ py: 10, height: 'auto', flexGrow: 'unset' }}
|
||||
|
@@ -81,6 +81,11 @@ export function ProductListView() {
|
||||
{ value: 'out of stock', label: t('Out of stock') },
|
||||
];
|
||||
|
||||
const PUBLISH_OPTIONS = [
|
||||
{ value: 'published', label: t('Published') },
|
||||
{ value: 'draft', label: t('Draft') },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
if (products.length) {
|
||||
setTableData(products);
|
||||
@@ -94,11 +99,6 @@ export function ProductListView() {
|
||||
filters: currentFilters,
|
||||
});
|
||||
|
||||
const PUBLISH_OPTIONS = [
|
||||
{ value: 'published', label: t('Published') },
|
||||
{ value: 'draft', label: t('Draft') },
|
||||
];
|
||||
|
||||
const handleDeleteSingleRow = useCallback(async () => {
|
||||
// const deleteRow = tableData.filter((row) => row.id !== id);
|
||||
|
||||
@@ -245,7 +245,7 @@ export function ProductListView() {
|
||||
confirmDeleteMultiItemsDialog.onFalse();
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
{t('Delete')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
@@ -269,7 +269,7 @@ export function ProductListView() {
|
||||
confirmDeleteSingleItemDialog.onFalse();
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
{t('Delete')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
@@ -279,7 +279,7 @@ export function ProductListView() {
|
||||
<>
|
||||
<DashboardContent sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column' }}>
|
||||
<CustomBreadcrumbs
|
||||
heading="List"
|
||||
heading={t('Product List')}
|
||||
links={[
|
||||
{ name: t('Dashboard'), href: paths.dashboard.root },
|
||||
{ name: t('Product'), href: paths.dashboard.product.root },
|
||||
|
@@ -27,6 +27,7 @@ import { ProductDetailsReview } from '../product-details-review';
|
||||
import { ProductDetailsSummary } from '../product-details-summary';
|
||||
import { ProductDetailsCarousel } from '../product-details-carousel';
|
||||
import { ProductDetailsDescription } from '../product-details-description';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -57,6 +58,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export function ProductShopDetailsView({ product, error, loading }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const { state: checkoutState, onAddToCart } = useCheckoutContext();
|
||||
|
||||
const containerStyles: SxProps<Theme> = {
|
||||
@@ -79,7 +81,7 @@ export function ProductShopDetailsView({ product, error, loading }: Props) {
|
||||
<Container sx={containerStyles}>
|
||||
<EmptyContent
|
||||
filled
|
||||
title="Product not found!"
|
||||
title={t('Product not found!')}
|
||||
action={
|
||||
<Button
|
||||
component={RouterLink}
|
||||
|
@@ -21,6 +21,7 @@ import { _socials } from 'src/_mock';
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
|
||||
import { ProfilePostItem } from './profile-post-item';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -31,6 +32,7 @@ type Props = {
|
||||
|
||||
export function ProfileHome({ info, posts }: Props) {
|
||||
const fileRef = useRef<HTMLInputElement>(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleAttach = () => {
|
||||
if (fileRef.current) {
|
||||
@@ -40,21 +42,18 @@ export function ProfileHome({ info, posts }: Props) {
|
||||
|
||||
const renderFollows = () => (
|
||||
<Card sx={{ py: 3, textAlign: 'center', typography: 'h4' }}>
|
||||
<Stack
|
||||
divider={<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />}
|
||||
sx={{ flexDirection: 'row' }}
|
||||
>
|
||||
<Stack divider={<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />} sx={{ flexDirection: 'row' }}>
|
||||
<Stack sx={{ width: 1 }}>
|
||||
{fNumber(info.totalFollowers)}
|
||||
<Box component="span" sx={{ color: 'text.secondary', typography: 'body2' }}>
|
||||
Follower
|
||||
{t('Follower')}
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Stack sx={{ width: 1 }}>
|
||||
{fNumber(info.totalFollowing)}
|
||||
<Box component="span" sx={{ color: 'text.secondary', typography: 'body2' }}>
|
||||
Following
|
||||
{t('Following')}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -63,7 +62,7 @@ export function ProfileHome({ info, posts }: Props) {
|
||||
|
||||
const renderAbout = () => (
|
||||
<Card>
|
||||
<CardHeader title="About" />
|
||||
<CardHeader title={t('About')} />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
@@ -143,16 +142,16 @@ export function ProfileHome({ info, posts }: Props) {
|
||||
>
|
||||
<Fab size="small" color="inherit" variant="softExtended" onClick={handleAttach}>
|
||||
<Iconify icon="solar:gallery-wide-bold" width={24} sx={{ color: 'success.main' }} />
|
||||
Image/Video
|
||||
{t('Image/Video')}
|
||||
</Fab>
|
||||
|
||||
<Fab size="small" color="inherit" variant="softExtended">
|
||||
<Iconify icon="solar:videocamera-record-bold" width={24} sx={{ color: 'error.main' }} />
|
||||
Streaming
|
||||
{t('Streaming')}
|
||||
</Fab>
|
||||
</Box>
|
||||
|
||||
<Button variant="contained">Post</Button>
|
||||
<Button variant="contained">{t('Post')}</Button>
|
||||
</Box>
|
||||
|
||||
<input ref={fileRef} type="file" style={{ display: 'none' }} />
|
||||
@@ -161,7 +160,7 @@ export function ProfileHome({ info, posts }: Props) {
|
||||
|
||||
const renderSocials = () => (
|
||||
<Card>
|
||||
<CardHeader title="Social" />
|
||||
<CardHeader title={t('Social')} />
|
||||
|
||||
<Box sx={{ p: 3, gap: 2, display: 'flex', flexDirection: 'column', typography: 'body2' }}>
|
||||
{_socials.map((social) => (
|
||||
|
@@ -7,15 +7,18 @@ import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Switch from '@mui/material/Switch';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { isValidPhoneNumber } from 'react-phone-number-input/input';
|
||||
import { createUser, deleteUser, saveUser } from 'src/actions/user';
|
||||
import { Field, Form, schemaHelper } from 'src/components/hook-form';
|
||||
import { Label } from 'src/components/label';
|
||||
import { toast } from 'src/components/snackbar';
|
||||
import { useRouter } from 'src/routes/hooks';
|
||||
import { paths } from 'src/routes/paths';
|
||||
import type { IUserItem } from 'src/types/user';
|
||||
import { fileToBase64 } from 'src/utils/file-to-base64';
|
||||
import { fData } from 'src/utils/format-number';
|
||||
import { z as zod } from 'zod';
|
||||
|
||||
@@ -24,26 +27,27 @@ import { z as zod } from 'zod';
|
||||
export type NewUserSchemaType = zod.infer<typeof NewUserSchema>;
|
||||
|
||||
export const NewUserSchema = zod.object({
|
||||
avatarUrl: schemaHelper.file({ message: 'Avatar is required!' }),
|
||||
name: zod.string().min(1, { message: 'Name is required!' }),
|
||||
city: zod.string().min(1, { message: 'City is required!' }),
|
||||
role: zod.string().min(1, { message: 'Role is required!' }),
|
||||
email: zod
|
||||
.string()
|
||||
.min(1, { message: 'Email is required!' })
|
||||
.email({ message: 'Email must be a valid email address!' }),
|
||||
phoneNumber: schemaHelper.phoneNumber({ isValid: isValidPhoneNumber }),
|
||||
state: zod.string().min(1, { message: 'State is required!' }),
|
||||
status: zod.string(),
|
||||
address: zod.string().min(1, { message: 'Address is required!' }),
|
||||
country: schemaHelper.nullableInput(zod.string().min(1, { message: 'Country is required!' }), {
|
||||
// message for null value
|
||||
message: 'Country is required!',
|
||||
}),
|
||||
address: zod.string().min(1, { message: 'Address is required!' }),
|
||||
company: zod.string().min(1, { message: 'Company is required!' }),
|
||||
state: zod.string().min(1, { message: 'State is required!' }),
|
||||
city: zod.string().min(1, { message: 'City is required!' }),
|
||||
role: zod.string().min(1, { message: 'Role is required!' }),
|
||||
zipCode: zod.string().min(1, { message: 'Zip code is required!' }),
|
||||
// Not required
|
||||
status: zod.string(),
|
||||
company: zod.string().min(1, { message: 'Company is required!' }),
|
||||
avatarUrl: schemaHelper.file({ message: 'Avatar is required!' }),
|
||||
phoneNumber: schemaHelper.phoneNumber({ isValid: isValidPhoneNumber }),
|
||||
isVerified: zod.boolean(),
|
||||
username: zod.string(),
|
||||
password: zod.string(),
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -61,16 +65,19 @@ export function UserNewEditForm({ currentUser }: Props) {
|
||||
status: '',
|
||||
avatarUrl: null,
|
||||
isVerified: true,
|
||||
name: '',
|
||||
email: '',
|
||||
phoneNumber: '',
|
||||
country: '',
|
||||
state: '',
|
||||
city: '',
|
||||
address: '',
|
||||
zipCode: '',
|
||||
company: '',
|
||||
role: '',
|
||||
name: '新用戶名字',
|
||||
email: 'user@123.com',
|
||||
phoneNumber: '+85291234567',
|
||||
country: 'Hong Kong',
|
||||
state: 'HK',
|
||||
city: 'hong kong',
|
||||
address: 'Kwun Tong, Sau Mau Ping',
|
||||
zipCode: '00000',
|
||||
company: 'test company',
|
||||
role: 'user',
|
||||
//
|
||||
username: '',
|
||||
password: '',
|
||||
};
|
||||
|
||||
const methods = useForm<NewUserSchemaType>({
|
||||
@@ -85,18 +92,50 @@ export function UserNewEditForm({ currentUser }: Props) {
|
||||
watch,
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting },
|
||||
formState: { errors, isSubmitting },
|
||||
} = methods;
|
||||
|
||||
const values = watch();
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
const [disableDeleteUserButton, setDisableDeleteUserButton] = useState<boolean>(false);
|
||||
const handleDeleteUserClick = async () => {
|
||||
setDisableDeleteUserButton(true);
|
||||
try {
|
||||
if (currentUser) {
|
||||
await deleteUser(currentUser.id);
|
||||
toast.success(t('user deleted'));
|
||||
router.push(paths.dashboard.user.list);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
setDisableDeleteUserButton(false);
|
||||
};
|
||||
|
||||
const onSubmit = handleSubmit(async (data: any) => {
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
reset();
|
||||
toast.success(currentUser ? 'Update success!' : 'Create success!');
|
||||
|
||||
const temp: any = data.avatarUrl;
|
||||
if (temp instanceof File) {
|
||||
data.avatarUrl = await fileToBase64(temp);
|
||||
}
|
||||
|
||||
if (currentUser) {
|
||||
// perform save
|
||||
await saveUser(currentUser.id, data);
|
||||
} else {
|
||||
// perform create
|
||||
await createUser(data);
|
||||
}
|
||||
|
||||
toast.success(currentUser ? t('Update success!') : t('Create success!'));
|
||||
|
||||
router.push(paths.dashboard.user.list);
|
||||
console.info('DATA', data);
|
||||
|
||||
// console.info('DATA', data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -135,8 +174,8 @@ export function UserNewEditForm({ currentUser }: Props) {
|
||||
color: 'text.disabled',
|
||||
}}
|
||||
>
|
||||
Allowed *.jpeg, *.jpg, *.png, *.gif
|
||||
<br /> max size of {fData(3145728)}
|
||||
{t('Allowed')} *.jpeg, *.jpg, *.png, *.gif
|
||||
<br /> {t('max size of')} {fData(3145728)}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
@@ -163,10 +202,10 @@ export function UserNewEditForm({ currentUser }: Props) {
|
||||
label={
|
||||
<>
|
||||
<Typography variant="subtitle2" sx={{ mb: 0.5 }}>
|
||||
Banned
|
||||
{t('Banned')}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
Apply disable account
|
||||
{t('Apply disable account')}
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
@@ -185,10 +224,10 @@ export function UserNewEditForm({ currentUser }: Props) {
|
||||
label={
|
||||
<>
|
||||
<Typography variant="subtitle2" sx={{ mb: 0.5 }}>
|
||||
Email verified
|
||||
{t('Email verified')}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
Disabling this will automatically send the user a verification email
|
||||
{t('Disabling this will automatically send the user a verification email')}
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
@@ -197,8 +236,14 @@ export function UserNewEditForm({ currentUser }: Props) {
|
||||
|
||||
{currentUser && (
|
||||
<Stack sx={{ mt: 3, alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Button variant="soft" color="error">
|
||||
Delete user
|
||||
<Button
|
||||
disabled={disableDeleteUserButton}
|
||||
loading={disableDeleteUserButton}
|
||||
variant="soft"
|
||||
color="error"
|
||||
onClick={handleDeleteUserClick}
|
||||
>
|
||||
{t('Delete user')}
|
||||
</Button>
|
||||
</Stack>
|
||||
)}
|
||||
@@ -215,32 +260,33 @@ export function UserNewEditForm({ currentUser }: Props) {
|
||||
gridTemplateColumns: { xs: 'repeat(1, 1fr)', sm: 'repeat(2, 1fr)' },
|
||||
}}
|
||||
>
|
||||
<Field.Text name="name" label="Full name" />
|
||||
<Field.Text name="email" label="Email address" />
|
||||
<Field.Phone
|
||||
name="phoneNumber"
|
||||
label="Phone number"
|
||||
country={!currentUser ? 'DE' : undefined}
|
||||
/>
|
||||
<Field.Text name="name" label={t('Full name')} />
|
||||
<Field.Text name="email" label={t('Email address')} />
|
||||
<Field.Phone name="phoneNumber" label={t('Phone number')} country="HK" />
|
||||
|
||||
<Field.CountrySelect
|
||||
fullWidth
|
||||
name="country"
|
||||
label="Country"
|
||||
placeholder="Choose a country"
|
||||
label={t('Country')}
|
||||
placeholder={t('Choose a country')}
|
||||
/>
|
||||
|
||||
<Field.Text name="state" label="State/region" />
|
||||
<Field.Text name="city" label="City" />
|
||||
<Field.Text name="state" label={t('State/region')} />
|
||||
<Field.Text name="city" label={t('City')} />
|
||||
<Field.Text name="address" label={t('Address')} />
|
||||
<Field.Text name="zipCode" label="Zip/code" />
|
||||
<Field.Text name="company" label="Company" />
|
||||
<Field.Text name="role" label="Role" />
|
||||
<Field.Text name="zipCode" label={t('Zip/code')} />
|
||||
<Field.Text name="company" label={t('Company')} />
|
||||
<Field.Text name="role" label={t('Role')} />
|
||||
</Box>
|
||||
|
||||
<Stack sx={{ mt: 3, alignItems: 'flex-end' }}>
|
||||
<Button type="submit" variant="contained" loading={isSubmitting}>
|
||||
{!currentUser ? 'Create user' : 'Save changes'}
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
>
|
||||
{!currentUser ? t('Create user') : t('Save changes')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
@@ -19,6 +19,7 @@ import { Label } from 'src/components/label';
|
||||
import { RouterLink } from 'src/routes/components';
|
||||
import type { IUserItem } from 'src/types/user';
|
||||
import { UserQuickEditForm } from './user-quick-edit-form';
|
||||
import { useState } from 'react';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -55,7 +56,7 @@ export function UserTableRow({ row, selected, editHref, onSelectRow, onDeleteRow
|
||||
<li>
|
||||
<MenuItem component={RouterLink} href={editHref} onClick={() => menuActions.onClose()}>
|
||||
<Iconify icon="solar:pen-bold" />
|
||||
Edit
|
||||
{t('Edit')}
|
||||
</MenuItem>
|
||||
</li>
|
||||
|
||||
@@ -67,21 +68,31 @@ export function UserTableRow({ row, selected, editHref, onSelectRow, onDeleteRow
|
||||
sx={{ color: 'error.main' }}
|
||||
>
|
||||
<Iconify icon="solar:trash-bin-trash-bold" />
|
||||
Delete
|
||||
{t('Delete')}
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</CustomPopover>
|
||||
);
|
||||
|
||||
const [disableDeleteButton, setDisableDeleteButton] = useState<boolean>(false);
|
||||
const renderConfirmDialog = () => (
|
||||
<ConfirmDialog
|
||||
open={confirmDialog.value}
|
||||
onClose={confirmDialog.onFalse}
|
||||
title="Delete"
|
||||
content="Are you sure want to delete?"
|
||||
title={t('Delete')}
|
||||
content={t('Are you sure want to delete user?')}
|
||||
action={
|
||||
<Button variant="contained" color="error" onClick={onDeleteRow}>
|
||||
Delete
|
||||
<Button
|
||||
disabled={disableDeleteButton}
|
||||
loading={disableDeleteButton}
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => {
|
||||
setDisableDeleteButton(true);
|
||||
onDeleteRow();
|
||||
}}
|
||||
>
|
||||
{t('Delete')}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
@@ -10,27 +10,25 @@ import { Iconify } from 'src/components/iconify';
|
||||
import { CustomBreadcrumbs } from 'src/components/custom-breadcrumbs';
|
||||
|
||||
import { UserCardList } from '../user-card-list';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function UserCardsView() {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<DashboardContent>
|
||||
<CustomBreadcrumbs
|
||||
heading="User cards"
|
||||
links={[
|
||||
{ name: 'Dashboard', href: paths.dashboard.root },
|
||||
{ name: 'User', href: paths.dashboard.user.root },
|
||||
{ name: 'Cards' },
|
||||
//
|
||||
{ name: t('Dashboard'), href: paths.dashboard.root },
|
||||
{ name: t('User'), href: paths.dashboard.user.root },
|
||||
{ name: t('Cards') },
|
||||
]}
|
||||
action={
|
||||
<Button
|
||||
component={RouterLink}
|
||||
href={paths.dashboard.user.new}
|
||||
variant="contained"
|
||||
startIcon={<Iconify icon="mingcute:add-line" />}
|
||||
>
|
||||
New user
|
||||
<Button component={RouterLink} href={paths.dashboard.user.new} variant="contained" startIcon={<Iconify icon="mingcute:add-line" />}>
|
||||
{t('New user')}
|
||||
</Button>
|
||||
}
|
||||
sx={{ mb: { xs: 3, md: 5 } }}
|
||||
|
@@ -5,18 +5,22 @@ import { DashboardContent } from 'src/layouts/dashboard';
|
||||
import { CustomBreadcrumbs } from 'src/components/custom-breadcrumbs';
|
||||
|
||||
import { UserNewEditForm } from '../user-new-edit-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export function UserCreateView() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<DashboardContent>
|
||||
<CustomBreadcrumbs
|
||||
heading="Create a new user"
|
||||
heading={t('Create a new user')}
|
||||
links={[
|
||||
{ name: 'Dashboard', href: paths.dashboard.root },
|
||||
{ name: 'User', href: paths.dashboard.user.root },
|
||||
{ name: 'New user' },
|
||||
//
|
||||
{ name: t('Dashboard'), href: paths.dashboard.root },
|
||||
{ name: t('User'), href: paths.dashboard.user.root },
|
||||
{ name: t('New user') },
|
||||
]}
|
||||
sx={{ mb: { xs: 3, md: 5 } }}
|
||||
/>
|
||||
|
@@ -3,6 +3,7 @@ import { DashboardContent } from 'src/layouts/dashboard';
|
||||
import { paths } from 'src/routes/paths';
|
||||
import type { IUserItem } from 'src/types/user';
|
||||
import { UserNewEditForm } from '../user-new-edit-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -11,14 +12,16 @@ type Props = {
|
||||
};
|
||||
|
||||
export function UserEditView({ user: currentUser }: Props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<DashboardContent>
|
||||
<CustomBreadcrumbs
|
||||
heading="Edit"
|
||||
backHref={paths.dashboard.user.list}
|
||||
links={[
|
||||
{ name: 'Dashboard', href: paths.dashboard.root },
|
||||
{ name: 'User', href: paths.dashboard.user.root },
|
||||
{ name: t('Dashboard'), href: paths.dashboard.root },
|
||||
{ name: t('User'), href: paths.dashboard.user.root },
|
||||
{ name: currentUser?.name },
|
||||
]}
|
||||
sx={{ mb: { xs: 3, md: 5 } }}
|
||||
|
@@ -13,7 +13,7 @@ import { varAlpha } from 'minimal-shared/utils';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { _roles, _userList, USER_STATUS_OPTIONS } from 'src/_mock';
|
||||
import { useGetUsers } from 'src/actions/user';
|
||||
import { deleteUser, useGetUsers } from 'src/actions/user';
|
||||
import { CustomBreadcrumbs } from 'src/components/custom-breadcrumbs';
|
||||
import { ConfirmDialog } from 'src/components/custom-dialog';
|
||||
import { Iconify } from 'src/components/iconify';
|
||||
@@ -39,6 +39,8 @@ import type { IUserItem, IUserTableFilters } from 'src/types/user';
|
||||
import { UserTableFiltersResult } from '../user-table-filters-result';
|
||||
import { UserTableRow } from '../user-table-row';
|
||||
import { UserTableToolbar } from '../user-table-toolbar';
|
||||
import { Router } from 'react-router';
|
||||
import { useRouter } from 'src/routes/hooks';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -48,6 +50,7 @@ const STATUS_OPTIONS = [{ value: 'all', label: 'All' }, ...USER_STATUS_OPTIONS];
|
||||
|
||||
export function UserListView() {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
|
||||
const TABLE_HEAD: TableHeadCellProps[] = [
|
||||
{ id: 'name', label: t('Name') },
|
||||
@@ -59,12 +62,13 @@ export function UserListView() {
|
||||
];
|
||||
|
||||
const { users, mutate } = useGetUsers();
|
||||
const [processNewUser, setProcessNewUser] = useState<boolean>(false);
|
||||
|
||||
const table = useTable();
|
||||
|
||||
const confirmDialog = useBoolean();
|
||||
|
||||
const [tableData, setTableData] = useState<IUserItem[]>(_userList);
|
||||
const [tableData, setTableData] = useState<IUserItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setTableData(users);
|
||||
@@ -87,16 +91,22 @@ export function UserListView() {
|
||||
const notFound = (!dataFiltered.length && canReset) || !dataFiltered.length;
|
||||
|
||||
const handleDeleteRow = useCallback(
|
||||
(id: string) => {
|
||||
const deleteRow = tableData.filter((row) => row.id !== id);
|
||||
async (id: string) => {
|
||||
// const deleteRow = tableData.filter((row) => row.id !== id);
|
||||
// toast.success('Delete success!');
|
||||
// setTableData(deleteRow);
|
||||
// table.onUpdatePageDeleteRow(dataInPage.length);
|
||||
|
||||
toast.success('Delete success!');
|
||||
|
||||
setTableData(deleteRow);
|
||||
|
||||
table.onUpdatePageDeleteRow(dataInPage.length);
|
||||
try {
|
||||
await deleteUser(id);
|
||||
toast.success('Delete success!');
|
||||
mutate();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error('Delete failed!');
|
||||
}
|
||||
},
|
||||
[dataInPage.length, table, tableData]
|
||||
[table, tableData, mutate]
|
||||
);
|
||||
|
||||
const handleDeleteRows = useCallback(() => {
|
||||
@@ -121,7 +131,7 @@ export function UserListView() {
|
||||
<ConfirmDialog
|
||||
open={confirmDialog.value}
|
||||
onClose={confirmDialog.onFalse}
|
||||
title="Delete"
|
||||
title={t('Delete')}
|
||||
content={
|
||||
<>
|
||||
Are you sure want to delete <strong> {table.selected.length} </strong> items?
|
||||
@@ -133,7 +143,7 @@ export function UserListView() {
|
||||
color="error"
|
||||
onClick={() => {
|
||||
handleDeleteRows();
|
||||
confirmDialog.onFalse();
|
||||
// confirmDialog.onFalse();
|
||||
}}
|
||||
>
|
||||
{t('Delete')}
|
||||
@@ -142,22 +152,33 @@ export function UserListView() {
|
||||
/>
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
mutate();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DashboardContent>
|
||||
<CustomBreadcrumbs
|
||||
heading="List"
|
||||
links={[
|
||||
{ name: 'Dashboard', href: paths.dashboard.root },
|
||||
{ name: 'User', href: paths.dashboard.user.root },
|
||||
{ name: 'List' },
|
||||
//
|
||||
{ name: t('Dashboard'), href: paths.dashboard.root },
|
||||
{ name: t('User'), href: paths.dashboard.user.root },
|
||||
{ name: t('List') },
|
||||
]}
|
||||
action={
|
||||
<Button
|
||||
component={RouterLink}
|
||||
href={paths.dashboard.user.new}
|
||||
disabled={processNewUser}
|
||||
loading={processNewUser}
|
||||
// component={RouterLink}
|
||||
// href={paths.dashboard.user.new}
|
||||
variant="contained"
|
||||
startIcon={<Iconify icon="mingcute:add-line" />}
|
||||
onClick={() => {
|
||||
setProcessNewUser(true);
|
||||
router.push(paths.dashboard.user.new);
|
||||
}}
|
||||
>
|
||||
{t('New user')}
|
||||
</Button>
|
||||
|
@@ -22,6 +22,7 @@ import { ProfileCover } from '../profile-cover';
|
||||
import { ProfileFriends } from '../profile-friends';
|
||||
import { ProfileGallery } from '../profile-gallery';
|
||||
import { ProfileFollowers } from '../profile-followers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -53,6 +54,8 @@ const NAV_ITEMS = [
|
||||
const TAB_PARAM = 'tab';
|
||||
|
||||
export function UserProfileView() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const selectedTab = searchParams.get(TAB_PARAM) ?? '';
|
||||
@@ -74,21 +77,12 @@ export function UserProfileView() {
|
||||
<DashboardContent>
|
||||
<CustomBreadcrumbs
|
||||
heading="Profile"
|
||||
links={[
|
||||
{ name: 'Dashboard', href: paths.dashboard.root },
|
||||
{ name: 'User', href: paths.dashboard.user.root },
|
||||
{ name: user?.displayName },
|
||||
]}
|
||||
links={[{ name: t('Dashboard'), href: paths.dashboard.root }, { name: t('User'), href: paths.dashboard.user.root }, { name: user?.displayName }]}
|
||||
sx={{ mb: { xs: 3, md: 5 } }}
|
||||
/>
|
||||
|
||||
<Card sx={{ mb: 3, height: 290 }}>
|
||||
<ProfileCover
|
||||
role={_userAbout.role}
|
||||
name={user?.displayName}
|
||||
avatarUrl={user?.photoURL}
|
||||
coverUrl={_userAbout.coverUrl}
|
||||
/>
|
||||
<ProfileCover role={_userAbout.role} name={user?.displayName} avatarUrl={user?.photoURL} coverUrl={_userAbout.coverUrl} />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
@@ -109,7 +103,7 @@ export function UserProfileView() {
|
||||
key={tab.value}
|
||||
value={tab.value}
|
||||
icon={tab.icon}
|
||||
label={tab.label}
|
||||
label={t(tab.label)}
|
||||
href={createRedirectPath(pathname, tab.value)}
|
||||
/>
|
||||
))}
|
||||
@@ -121,13 +115,7 @@ export function UserProfileView() {
|
||||
|
||||
{selectedTab === 'followers' && <ProfileFollowers followers={_userFollowers} />}
|
||||
|
||||
{selectedTab === 'friends' && (
|
||||
<ProfileFriends
|
||||
friends={_userFriends}
|
||||
searchFriends={searchFriends}
|
||||
onSearchFriends={handleSearchFriends}
|
||||
/>
|
||||
)}
|
||||
{selectedTab === 'friends' && <ProfileFriends friends={_userFriends} searchFriends={searchFriends} onSearchFriends={handleSearchFriends} />}
|
||||
|
||||
{selectedTab === 'gallery' && <ProfileGallery gallery={_userGallery} />}
|
||||
</DashboardContent>
|
||||
|
Reference in New Issue
Block a user