fix: clean folder
This commit is contained in:
@@ -1,67 +0,0 @@
|
||||
'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 FormControlLabel from '@mui/material/FormControlLabel'
|
||||
import Checkbox from '@mui/material/Checkbox'
|
||||
import Button from '@mui/material/Button'
|
||||
import FormControl from '@mui/material/FormControl'
|
||||
import FormHelperText from '@mui/material/FormHelperText'
|
||||
|
||||
// Third-party Imports
|
||||
import { useForm, Controller } from 'react-hook-form'
|
||||
|
||||
// Component Imports
|
||||
import ConfirmationDialog from '@components/dialogs/confirmation-dialog'
|
||||
|
||||
const AccountDelete = () => {
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
// Hooks
|
||||
const {
|
||||
control,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors }
|
||||
} = useForm({ defaultValues: { checkbox: false } })
|
||||
|
||||
// Vars
|
||||
const checkboxValue = watch('checkbox')
|
||||
|
||||
const onSubmit = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Delete Account' />
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<FormControl error={Boolean(errors.checkbox)} className='is-full mbe-6'>
|
||||
<Controller
|
||||
name='checkbox'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel control={<Checkbox {...field} />} label='I confirm my account deactivation' />
|
||||
)}
|
||||
/>
|
||||
{errors.checkbox && <FormHelperText error>Please confirm you want to delete account</FormHelperText>}
|
||||
</FormControl>
|
||||
<Button variant='contained' color='error' type='submit' disabled={!checkboxValue}>
|
||||
Deactivate Account
|
||||
</Button>
|
||||
<ConfirmationDialog open={open} setOpen={setOpen} type='delete-account' />
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountDelete
|
||||
@@ -1,300 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
import type { ChangeEvent } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Card from '@mui/material/Card'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Button from '@mui/material/Button'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import type { SelectChangeEvent } from '@mui/material/Select'
|
||||
|
||||
// Component Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
type Data = {
|
||||
firstName: string
|
||||
lastName: string
|
||||
email: string
|
||||
organization: string
|
||||
phoneNumber: number | string
|
||||
address: string
|
||||
state: string
|
||||
zipCode: string
|
||||
country: string
|
||||
language: string
|
||||
timezone: string
|
||||
currency: string
|
||||
}
|
||||
|
||||
// Vars
|
||||
const initialData: Data = {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: 'john.doe@example.com',
|
||||
organization: 'Pixinvent',
|
||||
phoneNumber: '+1 (917) 543-9876',
|
||||
address: '123 Main St, New York, NY 10001',
|
||||
state: 'New York',
|
||||
zipCode: '634880',
|
||||
country: 'usa',
|
||||
language: 'english',
|
||||
timezone: 'gmt-12',
|
||||
currency: 'usd'
|
||||
}
|
||||
|
||||
const languageData = ['English', 'Arabic', 'French', 'German', 'Portuguese']
|
||||
|
||||
const AccountDetails = () => {
|
||||
// States
|
||||
const [formData, setFormData] = useState<Data>(initialData)
|
||||
const [fileInput, setFileInput] = useState<string>('')
|
||||
const [imgSrc, setImgSrc] = useState<string>('/images/avatars/1.png')
|
||||
const [language, setLanguage] = useState<string[]>(['English'])
|
||||
|
||||
const handleDelete = (value: string) => {
|
||||
setLanguage(current => current.filter(item => item !== value))
|
||||
}
|
||||
|
||||
const handleChange = (event: SelectChangeEvent<string[]>) => {
|
||||
setLanguage(event.target.value as string[])
|
||||
}
|
||||
|
||||
const handleFormChange = (field: keyof Data, value: Data[keyof Data]) => {
|
||||
setFormData({ ...formData, [field]: value })
|
||||
}
|
||||
|
||||
const handleFileInputChange = (file: ChangeEvent) => {
|
||||
const reader = new FileReader()
|
||||
const { files } = file.target as HTMLInputElement
|
||||
|
||||
if (files && files.length !== 0) {
|
||||
reader.onload = () => setImgSrc(reader.result as string)
|
||||
reader.readAsDataURL(files[0])
|
||||
|
||||
if (reader.result !== null) {
|
||||
setFileInput(reader.result as string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleFileInputReset = () => {
|
||||
setFileInput('')
|
||||
setImgSrc('/images/avatars/1.png')
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className='mbe-4'>
|
||||
<div className='flex max-sm:flex-col items-center gap-6'>
|
||||
<img height={100} width={100} className='rounded' src={imgSrc} alt='Profile' />
|
||||
<div className='flex flex-grow flex-col gap-4'>
|
||||
<div className='flex flex-col sm:flex-row gap-4'>
|
||||
<Button component='label' variant='contained' htmlFor='account-settings-upload-image'>
|
||||
Upload New Photo
|
||||
<input
|
||||
hidden
|
||||
type='file'
|
||||
value={fileInput}
|
||||
accept='image/png, image/jpeg'
|
||||
onChange={handleFileInputChange}
|
||||
id='account-settings-upload-image'
|
||||
/>
|
||||
</Button>
|
||||
<Button variant='tonal' color='secondary' onClick={handleFileInputReset}>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
<Typography>Allowed JPG, GIF or PNG. Max size of 800K</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<form onSubmit={e => e.preventDefault()}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='First Name'
|
||||
value={formData.firstName}
|
||||
placeholder='John'
|
||||
onChange={e => handleFormChange('firstName', e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Last Name'
|
||||
value={formData.lastName}
|
||||
placeholder='Doe'
|
||||
onChange={e => handleFormChange('lastName', e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Email'
|
||||
value={formData.email}
|
||||
placeholder='john.doe@gmail.com'
|
||||
onChange={e => handleFormChange('email', e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Organization'
|
||||
value={formData.organization}
|
||||
placeholder='Pixinvent'
|
||||
onChange={e => handleFormChange('organization', e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Phone Number'
|
||||
value={formData.phoneNumber}
|
||||
placeholder='+1 (234) 567-8901'
|
||||
onChange={e => handleFormChange('phoneNumber', e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Address'
|
||||
value={formData.address}
|
||||
placeholder='Address'
|
||||
onChange={e => handleFormChange('address', e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='State'
|
||||
value={formData.state}
|
||||
placeholder='New York'
|
||||
onChange={e => handleFormChange('state', e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
type='number'
|
||||
label='Zip Code'
|
||||
value={formData.zipCode}
|
||||
placeholder='123456'
|
||||
onChange={e => handleFormChange('zipCode', e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Country'
|
||||
value={formData.country}
|
||||
onChange={e => handleFormChange('country', e.target.value)}
|
||||
>
|
||||
<MenuItem value='usa'>USA</MenuItem>
|
||||
<MenuItem value='uk'>UK</MenuItem>
|
||||
<MenuItem value='australia'>Australia</MenuItem>
|
||||
<MenuItem value='germany'>Germany</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Language'
|
||||
value={language}
|
||||
slotProps={{
|
||||
select: {
|
||||
multiple: true, // @ts-ignore
|
||||
onChange: handleChange,
|
||||
renderValue: selected => (
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
{(selected as string[]).map(value => (
|
||||
<Chip
|
||||
key={value}
|
||||
clickable
|
||||
onMouseDown={event => event.stopPropagation()}
|
||||
size='small'
|
||||
label={value}
|
||||
onDelete={() => handleDelete(value)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{languageData.map(name => (
|
||||
<MenuItem key={name} value={name}>
|
||||
{name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='TimeZone'
|
||||
value={formData.timezone}
|
||||
onChange={e => handleFormChange('timezone', e.target.value)}
|
||||
slotProps={{
|
||||
select: { MenuProps: { PaperProps: { style: { maxHeight: 250 } } } }
|
||||
}}
|
||||
>
|
||||
<MenuItem value='gmt-12'>(GMT-12:00) International Date Line West</MenuItem>
|
||||
<MenuItem value='gmt-11'>(GMT-11:00) Midway Island, Samoa</MenuItem>
|
||||
<MenuItem value='gmt-10'>(GMT-10:00) Hawaii</MenuItem>
|
||||
<MenuItem value='gmt-09'>(GMT-09:00) Alaska</MenuItem>
|
||||
<MenuItem value='gmt-08'>(GMT-08:00) Pacific Time (US & Canada)</MenuItem>
|
||||
<MenuItem value='gmt-08-baja'>(GMT-08:00) Tijuana, Baja California</MenuItem>
|
||||
<MenuItem value='gmt-07'>(GMT-07:00) Chihuahua, La Paz, Mazatlan</MenuItem>
|
||||
<MenuItem value='gmt-07-mt'>(GMT-07:00) Mountain Time (US & Canada)</MenuItem>
|
||||
<MenuItem value='gmt-06'>(GMT-06:00) Central America</MenuItem>
|
||||
<MenuItem value='gmt-06-ct'>(GMT-06:00) Central Time (US & Canada)</MenuItem>
|
||||
<MenuItem value='gmt-06-mc'>(GMT-06:00) Guadalajara, Mexico City, Monterrey</MenuItem>
|
||||
<MenuItem value='gmt-06-sk'>(GMT-06:00) Saskatchewan</MenuItem>
|
||||
<MenuItem value='gmt-05'>(GMT-05:00) Bogota, Lima, Quito, Rio Branco</MenuItem>
|
||||
<MenuItem value='gmt-05-et'>(GMT-05:00) Eastern Time (US & Canada)</MenuItem>
|
||||
<MenuItem value='gmt-05-ind'>(GMT-05:00) Indiana (East)</MenuItem>
|
||||
<MenuItem value='gmt-04'>(GMT-04:00) Atlantic Time (Canada)</MenuItem>
|
||||
<MenuItem value='gmt-04-clp'>(GMT-04:00) Caracas, La Paz</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Currency'
|
||||
value={formData.currency}
|
||||
onChange={e => handleFormChange('currency', e.target.value)}
|
||||
>
|
||||
<MenuItem value='usd'>USD</MenuItem>
|
||||
<MenuItem value='euro'>EUR</MenuItem>
|
||||
<MenuItem value='pound'>Pound</MenuItem>
|
||||
<MenuItem value='bitcoin'>Bitcoin</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex gap-4 flex-wrap'>
|
||||
<Button variant='contained' type='submit'>
|
||||
Save Changes
|
||||
</Button>
|
||||
<Button variant='tonal' type='reset' color='secondary' onClick={() => setFormData(initialData)}>
|
||||
Reset
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountDetails
|
||||
@@ -1,21 +0,0 @@
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
|
||||
// Component Imports
|
||||
import AccountDetails from './AccountDetails'
|
||||
import AccountDelete from './AccountDelete'
|
||||
|
||||
const Account = () => {
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<AccountDetails />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<AccountDelete />
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default Account
|
||||
@@ -1,85 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
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 MenuItem from '@mui/material/MenuItem'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
|
||||
// Component Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
const Address = () => {
|
||||
// States
|
||||
const [state, setState] = useState('')
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Billing Address' />
|
||||
<CardContent>
|
||||
<form>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField fullWidth label='Company Name' variant='outlined' placeholder='Pixinvent' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField fullWidth label='Billing Email' variant='outlined' placeholder='john.doe@example.com' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField fullWidth label='TAX ID' variant='outlined' placeholder='Enter TAX ID' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField fullWidth label='VAT Number' variant='outlined' placeholder='Enter VAT Number' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
type='number'
|
||||
label='Mobile Number'
|
||||
placeholder='202 555 0111'
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: <InputAdornment position='start'>US (+1)</InputAdornment>
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField select fullWidth label='Country' value={state} onChange={e => setState(e.target.value)}>
|
||||
<MenuItem value=''>Select Country</MenuItem>
|
||||
<MenuItem value='australia'>Australia</MenuItem>
|
||||
<MenuItem value='canada'>Canada</MenuItem>
|
||||
<MenuItem value='france'>France</MenuItem>
|
||||
<MenuItem value='united-kingdom'>United Kingdom</MenuItem>
|
||||
<MenuItem value='united-states'>United States</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField fullWidth label='Billing Address' variant='outlined' placeholder='Billing Address' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField fullWidth label='State' variant='outlined' placeholder='California' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField fullWidth type='number' label='Zip Code' variant='outlined' placeholder='231465' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex gap-4 flex-wrap'>
|
||||
<Button variant='contained'>Save Changes</Button>
|
||||
<Button variant='tonal' type='reset' color='secondary' onClick={() => setState('')}>
|
||||
Discard
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default Address
|
||||
@@ -1,96 +0,0 @@
|
||||
// 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 { PricingPlanType } from '@/types/pages/pricingTypes'
|
||||
import type { ThemeColor } from '@core/types'
|
||||
|
||||
// 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, color: ThemeColor, variant: ButtonProps['variant']): 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-6'>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<Typography color='text.primary' className='font-medium'>
|
||||
Your Current Plan is Basic
|
||||
</Typography>
|
||||
<Typography>A simple start for everyone</Typography>
|
||||
</div>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<Typography color='text.primary' className='font-medium'>
|
||||
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-1.5'>
|
||||
<Typography color='text.primary' className='font-medium'>
|
||||
$199 Per Month
|
||||
</Typography>
|
||||
<Chip color='primary' variant='tonal' label='Popular' size='small' />
|
||||
</div>
|
||||
<Typography>Standard plan for small to medium businesses</Typography>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }} className='flex flex-col gap-6'>
|
||||
<Alert severity='warning'>
|
||||
<AlertTitle>We need your attention!</AlertTitle>
|
||||
Your plan requires update
|
||||
</Alert>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<Typography color='text.primary' className='font-medium'>
|
||||
Days
|
||||
</Typography>
|
||||
<Typography color='text.primary' className='font-medium'>
|
||||
12 of 30 Days
|
||||
</Typography>
|
||||
</div>
|
||||
<LinearProgress variant='determinate' value={20} />
|
||||
<Typography variant='body2'>18 days remaining until your plan requires update</Typography>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex gap-4 flex-wrap'>
|
||||
<OpenDialogOnElementClick
|
||||
element={Button}
|
||||
elementProps={buttonProps('Upgrade Plan', 'primary', 'contained')}
|
||||
dialog={UpgradePlan}
|
||||
dialogProps={{ data: data }}
|
||||
/>
|
||||
<OpenDialogOnElementClick
|
||||
element={Button}
|
||||
elementProps={buttonProps('Cancel Subscription', 'error', 'tonal')}
|
||||
dialog={ConfirmationDialog}
|
||||
dialogProps={{ type: 'unsubscribe' }}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default CurrentPlan
|
||||
@@ -1,461 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
// MUI Imports
|
||||
import Button from '@mui/material/Button'
|
||||
import Card from '@mui/material/Card'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Checkbox from '@mui/material/Checkbox'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import type { TextFieldProps } from '@mui/material/TextField'
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
import Typography from '@mui/material/Typography'
|
||||
|
||||
// Third-party Imports
|
||||
import type { RankingInfo } from '@tanstack/match-sorter-utils'
|
||||
import { rankItem } from '@tanstack/match-sorter-utils'
|
||||
import type { ColumnDef, FilterFn } from '@tanstack/react-table'
|
||||
import {
|
||||
createColumnHelper,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFacetedMinMaxValues,
|
||||
getFacetedRowModel,
|
||||
getFacetedUniqueValues,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable
|
||||
} from '@tanstack/react-table'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { InvoiceType } from '@/types/apps/invoiceTypes'
|
||||
import type { Locale } from '@configs/i18n'
|
||||
import type { ThemeColor } from '@core/types'
|
||||
|
||||
// Component Imports
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
import OptionMenu from '@core/components/option-menu'
|
||||
|
||||
// Util Imports
|
||||
import { getInitials } from '@/utils/getInitials'
|
||||
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
|
||||
}
|
||||
|
||||
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)} />
|
||||
}
|
||||
|
||||
// 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 [status, setStatus] = useState<InvoiceType['invoiceStatus']>('')
|
||||
const [rowSelection, setRowSelection] = useState({})
|
||||
const [data, setData] = useState(...[invoiceData])
|
||||
const [filteredData, setFilteredData] = useState(data)
|
||||
const [globalFilter, setGlobalFilter] = useState('')
|
||||
|
||||
// Hooks
|
||||
const { lang: locale } = useParams()
|
||||
|
||||
const columns = useMemo<ColumnDef<InvoiceTypeWithAction, any>[]>(
|
||||
() => [
|
||||
{
|
||||
id: 'select',
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
{...{
|
||||
checked: table.getIsAllRowsSelected(),
|
||||
indeterminate: table.getIsSomeRowsSelected(),
|
||||
onChange: table.getToggleAllRowsSelectedHandler()
|
||||
}}
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
{...{
|
||||
checked: row.getIsSelected(),
|
||||
disabled: !row.getCanSelect(),
|
||||
indeterminate: row.getIsSomeSelected(),
|
||||
onChange: row.getToggleSelectedHandler()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
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('bs-4 is-4', invoiceStatusObj[row.original.invoiceStatus].icon)} />
|
||||
</CustomAvatar>
|
||||
</Tooltip>
|
||||
)
|
||||
}),
|
||||
columnHelper.accessor('name', {
|
||||
header: 'Client',
|
||||
cell: ({ row }) => (
|
||||
<div className='flex items-center gap-3'>
|
||||
{getAvatar({ avatar: row.original.avatar, name: row.original.name })}
|
||||
<div className='flex flex-col'>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{row.original.name}
|
||||
</Typography>
|
||||
<Typography variant='body2'>{row.original.companyEmail}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}),
|
||||
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('balance', {
|
||||
header: 'Balance',
|
||||
cell: ({ row }) => {
|
||||
return row.original.balance === 0 ? (
|
||||
<Chip label='Paid' color='success' size='small' variant='tonal' />
|
||||
) : (
|
||||
<Typography color='text.primary'>{row.original.balance}</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-pencil',
|
||||
href: getLocalizedUrl(`/apps/invoice/edit/${row.original.id}`, locale as Locale),
|
||||
linkProps: {
|
||||
className: 'flex items-center is-full plb-2 pli-4 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
|
||||
[data, filteredData]
|
||||
)
|
||||
|
||||
const table = useReactTable({
|
||||
data: filteredData 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 getAvatar = (params: Pick<InvoiceType, 'avatar' | 'name'>) => {
|
||||
const { avatar, name } = params
|
||||
|
||||
if (avatar) {
|
||||
return <CustomAvatar src={avatar} skin='light' size={34} />
|
||||
} else {
|
||||
return (
|
||||
<CustomAvatar skin='light' size={34}>
|
||||
{getInitials(name as string)}
|
||||
</CustomAvatar>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const filteredData = data?.filter(invoice => {
|
||||
if (status && invoice.invoiceStatus.toLowerCase().replace(/\s+/g, '-') !== status) return false
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
setFilteredData(filteredData)
|
||||
}, [status, data])
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className='flex justify-between flex-col items-start md:items-center md:flex-row gap-4'>
|
||||
<div className='flex items-center justify-between gap-4'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Typography className='hidden sm:block'>Show</Typography>
|
||||
<CustomTextField
|
||||
select
|
||||
value={table.getState().pagination.pageSize}
|
||||
onChange={e => table.setPageSize(Number(e.target.value))}
|
||||
className='max-sm:is-full sm:is-[70px]'
|
||||
>
|
||||
<MenuItem value='10'>10</MenuItem>
|
||||
<MenuItem value='25'>25</MenuItem>
|
||||
<MenuItem value='50'>50</MenuItem>
|
||||
</CustomTextField>
|
||||
</div>
|
||||
<Button
|
||||
variant='contained'
|
||||
component={Link}
|
||||
startIcon={<i className='tabler-plus' />}
|
||||
href={getLocalizedUrl('apps/invoice/add', locale as Locale)}
|
||||
className='max-sm:is-full'
|
||||
>
|
||||
Create Invoice
|
||||
</Button>
|
||||
</div>
|
||||
<div className='flex flex-col sm:flex-row max-sm:is-full items-start sm:items-center gap-4'>
|
||||
<DebouncedInput
|
||||
value={globalFilter ?? ''}
|
||||
onChange={value => setGlobalFilter(String(value))}
|
||||
placeholder='Search Invoice'
|
||||
className='max-sm:is-full sm:is-[250px]'
|
||||
/>
|
||||
<CustomTextField
|
||||
select
|
||||
id='select-status'
|
||||
value={status}
|
||||
onChange={e => setStatus(e.target.value)}
|
||||
className='max-sm:is-full sm:is-[160px]'
|
||||
slotProps={{
|
||||
select: { displayEmpty: true }
|
||||
}}
|
||||
>
|
||||
<MenuItem value=''>Invoice Status</MenuItem>
|
||||
<MenuItem value='downloaded'>Downloaded</MenuItem>
|
||||
<MenuItem value='draft'>Draft</MenuItem>
|
||||
<MenuItem value='paid'>Paid</MenuItem>
|
||||
<MenuItem value='partial-payment'>Partial Payment</MenuItem>
|
||||
<MenuItem value='past-due'>Past Due</MenuItem>
|
||||
<MenuItem value='sent'>Sent</MenuItem>
|
||||
</CustomTextField>
|
||||
</div>
|
||||
</CardContent>
|
||||
<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)
|
||||
}}
|
||||
onRowsPerPageChange={e => table.setPageSize(Number(e.target.value))}
|
||||
/> */}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default InvoiceListTable
|
||||
@@ -1,225 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Radio from '@mui/material/Radio'
|
||||
import Switch from '@mui/material/Switch'
|
||||
import Button from '@mui/material/Button'
|
||||
import RadioGroup from '@mui/material/RadioGroup'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import FormControlLabel from '@mui/material/FormControlLabel'
|
||||
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'
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
type DataType = {
|
||||
cardNumber?: string
|
||||
name?: string
|
||||
expiryDate?: string
|
||||
cardCvv?: string
|
||||
imgSrc?: string
|
||||
imgAlt?: 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',
|
||||
name: 'Mildred Wagner',
|
||||
expiryDate: '02/24',
|
||||
imgAlt: 'Visa card',
|
||||
cardNumber: '4532 3616 2070 5678',
|
||||
imgSrc: '/images/logos/visa.png'
|
||||
}
|
||||
]
|
||||
|
||||
const PaymentMethod = () => {
|
||||
// States
|
||||
const [paymentMethod, setPaymentMethod] = useState<'credit' | 'cod'>('credit')
|
||||
const [creditCard, setCreditCard] = useState(0)
|
||||
|
||||
// Hooks
|
||||
const [cardData, setCardData] = useState({
|
||||
cardNumber: '',
|
||||
name: '',
|
||||
expiryDate: '',
|
||||
cardCvv: ''
|
||||
})
|
||||
|
||||
const handleReset = () => {
|
||||
setCardData({
|
||||
cardNumber: '',
|
||||
name: '',
|
||||
expiryDate: '',
|
||||
cardCvv: ''
|
||||
})
|
||||
}
|
||||
|
||||
const buttonProps = (index: number): ButtonProps => ({
|
||||
variant: 'tonal',
|
||||
children: 'Edit',
|
||||
size: 'small',
|
||||
onClick: () => setCreditCard(index)
|
||||
})
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Payment Method' />
|
||||
<CardContent>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<RadioGroup
|
||||
row
|
||||
name='payment-method-radio'
|
||||
value={paymentMethod}
|
||||
onChange={e => setPaymentMethod(e.target.value as 'credit' | 'cod')}
|
||||
className='flex gap-4'
|
||||
>
|
||||
<FormControlLabel value='credit' control={<Radio />} label='Credit/Debit/ATM Card' />
|
||||
<FormControlLabel value='cash' control={<Radio />} label='COD/Cheque' />
|
||||
</RadioGroup>
|
||||
</Grid>
|
||||
{paymentMethod === 'credit' ? (
|
||||
<>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
name='number'
|
||||
autoComplete='off'
|
||||
label='Card Number'
|
||||
placeholder='0000 0000 0000 0000'
|
||||
value={cardData.cardNumber}
|
||||
onChange={e => setCardData({ ...cardData, cardNumber: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
name='name'
|
||||
label='Name'
|
||||
autoComplete='off'
|
||||
placeholder='John Doe'
|
||||
value={cardData.name}
|
||||
onChange={e => setCardData({ ...cardData, name: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 6, md: 3 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
name='expiry'
|
||||
autoComplete='off'
|
||||
label='Expiry Date'
|
||||
placeholder='MM/YY'
|
||||
value={cardData.expiryDate}
|
||||
onChange={e => setCardData({ ...cardData, expiryDate: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 6, md: 3 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
name='cvv'
|
||||
label='CVV Code'
|
||||
autoComplete='off'
|
||||
placeholder='654'
|
||||
value={cardData.cardCvv}
|
||||
onChange={e => setCardData({ ...cardData, cardCvv: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<FormControlLabel control={<Switch defaultChecked />} label='Save Card for future billing?' />
|
||||
</Grid>
|
||||
</>
|
||||
) : (
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Typography>
|
||||
Cash on delivery is a mode of payment where you make the payment after the goods/services are
|
||||
received.
|
||||
</Typography>
|
||||
<Typography>
|
||||
You can pay cash or make the payment via debit/credit card directly to the delivery person.
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid size={{ xs: 12 }} className='flex gap-4 flex-wrap'>
|
||||
<Button type='submit' variant='contained'>
|
||||
Save Changes
|
||||
</Button>
|
||||
<Button type='reset' variant='tonal' color='secondary' onClick={handleReset}>
|
||||
Cancel
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid size={{ xs: 12, md: 6 }} className='flex flex-col gap-6'>
|
||||
<Typography color='text.primary' className='font-medium'>
|
||||
My Cards
|
||||
</Typography>
|
||||
{data.map((item: DataType, index: number) => (
|
||||
<div
|
||||
key={index}
|
||||
className='flex flex-col rounded bg-actionHover sm:flex-row items-start sm:justify-between max-sm:gap-4 p-6'
|
||||
>
|
||||
<div className='flex flex-col items-start gap-2'>
|
||||
<img src={item.imgSrc} alt={item.imgAlt} />
|
||||
<div className='flex items-center gap-4'>
|
||||
<Typography className='text-textPrimary font-medium'>{item.name}</Typography>
|
||||
{item.cardStatus ? (
|
||||
<Chip color={item.badgeColor} variant='tonal' label={item.cardStatus} size='small' />
|
||||
) : 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 sm:items-end gap-4'>
|
||||
<div className='flex gap-4'>
|
||||
<OpenDialogOnElementClick
|
||||
element={Button}
|
||||
elementProps={buttonProps(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>
|
||||
))}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default PaymentMethod
|
||||
@@ -1,65 +0,0 @@
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
|
||||
// Component Imports
|
||||
import CurrentPlan from './CurrentPlan'
|
||||
import Address from './Address'
|
||||
import PaymentMethod from './PaymentMethod'
|
||||
import InvoiceListTable from './InvoiceListTable'
|
||||
|
||||
// Data Imports
|
||||
import { getPricingData, 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 `/pages/pricing` 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 getPricingData = async () => {
|
||||
// Vars
|
||||
const res = await fetch(`${process.env.API_URL}/pages/pricing`)
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error('Failed to fetch data')
|
||||
}
|
||||
|
||||
return res.json()
|
||||
} */
|
||||
|
||||
/* const getInvoiceData = async () => {
|
||||
// Vars
|
||||
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 BillingPlans = async () => {
|
||||
// Vars
|
||||
const data = await getPricingData()
|
||||
const invoiceData = await getInvoiceData()
|
||||
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CurrentPlan data={data} />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<PaymentMethod />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Address />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<InvoiceListTable invoiceData={invoiceData} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default BillingPlans
|
||||
@@ -1,156 +0,0 @@
|
||||
// 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'
|
||||
|
||||
// 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://in.linkedin.com/company/pixinvent'
|
||||
},
|
||||
{
|
||||
title: 'Dribbble',
|
||||
isConnected: false,
|
||||
logo: '/images/logos/dribbble.png'
|
||||
},
|
||||
{
|
||||
title: 'Behance',
|
||||
isConnected: false,
|
||||
logo: '/images/logos/behance.png'
|
||||
}
|
||||
]
|
||||
|
||||
const Connections = () => {
|
||||
return (
|
||||
<Card>
|
||||
<Grid container>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<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={32} width={32} src={item.logo} alt={item.title} />
|
||||
<div className='flex-grow'>
|
||||
<Typography className='text-textPrimary font-medium'>{item.title}</Typography>
|
||||
<Typography variant='body2'>{item.subtitle}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<Switch defaultChecked={item.checked} />
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<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={32} width={32} src={item.logo} alt={item.title} />
|
||||
<div className='flex-grow'>
|
||||
<Typography className='text-textPrimary font-medium'>{item.title}</Typography>
|
||||
{item.isConnected ? (
|
||||
<Typography
|
||||
variant='body2'
|
||||
color='primary.main'
|
||||
component={Link}
|
||||
href={item.href || '/'}
|
||||
target='_blank'
|
||||
>
|
||||
{item.username}
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography variant='body2'>Not Connected</Typography>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<CustomIconButton variant='tonal' color={item.isConnected ? 'error' : 'secondary'}>
|
||||
<i className={item.isConnected ? 'tabler-trash' : 'tabler-link'} />
|
||||
</CustomIconButton>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default Connections
|
||||
@@ -1,56 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
import type { SyntheticEvent, ReactElement } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Tab from '@mui/material/Tab'
|
||||
import TabContext from '@mui/lab/TabContext'
|
||||
import TabPanel from '@mui/lab/TabPanel'
|
||||
|
||||
// Component Imports
|
||||
import CustomTabList from '@core/components/mui/TabList'
|
||||
|
||||
const AccountSettings = ({ tabContentList }: { tabContentList: { [key: string]: ReactElement } }) => {
|
||||
// States
|
||||
const [activeTab, setActiveTab] = useState('account')
|
||||
|
||||
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 label='Account' icon={<i className='tabler-users' />} iconPosition='start' value='account' />
|
||||
<Tab label='Security' icon={<i className='tabler-lock' />} iconPosition='start' value='security' />
|
||||
<Tab
|
||||
label='Billing & Plans'
|
||||
icon={<i className='tabler-bookmark' />}
|
||||
iconPosition='start'
|
||||
value='billing-plans'
|
||||
/>
|
||||
<Tab
|
||||
label='Notifications'
|
||||
icon={<i className='tabler-bell' />}
|
||||
iconPosition='start'
|
||||
value='notifications'
|
||||
/>
|
||||
<Tab label='Connections' icon={<i className='tabler-link' />} iconPosition='start' value='connections' />
|
||||
</CustomTabList>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<TabPanel value={activeTab} className='p-0'>
|
||||
{tabContentList[activeTab]}
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabContext>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountSettings
|
||||
@@ -1,123 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// 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 Checkbox from '@mui/material/Checkbox'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
|
||||
// Component Imports
|
||||
import Link from '@components/Link'
|
||||
import Form from '@components/Form'
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
// 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: true,
|
||||
email: true,
|
||||
browser: true,
|
||||
type: 'New for you'
|
||||
},
|
||||
{
|
||||
app: true,
|
||||
email: true,
|
||||
browser: true,
|
||||
type: 'Account activity'
|
||||
},
|
||||
{
|
||||
app: false,
|
||||
email: true,
|
||||
browser: true,
|
||||
type: 'A new browser used to sign in'
|
||||
},
|
||||
{
|
||||
app: false,
|
||||
email: true,
|
||||
browser: false,
|
||||
type: 'A new device is linked'
|
||||
}
|
||||
]
|
||||
|
||||
const Notifications = () => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader
|
||||
title='Recent Devices'
|
||||
subheader={
|
||||
<>
|
||||
We need permission from your browser to show notifications.
|
||||
<Link className='text-primary'> Request Permission</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Form>
|
||||
<div className='overflow-x-auto'>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Email</th>
|
||||
<th>Browser</th>
|
||||
<th>App</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.email} />
|
||||
</td>
|
||||
<td>
|
||||
<Checkbox defaultChecked={data.browser} />
|
||||
</td>
|
||||
<td>
|
||||
<Checkbox defaultChecked={data.app} />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<CardContent>
|
||||
<Typography className='mbe-6 font-medium'>When should we send you notifications?</Typography>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 6, md: 4 }}>
|
||||
<CustomTextField select fullWidth defaultValue='online'>
|
||||
<MenuItem value='online'>Only when I'm online</MenuItem>
|
||||
<MenuItem value='anytime'>Anytime</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex gap-4 flex-wrap'>
|
||||
<Button variant='contained' type='submit'>
|
||||
Save Changes
|
||||
</Button>
|
||||
<Button variant='tonal' color='secondary' type='reset'>
|
||||
Discard
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Form>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default Notifications
|
||||
@@ -1,70 +0,0 @@
|
||||
// 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 Chip from '@mui/material/Chip'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
type ApiKeyListType = {
|
||||
title: string
|
||||
access: string
|
||||
date: string
|
||||
key: string
|
||||
}
|
||||
|
||||
// Vars
|
||||
const apiKeyList: ApiKeyListType[] = [
|
||||
{
|
||||
title: 'Server Key 1',
|
||||
access: 'Full Access',
|
||||
date: '28 Apr 2021, 18:20 GTM+4:10',
|
||||
key: '23eaf7f0-f4f7-495e-8b86-fad3261282ac'
|
||||
},
|
||||
{
|
||||
title: 'Server Key 2',
|
||||
access: 'Read Only',
|
||||
date: '12 Feb 2021, 10:30 GTM+2:30',
|
||||
key: 'bb98e571-a2e2-4de8-90a9-2e231b5e99'
|
||||
},
|
||||
{
|
||||
title: 'Server Key 3',
|
||||
access: 'Full Access',
|
||||
date: '28 Dec 2021, 12:21 GTM+4:10',
|
||||
key: '2e915e59-3105-47f2-8838-6e46bf83b711'
|
||||
}
|
||||
]
|
||||
|
||||
const ApiKeyList = () => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='API Key List & Access' className='pbe-4' />
|
||||
<CardContent className='flex flex-col gap-6'>
|
||||
<Typography>
|
||||
An API key is a simple encrypted string that identifies an application without any principal. They are useful
|
||||
for accessing public data anonymously, and are used to associate API requests with your project for quota and
|
||||
billing.
|
||||
</Typography>
|
||||
{apiKeyList.map((item, index) => (
|
||||
<div key={index} className='flex flex-col gap-2 p-4 rounded bg-actionHover'>
|
||||
<div className='flex items-center gap-3'>
|
||||
<Typography variant='h5'>{item.title}</Typography>
|
||||
<Chip color='primary' variant='tonal' label={item.access} size='small' />
|
||||
</div>
|
||||
<div className='flex items-center gap-1'>
|
||||
<Typography className='font-medium'>{item.key}</Typography>
|
||||
<div className='flex'>
|
||||
<IconButton size='small'>
|
||||
<i className='tabler-copy text-textSecondary' />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
<Typography color='text.disabled'>{`Created on ${item.date}`}</Typography>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default ApiKeyList
|
||||
@@ -1,136 +0,0 @@
|
||||
'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 Typography from '@mui/material/Typography'
|
||||
import Button from '@mui/material/Button'
|
||||
|
||||
//Component Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
const ChangePasswordCard = () => {
|
||||
// States
|
||||
const [isCurrentPasswordShown, setIsCurrentPasswordShown] = useState(false)
|
||||
const [isConfirmPasswordShown, setIsConfirmPasswordShown] = useState(false)
|
||||
const [isNewPasswordShown, setIsNewPasswordShown] = useState(false)
|
||||
|
||||
const handleClickShowCurrentPassword = () => {
|
||||
setIsCurrentPasswordShown(!isCurrentPasswordShown)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Change Password' />
|
||||
<CardContent>
|
||||
<form>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Current Password'
|
||||
type={isCurrentPasswordShown ? 'text' : 'password'}
|
||||
placeholder='············'
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowCurrentPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
>
|
||||
<i className={isCurrentPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container className='mbs-0' spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='New Password'
|
||||
type={isNewPasswordShown ? 'text' : 'password'}
|
||||
placeholder='············'
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={() => setIsNewPasswordShown(!isNewPasswordShown)}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
>
|
||||
<i className={isNewPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Confirm New Password'
|
||||
type={isConfirmPasswordShown ? 'text' : 'password'}
|
||||
placeholder='············'
|
||||
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 flex-col gap-4'>
|
||||
<Typography variant='h6'>Password Requirements:</Typography>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<div className='flex items-center gap-2.5'>
|
||||
<i className='tabler-circle-filled text-[8px]' />
|
||||
Minimum 8 characters long - the more, the better
|
||||
</div>
|
||||
<div className='flex items-center gap-2.5'>
|
||||
<i className='tabler-circle-filled text-[8px]' />
|
||||
At least one lowercase & one uppercase character
|
||||
</div>
|
||||
<div className='flex items-center gap-2.5'>
|
||||
<i className='tabler-circle-filled text-[8px]' />
|
||||
At least one number, symbol, or whitespace character
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex gap-4'>
|
||||
<Button variant='contained'>Save Changes</Button>
|
||||
<Button variant='tonal' type='reset' color='secondary'>
|
||||
Reset
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default ChangePasswordCard
|
||||
@@ -1,45 +0,0 @@
|
||||
'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 Button from '@mui/material/Button'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
|
||||
// Component Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
const CreateApiKey = () => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Create an API Key' />
|
||||
<CardContent className='!pb-0'>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<form className='flex justify-end items-end bs-full flex-col gap-5 pbe-6'>
|
||||
<CustomTextField select fullWidth label='Choose the API key type you want to create' defaultValue=''>
|
||||
<MenuItem value='full-control'>Full Control</MenuItem>
|
||||
<MenuItem value='modify'>Modify</MenuItem>
|
||||
<MenuItem value='read-execute'>Read & Execute</MenuItem>
|
||||
<MenuItem value='list-folder-contents'>List Folder Contents</MenuItem>
|
||||
<MenuItem value='read-only'>Read Only</MenuItem>
|
||||
<MenuItem value='read-write'>Read & Write</MenuItem>
|
||||
</CustomTextField>
|
||||
<CustomTextField label='Name the API key' placeholder='Server key 1' fullWidth />
|
||||
<Button variant='contained' fullWidth>
|
||||
Create Key
|
||||
</Button>
|
||||
</form>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }} className='flex items-end justify-center '>
|
||||
<img src='/images/illustrations/characters/4.png' width={197} height={224} alt='api illustration' />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateApiKey
|
||||
@@ -1,109 +0,0 @@
|
||||
// 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 RecentDeviceDataType = {
|
||||
browserIcon: ReactElement
|
||||
browserName: string
|
||||
device: string
|
||||
location: string
|
||||
date: string
|
||||
}
|
||||
|
||||
// Vars
|
||||
const recentDeviceData: RecentDeviceDataType[] = [
|
||||
{
|
||||
location: 'Switzerland',
|
||||
device: 'HP Spectre 360',
|
||||
date: '10, Sept 20:07',
|
||||
browserName: 'Chrome on Windows',
|
||||
browserIcon: <i className='tabler-brand-windows text-[22px] text-info' />
|
||||
},
|
||||
{
|
||||
location: 'Los Angeles, CA',
|
||||
device: 'Google Pixel 3a',
|
||||
date: '20 Apr 2022, 10:20',
|
||||
browserName: 'Chrome on Android',
|
||||
browserIcon: <i className='tabler-brand-android text-[22px] text-success' />
|
||||
},
|
||||
{
|
||||
location: 'San Francisco, CA',
|
||||
device: 'iPhone 12x',
|
||||
date: '16 Apr 2022, 04:20',
|
||||
browserName: 'Chrome on iPhone',
|
||||
browserIcon: <i className='tabler-device-mobile text-[22px] text-error' />
|
||||
},
|
||||
{
|
||||
location: 'India',
|
||||
device: 'Apple iMac',
|
||||
date: '28 Apr 2022, 18:20',
|
||||
browserName: 'Chrome on MacOS',
|
||||
browserIcon: <i className='tabler-brand-apple text-[22px] text-secondary' />
|
||||
},
|
||||
{
|
||||
location: 'Switzerland',
|
||||
device: 'Macbook Pro',
|
||||
date: '20 Apr 2022, 10:20',
|
||||
browserName: 'Chrome on Windows',
|
||||
browserIcon: <i className='tabler-brand-apple text-[22px] text-warning' />
|
||||
},
|
||||
{
|
||||
location: 'Dubai',
|
||||
device: 'Oneplus 9 Pro',
|
||||
date: '16 Apr 2022, 04:20',
|
||||
browserName: 'Chrome on Android',
|
||||
browserIcon: <i className='tabler-brand-android text-[22px] text-success' />
|
||||
}
|
||||
]
|
||||
|
||||
const RecentDevicesTable = () => {
|
||||
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-2.5'>
|
||||
{device.browserIcon}
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{device.browserName}
|
||||
</Typography>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<Typography>{device.device}</Typography>
|
||||
</td>
|
||||
<td>
|
||||
<Typography>{device.location}</Typography>
|
||||
</td>
|
||||
<td>
|
||||
<Typography>{device.date}</Typography>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default RecentDevicesTable
|
||||
@@ -1,45 +0,0 @@
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Button from '@mui/material/Button'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import type { ButtonProps } from '@mui/material/Button'
|
||||
|
||||
// Type Imports
|
||||
import Link from '@components/Link'
|
||||
|
||||
// Component Imports
|
||||
import TwoFactorAuth from '@components/dialogs/two-factor-auth'
|
||||
import OpenDialogOnElementClick from '@components/dialogs/OpenDialogOnElementClick'
|
||||
|
||||
const TwoFactorAuthenticationCard = () => {
|
||||
// Vars
|
||||
const buttonProps: ButtonProps = {
|
||||
variant: 'contained',
|
||||
children: 'Enable two-factor authentication'
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<CardHeader title='Two-steps verification' />
|
||||
<CardContent className='flex flex-col items-start gap-6'>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<Typography variant='h5' color='text.secondary'>
|
||||
Two factor authentication is not enabled yet.
|
||||
</Typography>
|
||||
<Typography>
|
||||
Two-factor authentication adds an additional layer of security to your account by requiring more than just
|
||||
a password to log in.
|
||||
<Link className='text-primary'>Learn more.</Link>
|
||||
</Typography>
|
||||
</div>
|
||||
<OpenDialogOnElementClick element={Button} elementProps={buttonProps} dialog={TwoFactorAuth} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TwoFactorAuthenticationCard
|
||||
@@ -1,33 +0,0 @@
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
|
||||
// Component Imports
|
||||
import ChangePasswordCard from './ChangePasswordCard'
|
||||
import TwoFactorAuthenticationCard from './TwoFactorAuthenticationCard'
|
||||
import CreateApiKey from './CreateApiKey'
|
||||
import ApiKeyList from './ApiKeyList'
|
||||
import RecentDevicesTable from './RecentDevicesTable'
|
||||
|
||||
const Security = () => {
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<ChangePasswordCard />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<TwoFactorAuthenticationCard />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CreateApiKey />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<ApiKeyList />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<RecentDevicesTable />
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default Security
|
||||
@@ -1,48 +0,0 @@
|
||||
// MUI Imports
|
||||
import Chip from '@mui/material/Chip'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Typography from '@mui/material/Typography'
|
||||
|
||||
// Component Imports
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
|
||||
const FaqFooter = () => {
|
||||
return (
|
||||
<>
|
||||
<div className='flex justify-center items-center flex-col text-center gap-2 plb-6'>
|
||||
<Chip label='Question' color='primary' variant='tonal' size='small' />
|
||||
<Typography variant='h4'>You still have a question?</Typography>
|
||||
<Typography>
|
||||
If you cannot find a question in our FAQ, you can always contact us. We will answer you shortly!
|
||||
</Typography>
|
||||
</div>
|
||||
<Grid container spacing={6} className='mbs-6'>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<div className='flex justify-center items-center flex-col gap-4 p-6 rounded bg-actionHover'>
|
||||
<CustomAvatar variant='rounded' color='primary' skin='light' size={46}>
|
||||
<i className='tabler-phone text-[26px]' />
|
||||
</CustomAvatar>
|
||||
|
||||
<div className='flex items-center flex-col gap-1'>
|
||||
<Typography variant='h5'>+ (810) 2548 2568</Typography>
|
||||
<Typography>We are always happy to help!</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<div className='flex justify-center items-center flex-col gap-4 p-6 rounded bg-actionHover'>
|
||||
<CustomAvatar variant='rounded' color='primary' skin='light' size={46}>
|
||||
<i className='tabler-mail text-[26px]' />
|
||||
</CustomAvatar>
|
||||
<div className='flex items-center flex-col gap-1'>
|
||||
<Typography variant='h5'>hello@help.com</Typography>
|
||||
<Typography>Best way to get answer faster!</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FaqFooter
|
||||
@@ -1,60 +0,0 @@
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import { styled } from '@mui/material/styles'
|
||||
import type { TextFieldProps } from '@mui/material/TextField'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Styles imports
|
||||
import styles from './styles.module.css'
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
// Styled CustomTextField component
|
||||
const CustomTextFieldStyled = styled(CustomTextField)<TextFieldProps>(({ theme }) => ({
|
||||
'& .MuiInputBase-root.MuiFilledInput-root': {
|
||||
width: '100%',
|
||||
backgroundColor: 'var(--mui-palette-background-paper) !important'
|
||||
},
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
width: '55%'
|
||||
}
|
||||
}))
|
||||
|
||||
type Props = {
|
||||
searchValue: string
|
||||
setSearchValue: (value: string) => void
|
||||
}
|
||||
|
||||
const FaqHeader = ({ searchValue, setSearchValue }: Props) => {
|
||||
return (
|
||||
<Card className={classnames('shadow-none bg-transparent bg-cover', styles.bgImage)} elevation={0}>
|
||||
<CardContent className='flex flex-col items-center is-full text-center !plb-[5.8125rem] pli-5'>
|
||||
<Typography variant='h4' className='mbe-2.5'>
|
||||
Hello, how can we help?
|
||||
</Typography>
|
||||
<Typography className='mbe-4'>or choose a category to quickly find the help you need</Typography>
|
||||
<CustomTextFieldStyled
|
||||
className='is-full sm:max-is-[55%] md:max-is-[600px]'
|
||||
placeholder='search articles...'
|
||||
value={searchValue}
|
||||
onChange={e => setSearchValue(e.target.value)}
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position='start'>
|
||||
<i className='tabler-search' />
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default FaqHeader
|
||||
@@ -1,122 +0,0 @@
|
||||
// React Imports
|
||||
import { useMemo, useState } from 'react'
|
||||
import type { SyntheticEvent } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Tab from '@mui/material/Tab'
|
||||
import TabPanel from '@mui/lab/TabPanel'
|
||||
import TabContext from '@mui/lab/TabContext'
|
||||
import Accordion from '@mui/material/Accordion'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import AccordionSummary from '@mui/material/AccordionSummary'
|
||||
import AccordionDetails from '@mui/material/AccordionDetails'
|
||||
|
||||
// Third-party Imports
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { FaqType } from '@/types/pages/faqTypes'
|
||||
|
||||
// Component Imports
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
import CustomTabList from '@core/components/mui/TabList'
|
||||
|
||||
type props = {
|
||||
faqData?: FaqType[]
|
||||
searchValue: string
|
||||
}
|
||||
|
||||
const FAQ = ({ faqData, searchValue }: props) => {
|
||||
// States
|
||||
const [activeTab, setActiveTab] = useState('payment')
|
||||
|
||||
// Hooks
|
||||
const filteredData = useMemo(() => {
|
||||
let returnVal = faqData
|
||||
|
||||
if (searchValue) {
|
||||
returnVal =
|
||||
faqData
|
||||
?.filter(category =>
|
||||
category.questionsAnswers.some(item => item.question.toLowerCase().includes(searchValue.toLowerCase()))
|
||||
)
|
||||
.map(category => ({
|
||||
...category,
|
||||
questionsAnswers: category.questionsAnswers.filter(item =>
|
||||
item.question.toLowerCase().includes(searchValue.toLowerCase())
|
||||
)
|
||||
})) ?? []
|
||||
}
|
||||
|
||||
setActiveTab(returnVal?.[0]?.id ?? '')
|
||||
|
||||
return returnVal
|
||||
}, [faqData, searchValue])
|
||||
|
||||
const handleChange = (event: SyntheticEvent, newValue: string) => {
|
||||
setActiveTab(newValue)
|
||||
}
|
||||
|
||||
return filteredData && filteredData.length > 0 ? (
|
||||
<TabContext value={activeTab}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 5, md: 4, xl: 3 }} className='flex flex-col items-center gap-4'>
|
||||
<CustomTabList orientation='vertical' onChange={handleChange} className='is-full' pill='true'>
|
||||
{filteredData?.map((faq, index) => (
|
||||
<Tab
|
||||
key={index}
|
||||
label={faq.title}
|
||||
value={faq.id}
|
||||
icon={<i className={classnames(faq.icon, '!mbe-0 mie-1.5')} />}
|
||||
className='flex-row justify-start !min-is-full'
|
||||
/>
|
||||
))}
|
||||
</CustomTabList>
|
||||
<img
|
||||
src='/images/illustrations/characters-with-objects/1.png'
|
||||
className='max-md:hidden is-[230px]'
|
||||
alt='john image'
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 7, md: 8, xl: 9 }}>
|
||||
{filteredData?.map((faq, index) => (
|
||||
<TabPanel key={index} value={faq.id} className='p-0'>
|
||||
<div className='flex items-center gap-4 mbe-4'>
|
||||
<CustomAvatar skin='light' color='primary' variant='rounded' size={50}>
|
||||
<i className={classnames(faq.icon, 'text-3xl')} />
|
||||
</CustomAvatar>
|
||||
<div>
|
||||
<Typography variant='h5'>{faq.title}</Typography>
|
||||
<Typography>{faq.subtitle}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{faq.questionsAnswers.map((items, index) => (
|
||||
<Accordion key={index}>
|
||||
<AccordionSummary
|
||||
expandIcon={<i className='tabler-chevron-right' />}
|
||||
aria-controls='panel1a-content'
|
||||
>
|
||||
<Typography>{items.question}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography>{items.answer}</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
))}
|
||||
</div>
|
||||
</TabPanel>
|
||||
))}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabContext>
|
||||
) : (
|
||||
<div className='flex justify-center items-center'>
|
||||
<i className='tabler-alert-circle' />
|
||||
<Typography>No results found</Typography>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FAQ
|
||||
@@ -1,36 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
|
||||
// Type Imports
|
||||
import type { FaqType } from '@/types/pages/faqTypes'
|
||||
|
||||
// Component Imports
|
||||
import FaqHeader from '@views/pages/faq/FaqHeader'
|
||||
import Faqs from '@views/pages/faq/Faqs'
|
||||
import FaqFooter from '@views/pages/faq/FaqFooter'
|
||||
|
||||
const FAQ = ({ data }: { data?: FaqType[] }) => {
|
||||
// States
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<FaqHeader searchValue={searchValue} setSearchValue={setSearchValue} />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Faqs faqData={data} searchValue={searchValue} />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<FaqFooter />
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default FAQ
|
||||
@@ -1,3 +0,0 @@
|
||||
.bgImage {
|
||||
background: url('/images/pages/faq-header.png');
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
|
||||
// Type Imports
|
||||
import type { PricingPlanType } from '@/types/pages/pricingTypes'
|
||||
|
||||
// Component Imports
|
||||
import Pricing from '@components/pricing'
|
||||
|
||||
const PricingPage = ({ data }: { data?: PricingPlanType[] }) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className='xl:!plb-16 xl:pli-[6.25rem] pbs-10 pbe-5 pli-5 sm:p-16'>
|
||||
<Pricing data={data} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default PricingPage
|
||||
@@ -1,47 +0,0 @@
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import CardMedia from '@mui/material/CardMedia'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Button from '@mui/material/Button'
|
||||
|
||||
// Type Imports
|
||||
import type { ProfileHeaderType } from '@/types/pages/profileTypes'
|
||||
|
||||
const UserProfileHeader = ({ data }: { data?: ProfileHeaderType }) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardMedia image={data?.coverImg} className='bs-[250px]' />
|
||||
<CardContent className='flex gap-5 justify-center flex-col items-center md:items-end md:flex-row !pt-0 md:justify-start'>
|
||||
<div className='flex rounded-bs-md mbs-[-40px] border-[5px] mis-[-5px] border-be-0 border-backgroundPaper bg-backgroundPaper'>
|
||||
<img height={120} width={120} src={data?.profileImg} className='rounded' alt='Profile Background' />
|
||||
</div>
|
||||
<div className='flex is-full justify-start self-end flex-col items-center gap-6 sm-gap-0 sm:flex-row sm:justify-between sm:items-end '>
|
||||
<div className='flex flex-col items-center sm:items-start gap-2'>
|
||||
<Typography variant='h4'>{data?.fullName}</Typography>
|
||||
<div className='flex flex-wrap gap-6 justify-center sm:justify-normal'>
|
||||
<div className='flex items-center gap-2'>
|
||||
{data?.designationIcon && <i className={data?.designationIcon} />}
|
||||
<Typography className='font-medium'>{data?.designation}</Typography>
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<i className='tabler-map-pin' />
|
||||
<Typography className='font-medium'>{data?.location}</Typography>
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<i className='tabler-calendar' />
|
||||
<Typography className='font-medium'>{data?.joiningDate}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant='contained' className='flex gap-2'>
|
||||
<i className='tabler-user-check !text-base'></i>
|
||||
<span>Connected</span>
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserProfileHeader
|
||||
@@ -1,86 +0,0 @@
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Avatar from '@mui/material/Avatar'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import Card from '@mui/material/Card'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Button from '@mui/material/Button'
|
||||
|
||||
// Type Imports
|
||||
import type { ConnectionsTabType } from '@/types/pages/profileTypes'
|
||||
|
||||
// Component Imports
|
||||
import OptionMenu from '@core/components/option-menu'
|
||||
import Link from '@components/Link'
|
||||
import CustomIconButton from '@core/components/mui/IconButton'
|
||||
|
||||
const Connections = ({ data }: { data?: ConnectionsTabType[] }) => {
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
{data &&
|
||||
data.map((item, index) => {
|
||||
return (
|
||||
<Grid size={{ xs: 12, sm: 6, md: 4 }} key={index}>
|
||||
<Card className='relative'>
|
||||
<OptionMenu
|
||||
iconClassName='text-textDisabled'
|
||||
options={[
|
||||
'Share Connection',
|
||||
'Block Connection',
|
||||
{ divider: true },
|
||||
{
|
||||
text: 'Delete',
|
||||
menuItemProps: { className: 'text-error hover:bg-[var(--mui-palette-error-lightOpacity)]' }
|
||||
}
|
||||
]}
|
||||
iconButtonProps={{ className: 'absolute top-6 end-5 text-textDisabled' }}
|
||||
/>
|
||||
<CardContent className='flex items-center flex-col gap-6'>
|
||||
<Avatar src={item.avatar} className='!mbs-5 bs-[100px] is-[100px]' />
|
||||
<div className='flex flex-col items-center'>
|
||||
<Typography variant='h5'>{item.name}</Typography>
|
||||
<Typography>{item.designation}</Typography>
|
||||
</div>
|
||||
<div className='flex items-center gap-4'>
|
||||
{item.chips.map((chip, index) => (
|
||||
<Link key={index}>
|
||||
<Chip variant='tonal' label={chip.title} color={chip.color} size='small' />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className='flex is-full items-center justify-around flex-wrap'>
|
||||
<div className='flex items-center flex-col'>
|
||||
<Typography variant='h5'>{item.projects}</Typography>
|
||||
<Typography>Projects</Typography>
|
||||
</div>
|
||||
<div className='flex items-center flex-col'>
|
||||
<Typography variant='h5'>{item.tasks}</Typography>
|
||||
<Typography>Tasks</Typography>
|
||||
</div>
|
||||
<div className='flex items-center flex-col'>
|
||||
<Typography variant='h5'>{item.connections}</Typography>
|
||||
<Typography>Connections</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center gap-4'>
|
||||
<Button
|
||||
variant={item.isConnected ? 'contained' : 'tonal'}
|
||||
startIcon={<i className={item.isConnected ? 'tabler-user-check' : 'tabler-user-plus'} />}
|
||||
>
|
||||
{item.isConnected ? 'Connected' : 'Connect'}
|
||||
</Button>
|
||||
<CustomIconButton variant='tonal' color='secondary'>
|
||||
<i className='tabler-mail' />
|
||||
</CustomIconButton>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default Connections
|
||||
@@ -1,85 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
import type { ReactElement, SyntheticEvent } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Tab from '@mui/material/Tab'
|
||||
import TabContext from '@mui/lab/TabContext'
|
||||
import TabPanel from '@mui/lab/TabPanel'
|
||||
|
||||
// Type Imports
|
||||
import type { Data } from '@/types/pages/profileTypes'
|
||||
|
||||
// Component Imports
|
||||
import UserProfileHeader from './UserProfileHeader'
|
||||
import CustomTabList from '@core/components/mui/TabList'
|
||||
|
||||
const UserProfile = ({ tabContentList, data }: { tabContentList: { [key: string]: ReactElement }; data?: Data }) => {
|
||||
// States
|
||||
const [activeTab, setActiveTab] = useState('profile')
|
||||
|
||||
const handleChange = (event: SyntheticEvent, value: string) => {
|
||||
setActiveTab(value)
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<UserProfileHeader data={data?.profileHeader} />
|
||||
</Grid>
|
||||
{activeTab === undefined ? null : (
|
||||
<Grid size={{ xs: 12 }} className='flex flex-col gap-6'>
|
||||
<TabContext value={activeTab}>
|
||||
<CustomTabList onChange={handleChange} variant='scrollable' pill='true'>
|
||||
<Tab
|
||||
label={
|
||||
<div className='flex items-center gap-1.5'>
|
||||
<i className='tabler-user-check text-lg' />
|
||||
Profile
|
||||
</div>
|
||||
}
|
||||
value='profile'
|
||||
/>
|
||||
<Tab
|
||||
label={
|
||||
<div className='flex items-center gap-1.5'>
|
||||
<i className='tabler-users text-lg' />
|
||||
Teams
|
||||
</div>
|
||||
}
|
||||
value='teams'
|
||||
/>
|
||||
<Tab
|
||||
label={
|
||||
<div className='flex items-center gap-1.5'>
|
||||
<i className='tabler-layout-grid text-lg' />
|
||||
Projects
|
||||
</div>
|
||||
}
|
||||
value='projects'
|
||||
/>
|
||||
<Tab
|
||||
label={
|
||||
<div className='flex items-center gap-1.5'>
|
||||
<i className='tabler-link text-lg' />
|
||||
Connections
|
||||
</div>
|
||||
}
|
||||
value='connections'
|
||||
/>
|
||||
</CustomTabList>
|
||||
|
||||
<TabPanel value={activeTab} className='p-0'>
|
||||
{tabContentList[activeTab]}
|
||||
</TabPanel>
|
||||
</TabContext>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default UserProfile
|
||||
@@ -1,88 +0,0 @@
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Card from '@mui/material/Card'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
|
||||
// Type Imports
|
||||
import type { ProfileTeamsType, ProfileCommonType, ProfileTabType } from '@/types/pages/profileTypes'
|
||||
|
||||
const renderList = (list: ProfileCommonType[]) => {
|
||||
return (
|
||||
list.length > 0 &&
|
||||
list.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className='flex items-center gap-2'>
|
||||
<i className={item.icon} />
|
||||
<div className='flex items-center flex-wrap gap-2'>
|
||||
<Typography className='font-medium'>
|
||||
{`${item.property.charAt(0).toUpperCase() + item.property.slice(1)}:`}
|
||||
</Typography>
|
||||
<Typography> {item.value.charAt(0).toUpperCase() + item.value.slice(1)}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const renderTeams = (teams: ProfileTeamsType[]) => {
|
||||
return (
|
||||
teams.length > 0 &&
|
||||
teams.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className='flex items-center flex-wrap gap-2'>
|
||||
<Typography className='font-medium'>
|
||||
{item.property.charAt(0).toUpperCase() + item.property.slice(1)}
|
||||
</Typography>
|
||||
<Typography>{item.value.charAt(0).toUpperCase() + item.value.slice(1)}</Typography>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const AboutOverview = ({ data }: { data?: ProfileTabType }) => {
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Card>
|
||||
<CardContent className='flex flex-col gap-6'>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<Typography className='uppercase' variant='body2' color='text.disabled'>
|
||||
About
|
||||
</Typography>
|
||||
{data?.about && renderList(data?.about)}
|
||||
</div>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<Typography className='uppercase' variant='body2' color='text.disabled'>
|
||||
Contacts
|
||||
</Typography>
|
||||
{data?.contacts && renderList(data?.contacts)}
|
||||
</div>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<Typography className='uppercase' variant='body2' color='text.disabled'>
|
||||
Teams
|
||||
</Typography>
|
||||
{data?.teams && renderTeams(data?.teams)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Card>
|
||||
<CardContent className='flex flex-col gap-6'>
|
||||
<div className='flex flex-col gap-4'>
|
||||
<Typography className='uppercase' variant='body2' color='text.disabled'>
|
||||
Overview
|
||||
</Typography>
|
||||
{data?.overview && renderList(data?.overview)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default AboutOverview
|
||||
@@ -1,106 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// 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 TimelineItem from '@mui/lab/TimelineItem'
|
||||
import TimelineSeparator from '@mui/lab/TimelineSeparator'
|
||||
import TimelineConnector from '@mui/lab/TimelineConnector'
|
||||
import TimelineContent from '@mui/lab/TimelineContent'
|
||||
import TimelineDot from '@mui/lab/TimelineDot'
|
||||
import Avatar from '@mui/material/Avatar'
|
||||
import AvatarGroup from '@mui/material/AvatarGroup'
|
||||
import { styled } from '@mui/material/styles'
|
||||
import MuiTimeline from '@mui/lab/Timeline'
|
||||
import type { TimelineProps } from '@mui/lab/Timeline'
|
||||
|
||||
//Component Imports
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
|
||||
// Styled Components
|
||||
const Timeline = styled(MuiTimeline)<TimelineProps>({
|
||||
'& .MuiTimelineItem-root': {
|
||||
'&:before': {
|
||||
display: 'none'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const ActivityTimeline = () => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader
|
||||
title='Activity Timeline'
|
||||
avatar={<i className='tabler-chart-bar text-textSecondary' />}
|
||||
titleTypographyProps={{ variant: 'h5' }}
|
||||
/>
|
||||
<CardContent>
|
||||
<Timeline>
|
||||
<TimelineItem>
|
||||
<TimelineSeparator>
|
||||
<TimelineDot color='primary' />
|
||||
<TimelineConnector />
|
||||
</TimelineSeparator>
|
||||
<TimelineContent>
|
||||
<div className='flex items-center justify-between flex-wrap gap-x-4 pbe-[7px]'>
|
||||
<Typography className='text-textPrimary font-medium'>12 Invoices have been paid</Typography>
|
||||
<Typography variant='caption'>12 min ago</Typography>
|
||||
</div>
|
||||
<Typography className='mbe-2'>Invoices have been paid to the company.</Typography>
|
||||
<div className='flex'>
|
||||
<div className='flex gap-2.5 items-center pli-2.5 bg-actionHover plb-[0.3125rem] rounded'>
|
||||
<img alt='invoice.pdf' src='/images/icons/pdf-document.png' className='bs-5' />
|
||||
<Typography className='font-medium'>invoice.pdf</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</TimelineContent>
|
||||
</TimelineItem>
|
||||
<TimelineItem>
|
||||
<TimelineSeparator>
|
||||
<TimelineDot color='success' />
|
||||
<TimelineConnector />
|
||||
</TimelineSeparator>
|
||||
<TimelineContent>
|
||||
<div className='flex items-center justify-between flex-wrap gap-x-4 pbe-[7px]'>
|
||||
<Typography className='text-textPrimary font-medium'>Client Meeting</Typography>
|
||||
<Typography variant='caption'>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>
|
||||
<Typography className='font-medium' variant='body2'>
|
||||
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 items-center justify-between flex-wrap gap-x-4 pbe-[7px]'>
|
||||
<Typography className='text-textPrimary font-medium'>Create a new project for client</Typography>
|
||||
<Typography variant='caption'>2 Day Ago</Typography>
|
||||
</div>
|
||||
<Typography className='mbe-2'>6 team members in a project</Typography>
|
||||
<AvatarGroup total={6}>
|
||||
<Avatar alt='Remy Sharp' src='/images/avatars/1.png' />
|
||||
<Avatar alt='Travis Howard' src='/images/avatars/2.png' />
|
||||
<Avatar alt='Cindy Baker' src='/images/avatars/3.png' />
|
||||
</AvatarGroup>
|
||||
</TimelineContent>
|
||||
</TimelineItem>
|
||||
</Timeline>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default ActivityTimeline
|
||||
@@ -1,96 +0,0 @@
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import CardActions from '@mui/material/CardActions'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Chip from '@mui/material/Chip'
|
||||
|
||||
// Type Imports
|
||||
import type { ProfileTeamsTechType, ProfileConnectionsType } from '@/types/pages/profileTypes'
|
||||
|
||||
// Component Imports
|
||||
import OptionMenu from '@core/components/option-menu'
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
import CustomIconButton from '@core/components/mui/IconButton'
|
||||
import Link from '@components/Link'
|
||||
|
||||
type Props = {
|
||||
teamsTech?: ProfileTeamsTechType[]
|
||||
connections?: ProfileConnectionsType[]
|
||||
}
|
||||
|
||||
const ConnectionsTeams = (props: Props) => {
|
||||
// props
|
||||
const { teamsTech, connections } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<Card>
|
||||
<CardHeader
|
||||
title='Connections'
|
||||
action={<OptionMenu options={['Share Connections', 'Suggest Edits', { divider: true }, 'Report Bug']} />}
|
||||
/>
|
||||
<CardContent className='flex flex-col gap-4'>
|
||||
{connections &&
|
||||
connections.map((connection, index) => (
|
||||
<div key={index} className='flex items-center gap-2'>
|
||||
<div className='flex items-center flex-grow gap-2'>
|
||||
<CustomAvatar src={connection.avatar} size={38} />
|
||||
<div className='flex flex-grow flex-col'>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{connection.name}
|
||||
</Typography>
|
||||
<Typography variant='body2'>{connection.connections} Connections</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<CustomIconButton color='primary' variant={connection.isFriend ? 'tonal' : 'contained'}>
|
||||
<i className={connection.isFriend ? 'tabler-user-check' : 'tabler-user-x'} />
|
||||
</CustomIconButton>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
<CardActions className='flex justify-center'>
|
||||
<Typography component={Link} color='primary.main'>
|
||||
View all connections
|
||||
</Typography>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<Card>
|
||||
<CardHeader
|
||||
title='Teams'
|
||||
action={<OptionMenu options={['Share Teams', 'Suggest Edits', { divider: true }, 'Report Bug']} />}
|
||||
/>
|
||||
<CardContent className='flex flex-col gap-4'>
|
||||
{teamsTech &&
|
||||
teamsTech.map((team: ProfileTeamsTechType, index) => (
|
||||
<div key={index} className='flex'>
|
||||
<div className='flex flex-grow items-center gap-2'>
|
||||
<CustomAvatar src={team.avatar} size={38} />
|
||||
<div className='flex flex-grow flex-col'>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{team.title}
|
||||
</Typography>
|
||||
<Typography variant='body2'>{team.members} Members</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<Chip color={team.ChipColor} label={team.chipText} size='small' variant='tonal' />
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
<CardActions className='flex justify-center'>
|
||||
<Typography component={Link} color='primary.main'>
|
||||
View all teams
|
||||
</Typography>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConnectionsTeams
|
||||
@@ -1,289 +0,0 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import AvatarGroup from '@mui/material/AvatarGroup'
|
||||
import Card from '@mui/material/Card'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import Checkbox from '@mui/material/Checkbox'
|
||||
import LinearProgress from '@mui/material/LinearProgress'
|
||||
import type { TextFieldProps } from '@mui/material/TextField'
|
||||
import Typography from '@mui/material/Typography'
|
||||
|
||||
// Third-party Imports
|
||||
import type { RankingInfo } from '@tanstack/match-sorter-utils'
|
||||
import { rankItem } from '@tanstack/match-sorter-utils'
|
||||
import type { ColumnDef, FilterFn } from '@tanstack/react-table'
|
||||
import {
|
||||
createColumnHelper,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFacetedMinMaxValues,
|
||||
getFacetedRowModel,
|
||||
getFacetedUniqueValues,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable
|
||||
} from '@tanstack/react-table'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Type Imports
|
||||
import type { ProjectTableRowType } from '@/types/pages/profileTypes'
|
||||
|
||||
// Component Imports
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
import OptionMenu from '@core/components/option-menu'
|
||||
|
||||
// Style Imports
|
||||
import tableStyles from '@core/styles/table.module.css'
|
||||
|
||||
declare module '@tanstack/table-core' {
|
||||
interface FilterFns {
|
||||
fuzzy: FilterFn<unknown>
|
||||
}
|
||||
interface FilterMeta {
|
||||
itemRank: RankingInfo
|
||||
}
|
||||
}
|
||||
|
||||
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<ProjectTableRowType>()
|
||||
|
||||
const ProjectTables = ({ projectTable }: { projectTable?: ProjectTableRowType[] }) => {
|
||||
// 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<ProjectTableRowType, any>[]>(
|
||||
() => [
|
||||
{
|
||||
id: 'select',
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
{...{
|
||||
checked: table.getIsAllRowsSelected(),
|
||||
indeterminate: table.getIsSomeRowsSelected(),
|
||||
onChange: table.getToggleAllRowsSelectedHandler()
|
||||
}}
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
{...{
|
||||
checked: row.getIsSelected(),
|
||||
disabled: !row.getCanSelect(),
|
||||
indeterminate: row.getIsSomeSelected(),
|
||||
onChange: row.getToggleSelectedHandler()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
columnHelper.accessor('title', {
|
||||
header: 'Project',
|
||||
cell: ({ row }) => (
|
||||
<div className='flex items-center gap-3'>
|
||||
<CustomAvatar src={row.original.avatar} size={34} />
|
||||
<div className='flex flex-col'>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{row.original.title}
|
||||
</Typography>
|
||||
<Typography variant='body2'>{row.original.subtitle}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}),
|
||||
columnHelper.accessor('leader', {
|
||||
header: 'Leader',
|
||||
cell: ({ row }) => <Typography color='text.primary'>{row.original.leader}</Typography>
|
||||
}),
|
||||
columnHelper.accessor('avatarGroup', {
|
||||
header: 'Team',
|
||||
cell: ({ row }) => (
|
||||
<AvatarGroup max={4} className='flex items-center pull-up'>
|
||||
{row.original.avatarGroup.map((avatar, index) => (
|
||||
<CustomAvatar key={index} src={avatar} size={26} />
|
||||
))}
|
||||
</AvatarGroup>
|
||||
),
|
||||
enableSorting: false
|
||||
}),
|
||||
columnHelper.accessor('status', {
|
||||
header: 'Progress',
|
||||
cell: ({ row }) => (
|
||||
<div className='flex items-center gap-3'>
|
||||
<LinearProgress color='primary' value={row.original.status} variant='determinate' className='is-20' />
|
||||
<Typography color='text.primary'>{`${row.original.status}%`}</Typography>
|
||||
</div>
|
||||
)
|
||||
}),
|
||||
columnHelper.accessor('actions', {
|
||||
header: 'Actions',
|
||||
cell: () => (
|
||||
<OptionMenu
|
||||
iconButtonProps={{ size: 'medium' }}
|
||||
iconClassName='text-textSecondary'
|
||||
options={[
|
||||
'Details',
|
||||
'Archive',
|
||||
{ divider: true },
|
||||
{ text: 'Delete', menuItemProps: { className: 'text-error' } }
|
||||
]}
|
||||
/>
|
||||
),
|
||||
enableSorting: false
|
||||
})
|
||||
],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
)
|
||||
|
||||
const table = useReactTable({
|
||||
data: data as ProjectTableRowType[],
|
||||
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
|
||||
className='flex-wrap gap-x-4 gap-y-2'
|
||||
title='Project List'
|
||||
action={
|
||||
<DebouncedInput
|
||||
value={globalFilter ?? ''}
|
||||
onChange={value => setGlobalFilter(String(value))}
|
||||
placeholder='Search Project'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<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>
|
||||
<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)
|
||||
}}
|
||||
onRowsPerPageChange={e => table.setPageSize(Number(e.target.value))}
|
||||
/> */}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProjectTables
|
||||
@@ -1,34 +0,0 @@
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
|
||||
// Type Imports
|
||||
import type { ProfileTabType } from '@/types/pages/profileTypes'
|
||||
|
||||
// Component Imports
|
||||
import AboutOverview from './AboutOverview'
|
||||
import ActivityTimeline from './ActivityTimeline'
|
||||
import ConnectionsTeams from './ConnectionsTeams'
|
||||
import ProjectsTable from './ProjectsTables'
|
||||
|
||||
const ProfileTab = ({ data }: { data?: ProfileTabType }) => {
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, md: 5, lg: 4 }}>
|
||||
<AboutOverview data={data} />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 7, lg: 8 }}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<ActivityTimeline />
|
||||
</Grid>
|
||||
<ConnectionsTeams connections={data?.connections} teamsTech={data?.teamsTech} />
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<ProjectsTable projectTable={data?.projectTable} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProfileTab
|
||||
@@ -1,141 +0,0 @@
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import Card from '@mui/material/Card'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Divider from '@mui/material/Divider'
|
||||
import LinearProgress from '@mui/material/LinearProgress'
|
||||
import AvatarGroup from '@mui/material/AvatarGroup'
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
|
||||
// Type Imports
|
||||
import type { ProjectsTabType } from '@/types/pages/profileTypes'
|
||||
|
||||
// Component Imports
|
||||
import OptionMenu from '@core/components/option-menu'
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
import Link from '@components/Link'
|
||||
|
||||
const Projects = ({ data }: { data?: ProjectsTabType[] }) => {
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
{data &&
|
||||
data.map((item, index) => {
|
||||
return (
|
||||
<Grid size={{ xs: 12, md: 6, lg: 4 }} key={index}>
|
||||
<Card>
|
||||
<CardContent className='flex flex-col gap-4'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex items-center gap-4'>
|
||||
<CustomAvatar src={item.avatar} size={38} />
|
||||
<div>
|
||||
<Typography variant='h5' component={Link} className='hover:text-primary'>
|
||||
{item.title}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<span className='font-medium'>Client: </span>
|
||||
{item.client}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<OptionMenu
|
||||
iconClassName='text-textDisabled'
|
||||
options={[
|
||||
'Rename Project',
|
||||
'View Details',
|
||||
'Add to Favorite',
|
||||
{ divider: true },
|
||||
{
|
||||
text: 'Leave Project',
|
||||
menuItemProps: { className: 'text-error hover:bg-[var(--mui-palette-error-lightOpacity)]' }
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center justify-between flex-wrap gap-4'>
|
||||
<div className='rounded bg-actionHover plb-2 pli-3'>
|
||||
<div className='flex'>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{item.budgetSpent}
|
||||
</Typography>
|
||||
<Typography>{`/${item.budget}`}</Typography>
|
||||
</div>
|
||||
<Typography>Total Budget</Typography>
|
||||
</div>
|
||||
<div className='flex flex-col'>
|
||||
<div className='flex'>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
Start Date:
|
||||
</Typography>
|
||||
<Typography>{item.startDate}</Typography>
|
||||
</div>
|
||||
<div className='flex'>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
Deadline:
|
||||
</Typography>
|
||||
<Typography>{item.deadline}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Typography>{item.description}</Typography>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<CardContent className='flex flex-col gap-4'>
|
||||
<div className='flex items-center justify-between '>
|
||||
<div className='flex'>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
All Hours:
|
||||
</Typography>
|
||||
<Typography>{item.hours}</Typography>
|
||||
</div>
|
||||
<Chip variant='tonal' size='small' color={item.chipColor} label={`${item.daysLeft} days left`} />
|
||||
</div>
|
||||
<div>
|
||||
<div className='flex items-center justify-between mbe-2'>
|
||||
<Typography
|
||||
variant='caption'
|
||||
className='text-textSecondary'
|
||||
>{`Tasks: ${item.completedTask}/${item.totalTask}`}</Typography>
|
||||
<Typography
|
||||
variant='caption'
|
||||
className='text-textSecondary'
|
||||
>{`${Math.round((item.completedTask / item.totalTask) * 100)}% Completed`}</Typography>
|
||||
</div>
|
||||
<LinearProgress
|
||||
color='primary'
|
||||
variant='determinate'
|
||||
value={Math.round((item.completedTask / item.totalTask) * 100)}
|
||||
className='bs-2'
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex items-center flex-grow gap-3'>
|
||||
<AvatarGroup className='items-center pull-up'>
|
||||
{item.avatarGroup.map((person, index) => {
|
||||
return (
|
||||
<Tooltip key={index} title={person.name}>
|
||||
<CustomAvatar src={person.avatar} alt={person.name} size={32} />
|
||||
</Tooltip>
|
||||
)
|
||||
})}
|
||||
</AvatarGroup>
|
||||
<Typography variant='body2' className='flex-grow'>
|
||||
{item.members}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className='flex items-center gap-1'>
|
||||
<i className='tabler-message-dots' />
|
||||
<Typography>{item.comments}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default Projects
|
||||
@@ -1,85 +0,0 @@
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Card from '@mui/material/Card'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Avatar from '@mui/material/Avatar'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import AvatarGroup from '@mui/material/AvatarGroup'
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
import Chip from '@mui/material/Chip'
|
||||
|
||||
// Type Imports
|
||||
import type { TeamsTabType } from '@/types/pages/profileTypes'
|
||||
|
||||
// Component Imports
|
||||
import OptionMenu from '@core/components/option-menu'
|
||||
import Link from '@components/Link'
|
||||
|
||||
const Teams = ({ data }: { data?: TeamsTabType[] }) => {
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
{data &&
|
||||
data.map((item, index) => {
|
||||
return (
|
||||
<Grid size={{ xs: 12, md: 6, lg: 4 }} key={index}>
|
||||
<Card>
|
||||
<CardContent className='flex flex-col gap-4'>
|
||||
<div className='flex items-center justify-between gap-2'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Avatar src={item.avatar} className='bs-[38px] is-[38px]' />
|
||||
<Typography variant='h5'>{item.title}</Typography>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<IconButton>
|
||||
<i className='tabler-star text-textDisabled' />
|
||||
</IconButton>
|
||||
<OptionMenu
|
||||
iconButtonProps={{ size: 'medium' }}
|
||||
iconClassName='text-textDisabled'
|
||||
options={[
|
||||
'Rename Team',
|
||||
'View Details',
|
||||
'Add to Favorite',
|
||||
{ divider: true },
|
||||
{
|
||||
text: 'Delete Team',
|
||||
menuItemProps: { className: 'text-error hover:bg-[var(--mui-palette-error-lightOpacity)]' }
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Typography>{item.description}</Typography>
|
||||
<div className='flex items-center justify-between flex-wrap gap-4'>
|
||||
<AvatarGroup
|
||||
total={item.extraMembers ? item.extraMembers + 3 : 3}
|
||||
sx={{ '& .MuiAvatar-root': { width: '2rem', height: '2rem', fontSize: '1rem' } }}
|
||||
className='items-center pull-up'
|
||||
>
|
||||
{item.avatarGroup.map((person, index) => {
|
||||
return (
|
||||
<Tooltip key={index} title={person.name}>
|
||||
<Avatar src={person.avatar} alt={person.name} />
|
||||
</Tooltip>
|
||||
)
|
||||
})}
|
||||
</AvatarGroup>
|
||||
<div className='flex items-center gap-2'>
|
||||
{item.chips.map((chip, index) => (
|
||||
<Link key={index}>
|
||||
<Chip variant='tonal' size='small' label={chip.title} color={chip.color} />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default Teams
|
||||
@@ -15,7 +15,6 @@ import OptionMenu from '@core/components/option-menu'
|
||||
|
||||
// Style Imports
|
||||
import tableStyles from '@core/styles/table.module.css'
|
||||
import styles from '@views/apps/logistics/dashboard/styles.module.css'
|
||||
|
||||
type dataTypes = {
|
||||
icon: string
|
||||
@@ -76,9 +75,9 @@ const VehicleOverview = () => {
|
||||
{data.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={classnames(item.widthClass, styles.linearRound, 'flex flex-col gap-[38px] relative')}
|
||||
className={classnames(item.widthClass, 'flex flex-col gap-[38px] relative')}
|
||||
>
|
||||
<Typography className={classnames(styles.header, 'relative max-sm:hidden')}>{item.heading}</Typography>
|
||||
<Typography className={classnames('relative max-sm:hidden')}>{item.heading}</Typography>
|
||||
<LinearProgress
|
||||
variant='determinate'
|
||||
value={-1}
|
||||
|
||||
Reference in New Issue
Block a user