This commit is contained in:
louiscklaw
2025-05-30 01:14:10 +08:00
parent 98bc3fe3ce
commit 834f58bde1
52 changed files with 624 additions and 604 deletions

View File

@@ -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}>

View File

@@ -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: [],

View File

@@ -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>

View File

@@ -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: (

View File

@@ -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>

View File

@@ -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);

View File

@@ -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' }}>

View File

@@ -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' }}

View File

@@ -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 },

View File

@@ -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}

View File

@@ -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) => (

View File

@@ -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>

View File

@@ -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>
}
/>

View File

@@ -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 } }}

View File

@@ -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 } }}
/>

View File

@@ -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 } }}

View File

@@ -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>

View File

@@ -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>