initial commit

This commit is contained in:
ferdiansyah783
2025-08-05 12:35:40 +07:00
commit fffa2ead5c
1069 changed files with 118056 additions and 0 deletions
@@ -0,0 +1,149 @@
// MUI Imports
import Card from '@mui/material/Card'
import CardContent from '@mui/material/CardContent'
import Typography from '@mui/material/Typography'
import Chip from '@mui/material/Chip'
import Divider from '@mui/material/Divider'
import Button from '@mui/material/Button'
import type { ButtonProps } from '@mui/material/Button'
// Type Imports
import type { ThemeColor } from '@core/types'
// Component Imports
import EditUserInfo from '@components/dialogs/edit-user-info'
import ConfirmationDialog from '@components/dialogs/confirmation-dialog'
import OpenDialogOnElementClick from '@components/dialogs/OpenDialogOnElementClick'
import CustomAvatar from '@core/components/mui/Avatar'
// Vars
const userData = {
firstName: 'Seth',
lastName: 'Hallam',
userName: '@shallamb',
billingEmail: 'shallamb@gmail.com',
status: 'active',
role: 'Subscriber',
taxId: 'Tax-8894',
contact: '+1 (234) 464-0600',
language: ['English'],
country: 'France',
useAsBillingAddress: true
}
const UserDetails = () => {
// Vars
const buttonProps = (children: string, color: ThemeColor, variant: ButtonProps['variant']): ButtonProps => ({
children,
color,
variant
})
return (
<>
<Card>
<CardContent className='flex flex-col pbs-12 gap-6'>
<div className='flex flex-col gap-6'>
<div className='flex items-center justify-center flex-col gap-4'>
<div className='flex flex-col items-center gap-4'>
<CustomAvatar alt='user-profile' src='/images/avatars/1.png' variant='rounded' size={120} />
<Typography variant='h5'>{`${userData.firstName} ${userData.lastName}`}</Typography>
</div>
<Chip label='Author' color='secondary' size='small' variant='tonal' />
</div>
<div className='flex items-center justify-around flex-wrap gap-4'>
<div className='flex items-center gap-4'>
<CustomAvatar variant='rounded' color='primary' skin='light'>
<i className='tabler-checkbox' />
</CustomAvatar>
<div>
<Typography variant='h5'>1.23k</Typography>
<Typography>Task Done</Typography>
</div>
</div>
<div className='flex items-center gap-4'>
<CustomAvatar variant='rounded' color='primary' skin='light'>
<i className='tabler-briefcase' />
</CustomAvatar>
<div>
<Typography variant='h5'>568</Typography>
<Typography>Project Done</Typography>
</div>
</div>
</div>
</div>
<div>
<Typography variant='h5'>Details</Typography>
<Divider className='mlb-4' />
<div className='flex flex-col gap-2'>
<div className='flex items-center flex-wrap gap-x-1.5'>
<Typography className='font-medium' color='text.primary'>
Username:
</Typography>
<Typography>{userData.userName}</Typography>
</div>
<div className='flex items-center flex-wrap gap-x-1.5'>
<Typography className='font-medium' color='text.primary'>
Billing Email:
</Typography>
<Typography>{userData.billingEmail}</Typography>
</div>
<div className='flex items-center flex-wrap gap-x-1.5'>
<Typography className='font-medium' color='text.primary'>
Status
</Typography>
<Typography color='text.primary'>{userData.status}</Typography>
</div>
<div className='flex items-center flex-wrap gap-x-1.5'>
<Typography className='font-medium' color='text.primary'>
Role:
</Typography>
<Typography color='text.primary'>{userData.role}</Typography>
</div>
<div className='flex items-center flex-wrap gap-x-1.5'>
<Typography className='font-medium' color='text.primary'>
Tax ID:
</Typography>
<Typography color='text.primary'>{userData.taxId}</Typography>
</div>
<div className='flex items-center flex-wrap gap-x-1.5'>
<Typography className='font-medium' color='text.primary'>
Contact:
</Typography>
<Typography color='text.primary'>{userData.contact}</Typography>
</div>
<div className='flex items-center flex-wrap gap-x-1.5'>
<Typography className='font-medium' color='text.primary'>
Language:
</Typography>
<Typography color='text.primary'>{userData.language}</Typography>
</div>
<div className='flex items-center flex-wrap gap-x-1.5'>
<Typography className='font-medium' color='text.primary'>
Country:
</Typography>
<Typography color='text.primary'>{userData.country}</Typography>
</div>
</div>
</div>
<div className='flex gap-4 justify-center'>
<OpenDialogOnElementClick
element={Button}
elementProps={buttonProps('Edit', 'primary', 'contained')}
dialog={EditUserInfo}
dialogProps={{ data: userData }}
/>
<OpenDialogOnElementClick
element={Button}
elementProps={buttonProps('Suspend', 'error', 'tonal')}
dialog={ConfirmationDialog}
dialogProps={{ type: 'suspend-account' }}
/>
</div>
</CardContent>
</Card>
</>
)
}
export default UserDetails
@@ -0,0 +1,72 @@
// MUI Imports
import Card from '@mui/material/Card'
import CardContent from '@mui/material/CardContent'
import Chip from '@mui/material/Chip'
import Typography from '@mui/material/Typography'
import LinearProgress from '@mui/material/LinearProgress'
import Button from '@mui/material/Button'
import type { ButtonProps } from '@mui/material/Button'
// Component Imports
import UpgradePlan from '@components/dialogs/upgrade-plan'
import OpenDialogOnElementClick from '@components/dialogs/OpenDialogOnElementClick'
const UserPlan = () => {
// Vars
const buttonProps: ButtonProps = {
variant: 'contained',
children: 'Upgrade Plan'
}
return (
<>
<Card className='border-2 border-primary rounded shadow-primarySm'>
<CardContent className='flex flex-col gap-6'>
<div className='flex justify-between'>
<Chip label='Standard' size='small' color='primary' variant='tonal' />
<div className='flex justify-center'>
<Typography variant='h5' component='sup' className='self-start' color='primary.main'>
$
</Typography>
<Typography component='span' variant='h1' color='primary.main'>
99
</Typography>
<Typography component='sub' className='self-end' color='text.primary'>
/month
</Typography>
</div>
</div>
<div className='flex flex-col gap-2'>
<div className='flex items-center gap-2'>
<i className='tabler-circle-filled text-[10px] text-secondary' />
<Typography component='span'>10 Users</Typography>
</div>
<div className='flex items-center gap-2'>
<i className='tabler-circle-filled text-[10px] text-secondary' />
<Typography component='span'>Up to 10 GB storage</Typography>
</div>
<div className='flex items-center gap-2'>
<i className='tabler-circle-filled text-[10px] text-secondary' />
<Typography component='span'>Basic Support</Typography>
</div>
</div>
<div className='flex flex-col gap-1'>
<div className='flex items-center justify-between'>
<Typography className='font-medium' color='text.primary'>
Days
</Typography>
<Typography className='font-medium' color='text.primary'>
26 of 30 Days
</Typography>
</div>
<LinearProgress variant='determinate' value={65} />
<Typography variant='body2'>4 days remaining</Typography>
</div>
<OpenDialogOnElementClick element={Button} elementProps={buttonProps} dialog={UpgradePlan} />
</CardContent>
</Card>
</>
)
}
export default UserPlan
@@ -0,0 +1,21 @@
// MUI Imports
import Grid from '@mui/material/Grid2'
// Component Imports
import UserDetails from './UserDetails'
import UserPlan from './UserPlan'
const UserLeftOverview = () => {
return (
<Grid container spacing={6}>
<Grid size={{ xs: 12 }}>
<UserDetails />
</Grid>
<Grid size={{ xs: 12 }}>
<UserPlan />
</Grid>
</Grid>
)
}
export default UserLeftOverview
@@ -0,0 +1,184 @@
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Button from '@mui/material/Button'
import Grid from '@mui/material/Grid2'
import Typography from '@mui/material/Typography'
import type { ButtonProps } from '@mui/material/Button'
// Component Imports
import AddNewAddress from '@components/dialogs/add-edit-address'
import OpenDialogOnElementClick from '@components/dialogs/OpenDialogOnElementClick'
// Vars
const data = {
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@gmail.com',
country: 'US',
address1: '100 Water Plant Avenue,',
address2: 'Building 1303 Wake Island',
landmark: 'Near Water Plant',
city: 'New York',
state: 'Capholim',
zipCode: '403114',
taxId: 'TAX-875623',
vatNumber: 'SDF754K77',
contact: '+1(609) 933-44-22'
}
const BillingAddress = () => {
const buttonProps: ButtonProps = {
variant: 'contained',
children: 'Edit Address',
size: 'small',
startIcon: <i className='tabler-plus' />
}
return (
<>
<Card>
<CardHeader
title='Billing Address'
action={
<OpenDialogOnElementClick
element={Button}
elementProps={buttonProps}
dialog={AddNewAddress}
dialogProps={{ data }}
/>
}
/>
<CardContent>
<Grid container>
<Grid size={{ xs: 12, md: 6 }}>
<table>
<tbody className='align-top'>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
Name:
</Typography>
</td>
<td className='p-1'>
<Typography>{`${data.firstName} ${data.lastName}`}</Typography>
</td>
</tr>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
Billing Email:
</Typography>
</td>
<td className='p-1'>
<Typography>{data.email}</Typography>
</td>
</tr>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
Tax ID:
</Typography>
</td>
<td className='p-1'>
<Typography>{data.taxId}</Typography>
</td>
</tr>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
VAT Number:
</Typography>
</td>
<td className='p-1'>
<Typography>{data.vatNumber}</Typography>
</td>
</tr>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
Billing Address:
</Typography>
</td>
<td className='p-1'>
<Typography>{`${data.address1} ${data.address2}`}</Typography>
</td>
</tr>
</tbody>
</table>
</Grid>
<Grid size={{ xs: 12, md: 6 }}>
<table>
<tbody className='align-top'>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
Contact:
</Typography>
</td>
<td className='p-1'>
<Typography>{data.contact}</Typography>
</td>
</tr>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
Landmark:
</Typography>
</td>
<td className='p-1'>
<Typography>{data.landmark}</Typography>
</td>
</tr>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
Landmark:
</Typography>
</td>
<td className='p-1'>
<Typography>{data.city}</Typography>
</td>
</tr>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
Country:
</Typography>
</td>
<td className='p-1'>
<Typography>{data.country}</Typography>
</td>
</tr>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
State:
</Typography>
</td>
<td className='p-1'>
<Typography>{data.state}</Typography>
</td>
</tr>
<tr>
<td className='p-1 pis-0 is-[150px]'>
<Typography className='font-medium' color='text.primary'>
Zip Code:
</Typography>
</td>
<td className='p-1'>
<Typography>{data.zipCode}</Typography>
</td>
</tr>
</tbody>
</table>
</Grid>
</Grid>
</CardContent>
</Card>
</>
)
}
export default BillingAddress
@@ -0,0 +1,94 @@
'use client'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Grid from '@mui/material/Grid2'
import Typography from '@mui/material/Typography'
import Button from '@mui/material/Button'
import Chip from '@mui/material/Chip'
import Alert from '@mui/material/Alert'
import AlertTitle from '@mui/material/AlertTitle'
import LinearProgress from '@mui/material/LinearProgress'
import type { ButtonProps } from '@mui/material/Button'
// Type Imports
import type { ThemeColor } from '@core/types'
import type { PricingPlanType } from '@/types/pages/pricingTypes'
// Component Imports
import ConfirmationDialog from '@components/dialogs/confirmation-dialog'
import UpgradePlan from '@components/dialogs/upgrade-plan'
import OpenDialogOnElementClick from '@components/dialogs/OpenDialogOnElementClick'
const CurrentPlan = ({ data }: { data?: PricingPlanType[] }) => {
const buttonProps = (children: string, variant: ButtonProps['variant'], color: ThemeColor): ButtonProps => ({
children,
variant,
color
})
return (
<Card>
<CardHeader title='Current Plan' />
<CardContent>
<Grid container spacing={6}>
<Grid size={{ xs: 12, md: 6 }} className='flex flex-col gap-4'>
<div>
<Typography className='font-medium text-textPrimary'>Your Current Plan is Basic</Typography>
<Typography>A simple start for everyone</Typography>
</div>
<div>
<Typography className='font-medium' color='text.primary'>
Active until Dec 09, 2021
</Typography>
<Typography>We will send you a notification upon Subscription expiration</Typography>
</div>
<div className='flex flex-col gap-1'>
<div className='flex items-center gap-2'>
<Typography className='font-medium' color='text.primary'>
$99 Per Month
</Typography>
<Chip color='primary' label='Popular' size='small' variant='tonal' />
</div>
<Typography>Standard plan for small to medium businesses</Typography>
</div>
</Grid>
<Grid size={{ xs: 12, md: 6 }}>
<Alert icon={false} severity='warning' className='mbe-4'>
<AlertTitle>We need your attention!</AlertTitle>
Your plan requires update
</Alert>
<div className='flex items-center justify-between'>
<Typography className='font-medium' color='text.primary'>
Days
</Typography>
<Typography className='font-medium' color='text.primary'>
26 of 30 Days
</Typography>
</div>
<LinearProgress variant='determinate' value={80} className='mlb-1 bs-2.5' />
<Typography variant='body2'>Your plan requires update</Typography>
</Grid>
<Grid size={{ xs: 12 }} className='flex gap-4 flex-wrap'>
<OpenDialogOnElementClick
element={Button}
elementProps={buttonProps('Upgrade plan', 'contained', 'primary')}
dialog={UpgradePlan}
dialogProps={{ data: data }}
/>
<OpenDialogOnElementClick
element={Button}
elementProps={buttonProps('Cancel Subscription', 'tonal', 'error')}
dialog={ConfirmationDialog}
dialogProps={{ type: 'unsubscribe' }}
/>
</Grid>
</Grid>
</CardContent>
</Card>
)
}
export default CurrentPlan
@@ -0,0 +1,143 @@
'use client'
// React Imports
import { useState } from 'react'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Typography from '@mui/material/Typography'
import Button from '@mui/material/Button'
import Chip from '@mui/material/Chip'
import type { ButtonProps } from '@mui/material/Button'
// Type Imports
import type { ThemeColor } from '@core/types'
// Component Imports
import BillingCard from '@components/dialogs/billing-card'
import OpenDialogOnElementClick from '@components/dialogs/OpenDialogOnElementClick'
type DataType = {
name: string
imgSrc: string
imgAlt: string
cardCvv: string
expiryDate: string
cardNumber: string
cardStatus?: string
badgeColor?: ThemeColor
}
// Vars
const data: DataType[] = [
{
cardCvv: '587',
name: 'Tom McBride',
expiryDate: '12/24',
imgAlt: 'Mastercard',
badgeColor: 'primary',
cardStatus: 'Primary',
cardNumber: '5577 0000 5577 9865',
imgSrc: '/images/logos/mastercard.png'
},
{
cardCvv: '681',
imgAlt: 'Visa card',
expiryDate: '02/24',
name: 'Mildred Wagner',
cardNumber: '4532 3616 2070 5678',
imgSrc: '/images/logos/visa.png'
},
{
cardCvv: '3845',
expiryDate: '08/20',
badgeColor: 'error',
cardStatus: 'Expired',
name: 'Lester Jennings',
imgAlt: 'American Express card',
cardNumber: '3700 000000 00002',
imgSrc: '/images/logos/american-express.png'
}
]
const PaymentMethod = () => {
// States
const [creditCard, setCreditCard] = useState(0)
const handleAddCard = () => {
setCreditCard(-1)
}
const handleClickOpen = (index: number) => {
setCreditCard(index)
}
// Vars
const addButtonProps: ButtonProps = {
variant: 'contained',
children: 'Add Card',
size: 'small',
color: 'primary',
startIcon: <i className='tabler-plus' />,
onClick: handleAddCard
}
const editButtonProps = (index: number): ButtonProps => ({
variant: 'tonal',
children: 'Edit',
size: 'small',
onClick: () => handleClickOpen(index)
})
return (
<>
<Card>
<CardHeader
title='Payment Methods'
action={<OpenDialogOnElementClick element={Button} elementProps={addButtonProps} dialog={BillingCard} />}
/>
<CardContent className='flex flex-col gap-4'>
{data.map((item, index) => (
<div
key={index}
className='flex justify-between border rounded sm:items-center p-6 flex-col !items-start sm:flex-row gap-2'
>
<div className='flex flex-col items-start gap-2'>
<img src={item.imgSrc} alt={item.imgAlt} height={25} />
<div className='flex items-center gap-2'>
<Typography className='font-medium' color='text.primary'>
{item.name}
</Typography>
{item.cardStatus ? (
<Chip color={item.badgeColor} label={item.cardStatus} size='small' variant='tonal' />
) : null}
</div>
<Typography>
{item.cardNumber && item.cardNumber.slice(0, -4).replace(/[0-9]/g, '*') + item.cardNumber.slice(-4)}
</Typography>
</div>
<div className='flex flex-col gap-4'>
<div className='flex items-center justify-end gap-4'>
<OpenDialogOnElementClick
element={Button}
elementProps={editButtonProps(index)}
dialog={BillingCard}
dialogProps={{ data: data[creditCard] }}
/>
<Button variant='tonal' color='error' size='small'>
Delete
</Button>
</div>
<Typography variant='body2'>Card expires at {item.expiryDate}</Typography>
</div>
</div>
))}
</CardContent>
</Card>
</>
)
}
export default PaymentMethod
@@ -0,0 +1,28 @@
// MUI Imports
import Grid from '@mui/material/Grid2'
// Type Imports
import type { PricingPlanType } from '@/types/pages/pricingTypes'
// Component Imports
import CurrentPlan from './CurrentPlan'
import PaymentMethod from './PaymentMethod'
import BillingAddress from './BillingAddress'
const BillingPlans = ({ data }: { data?: PricingPlanType[] }) => {
return (
<Grid container spacing={6}>
<Grid size={{ xs: 12 }}>
<CurrentPlan data={data} />
</Grid>
<Grid size={{ xs: 12 }}>
<PaymentMethod />
</Grid>
<Grid size={{ xs: 12 }}>
<BillingAddress />
</Grid>
</Grid>
)
}
export default BillingPlans
@@ -0,0 +1,161 @@
'use client'
// Next Imports
import Link from 'next/link'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Grid from '@mui/material/Grid2'
import Typography from '@mui/material/Typography'
import Switch from '@mui/material/Switch'
// Third-party Imports
import classnames from 'classnames'
// Component Imports
import CustomIconButton from '@core/components/mui/IconButton'
type ConnectedAccountsType = {
title: string
logo: string
checked: boolean
subtitle: string
}
type SocialAccountsType = {
title: string
logo: string
username?: string
isConnected: boolean
href?: string
}
// Vars
const connectedAccountsArr: ConnectedAccountsType[] = [
{
checked: true,
title: 'Google',
logo: '/images/logos/google.png',
subtitle: 'Calendar and Contacts'
},
{
checked: false,
title: 'Slack',
logo: '/images/logos/slack.png',
subtitle: 'Communications'
},
{
checked: true,
title: 'Github',
logo: '/images/logos/github.png',
subtitle: 'Manage your Git repositories'
},
{
checked: true,
title: 'Mailchimp',
subtitle: 'Email marketing service',
logo: '/images/logos/mailchimp.png'
},
{
title: 'Asana',
checked: false,
subtitle: 'Task Communication',
logo: '/images/logos/asana.png'
}
]
const socialAccountsArr: SocialAccountsType[] = [
{
title: 'Facebook',
isConnected: false,
logo: '/images/logos/facebook.png'
},
{
title: 'Twitter',
isConnected: true,
username: '@Pixinvent',
logo: '/images/logos/twitter.png',
href: 'https://twitter.com/pixinvents'
},
{
title: 'Linkedin',
isConnected: true,
username: '@Pixinvent',
logo: '/images/logos/linkedin.png',
href: 'https://www.linkedin.com/company/pixinvent'
},
{
title: 'Dribbble',
isConnected: false,
logo: '/images/logos/dribbble.png'
},
{
title: 'Behance',
isConnected: false,
logo: '/images/logos/behance.png'
}
]
const ConnectionsTab = () => {
return (
<Grid container spacing={6}>
<Grid size={{ xs: 12 }}>
<Card>
<CardHeader
title='Connected Accounts'
subheader='Display content from your connected accounts on your site'
/>
<CardContent className='flex flex-col gap-4'>
{connectedAccountsArr.map((item, index) => (
<div key={index} className='flex items-center justify-between gap-4'>
<div className='flex flex-grow items-center gap-4'>
<img height={36} width={36} src={item.logo} alt={item.title} />
<div className='flex flex-col flex-grow gap-0.5'>
<Typography className='font-medium' color='text.primary'>
{item.title}
</Typography>
<Typography>{item.subtitle}</Typography>
</div>
</div>
<Switch defaultChecked={item.checked} />
</div>
))}
</CardContent>
</Card>
</Grid>
<Grid size={{ xs: 12 }}>
<Card>
<CardHeader title='Social Accounts' subheader='Display content from social accounts on your site' />
<CardContent className='flex flex-col gap-4'>
{socialAccountsArr.map((item, index) => (
<div key={index} className='flex items-center justify-between gap-4'>
<div className='flex flex-grow items-center gap-4'>
<img height={36} width={36} src={item.logo} alt={item.title} />
<div className='flex flex-col flex-grow gap-0.5'>
<Typography className='font-medium' color='text.primary'>
{item.title}
</Typography>
{item.isConnected ? (
<Typography color='primary.main' component={Link} href={item.href || '/'} target='_blank'>
{item.username}
</Typography>
) : (
<Typography>Not Connected</Typography>
)}
</div>
</div>
<CustomIconButton variant='tonal' color={item.isConnected ? 'error' : 'secondary'}>
<i className={classnames(item.isConnected ? 'tabler-trash text-error' : 'tabler-link')} />
</CustomIconButton>
</div>
))}
</CardContent>
</Card>
</Grid>
</Grid>
)
}
export default ConnectionsTab
@@ -0,0 +1,58 @@
'use client'
// React Imports
import { useState } from 'react'
import type { SyntheticEvent, ReactElement } from 'react'
// MUI Imports
import Tab from '@mui/material/Tab'
import TabContext from '@mui/lab/TabContext'
import TabPanel from '@mui/lab/TabPanel'
import Grid from '@mui/material/Grid2'
// Component Imports
import CustomTabList from '@core/components/mui/TabList'
const UserRight = ({ tabContentList }: { tabContentList: { [key: string]: ReactElement } }) => {
// States
const [activeTab, setActiveTab] = useState('overview')
const handleChange = (event: SyntheticEvent, value: string) => {
setActiveTab(value)
}
return (
<>
<TabContext value={activeTab}>
<Grid container spacing={6}>
<Grid size={{ xs: 12 }}>
<CustomTabList onChange={handleChange} variant='scrollable' pill='true'>
<Tab icon={<i className='tabler-users' />} value='overview' label='Overview' iconPosition='start' />
<Tab icon={<i className='tabler-lock' />} value='security' label='Security' iconPosition='start' />
<Tab
icon={<i className='tabler-bookmark' />}
value='billing-plans'
label='Billing & Plans'
iconPosition='start'
/>
<Tab
icon={<i className='tabler-bell' />}
value='notifications'
label='Notifications'
iconPosition='start'
/>
<Tab icon={<i className='tabler-link' />} value='connections' label='Connections' iconPosition='start' />
</CustomTabList>
</Grid>
<Grid size={{ xs: 12 }}>
<TabPanel value={activeTab} className='p-0'>
{tabContentList[activeTab]}
</TabPanel>
</Grid>
</Grid>
</TabContext>
</>
)
}
export default UserRight
@@ -0,0 +1,93 @@
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardActions from '@mui/material/CardActions'
import Typography from '@mui/material/Typography'
import Checkbox from '@mui/material/Checkbox'
import Button from '@mui/material/Button'
// Style Imports
import tableStyles from '@core/styles/table.module.css'
type TableDataType = {
type: string
app: boolean
email: boolean
browser: boolean
}
// Vars
const tableData: TableDataType[] = [
{
app: false,
email: true,
browser: false,
type: 'New for you'
},
{
app: true,
email: false,
browser: true,
type: 'Account activity'
},
{
app: true,
email: true,
browser: true,
type: 'A new browser used to sign in'
},
{
app: false,
email: false,
browser: true,
type: 'A new device is linked'
}
]
const NotificationsTab = () => {
return (
<Card>
<CardHeader title='Notifications' subheader='You will receive notification for the below selected items' />
<div className='overflow-x-auto'>
<table className={tableStyles.table}>
<thead>
<tr>
<th>Type</th>
<th>App</th>
<th>Email</th>
<th>Browser</th>
</tr>
</thead>
<tbody className='border-be'>
{tableData.map((data, index) => (
<tr key={index}>
<td>
<Typography color='text.primary'>{data.type}</Typography>
</td>
<td>
<Checkbox defaultChecked={data.app} />
</td>
<td>
<Checkbox defaultChecked={data.email} />
</td>
<td>
<Checkbox defaultChecked={data.browser} />
</td>
</tr>
))}
</tbody>
</table>
</div>
<CardActions className='flex items-center'>
<Button variant='contained' type='submit'>
Save Changes
</Button>
<Button variant='tonal' color='secondary' type='reset'>
Discard
</Button>
</CardActions>
</Card>
)
}
export default NotificationsTab
@@ -0,0 +1,355 @@
'use client'
// React Imports
import { useState, useMemo } from 'react'
import type { MouseEvent } from 'react'
// Next Imports
import Link from 'next/link'
import { useParams } from 'next/navigation'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import Button from '@mui/material/Button'
import Typography from '@mui/material/Typography'
import IconButton from '@mui/material/IconButton'
import Menu from '@mui/material/Menu'
import MenuItem from '@mui/material/MenuItem'
import Tooltip from '@mui/material/Tooltip'
import TablePagination from '@mui/material/TablePagination'
// Third-party Imports
import classnames from 'classnames'
import { rankItem } from '@tanstack/match-sorter-utils'
import {
createColumnHelper,
flexRender,
getCoreRowModel,
useReactTable,
getFilteredRowModel,
getFacetedRowModel,
getFacetedUniqueValues,
getFacetedMinMaxValues,
getPaginationRowModel,
getSortedRowModel
} from '@tanstack/react-table'
import type { ColumnDef, FilterFn } from '@tanstack/react-table'
import type { RankingInfo } from '@tanstack/match-sorter-utils'
// Type Imports
import type { ThemeColor } from '@core/types'
import type { InvoiceType } from '@/types/apps/invoiceTypes'
import type { Locale } from '@configs/i18n'
// Component Imports
import OptionMenu from '@core/components/option-menu'
import CustomAvatar from '@core/components/mui/Avatar'
import CustomTextField from '@core/components/mui/TextField'
import TablePaginationComponent from '@components/TablePaginationComponent'
// Util Imports
import { getLocalizedUrl } from '@/utils/i18n'
// Style Imports
import tableStyles from '@core/styles/table.module.css'
declare module '@tanstack/table-core' {
interface FilterFns {
fuzzy: FilterFn<unknown>
}
interface FilterMeta {
itemRank: RankingInfo
}
}
type InvoiceTypeWithAction = InvoiceType & {
action?: string
}
type InvoiceStatusObj = {
[key: string]: {
icon: string
color: ThemeColor
}
}
const fuzzyFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
// Rank the item
const itemRank = rankItem(row.getValue(columnId), value)
// Store the itemRank info
addMeta({
itemRank
})
// Return if the item should be filtered in/out
return itemRank.passed
}
// Vars
const invoiceStatusObj: InvoiceStatusObj = {
Sent: { color: 'secondary', icon: 'tabler-send-2' },
Paid: { color: 'success', icon: 'tabler-check' },
Draft: { color: 'primary', icon: 'tabler-mail' },
'Partial Payment': { color: 'warning', icon: 'tabler-chart-pie-2' },
'Past Due': { color: 'error', icon: 'tabler-alert-circle' },
Downloaded: { color: 'info', icon: 'tabler-arrow-down' }
}
// Column Definitions
const columnHelper = createColumnHelper<InvoiceTypeWithAction>()
const InvoiceListTable = ({ invoiceData }: { invoiceData?: InvoiceType[] }) => {
// States
const [rowSelection, setRowSelection] = useState({})
const [data, setData] = useState(...[invoiceData])
const [globalFilter, setGlobalFilter] = useState('')
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
// Vars
const open = Boolean(anchorEl)
// Hooks
const { lang: locale } = useParams()
const columns = useMemo<ColumnDef<InvoiceTypeWithAction, any>[]>(
() => [
columnHelper.accessor('id', {
header: '#',
cell: ({ row }) => (
<Typography
component={Link}
href={getLocalizedUrl(`/apps/invoice/preview/${row.original.id}`, locale as Locale)}
color='primary.main'
>{`#${row.original.id}`}</Typography>
)
}),
columnHelper.accessor('invoiceStatus', {
header: 'Status',
cell: ({ row }) => (
<Tooltip
title={
<div>
<Typography variant='body2' component='span' className='text-inherit'>
{row.original.invoiceStatus}
</Typography>
<br />
<Typography variant='body2' component='span' className='text-inherit'>
Balance:
</Typography>{' '}
{row.original.balance}
<br />
<Typography variant='body2' component='span' className='text-inherit'>
Due Date:
</Typography>{' '}
{row.original.dueDate}
</div>
}
>
<CustomAvatar skin='light' color={invoiceStatusObj[row.original.invoiceStatus].color} size={28}>
<i className={classnames('text-base', invoiceStatusObj[row.original.invoiceStatus].icon)} />
</CustomAvatar>
</Tooltip>
)
}),
columnHelper.accessor('total', {
header: 'Total',
cell: ({ row }) => <Typography>{`$${row.original.total}`}</Typography>
}),
columnHelper.accessor('issuedDate', {
header: 'Issued Date',
cell: ({ row }) => <Typography>{row.original.issuedDate}</Typography>
}),
columnHelper.accessor('action', {
header: 'Action',
cell: ({ row }) => (
<div className='flex items-center'>
<IconButton onClick={() => setData(data?.filter(invoice => invoice.id !== row.original.id))}>
<i className='tabler-trash text-textSecondary' />
</IconButton>
<IconButton>
<Link
href={getLocalizedUrl(`/apps/invoice/preview/${row.original.id}`, locale as Locale)}
className='flex'
>
<i className='tabler-eye text-textSecondary' />
</Link>
</IconButton>
<OptionMenu
iconButtonProps={{ size: 'medium' }}
iconClassName='text-textSecondary'
options={[
{
text: 'Download',
icon: 'tabler-download',
menuItemProps: { className: 'flex items-center gap-2 text-textSecondary' }
},
{
text: 'Edit',
icon: 'tabler-edit',
href: getLocalizedUrl(`/apps/invoice/edit/${row.original.id}`, locale as Locale),
linkProps: {
className: classnames('flex items-center bs-[40px] plb-2 pli-4 is-full gap-2 text-textSecondary')
}
},
{
text: 'Duplicate',
icon: 'tabler-copy',
menuItemProps: { className: 'flex items-center gap-2 text-textSecondary' }
}
]}
/>
</div>
),
enableSorting: false
})
],
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
)
const table = useReactTable({
data: data as InvoiceType[],
columns,
filterFns: {
fuzzy: fuzzyFilter
},
state: {
rowSelection,
globalFilter
},
initialState: {
pagination: {
pageSize: 10
}
},
enableRowSelection: true, //enable row selection for all rows
// enableRowSelection: row => row.original.age > 18, // or enable row selection conditionally per row
globalFilterFn: fuzzyFilter,
onRowSelectionChange: setRowSelection,
getCoreRowModel: getCoreRowModel(),
onGlobalFilterChange: setGlobalFilter,
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(),
getFacetedMinMaxValues: getFacetedMinMaxValues()
})
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget)
}
const handleClose = () => {
setAnchorEl(null)
}
return (
<Card>
<CardHeader
title='Invoice List'
sx={{ '& .MuiCardHeader-action': { m: 0 } }}
className='flex items-center justify-between flex-wrap gap-4'
action={
<div className='flex items-center gap-4 flex-wrap'>
<div className='flex items-center gap-2'>
<Typography>Show</Typography>
<CustomTextField
select
value={table.getState().pagination.pageSize}
onChange={e => table.setPageSize(Number(e.target.value))}
className='is-[70px]'
>
<MenuItem value='10'>10</MenuItem>
<MenuItem value='25'>25</MenuItem>
<MenuItem value='50'>50</MenuItem>
</CustomTextField>
</div>
<Button
variant='tonal'
aria-haspopup='true'
onClick={handleClick}
color='secondary'
aria-expanded={open ? 'true' : undefined}
endIcon={<i className='tabler-upload' />}
aria-controls={open ? 'user-view-overview-export' : undefined}
>
Export
</Button>
<Menu open={open} anchorEl={anchorEl} onClose={handleClose} id='user-view-overview-export'>
<MenuItem onClick={handleClose} className='uppercase'>
pdf
</MenuItem>
<MenuItem onClick={handleClose} className='uppercase'>
xlsx
</MenuItem>
<MenuItem onClick={handleClose} className='uppercase'>
csv
</MenuItem>
</Menu>
</div>
}
/>
<div className='overflow-x-auto'>
<table className={tableStyles.table}>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => (
<th key={header.id} {...(header.id === 'action' && { className: 'is-24' })}>
{header.isPlaceholder ? null : (
<>
<div
className={classnames({
'flex items-center': header.column.getIsSorted(),
'cursor-pointer select-none': header.column.getCanSort()
})}
onClick={header.column.getToggleSortingHandler()}
>
{flexRender(header.column.columnDef.header, header.getContext())}
{{
asc: <i className='tabler-chevron-up text-xl' />,
desc: <i className='tabler-chevron-down text-xl' />
}[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
</div>
</>
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table
.getRowModel()
.rows.slice(0, table.getState().pagination.pageSize)
.map(row => {
return (
<tr key={row.id} className={classnames({ selected: row.getIsSelected() })}>
{row.getVisibleCells().map(cell => (
<td key={cell.id} {...(cell.id.includes('action') && { className: 'is-24' })}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
)
})}
</tbody>
</table>
</div>
<TablePagination
component={() => <TablePaginationComponent table={table} />}
count={table.getFilteredRowModel().rows.length}
rowsPerPage={table.getState().pagination.pageSize}
page={table.getState().pagination.pageIndex}
onPageChange={(_, page) => {
table.setPageIndex(page)
}}
/>
</Card>
)
}
export default InvoiceListTable
@@ -0,0 +1,354 @@
'use client'
// React Imports
import { useState, useMemo, useEffect } from 'react'
// MUI Imports
import Typography from '@mui/material/Typography'
import LinearProgress from '@mui/material/LinearProgress'
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import MenuItem from '@mui/material/MenuItem'
import TablePagination from '@mui/material/TablePagination'
import type { TextFieldProps } from '@mui/material/TextField'
// Third-party Imports
import classnames from 'classnames'
import { rankItem } from '@tanstack/match-sorter-utils'
import {
createColumnHelper,
flexRender,
getCoreRowModel,
useReactTable,
getFilteredRowModel,
getFacetedRowModel,
getFacetedUniqueValues,
getFacetedMinMaxValues,
getPaginationRowModel,
getSortedRowModel
} from '@tanstack/react-table'
import type { ColumnDef, FilterFn } from '@tanstack/react-table'
import type { RankingInfo } from '@tanstack/match-sorter-utils'
// Type Imports
import type { ThemeColor } from '@core/types'
// Component Imports
import CustomAvatar from '@core/components/mui/Avatar'
import CustomTextField from '@core/components/mui/TextField'
import TablePaginationComponent from '@components/TablePaginationComponent'
// Style Imports
import tableStyles from '@core/styles/table.module.css'
type ProjectListDataType = {
id: number
img: string
hours: string
totalTask: string
projectType: string
projectTitle: string
progressValue: number
progressColor: ThemeColor
}
declare module '@tanstack/table-core' {
interface FilterFns {
fuzzy: FilterFn<unknown>
}
interface FilterMeta {
itemRank: RankingInfo
}
}
// Vars
const projectTable: ProjectListDataType[] = [
{
id: 1,
hours: '18:42',
progressValue: 78,
totalTask: '122/240',
progressColor: 'success',
projectType: 'React Project',
projectTitle: 'BGC eCommerce App',
img: '/images/logos/react-bg.png'
},
{
id: 2,
hours: '20:42',
progressValue: 18,
totalTask: '9/56',
progressColor: 'error',
projectType: 'Figma Project',
projectTitle: 'Falcon Logo Design',
img: '/images/logos/figma-bg.png'
},
{
id: 3,
hours: '120:87',
progressValue: 62,
totalTask: '290/320',
progressColor: 'primary',
projectType: 'VueJs Project',
projectTitle: 'Dashboard Design',
img: '/images/logos/vue-bg.png'
},
{
id: 4,
hours: '89:19',
progressValue: 8,
totalTask: '7/63',
progressColor: 'error',
projectType: 'Xamarin Project',
projectTitle: 'Foodista Mobile App',
img: '/images/icons/mobile-bg.png'
},
{
id: 5,
hours: '230:10',
progressValue: 49,
totalTask: '120/186',
progressColor: 'warning',
projectType: 'Python Project',
projectTitle: 'Dojo React Project',
img: '/images/logos/python-bg.png'
},
{
id: 6,
hours: '342:41',
progressValue: 92,
totalTask: '99/109',
progressColor: 'success',
projectType: 'Sketch Project',
projectTitle: 'Blockchain Website',
img: '/images/logos/sketch-bg.png'
},
{
id: 7,
hours: '12:45',
progressValue: 88,
totalTask: '98/110',
progressColor: 'success',
projectType: 'HTML Project',
projectTitle: 'Hoffman Website',
img: '/images/logos/html-bg.png'
}
]
const fuzzyFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
// Rank the item
const itemRank = rankItem(row.getValue(columnId), value)
// Store the itemRank info
addMeta({
itemRank
})
// Return if the item should be filtered in/out
return itemRank.passed
}
const DebouncedInput = ({
value: initialValue,
onChange,
debounce = 500,
...props
}: {
value: string | number
onChange: (value: string | number) => void
debounce?: number
} & Omit<TextFieldProps, 'onChange'>) => {
// States
const [value, setValue] = useState(initialValue)
useEffect(() => {
setValue(initialValue)
}, [initialValue])
useEffect(() => {
const timeout = setTimeout(() => {
onChange(value)
}, debounce)
return () => clearTimeout(timeout)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value])
return <CustomTextField {...props} value={value} onChange={e => setValue(e.target.value)} />
}
// Column Definitions
const columnHelper = createColumnHelper<ProjectListDataType>()
const ProjectListTable = () => {
// States
const [rowSelection, setRowSelection] = useState({})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [data, setData] = useState(...[projectTable])
const [globalFilter, setGlobalFilter] = useState('')
// Hooks
const columns = useMemo<ColumnDef<ProjectListDataType, any>[]>(
() => [
columnHelper.accessor('projectTitle', {
header: 'Project',
cell: ({ row }) => (
<div className='flex items-center gap-4'>
<CustomAvatar src={row.original.img} size={34} />
<div className='flex flex-col'>
<Typography className='font-medium' color='text.primary'>
{row.original.projectTitle}
</Typography>
<Typography variant='body2'>{row.original.projectType}</Typography>
</div>
</div>
)
}),
columnHelper.accessor('totalTask', {
header: 'Total Task',
cell: ({ row }) => <Typography color='text.primary'>{row.original.totalTask}</Typography>
}),
columnHelper.accessor('progressValue', {
header: 'Progress',
cell: ({ row }) => (
<>
<Typography color='text.primary'>{`${row.original.progressValue}%`}</Typography>
<LinearProgress
color={row.original.progressColor}
value={row.original.progressValue}
variant='determinate'
className='is-full'
/>
</>
)
}),
columnHelper.accessor('hours', {
header: 'Hours',
cell: ({ row }) => <Typography>{row.original.hours}</Typography>
})
],
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
)
const table = useReactTable({
data,
columns,
filterFns: {
fuzzy: fuzzyFilter
},
state: {
rowSelection,
globalFilter
},
initialState: {
pagination: {
pageSize: 7
}
},
enableRowSelection: true, //enable row selection for all rows
// enableRowSelection: row => row.original.age > 18, // or enable row selection conditionally per row
globalFilterFn: fuzzyFilter,
onRowSelectionChange: setRowSelection,
getCoreRowModel: getCoreRowModel(),
onGlobalFilterChange: setGlobalFilter,
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(),
getFacetedMinMaxValues: getFacetedMinMaxValues()
})
return (
<Card>
<CardHeader title='User&#39;s Project List' className='flex flex-wrap gap-4' />
<div className='flex items-center justify-between p-6 gap-4'>
<div className='flex items-center gap-2'>
<Typography>Show</Typography>
<CustomTextField
select
value={table.getState().pagination.pageSize}
onChange={e => table.setPageSize(Number(e.target.value))}
className='is-[70px]'
>
<MenuItem value='5'>5</MenuItem>
<MenuItem value='7'>7</MenuItem>
<MenuItem value='10'>10</MenuItem>
</CustomTextField>
</div>
<DebouncedInput
value={globalFilter ?? ''}
onChange={value => setGlobalFilter(String(value))}
placeholder='Search Project'
/>
</div>
<div className='overflow-x-auto'>
<table className={tableStyles.table}>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => (
<th key={header.id}>
{header.isPlaceholder ? null : (
<>
<div
className={classnames({
'flex items-center': header.column.getIsSorted(),
'cursor-pointer select-none': header.column.getCanSort()
})}
onClick={header.column.getToggleSortingHandler()}
>
{flexRender(header.column.columnDef.header, header.getContext())}
{{
asc: <i className='tabler-chevron-up text-xl' />,
desc: <i className='tabler-chevron-down text-xl' />
}[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
</div>
</>
)}
</th>
))}
</tr>
))}
</thead>
{table.getFilteredRowModel().rows.length === 0 ? (
<tbody>
<tr>
<td colSpan={table.getVisibleFlatColumns().length} className='text-center'>
No data available
</td>
</tr>
</tbody>
) : (
<tbody>
{table
.getRowModel()
.rows.slice(0, table.getState().pagination.pageSize)
.map(row => {
return (
<tr key={row.id} className={classnames({ selected: row.getIsSelected() })}>
{row.getVisibleCells().map(cell => (
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
))}
</tr>
)
})}
</tbody>
)}
</table>
</div>
<TablePagination
component={() => <TablePaginationComponent table={table} />}
count={table.getFilteredRowModel().rows.length}
rowsPerPage={table.getState().pagination.pageSize}
page={table.getState().pagination.pageIndex}
onPageChange={(_, page) => {
table.setPageIndex(page)
}}
/>
</Card>
)
}
export default ProjectListTable
@@ -0,0 +1,115 @@
'use client'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Avatar from '@mui/material/Avatar'
import AvatarGroup from '@mui/material/AvatarGroup'
import { styled } from '@mui/material/styles'
import TimelineDot from '@mui/lab/TimelineDot'
import TimelineItem from '@mui/lab/TimelineItem'
import Typography from '@mui/material/Typography'
import TimelineContent from '@mui/lab/TimelineContent'
import TimelineSeparator from '@mui/lab/TimelineSeparator'
import TimelineConnector from '@mui/lab/TimelineConnector'
import MuiTimeline from '@mui/lab/Timeline'
import type { TimelineProps } from '@mui/lab/Timeline'
// Component Imports
import CustomAvatar from '@core/components/mui/Avatar'
// Styled Timeline component
const Timeline = styled(MuiTimeline)<TimelineProps>({
paddingLeft: 0,
paddingRight: 0,
'& .MuiTimelineItem-root': {
width: '100%',
'&:before': {
display: 'none'
}
}
})
const UserActivityTimeLine = () => {
return (
<Card>
<CardHeader title='User Activity Timeline' />
<CardContent>
<Timeline>
<TimelineItem>
<TimelineSeparator>
<TimelineDot color='primary' />
<TimelineConnector />
</TimelineSeparator>
<TimelineContent>
<div className='flex flex-wrap items-center justify-between gap-x-2 mbe-2.5'>
<Typography className='font-medium' color='text.primary'>
12 Invoices have been paid
</Typography>
<Typography variant='caption' color='text.disabled'>
12 min ago
</Typography>
</div>
<Typography className='mbe-2'>Invoices have been paid to the company</Typography>
<div className='flex items-center gap-2.5 is-fit bg-actionHover rounded plb-[5px] pli-2.5'>
<img height={20} alt='invoice.pdf' src='/images/icons/pdf-document.png' />
<Typography className='font-medium'>invoices.pdf</Typography>
</div>
</TimelineContent>
</TimelineItem>
<TimelineItem>
<TimelineSeparator>
<TimelineDot color='success' />
<TimelineConnector />
</TimelineSeparator>
<TimelineContent>
<div className='flex flex-wrap items-center justify-between gap-x-2 mbe-2.5'>
<Typography className='font-medium' color='text.primary'>
Client Meeting
</Typography>
<Typography variant='caption' color='text.disabled'>
45 min ago
</Typography>
</div>
<Typography className='mbe-2'>Project meeting with john @10:15am</Typography>
<div className='flex items-center gap-2.5'>
<CustomAvatar src='/images/avatars/1.png' size={32} />
<div className='flex flex-col flex-wrap'>
<Typography variant='body2' className='font-medium'>
Lester McCarthy (Client)
</Typography>
<Typography variant='body2'>CEO of Pixinvent</Typography>
</div>
</div>
</TimelineContent>
</TimelineItem>
<TimelineItem>
<TimelineSeparator>
<TimelineDot color='info' />
<TimelineConnector />
</TimelineSeparator>
<TimelineContent>
<div className='flex flex-wrap items-center justify-between gap-x-2 mbe-2.5'>
<Typography className='font-medium' color='text.primary'>
Create a new project for client
</Typography>
<Typography variant='caption' color='text.disabled'>
2 Day Ago
</Typography>
</div>
<Typography className='mbe-2'>6 team members in a project</Typography>
<AvatarGroup total={6} className='pull-up'>
<Avatar alt='Travis Howard' src='/images/avatars/1.png' />
<Avatar alt='Agnes Walker' src='/images/avatars/4.png' />
<Avatar alt='John Doe' src='/images/avatars/2.png' />
</AvatarGroup>
</TimelineContent>
</TimelineItem>
</Timeline>
</CardContent>
</Card>
)
}
export default UserActivityTimeLine
@@ -0,0 +1,48 @@
// MUI Imports
import Grid from '@mui/material/Grid2'
// Component Imports
import ProjectListTable from './ProjectListTable'
import UserActivityTimeLine from './UserActivityTimeline'
import InvoiceListTable from './InvoiceListTable'
// Data Imports
import { getInvoiceData } from '@/app/server/actions'
/**
* ! If you need data using an API call, uncomment the below API code, update the `process.env.API_URL` variable in the
* ! `.env` file found at root of your project and also update the API endpoints like `/apps/invoice` in below example.
* ! Also, remove the above server action import and the action itself from the `src/app/server/actions.ts` file to clean up unused code
* ! because we've used the server action for getting our static data.
*/
/* const getInvoiceData = async () => {
const res = await fetch(`${process.env.API_URL}/apps/invoice`)
if (!res.ok) {
throw new Error('Failed to fetch invoice data')
}
return res.json()
} */
const OverViewTab = async () => {
// Vars
const invoiceData = await getInvoiceData()
return (
<Grid container spacing={6}>
<Grid size={{ xs: 12 }}>
<ProjectListTable />
</Grid>
<Grid size={{ xs: 12 }}>
<UserActivityTimeLine />
</Grid>
<Grid size={{ xs: 12 }}>
<InvoiceListTable invoiceData={invoiceData} />
</Grid>
</Grid>
)
}
export default OverViewTab
@@ -0,0 +1,90 @@
'use client'
// React Imports
import { useState } from 'react'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import Grid from '@mui/material/Grid2'
import InputAdornment from '@mui/material/InputAdornment'
import IconButton from '@mui/material/IconButton'
import Alert from '@mui/material/Alert'
import AlertTitle from '@mui/material/AlertTitle'
import Button from '@mui/material/Button'
// Component Imports
import CustomTextField from '@core/components/mui/TextField'
const ChangePassword = () => {
// States
const [isPasswordShown, setIsPasswordShown] = useState(false)
const [isConfirmPasswordShown, setIsConfirmPasswordShown] = useState(false)
return (
<Card>
<CardHeader title='Change Password' />
<CardContent className='flex flex-col gap-4'>
<Alert icon={false} severity='warning' onClose={() => {}}>
<AlertTitle>Ensure that these requirements are met</AlertTitle>
Minimum 8 characters long, uppercase & symbol
</Alert>
<form>
<Grid container spacing={4}>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField
fullWidth
label='Password'
type={isPasswordShown ? 'text' : 'password'}
slotProps={{
input: {
endAdornment: (
<InputAdornment position='end'>
<IconButton
edge='end'
onClick={() => setIsPasswordShown(!isPasswordShown)}
onMouseDown={e => e.preventDefault()}
>
<i className={isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
</IconButton>
</InputAdornment>
)
}
}}
/>
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField
fullWidth
label='Confirm Password'
type={isConfirmPasswordShown ? 'text' : 'password'}
slotProps={{
input: {
endAdornment: (
<InputAdornment position='end'>
<IconButton
edge='end'
onClick={() => setIsConfirmPasswordShown(!isConfirmPasswordShown)}
onMouseDown={e => e.preventDefault()}
>
<i className={isConfirmPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
</IconButton>
</InputAdornment>
)
}
}}
/>
</Grid>
<Grid size={{ xs: 12 }} className='flex gap-4'>
<Button variant='contained'>Change Password</Button>
</Grid>
</Grid>
</form>
</CardContent>
</Card>
)
}
export default ChangePassword
@@ -0,0 +1,93 @@
// React Imports
import type { ReactElement } from 'react'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import Typography from '@mui/material/Typography'
// Style Imports
import tableStyles from '@core/styles/table.module.css'
type DataType = {
device: string
browser: string
location: string
recentActivity: string
browserIcon: ReactElement
}
// Vars
const recentDeviceData: DataType[] = [
{
device: 'Dell XPS 15',
location: 'United States',
browser: 'Chrome on Windows',
recentActivity: '10, Jan 2020 20:07',
browserIcon: <i className='tabler-brand-windows text-[22px] text-info' />
},
{
location: 'Ghana',
device: 'Google Pixel 3a',
browser: 'Chrome on Android',
recentActivity: '11, Jan 2020 10:16',
browserIcon: <i className='tabler-brand-android text-[22px] text-success' />
},
{
location: 'Mayotte',
device: 'Apple iMac',
browser: 'Chrome on MacOS',
recentActivity: '11, Jan 2020 12:10',
browserIcon: <i className='tabler-brand-apple text-[22px] text-secondary' />
},
{
location: 'Mauritania',
device: 'Apple iPhone XR',
browser: 'Chrome on iPhone',
recentActivity: '12, Jan 2020 8:29',
browserIcon: <i className='tabler-device-mobile text-[22px] text-error' />
}
]
const RecentDevice = () => {
return (
<Card>
<CardHeader title='Recent Devices' />
<div className='overflow-x-auto'>
<table className={tableStyles.table}>
<thead>
<tr>
<th>Browser</th>
<th>Device</th>
<th>Location</th>
<th>Recent Activities</th>
</tr>
</thead>
<tbody>
{recentDeviceData.map((device, index) => (
<tr key={index}>
<td>
<div className='flex items-center gap-4'>
{device.browserIcon}
<Typography color='text.primary'>{device.browser}</Typography>
</div>
</td>
<td>
<Typography>{device.device}</Typography>
</td>
<td>
<Typography>{device.location}</Typography>
</td>
<td>
<Typography>{device.recentActivity}</Typography>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
)
}
export default RecentDevice
@@ -0,0 +1,42 @@
'use client'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
import CardContent from '@mui/material/CardContent'
import InputLabel from '@mui/material/InputLabel'
import Typography from '@mui/material/Typography'
// Component Imports
import CustomIconButton from '@core/components/mui/IconButton'
import CustomTextField from '@core/components/mui/TextField'
const TwoStepVerification = () => {
return (
<Card>
<CardHeader title='Two-step verification' subheader='Keep your account secure with authentication step.' />
<CardContent>
<Typography htmlFor='sms' component={InputLabel} className='inline-flex font-medium mbe-1' color='text.primary'>
SMS
</Typography>
<div className='flex items-center mbe-4 gap-5'>
<CustomTextField id='sms' placeholder='+1(968) 819-2547' fullWidth />
<div className='flex items-center gap-1'>
<CustomIconButton color='secondary'>
<i className='tabler-edit' />
</CustomIconButton>
<CustomIconButton color='secondary'>
<i className='tabler-user-plus' />
</CustomIconButton>
</div>
</div>
<Typography>
Two-factor authentication adds an additional layer of security to your account by requiring more than just a
password to log in. <span className='text-primary'>Learn more.</span>
</Typography>
</CardContent>
</Card>
)
}
export default TwoStepVerification
@@ -0,0 +1,25 @@
// MUI Imports
import Grid from '@mui/material/Grid2'
// Component Imports
import ChangePassword from './ChangePassword'
import TwoStepVerification from './TwoStepVerification'
import RecentDevice from './RecentDevice'
const SecurityTab = () => {
return (
<Grid container spacing={6}>
<Grid size={{ xs: 12 }}>
<ChangePassword />
</Grid>
<Grid size={{ xs: 12 }}>
<TwoStepVerification />
</Grid>
<Grid size={{ xs: 12 }}>
<RecentDevice />
</Grid>
</Grid>
)
}
export default SecurityTab