add front-urban

This commit is contained in:
Valentin Romero
2026-06-30 22:08:11 -03:00
parent 25d4ba6cc0
commit 03294769a4
266 changed files with 38239 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
[*.yml]
[*.{yml,yaml}]
indent_size = 2
+6
View File
@@ -0,0 +1,6 @@
BACKEND_URL=http://localhost:3001
NEXT_PUBLIC_BACKEND_URL=http://localhost:3001
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_MIN_AUTH=30
REACT_EDITOR=code
+128
View File
@@ -0,0 +1,128 @@
module.exports = {
extends: ['next/core-web-vitals', 'plugin:@typescript-eslint/recommended', 'plugin:import/recommended', 'prettier'],
rules: {
'jsx-a11y/alt-text': 'off',
'react/display-name': 'off',
'react/no-children-prop': 'off',
'@next/next/no-img-element': 'off',
'@next/next/no-page-custom-font': 'off',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'lines-around-comment': [
'error',
{
beforeBlockComment: true,
beforeLineComment: true,
allowBlockStart: true,
allowObjectStart: true,
allowArrayStart: true
}
],
'padding-line-between-statements': [
'error',
{
blankLine: 'any',
prev: 'export',
next: 'export'
},
{
blankLine: 'always',
prev: ['const', 'let', 'var'],
next: '*'
},
{
blankLine: 'any',
prev: ['const', 'let', 'var'],
next: ['const', 'let', 'var']
},
{
blankLine: 'always',
prev: '*',
next: ['function', 'multiline-const', 'multiline-block-like']
},
{
blankLine: 'always',
prev: ['function', 'multiline-const', 'multiline-block-like'],
next: '*'
}
],
'newline-before-return': 'error',
'import/newline-after-import': [
'error',
{
count: 1
}
],
'import/order': [
'error',
{
groups: ['builtin', 'external', ['internal', 'parent', 'sibling', 'index'], ['object', 'unknown']],
pathGroups: [
{
pattern: 'react',
group: 'external',
position: 'before'
},
{
pattern: 'next/**',
group: 'external',
position: 'before'
},
{
pattern: '~/**',
group: 'external',
position: 'before'
},
{
pattern: '@/**',
group: 'internal'
}
],
pathGroupsExcludedImportTypes: ['react', 'type'],
'newlines-between': 'always-and-inside-groups'
}
],
'@typescript-eslint/ban-types': [
'error',
{
extendDefaults: true,
types: {
Function: 'Use a specific function type instead',
Object: 'Use object instead',
Boolean: 'Use boolean instead',
Number: 'Use number instead',
String: 'Use string instead',
Symbol: 'Use symbol instead',
any: false,
'{}': false
}
}
]
},
settings: {
react: {
version: 'detect'
},
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx']
},
'import/resolver': {
node: {},
typescript: {
project: './tsconfig.json'
}
}
},
overrides: [
{
files: ['*.ts', '*.tsx', 'src/iconify-bundle/*'],
rules: {
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-var-requires': 'off'
}
}
]
}
+41
View File
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
.env
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# icon generated file
src/assets/iconify-icons/generated-icons.js
src/assets/iconify-icons/generated-icons.css
+2
View File
@@ -0,0 +1,2 @@
auto-install-peers=true
shamefully-hoist=true
+17
View File
@@ -0,0 +1,17 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"bracketSameLine": false,
"jsxSingleQuote": true,
"printWidth": 120,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"useTabs": false
}
+16
View File
@@ -0,0 +1,16 @@
{
"plugins": [
"stylelint-use-logical-spec"
],
"overrides": [
{
"customSyntax": "postcss-styled-syntax",
"files": [
"**/*.{js,ts,jsx,tsx}"
]
}
],
"rules": {
"liberty/use-logical-spec": "always"
}
}
+109
View File
@@ -0,0 +1,109 @@
{
// JavaScript Snippets
"exportDefault": {
"prefix": "exp",
"body": [
"export default ${1:moduleName}",
""
]
},
"exportDestructing": {
"prefix": "exd",
"body": [
"export { ${2:destructuredModule} } from '${1:module}'",
""
]
},
"exportAs": {
"prefix": "exa",
"body": [
"export { ${2:originalName} as ${3:aliasName}} from '${1:module}'",
""
]
},
"exportDefaultFunction": {
"prefix": "edf",
"body": [
"export default (${1:params}) => {",
" $0",
"}",
""
]
},
"exportDefaultNamedFunction": {
"prefix": "ednf",
"body": [
"export default function ${1:functionName}(${2:params}) {",
" $0",
"}",
""
]
},
// React Snippets
"reactArrowFunctionExportComponent": {
"prefix": "rafce",
"body": [
"const ${1:ComponentName} = () => {",
" return (",
" $0",
" )",
"}",
"",
"export default ${1:ComponentName}",
""
]
},
"reactArrowFunctionComponent": {
"prefix": "rafc",
"body": [
"const ${1:ComponentName} = () => {",
" return (",
" $0",
" )",
"}",
""
]
},
"reactUseState": {
"prefix": "useState",
"body": [
"const [${1:state}, set${1/(.*)/${1:/capitalize}/}] = useState(${2:initialState})"
]
},
"reactUseEffect": {
"prefix": "useEffect",
"body": [
"useEffect(() => {",
" $0",
"}, [])"
]
},
// Type Snippets
"exportType": {
"prefix": "exptp",
"body": [
"export type ${1:Props} = {",
" ${2:name}: ${3:string}",
"}",
""
]
},
"type": {
"prefix": "tp",
"body": [
"type ${1:Props} = {",
" ${2:name}: ${3:string}",
"}",
""
]
},
// Next Snippets
"nextImport": {
"prefix": "impn",
"body": [
"// Next Import",
"import ${2:second} from '${1:first}'",
""
]
}
}
+26
View File
@@ -0,0 +1,26 @@
{
"recommendations": [
"formulahendry.auto-close-tag",
"steoates.autoimport",
"mgmcdermott.vscode-language-babel",
"aaron-bond.better-comments",
"MohammadBaqer.better-folding",
"pustelto.bracketeer",
"streetsidesoftware.code-spell-checker",
"naumovs.color-highlight",
"mikestead.dotenv",
"EditorConfig.EditorConfig",
"usernamehw.errorlens",
"dbaeumer.vscode-eslint",
"eamodio.gitlens",
"antfu.iconify",
"xabikos.JavaScriptSnippets",
"christian-kohler.npm-intellisense",
"christian-kohler.path-intellisense",
"esbenp.prettier-vscode",
"yoavbls.pretty-ts-errors",
"jasonnutter.search-node-modules",
"stylelint.vscode-stylelint",
"styled-components.vscode-styled-components"
]
}
+57
View File
@@ -0,0 +1,57 @@
{
// JS
"javascript.updateImportsOnFileMove.enabled": "always",
// JSON
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
// VSCode Editor
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never",
"source.fixAll.stylelint": "explicit"
},
// Extension: Git
"git.rebaseWhenSync": true,
"git.autofetch": true,
"git.enableSmartCommit": true,
// Extension: Prettier
"prettier.requireConfig": true,
// Extension: ESLint
"eslint.validate": [
"javascript",
"typescript",
"javascriptreact",
"typescriptreact"
],
"eslint.format.enable": true,
// Stylelint
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"stylelint.enable": true,
"stylelint.validate": [
"css",
"scss",
"typescript",
"typescriptreact"
],
"markdown.extension.toc.updateOnSave": false,
"files.insertFinalNewline": true,
"editor.linkedEditing": true,
"typescript.tsdk": "node_modules/typescript/lib",
"cSpell.words": [
"iconify"
],
"[typescriptreact]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
}
}
+55
View File
@@ -0,0 +1,55 @@
FROM node:22.9-alpine AS base
WORKDIR /app
FROM base AS deps
RUN apk add --no-cache libc6-compat
COPY package.json package-lock.json ./
# Instalar dependencias (lockfile desactualizado). Luego podrás volver a npm ci cuando esté sincronizado.
ENV npm_config_ignore_scripts=true
RUN npm install --legacy-peer-deps
ENV npm_config_ignore_scripts=false
FROM deps AS builder
ENV NEXT_TELEMETRY_DISABLED=1
COPY next.config.mjs ./next.config.mjs
COPY tsconfig.json ./tsconfig.json
COPY postcss.config.mjs ./postcss.config.mjs
COPY tailwind.config.ts ./tailwind.config.ts
COPY public ./public
COPY src ./src
COPY .env* ./
ENV NEXT_PUBLIC_BACKEND_URL=https://backend.cl-danzas.valentin-romero.online
ENV NEXT_PUBLIC_API_URL=https://backend.cl-danzas.valentin-romero.online
ENV NEXT_PUBLIC_APP_URL=https://frontend.cl-danzas.valentin-romero.online
ENV NEXT_PUBLIC_MIN_AUTH=30
ENV NEXT_PUBLIC_DOCS_URL=https://demos.themeselection.com/materio-mui-nextjs-admin-template/documentation
ENV NEXT_PUBLIC_PRO_URL=https://demos.themeselection.com/materio-mui-nextjs-admin-template/demo-1
ENV NEXT_PUBLIC_REPO_NAME=materio-mui-nextjs-admin-template-free
ENV NEXT_PUBLIC_WHATSAPP_NUMBER=3434067735
ENV REACT_EDITOR=code
ENV BACKEND_URL=http://urban_backend:3001
# Generate icons (was postinstall) then build Next.js with output tracing
RUN npm run build:icons && npm run build
FROM builder AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Use non-root user
RUN addgroup -g 1001 -S nodejs && adduser -S -u 1001 nodejs
# Copy only necessary runtime files
COPY --from=builder /app/package.json ./package.json
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
ENV PORT=3000
EXPOSE 3000
USER nodejs
CMD ["node", "server.js"]
+10
View File
@@ -0,0 +1,10 @@
node_modules
.next
.env*
.git
.gitignore
Dockerfile*
docker-compose*
**/*.log
coverage
pnpm-lock.yaml
+1
View File
@@ -0,0 +1 @@
REACT_EDITOR=code
+9
View File
@@ -0,0 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
eslint: {
ignoreDuringBuilds: true,
},
}
export default nextConfig
+10491
View File
File diff suppressed because it is too large Load Diff
+75
View File
@@ -0,0 +1,75 @@
{
"name": "next-app-test",
"version": "0.1.0",
"license": "Commercial",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint:fix": "next lint --fix",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
"build:icons": "tsx src/assets/iconify-icons/bundle-icons-css.ts",
"postinstall": "npm run build:icons"
},
"dependencies": {
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@mui/icons-material": "^5.14.0",
"@mui/lab": "5.0.0-alpha.170",
"@mui/material": "^5.15.19",
"@mui/material-nextjs": "^5.15.11",
"axios": "^1.10.0",
"classnames": "^2.5.1",
"dayjs": "^1.11.14",
"material-react-table": "^2.13.2",
"next": "^14.2.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-dropzone": "^14.3.8",
"react-perfect-scrollbar": "^1.5.8",
"react-apexcharts": "^1.8.0",
"apexcharts": "^4.0.0",
"@mui/x-date-pickers": "^6.20.0",
"react-use": "^17.5.0",
"recharts": "^3.5.0",
"server-only": "^0.0.1",
"swr": "^2.3.6"
},
"devDependencies": {
"@iconify/json": "^2.2.218",
"@iconify/tools": "^4.0.4",
"@iconify/types": "^2.0.0",
"@iconify/utils": "^2.1.24",
"@types/node": "^20.14.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-dropzone": "^4.2.2",
"@typescript-eslint/eslint-plugin": "^7.13.0",
"@typescript-eslint/parser": "^7.13.0",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-config-next": "^14.2.3",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"postcss": "^8.4.38",
"postcss-styled-syntax": "^0.6.4",
"prettier": "^3.3.2",
"stylelint": "^16.6.1",
"stylelint-use-logical-spec": "^5.0.1",
"tailwindcss": "^3.4.4",
"tailwindcss-logical": "^3.0.1",
"tsx": "^4.15.2",
"typescript": "^5.4.5"
},
"resolutions": {
"rimraf": "^5.0.7"
},
"overrides": {
"react": "^18.3.1",
"rimraf": "^5.0.7"
}
}
+5895
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {}
}
}
export default config
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

After

Width:  |  Height:  |  Size: 629 B

@@ -0,0 +1,51 @@
'use client'
// React Imports
import { forwardRef } from 'react'
// MUI Imports
import MuiAvatar from '@mui/material/Avatar'
import { lighten, styled } from '@mui/material/styles'
import type { AvatarProps } from '@mui/material/Avatar'
// Type Imports
import type { ThemeColor } from '@core/types'
export type CustomAvatarProps = AvatarProps & {
color?: ThemeColor
skin?: 'filled' | 'light' | 'light-static'
size?: number
}
const Avatar = styled(MuiAvatar)<CustomAvatarProps>(({ skin, color, size, theme }) => {
return {
...(color &&
skin === 'light' && {
backgroundColor: `var(--mui-palette-${color}-lightOpacity)`,
color: `var(--mui-palette-${color}-main)`
}),
...(color &&
skin === 'light-static' && {
backgroundColor: lighten(theme.palette[color as ThemeColor].main, 0.84),
color: `var(--mui-palette-${color}-main)`
}),
...(color &&
skin === 'filled' && {
backgroundColor: `var(--mui-palette-${color}-main)`,
color: `var(--mui-palette-${color}-contrastText)`
}),
...(size && {
height: size,
width: size
})
}
})
const CustomAvatar = forwardRef<HTMLDivElement, CustomAvatarProps>((props: CustomAvatarProps, ref) => {
// Props
const { color, skin = 'filled', ...rest } = props
return <Avatar color={color} skin={skin} ref={ref} {...rest} />
})
export default CustomAvatar
@@ -0,0 +1,75 @@
'use client'
import MuiButton from '@mui/material/Button'
import { styled } from '@mui/material/styles'
// Config Imports
import themeConfig from '@configs/themeConfig'
const CustomIconButton = styled(MuiButton)(({ color, size, theme, variant }) => {
return {
minInlineSize: 0,
...(size === 'small'
? {
fontSize: '20px',
padding: theme.spacing(variant === 'outlined' ? 1 : 1.25),
'& i, & svg': {
fontSize: 'inherit'
}
}
: {
...(size === 'large'
? {
fontSize: '24px',
padding: theme.spacing(variant === 'outlined' ? 2 : 2.25),
'& i, & svg': {
fontSize: 'inherit'
}
}
: {
fontSize: '22px',
padding: theme.spacing(variant === 'outlined' ? 1.5 : 1.75),
'& i, & svg': {
fontSize: 'inherit'
}
})
}),
...(!color && {
color: 'var(--mui-palette-action-active)',
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active': {
backgroundColor: 'rgb(var(--mui-palette-text-primaryChannel) / 0.08)'
},
...(themeConfig.disableRipple && {
'&.Mui-focusVisible:not(.Mui-disabled)': {
backgroundColor: 'rgb(var(--mui-palette-text-primaryChannel) / 0.08)'
}
}),
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-action-active)'
},
...(variant === 'outlined' && {
border: 'none !important',
...(size === 'small'
? {
padding: theme.spacing(1.5)
}
: {
...(size === 'large'
? {
padding: theme.spacing(2.25)
}
: {
padding: theme.spacing(1.75)
})
})
}),
...(variant === 'contained' && {
boxShadow: 'none !important',
backgroundColor: 'transparent'
})
})
}
}) as typeof MuiButton
export default CustomIconButton
@@ -0,0 +1,135 @@
'use client'
// React Imports
import { useRef, useState } from 'react'
import type { ReactElement, ReactNode, SyntheticEvent } from 'react'
// Next Imports
import Link from 'next/link'
// MUI Imports
import Tooltip from '@mui/material/Tooltip'
import Box from '@mui/material/Box'
import Popper from '@mui/material/Popper'
import MenuItem from '@mui/material/MenuItem'
import MenuList from '@mui/material/MenuList'
import ClickAwayListener from '@mui/material/ClickAwayListener'
import Fade from '@mui/material/Fade'
import Paper from '@mui/material/Paper'
import IconButton from '@mui/material/IconButton'
import Divider from '@mui/material/Divider'
// Third-party Imports
import classnames from 'classnames'
// Type Imports
import type { OptionsMenuType, OptionType, OptionMenuItemType } from './types'
const IconButtonWrapper = (props: Pick<OptionsMenuType, 'tooltipProps'> & { children: ReactElement }) => {
// Props
const { tooltipProps, children } = props
return tooltipProps?.title ? <Tooltip {...tooltipProps}>{children}</Tooltip> : children
}
const MenuItemWrapper = ({ children, option }: { children: ReactNode; option: OptionMenuItemType }) => {
if (option.href) {
return (
<Box component={Link} href={option.href} {...option.linkProps}>
{children}
</Box>
)
} else {
return <>{children}</>
}
}
const OptionMenu = (props: OptionsMenuType) => {
// Props
const { tooltipProps, icon, iconClassName, options, leftAlignMenu, iconButtonProps } = props
// States
const [open, setOpen] = useState(false)
// Refs
const anchorRef = useRef<HTMLButtonElement>(null)
const handleToggle = () => {
setOpen(prevOpen => !prevOpen)
}
const handleClose = (event: Event | SyntheticEvent) => {
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
return
}
setOpen(false)
}
return (
<>
<IconButtonWrapper tooltipProps={tooltipProps}>
<IconButton ref={anchorRef} size='small' onClick={handleToggle} {...iconButtonProps}>
{typeof icon === 'string' ? (
<i className={classnames(icon, iconClassName)} />
) : (icon as ReactNode) ? (
icon
) : (
<i className={classnames('ri-more-2-line', iconClassName)} />
)}
</IconButton>
</IconButtonWrapper>
<Popper
open={open}
anchorEl={anchorRef.current}
placement={leftAlignMenu ? 'bottom-start' : 'bottom-end'}
transition
disablePortal
sx={{ zIndex: 1 }}
>
{({ TransitionProps }) => (
<Fade {...TransitionProps}>
<Paper className='shadow-lg'>
<ClickAwayListener onClickAway={handleClose}>
<MenuList autoFocusItem={open}>
{options.map((option: OptionType, index: number) => {
if (typeof option === 'string') {
return (
<MenuItem key={index} onClick={handleClose}>
{option}
</MenuItem>
)
} else if ('divider' in option) {
return option.divider && <Divider key={index} {...option.dividerProps} />
} else {
return (
<MenuItem
key={index}
{...option.menuItemProps}
{...(option.href && { className: 'p-0' })}
onClick={e => {
handleClose(e)
option.menuItemProps && option.menuItemProps.onClick
? option.menuItemProps.onClick(e)
: null
}}
>
<MenuItemWrapper option={option}>
{(typeof option.icon === 'string' ? <i className={option.icon} /> : option.icon) || null}
{option.text}
</MenuItemWrapper>
</MenuItem>
)
}
})}
</MenuList>
</ClickAwayListener>
</Paper>
</Fade>
)}
</Popper>
</>
)
}
export default OptionMenu
@@ -0,0 +1,42 @@
// React Imports
import type { ReactNode } from 'react'
// Next Imports
import type { LinkProps } from 'next/link'
// MUI Imports
import type { IconButtonProps } from '@mui/material/IconButton'
import type { MenuItemProps } from '@mui/material/MenuItem'
import type { DividerProps } from '@mui/material/Divider'
import type { BoxProps } from '@mui/material/Box'
import type { TooltipProps } from '@mui/material/Tooltip'
export type OptionDividerType = {
divider: boolean
dividerProps?: DividerProps
href?: never
icon?: never
text?: never
linkProps?: never
menuItemProps?: never
}
export type OptionMenuItemType = {
text: ReactNode
icon?: ReactNode
linkProps?: BoxProps
href?: LinkProps['href']
menuItemProps?: MenuItemProps
divider?: never
dividerProps?: never
}
export type OptionType = string | OptionDividerType | OptionMenuItemType
export type OptionsMenuType = {
tooltipProps?: Omit<TooltipProps, 'children'>
icon?: ReactNode
iconClassName?: string
options: OptionType[]
leftAlignMenu?: boolean
iconButtonProps?: IconButtonProps
}
@@ -0,0 +1,122 @@
'use client'
// React Imports
import type { ReactNode } from 'react'
import { createContext, useMemo, useState } from 'react'
// Type Imports
import type { Mode } from '@core/types'
// Config Imports
import themeConfig from '@configs/themeConfig'
// Hook Imports
import { useObjectCookie } from '@core/hooks/useObjectCookie'
// Settings type
export type Settings = {
mode?: Mode
}
// UpdateSettingsOptions type
type UpdateSettingsOptions = {
updateCookie?: boolean
}
// SettingsContextProps type
type SettingsContextProps = {
settings: Settings
updateSettings: (settings: Partial<Settings>, options?: UpdateSettingsOptions) => void
isSettingsChanged: boolean
resetSettings: () => void
updatePageSettings: (settings: Partial<Settings>) => () => void
}
type Props = {
children: ReactNode
settingsCookie: Settings | null
mode?: Mode
}
// Initial Settings Context
export const SettingsContext = createContext<SettingsContextProps | null>(null)
// Settings Provider
export const SettingsProvider = (props: Props) => {
// Initial Settings
const initialSettings: Settings = {
mode: themeConfig.mode
}
const updatedInitialSettings = {
...initialSettings,
mode: props.mode || themeConfig.mode
}
// Cookies
const [settingsCookie, updateSettingsCookie] = useObjectCookie<Settings>(
themeConfig.settingsCookieName,
JSON.stringify(props.settingsCookie) !== '{}' ? props.settingsCookie : updatedInitialSettings
)
// State
const [_settingsState, _updateSettingsState] = useState<Settings>(
JSON.stringify(settingsCookie) !== '{}' ? settingsCookie : updatedInitialSettings
)
const updateSettings = (settings: Partial<Settings>, options?: UpdateSettingsOptions) => {
const { updateCookie = true } = options || {}
_updateSettingsState(prev => {
const newSettings = { ...prev, ...settings }
// Update cookie if needed
if (updateCookie) updateSettingsCookie(newSettings)
return newSettings
})
}
/**
* Updates the settings for page with the provided settings object.
* Updated settings won't be saved to cookie hence will be reverted once navigating away from the page.
*
* @param settings - The partial settings object containing the properties to update.
* @returns A function to reset the page settings.
*
* @example
* useEffect(() => {
* return updatePageSettings({ theme: 'dark' });
* }, []);
*/
const updatePageSettings = (settings: Partial<Settings>): (() => void) => {
updateSettings(settings, { updateCookie: false })
// Returns a function to reset the page settings
return () => updateSettings(settingsCookie, { updateCookie: false })
}
const resetSettings = () => {
updateSettings(initialSettings)
}
const isSettingsChanged = useMemo(
() => JSON.stringify(initialSettings) !== JSON.stringify(_settingsState),
// eslint-disable-next-line react-hooks/exhaustive-deps
[_settingsState]
)
return (
<SettingsContext.Provider
value={{
settings: _settingsState,
updateSettings,
isSettingsChanged,
resetSettings,
updatePageSettings
}}
>
{props.children}
</SettingsContext.Provider>
)
}
@@ -0,0 +1,18 @@
import apiClient from '@/lib/apiClient';
export const downloadInvoicePdf = async (invoiceId: string) => {
const response = await apiClient.get(
`/invoices/download/${invoiceId}`,
{ responseType: 'blob' }
);
const blob = new Blob([response.data], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `factura-${invoiceId}.pdf`;
a.click();
URL.revokeObjectURL(url);
};
@@ -0,0 +1,55 @@
// src/@core/hooks/useAccessLogs.ts
'use client';
import { useCallback, useEffect, useState } from 'react';
import apiClient from '@/lib/apiClient';
import { useUserAuth } from '@/app/context/UserAuth';
export type AccessLog = {
id: string;
userId: string;
direction: 'INGRESS' | 'EGRESS';
timestamp: string;
};
export type ResponseAccessLogs = {
userId: string;
name: string;
accessLogs: AccessLog[];
};
export function useAccessLogs(userId?: string, date?: string, enabled: boolean = true) {
const { token } = useUserAuth();
const [data, setData] = useState<ResponseAccessLogs | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchData = useCallback(
async (signal?: AbortSignal) => {
if (!enabled || !userId || !date) return;
setLoading(true);
setError(null);
try {
const res = await apiClient.get<ResponseAccessLogs>('/attendance', {
params: { userId, date },
signal,
});
setData(res.data);
} catch (e: any) {
if (e?.name === 'CanceledError') return;
setError(e?.response?.data?.message ?? 'No se pudieron cargar los registros de acceso.');
} finally {
setLoading(false);
}
},
[enabled, userId, date, token]
);
useEffect(() => {
const ctrl = new AbortController();
fetchData(ctrl.signal);
return () => ctrl.abort();
}, [fetchData]);
return { data, loading, error, refresh: fetchData };
}
@@ -0,0 +1,49 @@
// src/@core/hooks/useAttendeesSnapshot.ts
'use client';
import { useCallback, useEffect, useState } from 'react';
import apiClient from '@/lib/apiClient';
import { useUserAuth } from '@/app/context/UserAuth';
export type ResponseAttendeesSnapshot = {
dateRange: { start: string; end: string };
instructors: { id: string; name: string }[];
assistants: { id: string; name: string }[];
attendance: { id: string; name: string }[];
};
export function useAttendeesSnapshot(snapshotId?: string, enabled: boolean = true) {
const { token } = useUserAuth();
const [data, setData] = useState<ResponseAttendeesSnapshot | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchData = useCallback(
async (signal?: AbortSignal) => {
if (!enabled || !snapshotId) return;
setLoading(true);
setError(null);
try {
const res = await apiClient.get<ResponseAttendeesSnapshot>(
`/sessions/${snapshotId}/attendees`,
{ signal }
);
setData(res.data);
} catch (e: any) {
if (e?.name === 'CanceledError') return;
setError(e?.response?.data?.message ?? 'No se pudieron cargar los asistentes.');
} finally {
setLoading(false);
}
},
[enabled, snapshotId, token]
);
useEffect(() => {
const ctrl = new AbortController();
fetchData(ctrl.signal);
return () => ctrl.abort();
}, [fetchData]);
return { data, loading, error, refresh: fetchData };
}
+66
View File
@@ -0,0 +1,66 @@
// useClases.ts
'use client';
import { useCallback, useEffect, useRef, useState } from 'react';
import apiClient from '@/lib/apiClient';
import { Clase } from '@/views/clases/DetailClaseDialog';
type Options = {
/** Lanza el fetch al montar el hook (default: true) */
auto?: boolean;
/** Filtro de estado: 'all', 'active', 'inactive' */
status?: 'all' | 'active' | 'inactive';
};
type UseClasesResult = {
data: Clase[];
loading: boolean;
error: unknown;
/** Vuelve a pedir la lista */
refresh: () => Promise<void>;
/** Setea manualmente el array (útil para optimist updates) */
setData: React.Dispatch<React.SetStateAction<Clase[]>>;
};
export function useClases(opts: Options = {}): UseClasesResult {
const { auto = true, status = 'all' } = opts;
const [data, setData] = useState<Clase[]>([]);
const [loading, setLoading] = useState<boolean>(auto);
const [error, setError] = useState<unknown>(null);
// guardamos una referencia al abortController para cancelar en unmount
const abortRef = useRef<AbortController | null>(null);
const refresh = useCallback(async () => {
setLoading(true);
setError(null);
// cancelar request previa si hubiera
if (abortRef.current) abortRef.current.abort();
const ac = new AbortController();
abortRef.current = ac;
try {
const params = status && status !== 'all' ? { status } : undefined;
const res = await apiClient.get<Clase[]>('/sessions', { params, signal: ac.signal });
setData(res.data ?? []);
} catch (e: any) {
if (e?.name === 'CanceledError' || e?.message === 'canceled') return;
setError(e);
console.error('Error al obtener clases:', e);
} finally {
if (!ac.signal.aborted) setLoading(false);
}
}, [status]);
useEffect(() => {
if (auto) refresh();
return () => {
// cancelar si el componente se desmonta
abortRef.current?.abort();
};
}, [auto, refresh]);
return { data, loading, error, refresh, setData };
}
@@ -0,0 +1,65 @@
// useDashboard.ts
'use client'
import { useCallback, useEffect, useRef, useState } from 'react'
import apiClient from '@/lib/apiClient'
import { useUserAuth } from '@/app/context/UserAuth'
export type DashboardUser = {
message: string
accessLog: { currentAccess: string; lastAccess: string }
cardDetails: { sessions: number; users: number; assistants: number; revenue: number }
sessions: { id: string; description: string; instructors: string; startDate: string }[]
}
type Options = {
auto?: boolean
}
type UseDashboardResult = {
data: DashboardUser | null
loading: boolean
error: unknown
refresh: () => Promise<void>
}
export function useDashboard(opts: Options = {}): UseDashboardResult {
const { token } = useUserAuth()
const { auto = true } = opts
const [data, setData] = useState<DashboardUser | null>(null)
const [loading, setLoading] = useState<boolean>(false)
const [error, setError] = useState<unknown>(null)
const abortRef = useRef<AbortController | null>(null)
const refresh = useCallback(async () => {
// si no hay token aún, no pidas nada ni cambies loading
if (!token) return
setLoading(true)
setError(null)
abortRef.current?.abort()
const ac = new AbortController()
abortRef.current = ac
try {
const res = await apiClient.get<DashboardUser>('/dashboard', { signal: ac.signal })
setData(res.data ?? null)
} catch (e: any) {
if (e?.name === 'CanceledError' || e?.message === 'canceled') return
setError(e)
console.error('Error al obtener dashboard:', e)
} finally {
if (!ac.signal.aborted) setLoading(false)
}
}, [token]) // ← incluye token!
useEffect(() => {
if (auto && token) refresh() // ← solo cuando haya token
return () => abortRef.current?.abort()
}, [auto, token, refresh]) // ← depende de token también
return { data, loading, error, refresh }
}
@@ -0,0 +1,28 @@
// React Imports
import { useMemo } from 'react'
// Third-party imports
import { useColorScheme } from '@mui/material'
// Type imports
import type { Mode } from '@core/types'
export const useImageVariant = (mode: Mode, imgLight: string, imgDark: string): string => {
// Hooks
const { mode: muiMode } = useColorScheme()
return useMemo(() => {
const isServer = typeof window === 'undefined'
const currentMode = (() => {
if (isServer) return mode
return muiMode
})()
const isDarkMode = currentMode === 'dark'
return isDarkMode ? imgDark : imgLight
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mode, muiMode])
}
@@ -0,0 +1,74 @@
// src/@core/hooks/useInstructors.ts
'use client';
import { useCallback, useEffect, useState } from 'react';
import apiClient from '@/lib/apiClient';
import { useUserAuth } from '@/app/context/UserAuth';
import type { Instructor } from '@/app/modules/users/dto/instructor.dto';
type UseInstructorsResult = {
data: Instructor[];
loading: boolean;
error: string | null;
refresh: () => Promise<void>;
};
/**
* Hook para obtener la lista de instructores.
* - Lee el token del contexto.
* - Maneja estados data/loading/error.
* - Cancela la request al desmontar.
*/
export function useInstructors(enabled: boolean = true): UseInstructorsResult {
const { token } = useUserAuth();
const [data, setData] = useState<Instructor[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchData = useCallback(
async (signal?: AbortSignal) => {
if (!token) {
setData([]);
setLoading(false);
setError(null);
return;
}
try {
setLoading(true);
setError(null);
const res = await apiClient.get<Instructor[]>('/users/instructor', { signal });
setData(res.data ?? []);
} catch (err: any) {
if (err?.name !== 'CanceledError') {
const msg =
err?.response?.data?.message ||
err?.message ||
'Error al obtener instructores.';
setError(msg);
}
} finally {
setLoading(false);
}
},
[token]
);
useEffect(() => {
if (!enabled) return;
const ctrl = new AbortController();
fetchData(ctrl.signal);
return () => ctrl.abort();
}, [enabled, fetchData]);
const refresh = useCallback(async () => {
await fetchData();
}, [fetchData]);
return { data, loading, error, refresh };
}
export default useInstructors;
@@ -0,0 +1,68 @@
'use client';
import { useCallback, useEffect, useRef, useState } from 'react';
import apiClient from '@/lib/apiClient';
export type InvoiceStatus = 'PENDING' | 'PAID' | 'FAILED' | 'CANCELED';
export type InvoiceWithDescription = {
id: string;
subscriptionId: string;
base64Invoice: string | null;
linkPayment: string | null;
status: InvoiceStatus | string;
amount: number;
createdAt: string | Date;
updatedAt: string | Date;
description: string;
};
export type UseInvoicesOptions = {
auto?: boolean;
};
export type UseInvoicesResult = {
data: InvoiceWithDescription[];
loading: boolean;
error: unknown;
refresh: () => Promise<void>;
setData: React.Dispatch<React.SetStateAction<InvoiceWithDescription[]>>;
};
export function useInvoices(
userId: string,
opts: UseInvoicesOptions = {}
): UseInvoicesResult {
const { auto = true } = opts;
const [data, setData] = useState<InvoiceWithDescription[]>([]);
const [loading, setLoading] = useState<boolean>(auto);
const [error, setError] = useState<unknown>(null);
const abortRef = useRef<AbortController | null>(null);
const refresh = useCallback(async () => {
if (!userId) return;
setLoading(true);
setError(null);
if (abortRef.current) abortRef.current.abort();
const ac = new AbortController();
abortRef.current = ac;
try {
const res = await apiClient.get<InvoiceWithDescription[] | null>(`/invoices/${encodeURIComponent(userId)}`, { signal: ac.signal });
setData((res.data ?? []) as InvoiceWithDescription[]);
} catch (e: any) {
if (e?.name === 'CanceledError' || e?.message === 'canceled') return;
setError(e);
} finally {
if (!ac.signal.aborted) setLoading(false);
}
}, [userId]);
useEffect(() => {
if (auto) refresh();
return () => abortRef.current?.abort();
}, [auto, refresh, userId]);
return { data, loading, error, refresh, setData };
}
@@ -0,0 +1,19 @@
// React Imports
import { useMemo } from 'react'
// Third-party Imports
import { useCookie } from 'react-use'
export const useObjectCookie = <T>(key: string, fallback?: T | null): [T, (newVal: T) => void] => {
// Hooks
const [valStr, updateCookie] = useCookie(key)
// eslint-disable-next-line react-hooks/exhaustive-deps
const value = useMemo<T>(() => (valStr ? JSON.parse(valStr) : fallback), [valStr])
const updateValue = (newVal: T) => {
updateCookie(JSON.stringify(newVal))
}
return [value, updateValue]
}
@@ -0,0 +1,82 @@
'use client';
import { useState, useCallback, useEffect } from 'react';
import apiClient from '@/lib/apiClient';
export type ProfilePictureResponse = {
profilePicture: string | null;
updatedAt: string | null;
};
export type UseProfilePictureOptions = {
auto?: boolean;
};
export type UseProfilePictureResult = {
profilePicture: string | null;
updatedAt: string | null;
loading: boolean;
error: unknown;
refresh: () => Promise<void>;
};
/**
* Hook para obtener la foto de perfil de un usuario
*
* @param userId - ID del usuario
* @param options - Opciones de configuración
*
* @example
* ```tsx
* const { profilePicture, loading, error, refresh } = useProfilePicture('user-123', { auto: true });
*
* return (
* <img src={profilePicture || '/default-avatar.png'} alt="Profile" />
* );
* ```
*/
export function useProfilePicture(
userId: string,
options: UseProfilePictureOptions = {}
): UseProfilePictureResult {
const { auto = true } = options;
const [profilePicture, setProfilePicture] = useState<string | null>(null);
const [updatedAt, setUpdatedAt] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(auto);
const [error, setError] = useState<unknown>(null);
const refresh = useCallback(async () => {
if (!userId) return;
setLoading(true);
setError(null);
try {
const res = await apiClient.get<ProfilePictureResponse>(`/users/${encodeURIComponent(userId)}/profile-picture`);
setProfilePicture(res.data.profilePicture);
setUpdatedAt(res.data.updatedAt);
} catch (e: any) {
setError(e);
setProfilePicture(null);
setUpdatedAt(null);
} finally {
setLoading(false);
}
}, [userId]);
useEffect(() => {
if (auto && userId) {
refresh();
}
}, [auto, userId, refresh]);
return {
profilePicture,
updatedAt,
loading,
error,
refresh,
};
}
@@ -0,0 +1,91 @@
// useSessionInvoicesByMonth.ts
'use client';
import { useCallback, useEffect, useRef, useState } from 'react';
import apiClient from '@/lib/apiClient';
export type InvoiceStatus = 'PENDING' | 'PAID' | 'CANCELED';
export type AssistantInvoice = {
id: string;
name: string;
hasInvoice: boolean;
invoiceStatus: InvoiceStatus | null;
invoiceId: string | null;
amount: number | null;
};
export type SessionInvoicesByMonth = {
sessionId: string;
sessionCustomId: string;
sessionDescription: string;
sessionType: string;
month: string;
year: string;
assistants: AssistantInvoice[];
};
type Options = {
sessionId: string;
month: string; // "01" a "12"
year: string; // "YYYY"
auto?: boolean;
};
type UseSessionInvoicesByMonthResult = {
data: SessionInvoicesByMonth | null;
loading: boolean;
error: unknown;
refresh: () => Promise<void>;
};
export function useSessionInvoicesByMonth(
opts: Options
): UseSessionInvoicesByMonthResult {
const { sessionId, month, year, auto = true } = opts;
const [data, setData] = useState<SessionInvoicesByMonth | null>(null);
const [loading, setLoading] = useState<boolean>(auto);
const [error, setError] = useState<unknown>(null);
const abortRef = useRef<AbortController | null>(null);
const refresh = useCallback(async () => {
if (!sessionId || !month || !year) {
setData(null);
setLoading(false);
return;
}
setLoading(true);
setError(null);
// Cancelar request previa si hubiera
if (abortRef.current) abortRef.current.abort();
const ac = new AbortController();
abortRef.current = ac;
try {
const res = await apiClient.get<SessionInvoicesByMonth>(
`/sessions/${sessionId}/invoices-by-month`,
{ params: { month, year }, signal: ac.signal }
);
setData(res.data);
} catch (e: any) {
if (e?.name === 'CanceledError' || e?.message === 'canceled') return;
setError(e);
console.error('Error al obtener facturas por mes:', e);
} finally {
if (!ac.signal.aborted) setLoading(false);
}
}, [sessionId, month, year]);
useEffect(() => {
if (auto) refresh();
return () => {
abortRef.current?.abort();
};
}, [auto, refresh]);
return { data, loading, error, refresh };
}
@@ -0,0 +1,42 @@
// useSessionParticipants.ts
'use client';
import { useCallback, useEffect, useState } from 'react';
import apiClient from '@/lib/apiClient';
export type Participant = { id: string; name: string; customId?: string };
export function useSessionParticipants(token: string, sessionId: string, auto = true) {
const [selected, setSelected] = useState<Participant[]>([]);
const [available, setAvailable] = useState<Participant[]>([]);
const [loading, setLoading] = useState(auto);
const [error, setError] = useState<unknown>(null);
const refresh = useCallback(async () => {
setLoading(true); setError(null);
try {
const { data } = await apiClient.get(`/sessions/${sessionId}/participants`);
setSelected(data.selected);
setAvailable(data.available);
} catch (e) { setError(e); } finally { setLoading(false); }
}, [sessionId]);
const save = useCallback(async () => {
await apiClient.put(`/sessions/${sessionId}/participants`, {
userIds: selected.map(s => s.id),
});
}, [sessionId, selected]);
useEffect(() => { if (auto) refresh(); }, [auto, refresh]);
// helpers UI
const add = (p: Participant) => {
setAvailable(prev => prev.filter(x => x.id !== p.id));
setSelected(prev => [...prev, p]);
};
const remove = (p: Participant) => {
setSelected(prev => prev.filter(x => x.id !== p.id));
setAvailable(prev => [...prev, p]);
};
return { selected, available, setSelected, setAvailable, loading, error, refresh, save, add, remove };
}
@@ -0,0 +1,16 @@
// React Imports
import { useContext } from 'react'
// Context Imports
import { SettingsContext } from '@core/contexts/settingsContext'
export const useSettings = () => {
// Hooks
const context = useContext(SettingsContext)
if (!context) {
throw new Error('useSettingsContext must be used within a SettingsProvider')
}
return context
}
@@ -0,0 +1,94 @@
'use client';
import type { SystemConfig, UpdateSystemConfigDto } from '@/@core/types/system-config';
import apiClient from '@/lib/apiClient';
import { useCallback, useState } from 'react';
export type UseSystemConfigOptions = {};
export type UseSystemConfigResult = {
data: SystemConfig | null;
loading: boolean;
error: unknown;
fetchConfig: () => Promise<void>;
updateConfig: (dto: UpdateSystemConfigDto) => Promise<void>;
reloadConfig: () => Promise<void>;
updating: boolean;
reloading: boolean;
};
/**
* Hook para gestionar la configuración del sistema
*/
export function useSystemConfig(
options: UseSystemConfigOptions = {}
): UseSystemConfigResult {
const [data, setData] = useState<SystemConfig | null>(null);
const [loading, setLoading] = useState<boolean>(false);
const [updating, setUpdating] = useState<boolean>(false);
const [reloading, setReloading] = useState<boolean>(false);
const [error, setError] = useState<unknown>(null);
// Obtener configuración actual
const fetchConfig = useCallback(async () => {
setLoading(true);
setError(null);
try {
const res = await apiClient.get<SystemConfig>('/system-config');
setData(res.data);
} catch (e: any) {
console.log('Error fetching system config:', e);
setError(e);
throw e;
} finally {
setLoading(false);
}
}, []);
// Actualizar configuración
const updateConfig = useCallback(
async (dto: UpdateSystemConfigDto) => {
setUpdating(true);
setError(null);
try {
const res = await apiClient.patch<SystemConfig>('/system-config', dto);
setData(res.data);
} catch (e: any) {
setError(e);
throw e;
} finally {
setUpdating(false);
}
},
[]
);
// Recargar configuración desde BD
const reloadConfig = useCallback(async () => {
setReloading(true);
setError(null);
try {
const res = await apiClient.get<SystemConfig>('/system-config/reload');
setData(res.data);
} catch (e: any) {
setError(e);
throw e;
} finally {
setReloading(false);
}
}, []);
return {
data,
loading,
error,
fetchConfig,
updateConfig,
reloadConfig,
updating,
reloading,
};
}
@@ -0,0 +1,38 @@
import { useUserAuth } from '@/app/context/UserAuth';
import apiClient from '@/lib/apiClient';
import { useState } from 'react';
export const useToggleUserActive = () => {
const { token } = useUserAuth();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const toggleActive = async (userId: string, isActive: boolean) => {
setLoading(true);
setError(null);
try {
<<<<<<< Updated upstream
const deleted = isActive ? null : new Date().toISOString();
const response = await apiClient.put(`/users/${userId}/active`, { deleted });
console.log('✅ User active status updated successfully:', response.data);
=======
const url = `${process.env.NEXT_PUBLIC_BACKEND_URL ?? ''}/users/${userId}/active`;
const deleted = isActive ? null : new Date().toISOString();
const response = await axios.put(
url,
{ deleted },
{ headers: { Authorization: `Bearer ${token}` } }
);
>>>>>>> Stashed changes
return true;
} catch (err) {
console.error('❌ Error al cambiar estado del usuario', err);
setError('No se pudo cambiar el estado del usuario');
return false;
} finally {
setLoading(false);
}
};
return { toggleActive, loading, error };
};
@@ -0,0 +1,100 @@
'use client';
import { useState, useCallback } from 'react';
import apiClient from '@/lib/apiClient';
import type { InvoiceStatus } from './useInvoices';
export type UpdateInvoiceStatusParams = {
userId: string;
invoiceId: string;
status: InvoiceStatus;
};
export type UpdateInvoiceStatusOptions = {
onSuccess?: () => void | Promise<void>;
onError?: (error: any) => void;
};
export type UpdateInvoiceStatusResult = {
mutate: (params: UpdateInvoiceStatusParams) => Promise<void>;
loading: boolean;
error: unknown;
isSuccess: boolean;
reset: () => void;
};
/**
* Hook para actualizar el estado de una factura
*
* @example
* ```tsx
* const { mutate, loading, error, isSuccess } = useUpdateInvoiceStatus({
* onSuccess: () => {
* toast.success('Factura actualizada');
* refetchInvoices();
* },
* onError: (error) => {
* toast.error('Error al actualizar factura');
* }
* });
*
* // Usar la mutación
* await mutate({
* userId: 'user-123',
* invoiceId: 'invoice-456',
* status: 'PAID'
* });
* ```
*/
export function useUpdateInvoiceStatus(
options: UpdateInvoiceStatusOptions = {}
): UpdateInvoiceStatusResult {
const { onSuccess, onError } = options;
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<unknown>(null);
const [isSuccess, setIsSuccess] = useState<boolean>(false);
const mutate = useCallback(
async ({ userId, invoiceId, status }: UpdateInvoiceStatusParams) => {
if (!userId || !invoiceId || !status) {
const validationError = new Error('userId, invoiceId y status son requeridos');
setError(validationError);
onError?.(validationError);
throw validationError;
}
setLoading(true);
setError(null);
setIsSuccess(false);
try {
await apiClient.put(
`/invoices/${encodeURIComponent(userId)}/${encodeURIComponent(invoiceId)}`,
{ status }
);
setIsSuccess(true);
// Ejecutar callback de éxito
await onSuccess?.();
} catch (e: any) {
setError(e);
setIsSuccess(false);
onError?.(e);
throw e;
} finally {
setLoading(false);
}
},
[onSuccess, onError]
);
const reset = useCallback(() => {
setLoading(false);
setError(null);
setIsSuccess(false);
}, []);
return { mutate, loading, error, isSuccess, reset };
}
@@ -0,0 +1,78 @@
// useUsuarios.ts
'use client';
import { useCallback, useEffect, useRef, useState } from 'react';
import apiClient from '@/lib/apiClient';
import { Usuario } from '@/views/usuarios/usuarios.dto';
type Options = {
/** Lanza el fetch al montar el hook (default: true) */
auto?: boolean;
};
type UseUsuariosResult = {
data: Usuario[];
loading: boolean;
error: unknown;
/** Vuelve a pedir la lista */
refresh: () => Promise<void>;
/** Setea manualmente el array (útil para optimist updates) */
setData: React.Dispatch<React.SetStateAction<Usuario[]>>;
};
export function useUsuarios(opts: Options = {}): UseUsuariosResult {
const { auto = true } = opts;
const [data, setData] = useState<Usuario[]>([]);
const [loading, setLoading] = useState<boolean>(auto);
const [error, setError] = useState<unknown>(null);
// guardamos una referencia al abortController para cancelar en unmount
const abortRef = useRef<AbortController | null>(null);
const refresh = useCallback(async () => {
setLoading(true);
setError(null);
// cancelar request previa si hubiera
if (abortRef.current) abortRef.current.abort();
const ac = new AbortController();
abortRef.current = ac;
try {
<<<<<<< Updated upstream
const res = await apiClient.get<Usuario[]>('/users', { signal: ac.signal });
=======
if (!token) throw new Error('Token no encontrado');
// Asegurar que baseUrl no esté vacío
const backendUrl = baseUrl
const url = `${backendUrl}/users`;
const res = await axios.get<Usuario[]>(url, {
headers: { Authorization: `Bearer ${token}` },
signal: ac.signal, // axios v1 soporta AbortController
});
>>>>>>> Stashed changes
setData(res.data ?? []);
} catch (e: any) {
if (e?.name === 'CanceledError' || e?.message === 'canceled') return;
setError(e);
console.error('Error al obtener usuarios:', e);
} finally {
if (!ac.signal.aborted) setLoading(false);
}
}, []);
useEffect(() => {
if (auto) refresh();
return () => {
// cancelar si el componente se desmonta
abortRef.current?.abort();
};
}, [auto, refresh]);
return { data, loading, error, refresh, setData };
}
@@ -0,0 +1,36 @@
// hooks/useWhatsappStatus.ts
'use client';
import useSWR from 'swr';
import apiClient from '@/lib/apiClient';
const WHATSAPP_STATUS_PATH = '/whatsapp/status';
const fetcher = async (path: string) => {
const res = await apiClient.get(path);
return res.data; // { status, phone, qr }
};
export type UseWhatsappStatusOptions = {};
export function useWhatsappStatus(_options: UseWhatsappStatusOptions = {}) {
const { data, error, isLoading, mutate } = useSWR(
WHATSAPP_STATUS_PATH,
fetcher,
{
// más frecuencia si NO hay sesión; más relajado si ya está conectada
refreshInterval: (data) =>
data?.status === 'Correcto' ? 30_000 : 3_000,
revalidateOnFocus: false,
shouldRetryOnError: true,
}
);
return {
status: data?.status ?? 'Sin session',
phone: data?.phone ?? 'No disponible',
qr: data?.qr ?? null,
loading: isLoading,
error,
refresh: () => mutate(), // botón "Actualizar"
};
}
+103
View File
@@ -0,0 +1,103 @@
// MUI imports
import Box from '@mui/material/Box'
import { styled } from '@mui/material/styles'
import type { BoxProps } from '@mui/material/Box'
const StepperWrapper = styled(Box)<BoxProps>(({ theme }) => {
return {
[theme.breakpoints.down('md')]: {
'& .MuiStepper-horizontal:not(.MuiStepper-alternativeLabel)': {
flexDirection: 'column',
alignItems: 'flex-start'
}
},
'& .MuiStep-root': {
'& .MuiStepLabel-iconContainer:empty': {
display: 'none'
},
'& .step-label': {
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
},
'& .step-number': {
...theme.typography.h4,
marginRight: theme.spacing(2)
},
'&:not(:has(.step-subtitle)) .step-number': {
...theme.typography.h6
},
'& .step-title': {
...theme.typography.body1,
letterSpacing: 0.15,
fontWeight: 500
},
'& .step-subtitle': {
...theme.typography.body2,
color: 'var(--mui-palette-text-secondary)'
},
'& .MuiStepLabel-root.Mui-disabled': {
'& .step-number': {
color: 'var(--mui-palette-text-disabled)'
}
},
'& .Mui-error': {
'& .MuiStepLabel-labelContainer, & .step-number, & .step-title, & .step-subtitle': {
color: 'var(--mui-palette-error-main)'
}
}
},
'& .MuiStepConnector-root': {
'& .MuiStepConnector-line': {
borderBlockStartWidth: 3,
borderRadius: 3
},
'&.Mui-active, &.Mui-completed': {
'& .MuiStepConnector-line': {
borderColor: 'var(--mui-palette-primary-main)'
}
},
'&.Mui-disabled .MuiStepConnector-line': {
borderColor: 'var(--mui-palette-primary-lightOpacity)'
}
},
'& .MuiStepper-alternativeLabel': {
'& .MuiStepConnector-root': {
top: 9
},
'& .MuiStepLabel-labelContainer': {
display: 'flex',
alignItems: 'center',
flexDirection: 'column'
}
},
'& .MuiStepper-vertical': {
'& .MuiStep-root': {
'& .step-label': {
justifyContent: 'flex-start'
},
'& .MuiStepContent-root': {
borderInlineStartWidth: 3,
marginLeft: theme.spacing(2.25),
borderColor: 'var(--mui-palette-primary-main)'
},
'& .button-wrapper': {
marginTop: theme.spacing(4)
},
'&.active + .MuiStepConnector-root .MuiStepConnector-line': {
borderColor: 'var(--mui-palette-primary-main)'
}
},
'& .MuiStepConnector-root': {
marginLeft: theme.spacing(2.25),
'& .MuiStepConnector-line': {
borderBlockStartWidth: 0,
borderInlineStartWidth: 3,
borderRadius: 0
}
}
}
}
})
export default StepperWrapper
@@ -0,0 +1,92 @@
.table {
inline-size: 100%;
border-collapse: collapse;
font-size: 0.875rem;
white-space: nowrap;
[align='right'] > * {
text-align: end;
}
[align='center'] > * {
text-align: center;
}
thead {
text-transform: uppercase;
color: var(--mui-palette-text-primary);
th {
font-weight: 500;
font-size: 0.8125rem;
letter-spacing: 0.2px;
line-height: 1.8462;
text-align: start;
block-size: 56px;
background-color: var(--mui-palette-customColors-tableHeaderBg);
&:not(:first-of-type):not(:last-of-type) {
padding-block: 0.5rem;
padding-inline: 1rem;
}
&:first-of-type {
&:not(:has(input[type='checkbox'])) {
padding-block: 0.5rem;
padding-inline: 1.25rem 1rem;
}
&:has(input[type='checkbox']) {
padding-inline-start: 0.6875rem;
}
}
&:last-of-type {
padding-block: 0.5rem;
padding-inline: 1rem 1.25rem;
}
}
}
tbody {
color: var(--mui-palette-text-secondary);
th,
td {
font-size: 0.9375rem;
line-height: 1.4667;
block-size: 50px;
&:not(:first-of-type):not(:last-of-type) {
padding-block: 0.5rem;
padding-inline: 1rem;
}
&:first-of-type {
&:not(:has(input[type='checkbox'])) {
padding-block: 0.5rem;
padding-inline: 1.25rem 1rem;
}
&:has(input[type='checkbox']) {
padding-inline-start: 0.6875rem;
}
}
&:last-of-type {
padding-block: 0.5rem;
padding-inline: 1rem 1.25rem;
}
}
tr:not(:last-child) {
border-block-end: 1px solid var(--border-color);
}
}
}
.cellWithInput input {
inline-size: 100%;
background-color: transparent;
font-size: inherit;
color: inherit;
border-radius: var(--mui-shape-customBorderRadius-sm);
padding-block: 6px;
padding-inline: 10px;
margin-inline: -10px;
&:focus-visible {
outline: 1px solid var(--mui-palette-primary-main);
}
}
@@ -0,0 +1,99 @@
// MUI Imports
import { lighten } from '@mui/material/styles'
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { MenuItemStyles } from '@menu/types'
// Util Imports
import { menuClasses } from '@menu/utils/menuClasses'
const menuItemStyles = (theme: Theme): MenuItemStyles => {
return {
root: {
marginBlockStart: theme.spacing(1.5),
[`&.${menuClasses.subMenuRoot}.${menuClasses.open} > .${menuClasses.button}, &.${menuClasses.subMenuRoot} > .${menuClasses.button}.${menuClasses.active}`]:
{
backgroundColor: 'var(--mui-palette-action-selected) !important'
},
[`&.${menuClasses.disabled} > .${menuClasses.button}`]: {
color: 'var(--mui-palette-text-disabled)',
[`& .${menuClasses.icon}`]: {
color: 'inherit'
}
},
[`&:not(.${menuClasses.subMenuRoot}) > .${menuClasses.button}.${menuClasses.active}`]: {
color: 'var(--mui-palette-primary-contrastText)',
background:
theme.direction === 'ltr'
? `linear-gradient(270deg, var(--mui-palette-primary-main), ${lighten(
theme.palette.primary.main,
0.5
)} 100%)`
: `linear-gradient(270deg, ${lighten(
theme.palette.primary.main,
0.5
)}, var(--mui-palette-primary-main) 100%)`,
[`& .${menuClasses.icon}`]: {
color: 'inherit'
}
}
},
button: ({ active }) => ({
paddingBlock: theme.spacing(2),
'&:has(.MuiChip-root)': {
paddingBlock: theme.spacing(1.75)
},
paddingInlineStart: theme.spacing(5.5),
paddingInlineEnd: theme.spacing(3.5),
borderStartEndRadius: 50,
borderEndEndRadius: 50,
...(!active && {
'&:hover, &:focus-visible': {
backgroundColor: 'var(--mui-palette-action-hover)'
},
'&[aria-expanded="true"]': {
backgroundColor: 'var(--mui-palette-action-selected)'
}
})
}),
icon: ({ level }) => ({
...(level === 0 && {
fontSize: '1.375rem',
marginInlineEnd: theme.spacing(2)
}),
...(level > 0 && {
fontSize: '0.75rem',
color: 'var(--mui-palette-text-secondary)',
marginInlineEnd: theme.spacing(3.5)
}),
...(level === 1 && {
marginInlineStart: theme.spacing(1.5)
}),
...(level > 1 && {
marginInlineStart: theme.spacing(1.5 + 2.5 * (level - 1))
}),
'& > i, & > svg': {
fontSize: 'inherit'
}
}),
prefix: {
marginInlineEnd: theme.spacing(2)
},
suffix: {
marginInlineStart: theme.spacing(2)
},
subMenuExpandIcon: {
fontSize: '1.375rem',
marginInlineStart: theme.spacing(2),
'& i, & svg': {
fontSize: 'inherit'
}
},
subMenuContent: {
backgroundColor: 'transparent'
}
}
}
export default menuItemStyles
@@ -0,0 +1,42 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { MenuProps } from '@menu/vertical-menu'
// Util Imports
import { menuClasses } from '@menu/utils/menuClasses'
const menuSectionStyles = (theme: Theme): MenuProps['menuSectionStyles'] => {
return {
root: {
marginBlockStart: theme.spacing(7),
[`& .${menuClasses.menuSectionContent}`]: {
color: 'var(--mui-palette-text-disabled)',
paddingInline: '0 !important',
paddingBlock: `${theme.spacing(1.75)} !important`,
gap: theme.spacing(2.5),
'&:before': {
content: '""',
blockSize: 1,
inlineSize: '0.875rem',
backgroundColor: 'var(--mui-palette-divider)'
},
'&:after': {
content: '""',
blockSize: 1,
flexGrow: 1,
backgroundColor: 'var(--mui-palette-divider)'
}
},
[`& .${menuClasses.menuSectionLabel}`]: {
flexGrow: 0,
fontSize: '13px',
lineHeight: 1.38462
}
}
}
}
export default menuSectionStyles
@@ -0,0 +1,35 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Util Imports
import { menuClasses, verticalNavClasses } from '@menu/utils/menuClasses'
const navigationCustomStyles = (theme: Theme) => {
return {
color: 'var(--mui-palette-text-primary)',
zIndex: 'var(--drawer-z-index) !important',
[`& .${verticalNavClasses.bgColorContainer}`]: {
backgroundColor: 'var(--mui-palette-background-default)'
},
[`& .${verticalNavClasses.header}`]: {
paddingBlock: theme.spacing(5),
paddingInline: theme.spacing(5.5, 4)
},
[`& .${verticalNavClasses.container}`]: {
transition: 'none',
borderColor: 'transparent',
[`& .${verticalNavClasses.toggled}`]: {
boxShadow: 'var(--mui-customShadows-lg)'
}
},
[`& .${menuClasses.root}`]: {
paddingBlockEnd: theme.spacing(2),
paddingInlineEnd: theme.spacing(4)
},
[`& .${verticalNavClasses.backdrop}`]: {
backgroundColor: 'var(--backdrop-color)'
}
}
}
export default navigationCustomStyles
+77
View File
@@ -0,0 +1,77 @@
// React Imports
import type { SVGAttributes } from 'react'
const Logo = (props: SVGAttributes<SVGElement>) => {
return (
<svg width='1.2658em' height='1em' viewBox='0 0 100 79' fill='none' xmlns='http://www.w3.org/2000/svg' {...props}>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M4.92008 0.501904L22.662 11.4573C23.614 12.0451 24.1936 13.0844 24.1936 14.2036V64.2521C24.1936 65.3871 23.5976 66.4387 22.6241 67.0214L4.8822 77.6429C3.35344 78.5581 1.37254 78.0602 0.457741 76.5307C0.158194 76.0299 0 75.4572 0 74.8736V3.24818C0 1.46582 1.44424 0.0209274 3.22581 0.0209274C3.82422 0.0209274 4.41085 0.18746 4.92008 0.501904Z'
fill='currentColor'
/>
<path
opacity='0.077704'
fillRule='evenodd'
clipRule='evenodd'
d='M0 26.1063L24.1936 39.9852V53.5915L0 26.1063Z'
fill='black'
/>
<path
opacity='0.077704'
fillRule='evenodd'
clipRule='evenodd'
d='M0 26.1063L24.1936 39.6319V47.9438L0 26.1063Z'
fill='black'
/>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M95.084 0.489601L77.3421 11.4083C76.3878 11.9956 75.8064 13.0362 75.8064 14.1571V64.2526C75.8064 65.3875 76.4024 66.4391 77.3759 67.0219L95.1178 77.6433C96.6466 78.5585 98.6275 78.0606 99.5423 76.5312C99.8418 76.0303 100 75.4576 100 74.874V3.23842C100 1.45605 98.5558 0.0111618 96.7742 0.0111618C96.1774 0.0111618 95.5923 0.176782 95.084 0.489601Z'
fill='currentColor'
/>
<path
opacity='0.077704'
fillRule='evenodd'
clipRule='evenodd'
d='M100 26.1063L75.8064 39.956V54.0023L100 26.1063Z'
fill='black'
/>
<path
opacity='0.077704'
fillRule='evenodd'
clipRule='evenodd'
d='M100 26.1063L75.8064 39.6199V48.3546L100 26.1063Z'
fill='black'
/>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M4.91149 0.475694L50 28.123V54.7479L0 26.0986V3.22726C0 1.44489 1.44424 0 3.22581 0C3.8208 0 4.4042 0.164633 4.91149 0.475694Z'
fill='currentColor'
/>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M4.91149 0.475694L50 28.123V54.7479L0 26.0986V3.22726C0 1.44489 1.44424 0 3.22581 0C3.8208 0 4.4042 0.164633 4.91149 0.475694Z'
fill='white'
fillOpacity='0.15'
/>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M95.0885 0.475694L50 28.123V54.7479L100 26.0986V3.22726C100 1.44489 98.5558 0 96.7742 0C96.1792 0 95.5958 0.164633 95.0885 0.475694Z'
fill='currentColor'
/>
<path
fillRule='evenodd'
clipRule='evenodd'
d='M95.0885 0.475694L50 28.123V54.7479L100 26.0986V3.22726C100 1.44489 98.5558 0 96.7742 0C96.1792 0 95.5958 0.164633 95.0885 0.475694Z'
fill='white'
fillOpacity='0.3'
/>
</svg>
)
}
export default Logo
+74
View File
@@ -0,0 +1,74 @@
import plugin from 'tailwindcss/plugin'
module.exports = plugin(function () {}, {
theme: {
borderColor: ({ theme }) => ({
...theme('colors'),
DEFAULT: 'var(--border-color, currentColor)'
}),
borderRadius: {
none: '0px',
xs: 'var(--mui-shape-customBorderRadius-xs)',
sm: 'var(--mui-shape-customBorderRadius-sm)',
DEFAULT: '0.375rem',
md: 'var(--mui-shape-customBorderRadius-md)',
lg: 'var(--mui-shape-customBorderRadius-lg)',
xl: 'var(--mui-shape-customBorderRadius-xl)',
'2xl': '0.75rem',
'3xl': '1rem',
'4xl': '1.5rem',
full: '9999px'
},
screens: {
sm: '600px',
md: '900px',
lg: '1200px',
xl: '1536px',
'2xl': '1920px'
},
extend: {
boxShadow: {
xs: 'var(--mui-customShadows-xs)',
sm: 'var(--mui-customShadows-sm)',
DEFAULT: 'var(--mui-customShadows-md)',
md: 'var(--mui-customShadows-md)',
lg: 'var(--mui-customShadows-lg)',
xl: 'var(--mui-customShadows-xl)'
},
colors: {
primary: 'var(--primary-color)',
primaryLight: 'var(--mui-palette-primary-lightOpacity)',
primaryLighter: 'var(--mui-palette-primary-lighterOpacity)',
secondary: 'var(--mui-palette-secondary-main)',
error: 'var(--mui-palette-error-main)',
errorLight: 'var(--mui-palette-error-lightOpacity)',
errorLighter: 'var(--mui-palette-error-lighterOpacity)',
warning: 'var(--mui-palette-warning-main)',
info: 'var(--mui-palette-info-main)',
success: 'var(--mui-palette-success-main)',
textPrimary: 'var(--mui-palette-text-primary)',
textSecondary: 'var(--mui-palette-text-secondary)',
textDisabled: 'var(--mui-palette-text-disabled)',
actionActive: 'var(--mui-palette-action-active)',
actionHover: 'var(--mui-palette-action-hover)',
actionSelected: 'var(--mui-palette-action-selected)',
actionFocus: 'var(--mui-palette-action-focus)',
backgroundPaper: 'var(--mui-palette-background-paper)',
backgroundDefault: 'var(--mui-palette-background-default)',
track: 'var(--mui-palette-customColors-trackBg)',
backdrop: 'var(--backdrop-color)',
facebook: '#497ce2',
twitter: '#1da1f2',
github: '#272727',
googlePlus: '#db4437'
},
zIndex: {
header: 'var(--header-z-index)',
footer: 'var(--footer-z-index)',
customizer: 'var(--customizer-z-index)',
search: 'var(--search-z-index)',
drawer: 'var(--drawer-z-index)'
}
}
}
})
+329
View File
@@ -0,0 +1,329 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const colorSchemes = (): Theme['colorSchemes'] => {
const skin = 'default' as string
return {
light: {
palette: {
primary: {
main: '#8C57FF',
light: '#A379FF',
dark: '#7E4EE6',
lighterOpacity: 'rgb(var(--mui-palette-primary-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-primary-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-primary-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-primary-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-primary-mainChannel) / 0.38)'
},
secondary: {
main: '#8A8D93',
light: '#A1A4A9',
dark: '#7C7F84',
contrastText: '#fff',
lighterOpacity: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.38)'
},
error: {
main: '#FF4C51',
light: '#FF7074',
dark: '#E64449',
contrastText: '#fff',
lighterOpacity: 'rgb(var(--mui-palette-error-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-error-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-error-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-error-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-error-mainChannel) / 0.38)'
},
warning: {
main: '#FFB400',
light: '#FFC333',
dark: '#E6A200',
contrastText: '#fff',
lighterOpacity: 'rgb(var(--mui-palette-warning-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-warning-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-warning-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-warning-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-warning-mainChannel) / 0.38)'
},
info: {
main: '#16B1FF',
light: '#45C1FF',
dark: '#149FE6',
contrastText: '#fff',
lighterOpacity: 'rgb(var(--mui-palette-info-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-info-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-info-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-info-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-info-mainChannel) / 0.38)'
},
success: {
main: '#56CA00',
light: '#78D533',
dark: '#4DB600',
contrastText: '#fff',
lighterOpacity: 'rgb(var(--mui-palette-success-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-success-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-success-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-success-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-success-mainChannel) / 0.38)'
},
text: {
primary: `rgb(var(--mui-mainColorChannels-light) / 0.9)`,
secondary: `rgb(var(--mui-mainColorChannels-light) / 0.7)`,
disabled: `rgb(var(--mui-mainColorChannels-light) / 0.4)`,
primaryChannel: 'var(--mui-mainColorChannels-light)',
secondaryChannel: 'var(--mui-mainColorChannels-light)'
},
divider: `rgb(var(--mui-mainColorChannels-light) / 0.12)`,
dividerChannel: 'var(--mui-mainColorChannels-light)',
background: {
default: skin === 'bordered' ? '#FFFFFF' : '#F4F5FA',
paper: '#FFFFFF'
},
action: {
active: `rgb(var(--mui-mainColorChannels-light) / 0.6)`,
hover: `rgb(var(--mui-mainColorChannels-light) / 0.04)`,
selected: `rgb(var(--mui-mainColorChannels-light) / 0.08)`,
disabled: `rgb(var(--mui-mainColorChannels-light) / 0.3)`,
disabledBackground: `rgb(var(--mui-mainColorChannels-light) / 0.12)`,
focus: `rgb(var(--mui-mainColorChannels-light) / 0.1)`,
focusOpacity: 0.1,
activeChannel: 'var(--mui-mainColorChannels-light)',
selectedChannel: 'var(--mui-mainColorChannels-light)'
},
Alert: {
errorColor: 'var(--mui-palette-error-main)',
warningColor: 'var(--mui-palette-warning-main)',
infoColor: 'var(--mui-palette-info-main)',
successColor: 'var(--mui-palette-success-main)',
errorStandardBg: 'var(--mui-palette-error-lightOpacity)',
warningStandardBg: 'var(--mui-palette-warning-lightOpacity)',
infoStandardBg: 'var(--mui-palette-info-lightOpacity)',
successStandardBg: 'var(--mui-palette-success-lightOpacity)',
errorFilledColor: 'var(--mui-palette-error-contrastText)',
warningFilledColor: 'var(--mui-palette-warning-contrastText)',
infoFilledColor: 'var(--mui-palette-info-contrastText)',
successFilledColor: 'var(--mui-palette-success-contrastText)',
errorFilledBg: 'var(--mui-palette-error-main)',
warningFilledBg: 'var(--mui-palette-warning-main)',
infoFilledBg: 'var(--mui-palette-info-main)',
successFilledBg: 'var(--mui-palette-success-main)'
},
Avatar: {
defaultBg: '#F0EFF0'
},
Chip: {
defaultBorder: 'var(--mui-palette-divider)'
},
FilledInput: {
bg: `rgb(var(--mui-mainColorChannels-light) / 0.06)`,
hoverBg: `rgb(var(--mui-mainColorChannels-light) / 0.08)`,
disabledBg: `rgb(var(--mui-mainColorChannels-light) / 0.06)`
},
LinearProgress: {
primaryBg: 'var(--mui-palette-primary-mainOpacity)',
secondaryBg: 'var(--mui-palette-secondary-mainOpacity)',
errorBg: 'var(--mui-palette-error-mainOpacity)',
warningBg: 'var(--mui-palette-warning-mainOpacity)',
infoBg: 'var(--mui-palette-info-mainOpacity)',
successBg: 'var(--mui-palette-success-mainOpacity)'
},
SnackbarContent: {
bg: '#1A0E33',
color: 'var(--mui-palette-background-paper)'
},
Switch: {
defaultColor: 'var(--mui-palette-common-white)',
defaultDisabledColor: 'var(--mui-palette-common-white)',
primaryDisabledColor: 'var(--mui-palette-common-white)',
secondaryDisabledColor: 'var(--mui-palette-common-white)',
errorDisabledColor: 'var(--mui-palette-common-white)',
warningDisabledColor: 'var(--mui-palette-common-white)',
infoDisabledColor: 'var(--mui-palette-common-white)',
successDisabledColor: 'var(--mui-palette-common-white)'
},
Tooltip: {
bg: '#1A0E33'
},
TableCell: {
border: 'var(--mui-palette-divider)'
},
customColors: {
bodyBg: '#F4F5FA',
chatBg: '#F7F6FA',
greyLightBg: '#FAFAFA',
inputBorder: `rgb(var(--mui-mainColorChannels-light) / 0.22)`,
tableHeaderBg: '#F6F7FB',
tooltipText: '#FFFFFF',
trackBg: '#F0F2F8'
}
}
},
dark: {
palette: {
primary: {
main: '#8C57FF',
light: '#A379FF',
dark: '#7E4EE6',
lighterOpacity: 'rgb(var(--mui-palette-primary-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-primary-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-primary-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-primary-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-primary-mainChannel) / 0.38)'
},
secondary: {
main: '#8A8D93',
light: '#A1A4A9',
dark: '#7C7F84',
contrastText: '#fff',
lighterOpacity: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.38)'
},
error: {
main: '#FF4C51',
light: '#FF7074',
dark: '#E64449',
contrastText: '#fff',
lighterOpacity: 'rgb(var(--mui-palette-error-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-error-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-error-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-error-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-error-mainChannel) / 0.38)'
},
warning: {
main: '#FFB400',
light: '#FFC333',
dark: '#E6A200',
contrastText: '#fff',
lighterOpacity: 'rgb(var(--mui-palette-warning-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-warning-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-warning-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-warning-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-warning-mainChannel) / 0.38)'
},
info: {
main: '#16B1FF',
light: '#45C1FF',
dark: '#149FE6',
contrastText: '#fff',
lighterOpacity: 'rgb(var(--mui-palette-info-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-info-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-info-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-info-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-info-mainChannel) / 0.38)'
},
success: {
main: '#56CA00',
light: '#78D533',
dark: '#4DB600',
contrastText: '#fff',
lighterOpacity: 'rgb(var(--mui-palette-success-mainChannel) / 0.08)',
lightOpacity: 'rgb(var(--mui-palette-success-mainChannel) / 0.16)',
mainOpacity: 'rgb(var(--mui-palette-success-mainChannel) / 0.24)',
darkOpacity: 'rgb(var(--mui-palette-success-mainChannel) / 0.32)',
darkerOpacity: 'rgb(var(--mui-palette-success-mainChannel) / 0.38)'
},
text: {
primary: `rgb(var(--mui-mainColorChannels-dark) / 0.9)`,
secondary: `rgb(var(--mui-mainColorChannels-dark) / 0.7)`,
disabled: `rgb(var(--mui-mainColorChannels-dark) / 0.4)`,
primaryChannel: 'var(--mui-mainColorChannels-dark)',
secondaryChannel: 'var(--mui-mainColorChannels-dark)'
},
divider: `rgb(var(--mui-mainColorChannels-dark) / 0.12)`,
dividerChannel: 'var(--mui-mainColorChannels-dark)',
background: {
default: skin === 'bordered' ? '#312D4B' : '#28243D',
paper: '#312D4B'
},
action: {
active: `rgb(var(--mui-mainColorChannels-dark) / 0.6)`,
hover: `rgb(var(--mui-mainColorChannels-dark) / 0.04)`,
selected: `rgb(var(--mui-mainColorChannels-dark) / 0.08)`,
disabled: `rgb(var(--mui-mainColorChannels-dark) / 0.3)`,
disabledBackground: `rgb(var(--mui-mainColorChannels-dark) / 0.12)`,
focus: `rgb(var(--mui-mainColorChannels-dark) / 0.1)`,
focusOpacity: 0.1,
activeChannel: 'var(--mui-mainColorChannels-dark)',
selectedChannel: 'var(--mui-mainColorChannels-dark)'
},
Alert: {
errorColor: 'var(--mui-palette-error-main)',
warningColor: 'var(--mui-palette-warning-main)',
infoColor: 'var(--mui-palette-info-main)',
successColor: 'var(--mui-palette-success-main)',
errorStandardBg: 'var(--mui-palette-error-lightOpacity)',
warningStandardBg: 'var(--mui-palette-warning-lightOpacity)',
infoStandardBg: 'var(--mui-palette-info-lightOpacity)',
successStandardBg: 'var(--mui-palette-success-lightOpacity)',
errorFilledColor: 'var(--mui-palette-error-contrastText)',
warningFilledColor: 'var(--mui-palette-warning-contrastText)',
infoFilledColor: 'var(--mui-palette-info-contrastText)',
successFilledColor: 'var(--mui-palette-success-contrastText)',
errorFilledBg: 'var(--mui-palette-error-main)',
warningFilledBg: 'var(--mui-palette-warning-main)',
infoFilledBg: 'var(--mui-palette-info-main)',
successFilledBg: 'var(--mui-palette-success-main)'
},
Avatar: {
defaultBg: '#3F3B59'
},
Chip: {
defaultBorder: 'var(--mui-palette-divider)'
},
FilledInput: {
bg: `rgb(var(--mui-mainColorChannels-dark) / 0.06)`,
hoverBg: `rgb(var(--mui-mainColorChannels-dark) / 0.08)`,
disabledBg: `rgb(var(--mui-mainColorChannels-dark) / 0.06)`
},
LinearProgress: {
primaryBg: 'var(--mui-palette-primary-mainOpacity)',
secondaryBg: 'var(--mui-palette-secondary-mainOpacity)',
errorBg: 'var(--mui-palette-error-mainOpacity)',
warningBg: 'var(--mui-palette-warning-mainOpacity)',
infoBg: 'var(--mui-palette-info-mainOpacity)',
successBg: 'var(--mui-palette-success-mainOpacity)'
},
SnackbarContent: {
bg: '#F7F4FF',
color: 'var(--mui-palette-background-paper)'
},
Switch: {
defaultColor: 'var(--mui-palette-common-white)',
defaultDisabledColor: 'var(--mui-palette-common-white)',
primaryDisabledColor: 'var(--mui-palette-common-white)',
secondaryDisabledColor: 'var(--mui-palette-common-white)',
errorDisabledColor: 'var(--mui-palette-common-white)',
warningDisabledColor: 'var(--mui-palette-common-white)',
infoDisabledColor: 'var(--mui-palette-common-white)',
successDisabledColor: 'var(--mui-palette-common-white)'
},
Tooltip: {
bg: '#F7F4FF'
},
TableCell: {
border: 'var(--mui-palette-divider)'
},
customColors: {
bodyBg: '#28243D',
chatBg: '#373452',
greyLightBg: '#373350',
inputBorder: `rgb(var(--mui-mainColorChannels-dark) / 0.22)`,
tableHeaderBg: '#3D3759',
tooltipText: '#312D4B',
trackBg: '#474360'
}
}
}
} as Theme['colorSchemes']
}
export default colorSchemes
@@ -0,0 +1,17 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { SystemMode } from '@core/types'
const customShadows = (mode: SystemMode): Theme['customShadows'] => {
return {
xs: `0px 2px 4px rgb(var(--mui-mainColorChannels-${mode}Shadow) / ${mode === 'light' ? 0.16 : 0.2})`,
sm: `0px 3px 6px rgb(var(--mui-mainColorChannels-${mode}Shadow) / ${mode === 'light' ? 0.18 : 0.22})`,
md: `0px 4px 10px rgb(var(--mui-mainColorChannels-${mode}Shadow) / ${mode === 'light' ? 0.2 : 0.24})`,
lg: `0px 6px 16px rgb(var(--mui-mainColorChannels-${mode}Shadow) / ${mode === 'light' ? 0.22 : 0.26})`,
xl: `0px 8px 28px rgb(var(--mui-mainColorChannels-${mode}Shadow) / ${mode === 'light' ? 0.24 : 0.28})`
}
}
export default customShadows
+48
View File
@@ -0,0 +1,48 @@
// Next Imports
import { Inter } from 'next/font/google'
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { SystemMode } from '@core/types'
// Theme Options Imports
import overrides from './overrides'
import colorSchemes from './colorSchemes'
import spacing from './spacing'
import shadows from './shadows'
import customShadows from './customShadows'
import typography from './typography'
const inter = Inter({ subsets: ['latin'], weight: ['300', '400', '500', '600', '700', '800', '900'] })
const theme = (mode: SystemMode, direction: Theme['direction']): Theme => {
return {
direction,
components: overrides(),
colorSchemes: colorSchemes(),
...spacing,
shape: {
borderRadius: 6,
customBorderRadius: {
xs: 2,
sm: 4,
md: 6,
lg: 8,
xl: 10
}
},
shadows: shadows(mode),
typography: typography(inter.style.fontFamily),
customShadows: customShadows(mode),
mainColorChannels: {
light: '46 38 61',
dark: '231 227 252',
lightShadow: '46 38 61',
darkShadow: '19 17 32'
}
} as Theme
}
export default theme
@@ -0,0 +1,90 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { Skin } from '@core/types'
const accordion = (skin: Skin): Theme['components'] => ({
MuiAccordion: {
defaultProps: {
...(skin === 'bordered' && {
variant: 'outlined'
})
},
styleOverrides: {
root: ({ theme }) => ({
transition: theme.transitions.create(['margin', 'border-radius', 'box-shadow']),
...(skin !== 'bordered'
? {
boxShadow: 'var(--mui-customShadows-xs)'
}
: {
'&:not(.Mui-expanded) + &:not(.Mui-expanded)': {
borderBlockStart: 0
},
'&:not(.Mui-expanded):has(+ &:not(.Mui-expanded))': {
borderBlockEnd: 0
}
}),
'&:not(.Mui-expanded):has(+ .Mui-expanded)': {
borderBottomLeftRadius: 'var(--mui-shape-borderRadius)',
borderBottomRightRadius: 'var(--mui-shape-borderRadius)'
},
'&.Mui-expanded': {
borderRadius: 'var(--mui-shape-borderRadius)',
...(skin !== 'bordered' && {
boxShadow: 'var(--mui-customShadows-md)'
}),
margin: theme.spacing(2, 0),
'& + .MuiAccordion-root': {
borderTopLeftRadius: 'var(--mui-shape-borderRadius)',
borderTopRightRadius: 'var(--mui-shape-borderRadius)',
'&:before': {
opacity: 0
}
}
}
})
}
},
MuiAccordionSummary: {
defaultProps: {
expandIcon: <i className='ri-arrow-down-s-line' />
},
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(3, 5),
color: 'var(--mui-palette-text-primary)',
'&.Mui-expanded': {
minHeight: 48
},
'& .MuiTypography-root': {
color: 'inherit',
fontWeight: theme.typography.fontWeightMedium
}
}),
content: {
margin: '0 !important'
},
expandIconWrapper: {
color: 'var(--mui-palette-text-primary)',
fontSize: '1.25rem',
'& i, & svg': {
fontSize: 'inherit'
}
}
}
},
MuiAccordionDetails: {
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(0, 5, 5),
'& .MuiTypography-root': {
color: 'var(--mui-palette-text-secondary)'
}
})
}
}
})
export default accordion
@@ -0,0 +1,179 @@
// React Imports
import React from 'react'
// MUI Imports
import type { Theme } from '@mui/material/styles'
const alerts: Theme['components'] = {
MuiAlert: {
defaultProps: {
iconMapping: {
error: <i className='ri-error-warning-line' />,
warning: <i className='ri-alert-line' />,
info: <i className='ri-information-line' />,
success: <i className='ri-checkbox-circle-line' />
}
},
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(4),
gap: theme.spacing(4),
...theme.typography.body1,
'&:not(:has(.MuiAlertTitle-root))': {
'& .MuiAlert-icon + .MuiAlert-message': {
alignSelf: 'center'
}
}
}),
icon: {
padding: 0,
margin: 0,
minWidth: 30,
height: 30,
borderRadius: 'var(--mui-shape-borderRadius)',
alignItems: 'center',
justifyContent: 'center',
'& i, & svg': {
fontSize: 'inherit'
}
},
message: {
padding: 0
},
action: {
padding: 0,
marginRight: 0
}
},
variants: [
{
props: { variant: 'standard', severity: 'error' },
style: {
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-error-main)',
color: 'var(--mui-palette-error-contrastText)'
}
}
},
{
props: { variant: 'standard', severity: 'warning' },
style: {
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-warning-main)',
color: 'var(--mui-palette-warning-contrastText)'
}
}
},
{
props: { variant: 'standard', severity: 'info' },
style: {
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-info-main)',
color: 'var(--mui-palette-info-contrastText)'
}
}
},
{
props: { variant: 'standard', severity: 'success' },
style: {
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-success-main)',
color: 'var(--mui-palette-success-contrastText)'
}
}
},
{
props: { variant: 'outlined', severity: 'error' },
style: {
borderColor: 'var(--mui-palette-error-main)',
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-error-mainOpacity)',
color: 'var(--mui-palette-error-main)'
}
}
},
{
props: { variant: 'outlined', severity: 'warning' },
style: {
borderColor: 'var(--mui-palette-warning-main)',
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-warning-mainOpacity)',
color: 'var(--mui-palette-warning-main)'
}
}
},
{
props: { variant: 'outlined', severity: 'info' },
style: {
borderColor: 'var(--mui-palette-info-main)',
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-info-mainOpacity)',
color: 'var(--mui-palette-info-main)'
}
}
},
{
props: { variant: 'outlined', severity: 'success' },
style: {
borderColor: 'var(--mui-palette-success-main)',
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-success-mainOpacity)',
color: 'var(--mui-palette-success-main)'
}
}
},
{
props: { variant: 'filled', severity: 'error' },
style: {
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-common-white)',
color: 'var(--mui-palette-error-main)',
boxShadow: 'var(--mui-customShadows-xs)'
}
}
},
{
props: { variant: 'filled', severity: 'warning' },
style: {
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-common-white)',
color: 'var(--mui-palette-warning-main)',
boxShadow: 'var(--mui-customShadows-xs)'
}
}
},
{
props: { variant: 'filled', severity: 'info' },
style: {
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-common-white)',
color: 'var(--mui-palette-info-main)',
boxShadow: 'var(--mui-customShadows-xs)'
}
}
},
{
props: { variant: 'filled', severity: 'success' },
style: {
'& .MuiAlert-icon': {
backgroundColor: 'var(--mui-palette-common-white)',
color: 'var(--mui-palette-success-main)',
boxShadow: 'var(--mui-customShadows-xs)'
}
}
}
]
},
MuiAlertTitle: {
styleOverrides: {
root: ({ theme }) => ({
...theme.typography.h5,
marginTop: 0,
marginBottom: theme.spacing(1),
color: 'inherit'
})
}
}
}
export default alerts
@@ -0,0 +1,76 @@
// React Imports
import React from 'react'
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { Skin } from '@core/types'
const autocomplete = (skin: Skin): Theme['components'] => ({
MuiAutocomplete: {
defaultProps: {
...(skin === 'bordered' && {
slotProps: {
paper: {
variant: 'outlined'
}
}
}),
ChipProps: {
size: 'small'
},
popupIcon: <i className='ri-arrow-down-s-line' />
},
styleOverrides: {
root: {
'& .MuiButtonBase-root.Mui-disabled i, & .MuiButtonBase-root.Mui-disabled svg': {
color: 'var(--mui-palette-action-disabled)'
},
'& .MuiOutlinedInput-input': {
height: '1.4375em'
}
},
input: {
'& + .MuiAutocomplete-endAdornment': {
right: '1rem',
'& i, & svg': {
fontSize: '1.5rem',
color: 'var(--mui-palette-text-primary)'
},
'& .MuiAutocomplete-clearIndicator': {
padding: 2
}
},
'&.MuiInputBase-inputSizeSmall + .MuiAutocomplete-endAdornment': {
'& i, & svg': {
fontSize: '1.375rem'
}
}
},
paper: {
...(skin !== 'bordered' && {
boxShadow: 'var(--mui-customShadows-lg)',
marginBlockStart: '0.125rem'
})
},
listbox: ({ theme }) => ({
'& .MuiAutocomplete-option': {
padding: theme.spacing(2, 5),
'&[aria-selected="true"]': {
backgroundColor: 'var(--mui-palette-primary-lightOpacity)',
color: 'var(--mui-palette-primary-main)',
'&.Mui-focused, &.Mui-focusVisible': {
backgroundColor: 'var(--mui-palette-primary-mainOpacity)'
}
}
},
'& .MuiAutocomplete-option.Mui-focusVisible': {
backgroundColor: 'var(--mui-palette-action-hover)'
}
})
}
}
})
export default autocomplete
@@ -0,0 +1,38 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const avatar: Theme['components'] = {
MuiAvatarGroup: {
styleOverrides: {
root: ({ theme }) => ({
justifyContent: 'flex-end',
'& .MuiAvatar-root': {
borderColor: 'var(--mui-palette-background-paper)'
},
'&.pull-up .MuiAvatar-root': {
cursor: 'pointer',
transition: theme.transitions.create(['box-shadow', 'transform'], {
easing: 'ease',
duration: theme.transitions.duration.shorter
}),
'&:hover': {
zIndex: 2,
boxShadow: 'var(--mui-customShadows-md)',
transform: 'translateY(-5px)'
}
}
})
}
},
MuiAvatar: {
styleOverrides: {
root: ({ theme }) => ({
color: 'var(--mui-palette-text-primary)',
fontSize: theme.typography.body1.fontSize,
lineHeight: 1.2
})
}
}
}
export default avatar
@@ -0,0 +1,16 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const backdrop: Theme['components'] = {
MuiBackdrop: {
styleOverrides: {
root: {
'&:not(.MuiBackdrop-invisible)': {
backgroundColor: 'var(--backdrop-color)'
}
}
}
}
}
export default backdrop
@@ -0,0 +1,19 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const badges: Theme['components'] = {
MuiBadge: {
styleOverrides: {
standard: ({ theme }) => ({
height: 22,
minWidth: 22,
borderRadius: 20,
fontSize: theme.typography.subtitle2.fontSize,
lineHeight: 1.07,
padding: theme.spacing(1, 2)
})
}
}
}
export default badges
@@ -0,0 +1,29 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const breadcrumbs: Theme['components'] = {
MuiBreadcrumbs: {
styleOverrides: {
root: {
'& svg, & i': {
fontSize: '1.25rem'
},
'& a': {
textDecoration: 'none',
color: 'var(--mui-palette-text-secondary)',
'&:hover': {
color: 'var(--mui-palette-text-primary)'
}
}
},
li: ({ theme }) => ({
lineHeight: theme.typography.body1.lineHeight,
'& > *:not(a)': {
color: 'var(--mui-palette-text-primary)'
}
})
}
}
}
export default breadcrumbs
@@ -0,0 +1,73 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Config Imports
import themeConfig from '@configs/themeConfig'
const buttonGroup: Theme['components'] = {
MuiButtonGroup: {
defaultProps: {
disableRipple: themeConfig.disableRipple
},
styleOverrides: {
contained: ({ ownerState }) => ({
boxShadow: 'var(--mui-customShadows-xs)',
...(ownerState.disabled && {
boxShadow: 'none'
})
})
},
variants: [
{
props: { variant: 'text', color: 'primary' },
style: {
'& .MuiButtonGroup-firstButton, & .MuiButtonGroup-middleButton': {
borderColor: 'var(--mui-palette-primary-main)'
}
}
},
{
props: { variant: 'text', color: 'secondary' },
style: {
'& .MuiButtonGroup-firstButton, & .MuiButtonGroup-middleButton': {
borderColor: 'var(--mui-palette-secondary-main)'
}
}
},
{
props: { variant: 'text', color: 'error' },
style: {
'& .MuiButtonGroup-firstButton, & .MuiButtonGroup-middleButton': {
borderColor: 'var(--mui-palette-error-main)'
}
}
},
{
props: { variant: 'text', color: 'warning' },
style: {
'& .MuiButtonGroup-firstButton, & .MuiButtonGroup-middleButton': {
borderColor: 'var(--mui-palette-warning-main)'
}
}
},
{
props: { variant: 'text', color: 'info' },
style: {
'& .MuiButtonGroup-firstButton, & .MuiButtonGroup-middleButton': {
borderColor: 'var(--mui-palette-info-main)'
}
}
},
{
props: { variant: 'text', color: 'success' },
style: {
'& .MuiButtonGroup-firstButton, & .MuiButtonGroup-middleButton': {
borderColor: 'var(--mui-palette-success-main)'
}
}
}
]
}
}
export default buttonGroup
@@ -0,0 +1,377 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Config Imports
import themeConfig from '@configs/themeConfig'
const iconStyles = (size?: string) => ({
'& > *:nth-of-type(1)': {
...(size === 'small'
? {
fontSize: '14px'
}
: {
...(size === 'medium'
? {
fontSize: '16px'
}
: {
fontSize: '20px'
})
})
}
})
const button: Theme['components'] = {
MuiButtonBase: {
defaultProps: {
disableRipple: themeConfig.disableRipple
}
},
MuiButton: {
styleOverrides: {
root: ({ theme, ownerState }) => ({
...(ownerState.variant === 'text'
? {
...(ownerState.size === 'small' && {
padding: theme.spacing(2, 2.5)
}),
...(ownerState.size === 'medium' && {
padding: theme.spacing(2, 3.5)
}),
...(ownerState.size === 'large' && {
padding: theme.spacing(2, 4.5)
})
}
: {
...(ownerState.variant === 'outlined'
? {
...(ownerState.size === 'small' && {
padding: theme.spacing(1.75, 3.25)
}),
...(ownerState.size === 'medium' && {
padding: theme.spacing(1.75, 4.25)
}),
...(ownerState.size === 'large' && {
padding: theme.spacing(1.75, 5.25)
})
}
: {
...(ownerState.size === 'small' && {
padding: theme.spacing(2, 3.5)
}),
...(ownerState.size === 'medium' && {
padding: theme.spacing(2, 4.5)
}),
...(ownerState.size === 'large' && {
padding: theme.spacing(2, 5.5)
})
})
})
}),
contained: ({ ownerState }) => ({
boxShadow: 'var(--mui-customShadows-xs)',
...(!ownerState.disabled && {
'&:hover, &.Mui-focusVisible': {
boxShadow: 'var(--mui-customShadows-xs)'
},
'&:active': {
boxShadow: 'none'
}
})
}),
sizeSmall: ({ theme }) => ({
lineHeight: 1.38462,
fontSize: theme.typography.body2.fontSize,
borderRadius: 'var(--mui-shape-customBorderRadius-sm)'
}),
sizeLarge: {
fontSize: '1.0625rem',
lineHeight: 1.529412,
borderRadius: 'var(--mui-shape-customBorderRadius-lg)'
},
startIcon: ({ theme, ownerState }) => ({
...(ownerState.size === 'small'
? {
marginInlineEnd: theme.spacing(1.5)
}
: {
...(ownerState.size === 'medium'
? {
marginInlineEnd: theme.spacing(2)
}
: {
marginInlineEnd: theme.spacing(2.5)
})
}),
...iconStyles(ownerState.size)
}),
endIcon: ({ theme, ownerState }) => ({
...(ownerState.size === 'small'
? {
marginInlineStart: theme.spacing(1.5)
}
: {
...(ownerState.size === 'medium'
? {
marginInlineStart: theme.spacing(2)
}
: {
marginInlineStart: theme.spacing(2.5)
})
}),
...iconStyles(ownerState.size)
})
},
variants: [
{
props: { variant: 'text', color: 'primary' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-primary-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-primary-main)'
}
}
},
{
props: { variant: 'text', color: 'secondary' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-secondary-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-secondary-main)'
}
}
},
{
props: { variant: 'text', color: 'error' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-error-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-error-main)'
}
}
},
{
props: { variant: 'text', color: 'warning' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-warning-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-warning-main)'
}
}
},
{
props: { variant: 'text', color: 'info' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-info-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-info-main)'
}
}
},
{
props: { variant: 'text', color: 'success' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-success-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-success-main)'
}
}
},
{
props: { variant: 'outlined', color: 'primary' },
style: {
borderColor: 'var(--mui-palette-primary-main)',
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-primary-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-primary-main)',
borderColor: 'var(--mui-palette-primary-main)'
}
}
},
{
props: { variant: 'outlined', color: 'secondary' },
style: {
borderColor: 'var(--mui-palette-secondary-main)',
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-secondary-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-secondary-main)',
borderColor: 'var(--mui-palette-secondary-main)'
}
}
},
{
props: { variant: 'outlined', color: 'error' },
style: {
borderColor: 'var(--mui-palette-error-main)',
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-error-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-error-main)',
borderColor: 'var(--mui-palette-error-main)'
}
}
},
{
props: { variant: 'outlined', color: 'warning' },
style: {
borderColor: 'var(--mui-palette-warning-main)',
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-warning-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-warning-main)',
borderColor: 'var(--mui-palette-warning-main)'
}
}
},
{
props: { variant: 'outlined', color: 'info' },
style: {
borderColor: 'var(--mui-palette-info-main)',
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-info-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-info-main)',
borderColor: 'var(--mui-palette-info-main)'
}
}
},
{
props: { variant: 'outlined', color: 'success' },
style: {
borderColor: 'var(--mui-palette-success-main)',
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))':
{
backgroundColor: 'var(--mui-palette-success-lighterOpacity)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-success-main)',
borderColor: 'var(--mui-palette-success-main)'
}
}
},
{
props: { variant: 'contained', color: 'primary' },
style: {
'&:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-primary-dark)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-primary-contrastText)',
backgroundColor: 'var(--mui-palette-primary-main)'
}
}
},
{
props: { variant: 'contained', color: 'secondary' },
style: {
'&:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-secondary-dark)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-secondary-contrastText)',
backgroundColor: 'var(--mui-palette-secondary-main)'
}
}
},
{
props: { variant: 'contained', color: 'error' },
style: {
'&:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-error-dark)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-error-contrastText)',
backgroundColor: 'var(--mui-palette-error-main)'
}
}
},
{
props: { variant: 'contained', color: 'warning' },
style: {
'&:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-warning-dark)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-warning-contrastText)',
backgroundColor: 'var(--mui-palette-warning-main)'
}
}
},
{
props: { variant: 'contained', color: 'info' },
style: {
'&:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-info-dark)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-info-contrastText)',
backgroundColor: 'var(--mui-palette-info-main)'
}
}
},
{
props: { variant: 'contained', color: 'success' },
style: {
'&:not(.Mui-disabled):active, &.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-success-dark)'
},
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-success-contrastText)',
backgroundColor: 'var(--mui-palette-success-main)'
}
}
}
]
}
}
export default button
@@ -0,0 +1,91 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { Skin } from '@core/types'
const card = (skin: Skin): Theme['components'] => {
return {
MuiCard: {
defaultProps: {
...(skin === 'bordered' && {
variant: 'outlined'
})
},
styleOverrides: {
root: ({ ownerState }) => ({
...(ownerState.variant !== 'outlined' && {
boxShadow: 'var(--mui-customShadows-md)'
})
})
}
},
MuiCardHeader: {
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(5),
'& + .MuiCardContent-root, & + .MuiCardActions-root': {
paddingBlockStart: 0
},
'& + .MuiCollapse-root .MuiCardContent-root:first-child, & + .MuiCollapse-root .MuiCardActions-root:first-child':
{
paddingBlockStart: 0
}
}),
subheader: ({ theme }) => ({
...theme.typography.subtitle1,
color: 'rgb(var(--mui-palette-text-primaryChannel) / 0.55)'
}),
action: ({ theme }) => ({
...theme.typography.body1,
color: 'var(--mui-palette-text-disabled)',
marginBlock: 0,
marginInlineEnd: 0,
'& .MuiIconButton-root': {
color: 'inherit'
}
})
}
},
MuiCardContent: {
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(5),
color: 'var(--mui-palette-text-secondary)',
'&:last-child': {
paddingBlockEnd: theme.spacing(5)
},
'& + .MuiCardHeader-root, & + .MuiCardContent-root, & + .MuiCardActions-root': {
paddingBlockStart: 0
},
'& + .MuiCollapse-root .MuiCardHeader-root:first-child, & + .MuiCollapse-root .MuiCardContent-root:first-child, & + .MuiCollapse-root .MuiCardActions-root:first-child':
{
paddingBlockStart: 0
}
})
}
},
MuiCardActions: {
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(5),
'&:where(.card-actions-dense)': {
padding: theme.spacing(2.5),
'& .MuiButton-text': {
paddingInline: theme.spacing(2.5)
}
},
'& + .MuiCardHeader-root, & + .MuiCardContent-root, & + .MuiCardActions-root': {
paddingBlockStart: 0
},
'& + .MuiCollapse-root .MuiCardHeader-root:first-child, & + .MuiCollapse-root .MuiCardContent-root:first-child, & + .MuiCollapse-root .MuiCardActions-root:first-child':
{
paddingBlockStart: 0
}
})
}
}
}
}
export default card
@@ -0,0 +1,94 @@
// React Imports
import React from 'react'
// MUI Imports
import type { Theme } from '@mui/material/styles'
const Icon = () => {
return (
<svg width='1em' height='1em' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d='M8 4h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4Z'
stroke='var(--mui-palette-text-secondary)'
strokeWidth='2'
/>
</svg>
)
}
const IndeterminateIcon = () => {
return (
<svg width='1em' height='1em' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M3 8a5 5 0 0 1 5-5h8a5 5 0 0 1 5 5v8a5 5 0 0 1-5 5H8a5 5 0 0 1-5-5V8Z' fill='currentColor' />
<path d='M8.5 11.5h7v1h-7v-1Z' fill='var(--mui-palette-common-white)' />
</svg>
)
}
const CheckedIcon = () => {
return (
<svg width='1em' height='1em' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M3 8a5 5 0 0 1 5-5h8a5 5 0 0 1 5 5v8a5 5 0 0 1-5 5H8a5 5 0 0 1-5-5V8Z' fill='currentColor' />
<path
d='m11 13.586 4.596-4.597.707.707L11 15l-3.182-3.182.707-.707L11 13.586Z'
fill='var(--mui-palette-common-white)'
/>
</svg>
)
}
const checkbox: Theme['components'] = {
MuiCheckbox: {
defaultProps: {
icon: <Icon />,
indeterminateIcon: <IndeterminateIcon />,
checkedIcon: <CheckedIcon />
},
styleOverrides: {
root: ({ theme, ownerState }) => ({
...(ownerState.size === 'small'
? {
padding: theme.spacing(1),
'& svg': {
fontSize: '1.25rem'
}
}
: {
padding: theme.spacing(1.5),
'& svg': {
fontSize: '1.5rem'
}
}),
'&.Mui-checked:not(.Mui-disabled) svg': {
filter: 'drop-shadow(var(--mui-customShadows-xs))'
},
'&.Mui-disabled': {
opacity: 0.45,
'&:not(.Mui-checked)': {
color: 'var(--mui-palette-text-secondary)'
},
'&.Mui-checked.MuiCheckbox-colorPrimary': {
color: 'var(--mui-palette-primary-main)'
},
'&.Mui-checked.MuiCheckbox-colorSecondary': {
color: 'var(--mui-palette-secondary-main)'
},
'&.Mui-checked.MuiCheckbox-colorError': {
color: 'var(--mui-palette-error-main)'
},
'&.Mui-checked.MuiCheckbox-colorWarning': {
color: 'var(--mui-palette-warning-main)'
},
'&.Mui-checked.MuiCheckbox-colorInfo': {
color: 'var(--mui-palette-info-main)'
},
'&.Mui-checked.MuiCheckbox-colorSuccess': {
color: 'var(--mui-palette-success-main)'
}
}
})
}
}
}
export default checkbox
@@ -0,0 +1,190 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const chip: Theme['components'] = {
MuiChip: {
variants: [
{
props: { variant: 'tonal', color: 'primary' },
style: {
backgroundColor: 'var(--mui-palette-primary-lightOpacity)',
color: 'var(--mui-palette-primary-main)',
'&.Mui-focusVisible': {
backgroundColor: 'var(--mui-palette-primary-mainOpacity)'
},
'& .MuiChip-deleteIcon': {
color: 'rgb(var(--mui-palette-primary-mainChannel) / 0.7)',
'&:hover': {
color: 'var(--mui-palette-primary-main)'
}
},
'&.MuiChip-clickable:hover': {
backgroundColor: 'var(--mui-palette-primary-main)',
color: 'var(--mui-palette-common-white)'
}
}
},
{
props: { variant: 'tonal', color: 'secondary' },
style: {
backgroundColor: 'var(--mui-palette-secondary-lightOpacity)',
color: 'var(--mui-palette-secondary-main)',
'&.Mui-focusVisible': {
backgroundColor: 'var(--mui-palette-secondary-mainOpacity)'
},
'& .MuiChip-deleteIcon': {
color: 'rgb(var(--mui-palette-secondary-mainChannel) / 0.7)',
'&:hover': {
color: 'var(--mui-palette-secondary-main)'
}
},
'&.MuiChip-clickable:hover': {
backgroundColor: 'var(--mui-palette-secondary-main)',
color: 'var(--mui-palette-common-white)'
}
}
},
{
props: { variant: 'tonal', color: 'error' },
style: {
backgroundColor: 'var(--mui-palette-error-lightOpacity)',
color: 'var(--mui-palette-error-main)',
'&.Mui-focusVisible': {
backgroundColor: 'var(--mui-palette-error-mainOpacity)'
},
'& .MuiChip-deleteIcon': {
color: 'rgb(var(--mui-palette-error-mainChannel) / 0.7)',
'&:hover': {
color: 'var(--mui-palette-error-main)'
}
},
'&.MuiChip-clickable:hover': {
backgroundColor: 'var(--mui-palette-error-main)',
color: 'var(--mui-palette-common-white)'
}
}
},
{
props: { variant: 'tonal', color: 'warning' },
style: {
backgroundColor: 'var(--mui-palette-warning-lightOpacity)',
color: 'var(--mui-palette-warning-main)',
'&.Mui-focusVisible': {
backgroundColor: 'var(--mui-palette-warning-mainOpacity)'
},
'& .MuiChip-deleteIcon': {
color: 'rgb(var(--mui-palette-warning-mainChannel) / 0.7)',
'&:hover': {
color: 'var(--mui-palette-warning-main)'
}
},
'&.MuiChip-clickable:hover': {
backgroundColor: 'var(--mui-palette-warning-main)',
color: 'var(--mui-palette-common-white)'
}
}
},
{
props: { variant: 'tonal', color: 'info' },
style: {
backgroundColor: 'var(--mui-palette-info-lightOpacity)',
color: 'var(--mui-palette-info-main)',
'&.Mui-focusVisible': {
backgroundColor: 'var(--mui-palette-info-mainOpacity)'
},
'& .MuiChip-deleteIcon': {
color: 'rgb(var(--mui-palette-info-mainChannel) / 0.7)',
'&:hover': {
color: 'var(--mui-palette-info-main)'
}
},
'&.MuiChip-clickable:hover': {
backgroundColor: 'var(--mui-palette-info-main)',
color: 'var(--mui-palette-common-white)'
}
}
},
{
props: { variant: 'tonal', color: 'success' },
style: {
backgroundColor: 'var(--mui-palette-success-lightOpacity)',
color: 'var(--mui-palette-success-main)',
'&.Mui-focusVisible': {
backgroundColor: 'var(--mui-palette-success-mainOpacity)'
},
'& .MuiChip-deleteIcon': {
color: 'rgb(var(--mui-palette-success-mainChannel) / 0.7)',
'&:hover': {
color: 'var(--mui-palette-success-main)'
}
},
'&.MuiChip-clickable:hover': {
backgroundColor: 'var(--mui-palette-success-main)',
color: 'var(--mui-palette-common-white)'
}
}
}
],
styleOverrides: {
root: ({ ownerState, theme }) => ({
...theme.typography.body2,
fontWeight: theme.typography.fontWeightMedium,
'& .MuiChip-deleteIcon': {
...(ownerState.size === 'small'
? {
fontSize: '1rem',
marginInlineEnd: theme.spacing(1),
marginInlineStart: theme.spacing(-2)
}
: {
fontSize: '1.25rem',
marginInlineEnd: theme.spacing(2),
marginInlineStart: theme.spacing(-3)
})
},
'& .MuiChip-avatar, & .MuiChip-icon': {
'& i, & svg': {
...(ownerState.size === 'small'
? {
fontSize: 13
}
: {
fontSize: 15
})
},
...(ownerState.size === 'small'
? {
height: 16,
width: 16,
marginInlineStart: theme.spacing(1),
marginInlineEnd: theme.spacing(-2)
}
: {
height: 20,
width: 20,
marginInlineStart: theme.spacing(2),
marginInlineEnd: theme.spacing(-3)
})
}
}),
label: ({ ownerState, theme }) => ({
...(ownerState.size === 'small'
? {
paddingInline: theme.spacing(3)
}
: {
paddingInline: theme.spacing(4)
})
}),
iconMedium: {
fontSize: '1.25rem'
},
iconSmall: {
fontSize: '1rem'
}
}
}
}
export default chip
@@ -0,0 +1,67 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { Skin } from '@core/types'
const dialog = (skin: Skin): Theme['components'] => ({
MuiDialog: {
styleOverrides: {
paper: ({ theme }) => ({
...(skin !== 'bordered'
? {
boxShadow: 'var(--mui-customShadows-xl)'
}
: {
boxShadow: 'none'
}),
[theme.breakpoints.down('sm')]: {
'&:not(.MuiDialog-paperFullScreen)': {
margin: theme.spacing(6)
}
}
})
}
},
MuiDialogTitle: {
defaultProps: {
variant: 'h5'
},
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(5),
'& + .MuiDialogActions-root': {
paddingTop: 0
}
})
}
},
MuiDialogContent: {
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(5),
'& + .MuiDialogContent-root, & + .MuiDialogActions-root': {
paddingTop: 0
}
})
}
},
MuiDialogActions: {
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(5),
'& .MuiButtonBase-root:not(:first-of-type)': {
marginInlineStart: theme.spacing(4)
},
'&:where(.dialog-actions-dense)': {
padding: theme.spacing(2.5),
'& .MuiButton-text': {
paddingInline: theme.spacing(2.5)
}
}
})
}
}
})
export default dialog
@@ -0,0 +1,26 @@
// MUI Imports
import type { Theme } from '@mui/material'
// Type Imports
import type { Skin } from '@core/types'
const drawer = (skin: Skin): Theme['components'] => ({
MuiDrawer: {
defaultProps: {
...(skin === 'bordered' && {
PaperProps: {
elevation: 0
}
})
},
styleOverrides: {
paper: {
...(skin !== 'bordered' && {
boxShadow: 'var(--mui-customShadows-lg)'
})
}
}
}
})
export default drawer
@@ -0,0 +1,68 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const fab: Theme['components'] = {
MuiFab: {
variants: [
{
props: { color: 'default' },
style: {
color: 'rgb(var(--mui-mainColorChannels-light) / 0.9)',
'&.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-grey-A100)'
}
}
},
{
props: { color: 'primary' },
style: {
'&.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-primary-dark)'
}
}
},
{
props: { color: 'secondary' },
style: {
'&.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-secondary-dark)'
}
}
},
{
props: { color: 'error' },
style: {
'&.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-error-dark)'
}
}
},
{
props: { color: 'warning' },
style: {
'&.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-warning-dark)'
}
}
},
{
props: { color: 'info' },
style: {
'&.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-info-dark)'
}
}
},
{
props: { color: 'success' },
style: {
'&.Mui-focusVisible:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'var(--mui-palette-success-dark)'
}
}
}
]
}
}
export default fab
@@ -0,0 +1,22 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const formControlLabel: Theme['components'] = {
MuiFormControlLabel: {
styleOverrides: {
root: ({ theme }) => ({
marginInlineStart: theme.spacing(-2)
}),
label: {
'&, &.Mui-disabled': {
color: 'var(--mui-palette-text-primary)'
},
'&.Mui-disabled': {
opacity: 0.45
}
}
}
}
}
export default formControlLabel
@@ -0,0 +1,140 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Config Imports
import themeConfig from '@configs/themeConfig'
const iconButton: Theme['components'] = {
MuiIconButton: {
styleOverrides: {
root: {
'& .MuiSvgIcon-root, & i, & svg': {
fontSize: 'inherit'
}
},
sizeSmall: ({ theme }) => ({
padding: theme.spacing(1.75),
fontSize: '1.25rem'
}),
sizeMedium: ({ theme }) => ({
padding: theme.spacing(2),
fontSize: '1.375rem'
}),
sizeLarge: ({ theme }) => ({
padding: theme.spacing(2.25),
fontSize: '1.5rem'
})
},
variants: [
{
props: { color: 'default' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active': {
backgroundColor: 'rgb(var(--mui-palette-text-primaryChannel) / 0.08)'
},
...(themeConfig.disableRipple && {
'&.Mui-focusVisible:not(.Mui-disabled)': {
backgroundColor: 'rgb(var(--mui-palette-text-primaryChannel) / 0.08)'
}
}),
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-action-active)'
}
}
},
{
props: { color: 'primary' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active': {
backgroundColor: 'var(--mui-palette-primary-lighterOpacity)'
},
...(themeConfig.disableRipple && {
'&.Mui-focusVisible:not(.Mui-disabled)': { backgroundColor: 'var(--mui-palette-primary-lighterOpacity)' }
}),
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-primary-main)'
}
}
},
{
props: { color: 'secondary' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active': {
backgroundColor: 'var(--mui-palette-secondary-lighterOpacity)'
},
...(themeConfig.disableRipple && {
'&.Mui-focusVisible:not(.Mui-disabled)': { backgroundColor: 'var(--mui-palette-secondary-lighterOpacity)' }
}),
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-secondary-main)'
}
}
},
{
props: { color: 'error' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active': {
backgroundColor: 'var(--mui-palette-error-lighterOpacity)'
},
...(themeConfig.disableRipple && {
'&.Mui-focusVisible:not(.Mui-disabled)': { backgroundColor: 'var(--mui-palette-error-lighterOpacity)' }
}),
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-error-main)'
}
}
},
{
props: { color: 'warning' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active': {
backgroundColor: 'var(--mui-palette-warning-lighterOpacity)'
},
...(themeConfig.disableRipple && {
'&.Mui-focusVisible:not(.Mui-disabled)': { backgroundColor: 'var(--mui-palette-warning-lighterOpacity)' }
}),
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-warning-main)'
}
}
},
{
props: { color: 'info' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active': {
backgroundColor: 'var(--mui-palette-info-lighterOpacity)'
},
...(themeConfig.disableRipple && {
'&.Mui-focusVisible:not(.Mui-disabled)': { backgroundColor: 'var(--mui-palette-info-lighterOpacity)' }
}),
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-info-main)'
}
}
},
{
props: { color: 'success' },
style: {
'&:not(.Mui-disabled):hover, &:not(.Mui-disabled):active': {
backgroundColor: 'var(--mui-palette-success-lighterOpacity)'
},
...(themeConfig.disableRipple && {
'&.Mui-focusVisible:not(.Mui-disabled)': { backgroundColor: 'var(--mui-palette-success-lighterOpacity)' }
}),
'&.Mui-disabled': {
opacity: 0.45,
color: 'var(--mui-palette-success-main)'
}
}
}
]
}
}
export default iconButton
@@ -0,0 +1,83 @@
// Override Imports
import Accordion from './accordion'
import Alerts from './alerts'
import Autocomplete from './autocomplete'
import avatar from './avatar'
import backdrop from './backdrop'
import badges from './badges'
import breadcrumbs from './breadcrumbs'
import button from './button'
import buttonGroup from './button-group'
import card from './card'
import Checkbox from './checkbox'
import chip from './chip'
import dialog from './dialog'
import drawer from './drawer'
import fab from './fab'
import formControlLabel from './form-control-label'
import iconButton from './icon-button'
import input from './input'
import list from './list'
import menu from './menu'
import pagination from './pagination'
import paper from './paper'
import popover from './popover'
import progress from './progress'
import Radio from './radio'
import Rating from './rating'
import Select from './select'
import slider from './slider'
import snackbar from './snackbar'
import switchOverrides from './switch'
import tablePagination from './table-pagination'
import tabs from './tabs'
import timeline from './timeline'
import toggleButton from './toggle-button'
import tooltip from './tooltip'
import typography from './typography'
const overrides = () => {
const skin = 'default'
return Object.assign(
{},
Accordion(skin),
Alerts,
Autocomplete(skin),
avatar,
backdrop,
badges,
breadcrumbs,
button,
buttonGroup,
card(skin),
Checkbox,
chip,
dialog(skin),
drawer(skin),
fab,
formControlLabel,
iconButton,
input,
list,
menu(skin),
pagination,
paper,
popover(skin),
progress,
Radio,
Rating,
Select,
slider,
snackbar(skin),
switchOverrides,
tablePagination,
tabs,
timeline,
toggleButton,
tooltip,
typography
)
}
export default overrides
@@ -0,0 +1,112 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const input: Theme['components'] = {
MuiFormControl: {
styleOverrides: {
root: {
'&:has(.MuiRadio-root) .MuiFormHelperText-root, &:has(.MuiCheckbox-root) .MuiFormHelperText-root, &:has(.MuiSwitch-root) .MuiFormHelperText-root':
{
marginInline: 0
}
}
}
},
MuiInputBase: {
styleOverrides: {
root: {
lineHeight: 1.6,
'&.MuiInput-underline': {
'&:before': {
borderColor: 'var(--mui-palette-customColors-inputBorder)'
},
'&:not(.Mui-disabled, .Mui-error):hover:before': {
borderColor: 'var(--mui-palette-action-active)'
}
},
'&.Mui-disabled .MuiInputAdornment-root, &.Mui-disabled .MuiInputAdornment-root > *': {
color: 'var(--mui-palette-action-disabled)'
}
}
}
},
MuiFilledInput: {
styleOverrides: {
root: {
'&:before': {
borderBottom: '1px solid var(--mui-palette-text-secondary)'
},
'&.Mui-disabled:before': {
borderBottomStyle: 'solid'
}
}
}
},
MuiInputLabel: {
styleOverrides: {
shrink: ({ ownerState }) => ({
...(ownerState.variant === 'outlined' && {
color: 'var(--mui-palette-text-secondary)',
transform: 'translate(14px, -8px) scale(0.867)'
}),
...(ownerState.variant === 'filled' && {
transform: 'translate(12px, 7px) scale(0.867)'
}),
...(ownerState.variant === 'standard' && {
transform: 'translate(0, -1.5px) scale(0.867)'
})
})
}
},
MuiOutlinedInput: {
styleOverrides: {
root: {
'&:not(.Mui-focused):not(.Mui-error):not(.Mui-disabled):hover .MuiOutlinedInput-notchedOutline': {
borderColor: 'var(--mui-palette-action-active)'
},
'&.Mui-disabled .MuiOutlinedInput-notchedOutline': {
borderColor: 'var(--mui-palette-action-disabledBackground)'
}
},
input: ({ theme, ownerState }) => ({
...(ownerState?.size === 'medium' && {
'&:not(.MuiInputBase-inputMultiline, .MuiInputBase-inputAdornedStart)': {
paddingBlock: theme.spacing(4)
},
height: '1.5em'
}),
'& ~ .MuiOutlinedInput-notchedOutline': {
borderColor: 'var(--mui-palette-customColors-inputBorder)'
}
}),
notchedOutline: {
'& legend': {
fontSize: '0.867em'
}
}
}
},
MuiInputAdornment: {
styleOverrides: {
root: {
color: 'var(--mui-palette-text-primary)',
'& i, & svg': {
fontSize: '1.25rem'
},
'& *': {
color: 'inherit !important'
}
}
}
},
MuiFormHelperText: {
styleOverrides: {
root: {
lineHeight: 1,
letterSpacing: 'unset'
}
}
}
}
export default input
@@ -0,0 +1,81 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const list: Theme['components'] = {
MuiListItem: {
styleOverrides: {
root: ({ theme }) => ({
gap: theme.spacing(4)
}),
padding: ({ theme, ownerState }) => ({
...(!ownerState.dense && {
paddingBlock: theme.spacing(2),
paddingInlineStart: theme.spacing(5)
})
})
}
},
MuiListItemAvatar: {
styleOverrides: {
root: {
minWidth: 'unset'
}
}
},
MuiListItemIcon: {
styleOverrides: {
root: {
minWidth: 0,
color: 'var(--mui-palette-text-primary)',
fontSize: '1.375rem',
'& > svg, & > i': {
fontSize: 'inherit'
}
}
}
},
MuiListItemButton: {
styleOverrides: {
root: ({ theme, ownerState }) => ({
gap: theme.spacing(4),
...(!ownerState.dense && {
paddingBlock: theme.spacing(2)
}),
paddingInlineStart: theme.spacing(5),
'&.Mui-selected': {
backgroundColor: 'var(--mui-palette-primary-lightOpacity)',
'&:hover, &.Mui-focused, &.Mui-focusVisible': {
backgroundColor: 'var(--mui-palette-primary-mainOpacity)'
},
'& .MuiTypography-root': {
color: 'var(--mui-palette-primary-main)'
},
'& + .MuiListItemSecondaryAction-root .MuiIconButton-root': {
color: 'var(--mui-palette-primary-main)'
}
}
})
}
},
MuiListItemText: {
styleOverrides: {
root: {
margin: 0
},
primary: {
color: 'var(--mui-palette-text-primary)'
}
}
},
MuiListSubheader: {
styleOverrides: {
root: ({ theme }) => ({
...theme.typography.subtitle2,
paddingBlock: 10,
paddingInline: theme.spacing(5)
})
}
}
}
export default list
@@ -0,0 +1,57 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { Skin } from '@core/types'
const menu = (skin: Skin): Theme['components'] => ({
MuiMenu: {
defaultProps: {
...(skin === 'bordered' && {
slotProps: {
paper: {
elevation: 0
}
}
})
},
styleOverrides: {
paper: ({ theme }) => ({
marginBlockStart: theme.spacing(0.5),
...(skin !== 'bordered' && {
boxShadow: 'var(--mui-customShadows-lg)'
})
})
}
},
MuiMenuItem: {
styleOverrides: {
root: ({ theme }) => ({
paddingBlock: theme.spacing(2),
color: 'var(--mui-palette-text-primary)',
'& i, & svg': {
fontSize: '1.375rem'
},
'& .MuiListItemIcon-root': {
minInlineSize: 0
},
'&.Mui-selected': {
backgroundColor: 'var(--mui-palette-primary-lightOpacity)',
color: 'var(--mui-palette-primary-main)',
'& .MuiListItemIcon-root': {
color: 'var(--mui-palette-primary-main)'
},
'&:hover, &.Mui-focused, &.Mui-focusVisible': {
backgroundColor: 'var(--mui-palette-primary-mainOpacity)'
}
},
'&.Mui-disabled': {
color: 'var(--mui-palette-text-disabled)',
opacity: 1
}
})
}
}
})
export default menu
@@ -0,0 +1,172 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const pagination: Theme['components'] = {
MuiPagination: {
styleOverrides: {
ul: {
rowGap: 6
}
},
variants: [
{
props: { variant: 'text', color: 'primary' },
style: {
'& .MuiPaginationItem-root.Mui-selected.Mui-disabled': {
backgroundColor: 'var(--mui-palette-primary-main)',
color: 'var(--mui-palette-primary-contrastText)'
}
}
},
{
props: { variant: 'text', color: 'secondary' },
style: {
'& .MuiPaginationItem-root.Mui-selected.Mui-disabled': {
backgroundColor: 'var(--mui-palette-secondary-main)',
color: 'var(--mui-palette-secondary-contrastText)'
}
}
},
{
props: { variant: 'outlined', color: 'standard' },
style: {
'& .MuiPaginationItem-root.Mui-selected.Mui-disabled': {
borderColor: 'var(--mui-palette-action-selected)'
}
}
},
{
props: { variant: 'outlined', color: 'primary' },
style: {
'& .MuiPaginationItem-root.Mui-selected.Mui-disabled': {
color: 'var(--mui-palette-primary-main)',
backgroundColor: 'rgb(var(--mui-palette-primary-mainChannel) / var(--mui-palette-action-activatedOpacity))',
borderColor: 'rgba(var(--mui-palette-primary-mainChannel) / 0.5)'
}
}
},
{
props: { variant: 'outlined', color: 'secondary' },
style: {
'& .MuiPaginationItem-root.Mui-selected.Mui-disabled': {
color: 'var(--mui-palette-secondary-main)',
backgroundColor:
'rgb(var(--mui-palette-secondary-mainChannel) / var(--mui-palette-action-activatedOpacity))',
borderColor: 'rgba(var(--mui-palette-secondary-mainChannel) / 0.5)'
}
}
},
{
props: { variant: 'tonal' },
style: {
'& .MuiPaginationItem-root:not(.MuiPaginationItem-ellipsis)': {
backgroundColor: 'var(--mui-palette-action-selected)'
}
}
},
{
props: { variant: 'tonal', color: 'standard' },
style: {
'& .MuiPaginationItem-root.Mui-selected': {
backgroundColor: 'var(--mui-palette-primary-lightOpacity)',
color: 'var(--mui-palette-primary-main)',
'&:hover': {
backgroundColor: 'var(--mui-palette-primary-mainOpacity)'
}
},
'& .MuiPaginationItem-root:hover:not(.Mui-selected):not(.MuiPaginationItem-ellipsis)': {
backgroundColor: 'var(--mui-palette-action-focus)'
},
'& .MuiPaginationItem-root.Mui-selected.Mui-disabled': {
backgroundColor: 'var(--mui-palette-primary-lightOpacity)',
color: 'var(--mui-palette-primary-main)'
}
}
},
{
props: { variant: 'tonal', color: 'primary' },
style: {
'& .MuiPaginationItem-root.Mui-selected': {
backgroundColor: 'var(--mui-palette-primary-main)',
color: 'var(--mui-palette-primary-contrastText)',
'&:not(.Mui-disabled)': {
boxShadow: 'var(--mui-customShadows-xs)'
},
'&:hover': {
backgroundColor: 'var(--mui-palette-primary-dark)'
}
},
'& .MuiPaginationItem-root:hover:not(.Mui-selected):not(.MuiPaginationItem-ellipsis)': {
backgroundColor: 'var(--mui-palette-primary-lightOpacity)',
color: 'var(--mui-palette-primary-main)'
},
'& .MuiPaginationItem-root.Mui-selected.Mui-disabled': {
backgroundColor: 'var(--mui-palette-primary-main)',
color: 'var(--mui-palette-primary-contrastText)'
}
}
},
{
props: { variant: 'tonal', color: 'secondary' },
style: {
'& .MuiPaginationItem-root.Mui-selected': {
backgroundColor: 'var(--mui-palette-secondary-main)',
color: 'var(--mui-palette-secondary-contrastText)',
'&:not(.Mui-disabled)': {
boxShadow: 'var(--mui-customShadows-xs)'
},
'&:hover': {
backgroundColor: 'var(--mui-palette-secondary-dark)'
}
},
'& .MuiPaginationItem-root:hover:not(.Mui-selected):not(.MuiPaginationItem-ellipsis)': {
backgroundColor: 'var(--mui-palette-secondary-mainOpacity)',
color: 'var(--mui-palette-secondary-main)'
},
'& .MuiPaginationItem-root.Mui-selected.Mui-disabled': {
backgroundColor: 'var(--mui-palette-secondary-main)',
color: 'var(--mui-palette-secondary-contrastText)'
}
}
}
]
},
MuiPaginationItem: {
styleOverrides: {
root: ({ ownerState }) => ({
...(ownerState.size === 'medium' && {
height: '2.375rem',
minWidth: '2.375rem'
}),
...(ownerState.shape !== 'rounded' && {
borderRadius: '50px'
}),
'&.Mui-selected.Mui-disabled': {
color: 'var(--mui-palette-text-primary)',
opacity: 0.45
},
'&.Mui-disabled': {
opacity: 0.45
},
...(ownerState.shape === 'rounded' &&
ownerState.size === 'small' && {
borderRadius: 'var(--mui-shape-customBorderRadius-sm)'
}),
...(ownerState.shape === 'rounded' &&
ownerState.size === 'large' && {
borderRadius: 'var(--mui-shape-customBorderRadius-lg)'
})
}),
sizeSmall: {
height: '2.125rem',
minWidth: '2.125rem'
},
sizeLarge: {
height: '2.625rem',
minWidth: '2.625rem'
}
}
}
}
export default pagination
@@ -0,0 +1,14 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const paper: Theme['components'] = {
MuiPaper: {
styleOverrides: {
root: {
backgroundImage: 'none'
}
}
}
}
export default paper
@@ -0,0 +1,21 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { Skin } from '@core/types'
const popover = (skin: Skin): Theme['components'] => ({
MuiPopover: {
styleOverrides: {
paper: {
...(skin === 'bordered'
? { boxShadow: 'none', border: '1px solid var(--mui-palette-divider)' }
: {
boxShadow: 'var(--mui-customShadows-sm)'
})
}
}
}
})
export default popover
@@ -0,0 +1,18 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const progress: Theme['components'] = {
MuiLinearProgress: {
styleOverrides: {
root: ({ theme }) => ({
height: 6,
borderRadius: theme.shape.borderRadius,
'& .MuiLinearProgress-bar': {
borderRadius: theme.shape.borderRadius
}
})
}
}
}
export default progress
@@ -0,0 +1,81 @@
// React Imports
import React from 'react'
// MUI Imports
import type { Theme } from '@mui/material/styles'
const IconChecked = () => {
return (
<svg width='1em' height='1em' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d='M12 18.5a6.5 6.5 0 1 1 0-13 6.5 6.5 0 0 1 0 13Z'
fill='var(--mui-palette-common-white)'
stroke='currentColor'
strokeWidth='5'
/>
</svg>
)
}
const UncheckedIcon = () => {
return (
<svg width='1em' height='1em' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M12 20a8 8 0 1 1 0-16 8 8 0 0 1 0 16Z' stroke='var(--mui-palette-text-secondary)' strokeWidth='2' />
</svg>
)
}
const radio: Theme['components'] = {
MuiRadio: {
defaultProps: {
icon: <UncheckedIcon />,
checkedIcon: <IconChecked />
},
styleOverrides: {
root: ({ theme, ownerState }) => ({
...(ownerState.size === 'small'
? {
padding: theme.spacing(1),
'& svg': {
fontSize: '1.25rem'
}
}
: {
padding: theme.spacing(1.5),
'& svg': {
fontSize: '1.5rem'
}
}),
'&.Mui-checked:not(.Mui-disabled) svg': {
filter: 'drop-shadow(var(--mui-customShadows-xs))'
},
'&.Mui-disabled': {
opacity: 0.45,
'&:not(.Mui-checked)': {
color: 'var(--mui-palette-text-secondary)'
},
'&.Mui-checked.MuiRadio-colorPrimary': {
color: 'var(--mui-palette-primary-main)'
},
'&.Mui-checked.MuiRadio-colorSecondary': {
color: 'var(--mui-palette-secondary-main)'
},
'&.Mui-checked.MuiRadio-colorError': {
color: 'var(--mui-palette-error-main)'
},
'&.Mui-checked.MuiRadio-colorWarning': {
color: 'var(--mui-palette-warning-main)'
},
'&.Mui-checked.MuiRadio-colorInfo': {
color: 'var(--mui-palette-info-main)'
},
'&.Mui-checked.MuiRadio-colorSuccess': {
color: 'var(--mui-palette-success-main)'
}
}
})
}
}
}
export default radio
@@ -0,0 +1,35 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const rating: Theme['components'] = {
MuiRating: {
defaultProps: {
emptyIcon: <i className='ri-star-line' />,
icon: <i className='ri-star-fill' />
},
styleOverrides: {
root: {
gap: '2px',
color: 'var(--mui-palette-warning-main)',
'& i, & svg': {
flexShrink: 0
},
'& .MuiRating-decimal > label:first-of-type, & .MuiRating-decimal > span:first-of-type': {
zIndex: 1
}
},
sizeSmall: {
'& .MuiRating-icon i, & .MuiRating-icon svg': {
fontSize: '1.25rem'
}
},
sizeLarge: {
'& .MuiRating-icon i, & .MuiRating-icon svg': {
fontSize: '1.75rem'
}
}
}
}
}
export default rating
@@ -0,0 +1,61 @@
// React Imports
import React from 'react'
// MUI Imports
import type { Theme } from '@mui/material/styles'
const SelectIcon = () => {
return <i className='ri-arrow-down-s-line' />
}
const iconStyles = (theme: Theme) => ({
userSelect: 'none',
display: 'inline-block',
fill: 'currentColor',
flexShrink: 0,
transition: theme.transitions.create('fill', {
duration: theme.transitions.duration.shorter
}),
fontSize: '1.25rem',
position: 'absolute',
right: '1rem',
top: 'calc(50% - 0.5em)',
pointerEvents: 'none'
})
const select: Theme['components'] = {
MuiSelect: {
defaultProps: {
IconComponent: SelectIcon
},
styleOverrides: {
select: ({ theme, ownerState }) => ({
...(ownerState.variant === 'outlined' && {
minHeight: '1.5em'
}),
'&[aria-expanded="true"] ~ i, &[aria-expanded="true"] ~ svg': {
transform: 'rotate(180deg)'
},
'& ~ i, & ~ svg': iconStyles(theme as Theme),
'&.MuiInputBase-inputSizeSmall': {
'& ~ i, & ~ svg': {
height: '1.375rem',
width: '1.375rem'
}
},
'&:not(aria-label="Without label") ~ .MuiOutlinedInput-notchedOutline > legend > span': {
paddingInline: '5px'
}
})
}
},
MuiNativeSelect: {
styleOverrides: {
select: ({ theme }) => ({
'& + i, & + svg': iconStyles(theme as Theme)
})
}
}
}
export default select
@@ -0,0 +1,100 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const slider: Theme['components'] = {
MuiSlider: {
styleOverrides: {
root: ({ ownerState }) => ({
boxSizing: 'border-box',
...(ownerState.orientation === 'horizontal'
? ownerState.size !== 'small'
? { height: 6 }
: { height: 4 }
: ownerState.size !== 'small'
? { width: 6 }
: { width: 4 }),
'&.Mui-disabled': {
opacity: 0.45,
color: `var(--mui-palette-${ownerState.color}-main)`
}
}),
thumb: ({ ownerState }) => ({
...(ownerState.size === 'small'
? {
height: 14,
width: 14,
border: '2px solid currentColor',
'&:hover, &.Mui-focusVisible': {
boxShadow: `0 0 0 7px var(--mui-palette-${ownerState.color}-lightOpacity)`
},
'&.Mui-active.Mui-focusVisible': {
boxShadow: `0 0 0 10px var(--mui-palette-${ownerState.color}-lightOpacity)`
}
}
: {
height: 22,
width: 22,
border: '4px solid currentColor'
}),
backgroundColor: 'var(--mui-palette-common-white)',
...(!ownerState.disabled && {
boxShadow: 'var(--mui-customShadows-sm)'
}),
'&:before': {
boxShadow: 'none'
},
'&:after': {
...(ownerState.size === 'small'
? {
height: 28,
width: 28
}
: {
height: 38,
width: 38
})
},
'&:hover, &.Mui-focusVisible': {
boxShadow: `0 0 0 8px var(--mui-palette-${ownerState.color}-lightOpacity)`
},
'&.Mui-active.Mui-focusVisible': {
boxShadow: `0 0 0 13px var(--mui-palette-${ownerState.color}-lightOpacity)`
}
}),
rail: ({ ownerState }) => ({
opacity: 1,
color: `var(--mui-palette-${ownerState.color}-lightOpacity)`,
...(ownerState.track === 'inverted' && {
backgroundColor: `var(--mui-palette-${ownerState.color}-main)`
})
}),
valueLabel: ({ theme, ownerState }) => ({
...(ownerState.size === 'small'
? {
...theme.typography.caption,
borderRadius: 'var(--mui-shape-customBorderRadius-sm)',
padding: theme.spacing(1, 2)
}
: {
...theme.typography.body2,
fontWeight: theme.typography.fontWeightMedium,
borderRadius: 'var(--mui-shape-borderRadius)',
padding: theme.spacing(1, 2.5)
}),
color: 'var(--mui-palette-customColors-tooltipText)',
backgroundColor: 'var(--mui-palette-Tooltip-bg)',
'&:before': {
display: 'none'
}
}),
track: ({ theme, ownerState }) => ({
...(ownerState.track === 'inverted' && {
backgroundColor: `color-mix(in srgb, ${theme.palette[ownerState.color || 'primary'].main} 16%, var(--mui-palette-background-paper))`,
borderColor: `color-mix(in srgb, ${theme.palette[ownerState.color || 'primary'].main} 16%, var(--mui-palette-background-paper))`
})
})
}
}
}
export default slider
@@ -0,0 +1,27 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { Skin } from '@core/types'
const snackbar = (skin: Skin): Theme['components'] => ({
MuiSnackbarContent: {
styleOverrides: {
root: ({ theme }) => ({
padding: theme.spacing(0, 4),
...(skin !== 'bordered'
? {
boxShadow: 'var(--mui-customShadows-xs)'
}
: {
boxShadow: 'none'
}),
'& .MuiSnackbarContent-message': {
paddingBlock: theme.spacing(3)
}
})
}
}
})
export default snackbar
@@ -0,0 +1,69 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const switchOverrides: Theme['components'] = {
MuiSwitch: {
defaultProps: {
disableRipple: true
},
styleOverrides: {
root: ({ theme, ownerState }) => ({
'&:has(.Mui-disabled)': {
opacity: 0.45
},
...(ownerState.size !== 'small'
? {
width: 46,
height: 36,
padding: theme.spacing(2.25, 2)
}
: {
width: 42,
height: 30,
padding: theme.spacing(1.75, 2),
'& .MuiSwitch-thumb': {
width: 12,
height: 12
},
'& .MuiSwitch-switchBase': {
padding: 7,
left: 3,
'&.Mui-checked': {
left: -3
}
}
})
}),
switchBase: {
top: 2,
left: 1,
'&.Mui-checked': {
left: -7,
color: 'var(--mui-palette-common-white)',
'& + .MuiSwitch-track': {
opacity: 1
}
},
'&.Mui-disabled + .MuiSwitch-track': {
opacity: 1
},
'&:hover:not(:has(span.MuiTouchRipple-root))': {
backgroundColor: 'transparent'
}
},
thumb: {
width: 14,
height: 14,
boxShadow: 'var(--mui-customShadows-xs)'
},
track: {
opacity: 1,
borderRadius: 10,
backgroundColor: 'var(--mui-palette-action-focus)',
boxShadow: `0 0 4px rgb(0 0 0 / 0.16) inset`
}
}
}
}
export default switchOverrides
@@ -0,0 +1,42 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const tablePagination: Theme['components'] = {
MuiTablePagination: {
styleOverrides: {
toolbar: ({ theme }) => ({
paddingInlineEnd: `${theme.spacing(3)} !important`
}),
select: ({ theme }) => ({
...theme.typography.body1,
paddingInlineStart: 0,
'& ~ i, & ~ svg': {
fontSize: 20,
right: '2px !important',
color: 'var(--mui-palette-action-active)'
}
}),
selectLabel: ({ theme }) => ({
...theme.typography.body1,
color: 'var(--mui-palette-text-secondary)'
}),
input: ({ theme }) => ({
marginInlineEnd: theme.spacing(6)
}),
displayedRows: ({ theme }) => ({
...theme.typography.body1
}),
actions: ({ theme }) => ({
marginInlineStart: theme.spacing(6),
'& .Mui-disabled': {
color: 'var(--mui-palette-action-active)'
},
'& .MuiIconButton-root:last-of-type': {
marginInlineStart: theme.spacing(2)
}
})
}
}
}
export default tablePagination
@@ -0,0 +1,92 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const tabs: Theme['components'] = {
MuiTabs: {
styleOverrides: {
root: ({ theme, ownerState }) => ({
minBlockSize: 38,
...(ownerState.orientation === 'horizontal'
? {
borderBlockEnd: '1px solid var(--mui-palette-divider)'
}
: {
borderInlineEnd: '1px solid var(--mui-palette-divider)'
}),
'& .MuiTab-root:hover': {
...(ownerState.orientation === 'horizontal'
? {
paddingBlockEnd: theme.spacing(1.5),
...(ownerState.textColor === 'secondary'
? {
color: 'var(--mui-palette-secondary-main)',
borderBlockEnd: '2px solid var(--mui-palette-secondary-lightOpacity)'
}
: {
color: 'var(--mui-palette-primary-main)',
borderBlockEnd: '2px solid var(--mui-palette-primary-lightOpacity)'
})
}
: {
paddingInlineEnd: theme.spacing(5),
...(ownerState.textColor === 'secondary'
? {
color: 'var(--mui-palette-secondary-main)',
borderInlineEnd: '2px solid var(--mui-palette-secondary-mainOpacity)'
}
: {
color: 'var(--mui-palette-primary-main)',
borderInlineEnd: '2px solid var(--mui-palette-primary-mainOpacity)'
})
}),
'& .MuiTabScrollButton-root': {
borderRadius: theme.shape.borderRadius
}
},
'& ~ .MuiTabPanel-root': {
...(ownerState.orientation === 'horizontal'
? {
paddingBlockStart: theme.spacing(5)
}
: {
paddingInlineStart: theme.spacing(5)
})
}
}),
vertical: {
minWidth: 131,
'& .MuiTab-root': {
minWidth: 130
}
}
}
},
MuiTab: {
styleOverrides: {
root: ({ theme, ownerState }) => ({
lineHeight: 1.4667,
padding: theme.spacing(2, 5.5),
minBlockSize: 38,
color: 'var(--mui-palette-text-primary)',
'& > .MuiTab-iconWrapper': {
fontSize: '1.125rem',
...(ownerState.iconPosition === 'start' && {
marginInlineEnd: theme.spacing(1.5)
}),
...(ownerState.iconPosition === 'end' && {
marginInlineStart: theme.spacing(1.5)
})
}
})
}
},
MuiTabPanel: {
styleOverrides: {
root: {
padding: 0
}
}
}
}
export default tabs
@@ -0,0 +1,161 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const timeline: Theme['components'] = {
MuiTimeline: {
styleOverrides: {
root: {
padding: 0
}
}
},
MuiTimelineDot: {
styleOverrides: {
root: ({ theme }) => ({
margin: theme.spacing(3, 0),
boxShadow: 'none',
'&:has(> i), &:has(> svg)': {
padding: 6
},
'& > svg, & > i': {
fontSize: '1.25rem'
},
'&:has(svg)': {
width: 32,
height: 32,
alignItems: 'center',
justifyContent: 'center'
}
})
},
variants: [
{
props: { variant: 'outlined' },
style: {
padding: 5,
'& + .MuiTimelineConnector-root': {
backgroundColor: 'transparent',
borderInlineStart: '1px dashed var(--mui-palette-divider)'
},
'&:has(+ .MuiTimelineConnector-root)': {
marginBlock: '0.625rem'
}
}
},
{
props: { variant: 'filled', color: 'grey' },
style: {
boxShadow: '0 0 0 3px rgb(var(--mui-mainColorChannels-light) / 0.04)'
}
},
{
props: { variant: 'filled', color: 'primary' },
style: {
boxShadow: '0 0 0 3px var(--mui-palette-primary-lightOpacity)'
}
},
{
props: { variant: 'filled', color: 'secondary' },
style: {
boxShadow: '0 0 0 3px var(--mui-palette-secondary-lightOpacity)'
}
},
{
props: { variant: 'filled', color: 'error' },
style: {
boxShadow: '0 0 0 3px var(--mui-palette-error-lightOpacity)'
}
},
{
props: { variant: 'filled', color: 'warning' },
style: {
boxShadow: '0 0 0 3px var(--mui-palette-warning-lightOpacity)'
}
},
{
props: { variant: 'filled', color: 'info' },
style: {
boxShadow: '0 0 0 3px var(--mui-palette-info-lightOpacity)'
}
},
{
props: { variant: 'filled', color: 'success' },
style: {
boxShadow: '0 0 0 3px var(--mui-palette-success-lightOpacity)'
}
},
{
props: { variant: 'tonal' },
style: {
border: 0
}
},
{
props: { variant: 'tonal', color: 'grey' },
style: {
backgroundColor: 'var(--mui-palette-action-selected)',
color: 'var(--mui-palette-text-primary)'
}
},
{
props: { variant: 'tonal', color: 'primary' },
style: {
backgroundColor: 'var(--mui-palette-primary-lightOpacity)',
color: 'var(--mui-palette-primary-main)'
}
},
{
props: { variant: 'tonal', color: 'secondary' },
style: {
backgroundColor: 'var(--mui-palette-secondary-lightOpacity)',
color: 'var(--mui-palette-secondary-main)'
}
},
{
props: { variant: 'tonal', color: 'error' },
style: {
backgroundColor: 'var(--mui-palette-error-lightOpacity)',
color: 'var(--mui-palette-error-main)'
}
},
{
props: { variant: 'tonal', color: 'warning' },
style: {
backgroundColor: 'var(--mui-palette-warning-lightOpacity)',
color: 'var(--mui-palette-warning-main)'
}
},
{
props: { variant: 'tonal', color: 'info' },
style: {
backgroundColor: 'var(--mui-palette-info-lightOpacity)',
color: 'var(--mui-palette-info-main)'
}
},
{
props: { variant: 'tonal', color: 'success' },
style: {
backgroundColor: 'var(--mui-palette-success-lightOpacity)',
color: 'var(--mui-palette-success-main)'
}
}
]
},
MuiTimelineConnector: {
styleOverrides: {
root: {
width: 1,
backgroundColor: 'var(--mui-palette-divider)'
}
}
},
MuiTimelineContent: {
styleOverrides: {
root: {
paddingBottom: '1rem'
}
}
}
}
export default timeline
@@ -0,0 +1,34 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const toggleButton: Theme['components'] = {
MuiToggleButtonGroup: {
styleOverrides: {
root: ({ ownerState }) => ({
...(ownerState.size === 'small' && {
borderRadius: 'var(--mui-shape-customBorderRadius-sm)'
}),
...(ownerState.size === 'large' && {
borderRadius: 'var(--mui-shape-customBorderRadius-lg)'
})
})
}
},
MuiToggleButton: {
styleOverrides: {
root: {
'&:not(.Mui-selected):not(.Mui-disabled)': {
color: 'var(--mui-palette-text-secondary)'
}
},
sizeSmall: {
borderRadius: 'var(--mui-shape-customBorderRadius-sm)'
},
sizeLarge: {
borderRadius: 'var(--mui-shape-customBorderRadius-lg)'
}
}
}
}
export default toggleButton
@@ -0,0 +1,32 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const tooltip: Theme['components'] = {
MuiTooltip: {
styleOverrides: {
popper: {
'&[data-popper-placement*="bottom"] .MuiTooltip-tooltip': {
marginTop: '6px !important'
},
'&[data-popper-placement*="top"] .MuiTooltip-tooltip': {
marginBottom: '6px !important'
},
'&[data-popper-placement*="left"] .MuiTooltip-tooltip': {
marginRight: '6px !important'
},
'&[data-popper-placement*="right"] .MuiTooltip-tooltip': {
marginLeft: '6px !important'
}
},
tooltip: ({ theme }) => ({
borderRadius: 'var(--mui-shape-customBorderRadius-sm)',
fontSize: theme.typography.subtitle2.fontSize,
lineHeight: 1.539,
color: 'var(--mui-palette-customColors-tooltipText)',
paddingInline: theme.spacing(3)
})
}
}
}
export default tooltip
@@ -0,0 +1,68 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const typography: Theme['components'] = {
MuiTypography: {
styleOverrides: {
gutterBottom: ({ theme }) => ({
marginBottom: theme.spacing(2)
})
},
variants: [
{
props: { variant: 'h1' },
style: { color: 'var(--mui-palette-text-primary)' }
},
{
props: { variant: 'h2' },
style: { color: 'var(--mui-palette-text-primary)' }
},
{
props: { variant: 'h3' },
style: { color: 'var(--mui-palette-text-primary)' }
},
{
props: { variant: 'h4' },
style: { color: 'var(--mui-palette-text-primary)' }
},
{
props: { variant: 'h5' },
style: { color: 'var(--mui-palette-text-primary)' }
},
{
props: { variant: 'h6' },
style: { color: 'var(--mui-palette-text-primary)' }
},
{
props: { variant: 'subtitle1' },
style: { color: 'rgb(var(--mui-palette-text-primaryChannel) / 0.55)' }
},
{
props: { variant: 'subtitle2' },
style: { color: 'rgb(var(--mui-palette-text-primaryChannel) / 0.55)' }
},
{
props: { variant: 'body1' },
style: { color: 'var(--mui-palette-text-secondary)' }
},
{
props: { variant: 'body2' },
style: { color: 'var(--mui-palette-text-secondary)' }
},
{
props: { variant: 'button' },
style: { color: 'var(--mui-palette-text-primary)' }
},
{
props: { variant: 'caption' },
style: { color: 'var(--mui-palette-text-disabled)', display: 'inline-block' }
},
{
props: { variant: 'overline' },
style: { color: 'var(--mui-palette-text-primary)', display: 'inline-block' }
}
]
}
}
export default typography
+39
View File
@@ -0,0 +1,39 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
// Type Imports
import type { SystemMode } from '@core/types'
const shadows = (mode: SystemMode): Theme['shadows'] => {
const color = `var(--mui-mainColorChannels-${mode}Shadow)`
return [
'none',
`0px 2px 1px -1px rgb(${color} / 0.2),0px 1px 1px 0px rgb(${color} / 0.14),0px 1px 3px 0px rgb(${color} / 0.12)`,
`0px 3px 1px -2px rgb(${color} / 0.2),0px 2px 2px 0px rgb(${color} / 0.14),0px 1px 5px 0px rgb(${color} / 0.12)`,
`0px 3px 3px -2px rgb(${color} / 0.2),0px 3px 4px 0px rgb(${color} / 0.14),0px 1px 8px 0px rgb(${color} / 0.12)`,
`0px 2px 4px -1px rgb(${color} / 0.2),0px 4px 5px 0px rgb(${color} / 0.14),0px 1px 10px 0px rgb(${color} / 0.12)`,
`0px 3px 5px -1px rgb(${color} / 0.2),0px 5px 8px 0px rgb(${color} / 0.14),0px 1px 14px 0px rgb(${color} / 0.12)`,
`0px 3px 5px -1px rgb(${color} / 0.2),0px 6px 10px 0px rgb(${color} / 0.14),0px 1px 18px 0px rgb(${color} / 0.12)`,
`0px 4px 5px -2px rgb(${color} / 0.2),0px 7px 10px 1px rgb(${color} / 0.14),0px 2px 16px 1px rgb(${color} / 0.12)`,
`0px 5px 5px -3px rgb(${color} / 0.2),0px 8px 10px 1px rgb(${color} / 0.14),0px 3px 14px 2px rgb(${color} / 0.12)`,
`0px 5px 6px -3px rgb(${color} / 0.2),0px 9px 12px 1px rgb(${color} / 0.14),0px 3px 16px 2px rgb(${color} / 0.12)`,
`0px 6px 6px -3px rgb(${color} / 0.2),0px 10px 14px 1px rgb(${color} / 0.14),0px 4px 18px 3px rgb(${color} / 0.12)`,
`0px 6px 7px -4px rgb(${color} / 0.2),0px 11px 15px 1px rgb(${color} / 0.14),0px 4px 20px 3px rgb(${color} / 0.12)`,
`0px 7px 8px -4px rgb(${color} / 0.2),0px 12px 17px 2px rgb(${color} / 0.14),0px 5px 22px 4px rgb(${color} / 0.12)`,
`0px 7px 8px -4px rgb(${color} / 0.2),0px 13px 19px 2px rgb(${color} / 0.14),0px 5px 24px 4px rgb(${color} / 0.12)`,
`0px 7px 9px -4px rgb(${color} / 0.2),0px 14px 21px 2px rgb(${color} / 0.14),0px 5px 26px 4px rgb(${color} / 0.12)`,
`0px 8px 9px -5px rgb(${color} / 0.2),0px 15px 22px 2px rgb(${color} / 0.14),0px 6px 28px 5px rgb(${color} / 0.12)`,
`0px 8px 10px -5px rgb(${color} / 0.2),0px 16px 24px 2px rgb(${color} / 0.14),0px 6px 30px 5px rgb(${color} / 0.12)`,
`0px 8px 11px -5px rgb(${color} / 0.2),0px 17px 26px 2px rgb(${color} / 0.14),0px 6px 32px 5px rgb(${color} / 0.12)`,
`0px 9px 11px -5px rgb(${color} / 0.2),0px 18px 28px 2px rgb(${color} / 0.14),0px 7px 34px 6px rgb(${color} / 0.12)`,
`0px 9px 12px -6px rgb(${color} / 0.2),0px 19px 29px 2px rgb(${color} / 0.14),0px 7px 36px 6px rgb(${color} / 0.12)`,
`0px 10px 13px -6px rgb(${color} / 0.2),0px 20px 31px 3px rgb(${color} / 0.14),0px 8px 38px 7px rgb(${color} / 0.12)`,
`0px 10px 13px -6px rgb(${color} / 0.2),0px 21px 33px 3px rgb(${color} / 0.14),0px 8px 40px 7px rgb(${color} / 0.12)`,
`0px 10px 14px -6px rgb(${color} / 0.2),0px 22px 35px 3px rgb(${color} / 0.14),0px 8px 42px 7px rgb(${color} / 0.12)`,
`0px 11px 14px -7px rgb(${color} / 0.2),0px 23px 36px 3px rgb(${color} / 0.14),0px 9px 44px 8px rgb(${color} / 0.12)`,
`0px 11px 15px -7px rgb(${color} / 0.2),0px 24px 38px 3px rgb(${color} / 0.14),0px 9px 46px 8px rgb(${color} / 0.12)`
]
}
export default shadows
+5
View File
@@ -0,0 +1,5 @@
const spacing = {
spacing: (factor: number) => `${0.25 * factor}rem`
}
export default spacing
+88
View File
@@ -0,0 +1,88 @@
// MUI Imports
import type { Theme } from '@mui/material/styles'
const typography = (fontFamily: string): Theme['typography'] =>
({
fontFamily:
typeof fontFamily === 'undefined' || fontFamily === ''
? [
'Inter',
'sans-serif',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"'
].join(',')
: fontFamily,
fontSize: 13.125,
h1: {
fontSize: '2.875rem',
fontWeight: 500,
lineHeight: 1.478261
},
h2: {
fontSize: '2.375rem',
fontWeight: 500,
lineHeight: 1.47368421
},
h3: {
fontSize: '1.75rem',
fontWeight: 500,
lineHeight: 1.5
},
h4: {
fontSize: '1.5rem',
fontWeight: 500,
lineHeight: 1.58334
},
h5: {
fontSize: '1.125rem',
fontWeight: 500,
lineHeight: 1.5556
},
h6: {
fontSize: '0.9375rem',
fontWeight: 500,
lineHeight: 1.46667
},
subtitle1: {
fontSize: '0.9375rem',
lineHeight: 1.46667
},
subtitle2: {
fontSize: '0.8125rem',
fontWeight: 400,
lineHeight: 1.53846154
},
body1: {
fontSize: '0.9375rem',
lineHeight: 1.46667
},
body2: {
fontSize: '0.8125rem',
lineHeight: 1.53846154
},
button: {
fontSize: '0.9375rem',
lineHeight: 1.46667,
textTransform: 'none'
},
caption: {
fontSize: '0.8125rem',
lineHeight: 1.38462,
letterSpacing: '0.4px'
},
overline: {
fontSize: '0.75rem',
lineHeight: 1.16667,
letterSpacing: '0.8px'
}
}) as Theme['typography']
export default typography
+16
View File
@@ -0,0 +1,16 @@
// React Imports
import type { ReactNode } from 'react'
export type Skin = 'default' | 'bordered'
export type Mode = 'light' | 'dark'
export type SystemMode = 'light' | 'dark'
export type Direction = 'ltr' | 'rtl'
export type ChildrenType = {
children: ReactNode
}
export type ThemeColor = 'primary' | 'secondary' | 'error' | 'warning' | 'info' | 'success'
@@ -0,0 +1,18 @@
export type SystemConfig = {
id: string;
sessionCleanupIntervalMinutes: number;
invoiceGenerationDayOfMonth: number;
tokenExpirationMinutes: number;
tokenCleanupIntervalMinutes: number;
profilePictureUpdateIntervalDays: number;
createdAt: string;
updatedAt: string;
};
export type UpdateSystemConfigDto = {
sessionCleanupIntervalMinutes?: number;
invoiceGenerationDayOfMonth?: number;
tokenExpirationMinutes?: number;
tokenCleanupIntervalMinutes?: number;
profilePictureUpdateIntervalDays?: number;
};
@@ -0,0 +1,40 @@
import 'server-only'
// Next Imports
import { cookies } from 'next/headers'
// Type Imports
import type { Settings } from '@core/contexts/settingsContext'
import type { SystemMode } from '@core/types'
// Config Imports
import themeConfig from '@configs/themeConfig'
export const getSettingsFromCookie = (): Settings => {
const cookieStore = cookies()
const cookieName = themeConfig.settingsCookieName
return JSON.parse(cookieStore.get(cookieName)?.value || '{}')
}
export const getMode = () => {
const settingsCookie = getSettingsFromCookie()
// Get mode from cookie or fallback to theme config
const _mode = settingsCookie.mode || themeConfig.mode
return _mode
}
export const getSystemMode = (): SystemMode => {
const mode = getMode()
return mode
}
export const getServerMode = () => {
const mode = getMode()
return mode
}
+16
View File
@@ -0,0 +1,16 @@
'use client'
// Third-party Imports
import classnames from 'classnames'
// Type Imports
import type { ChildrenType } from '@core/types'
// Util Imports
import { blankLayoutClasses } from './utils/layoutClasses'
const BlankLayout = ({ children }: ChildrenType) => {
return <div className={classnames(blankLayoutClasses.root, 'is-full bs-full')}>{children}</div>
}
export default BlankLayout
@@ -0,0 +1,11 @@
'use client'
// React Imports
import type { ReactElement } from 'react'
const LayoutWrapper = ({ verticalLayout }: { verticalLayout: ReactElement }) => {
// Return the layout based on the layout context
return <div className='flex flex-col flex-auto'>{verticalLayout}</div>
}
export default LayoutWrapper
@@ -0,0 +1,39 @@
// React Imports
import type { ReactNode } from 'react'
// Third-party Imports
import classnames from 'classnames'
// Type Imports
import type { ChildrenType } from '@core/types'
// Component Imports
import LayoutContent from './components/vertical/LayoutContent'
// Util Imports
import { verticalLayoutClasses } from './utils/layoutClasses'
type VerticalLayoutProps = ChildrenType & {
navigation?: ReactNode
navbar?: ReactNode
footer?: ReactNode
}
const VerticalLayout = (props: VerticalLayoutProps) => {
// Props
const { navbar, footer, navigation, children } = props
return (
<div className={classnames(verticalLayoutClasses.root, 'flex flex-auto')}>
{navigation || null}
<div className={classnames(verticalLayoutClasses.contentWrapper, 'flex flex-col min-is-0 is-full')}>
{navbar || null}
{/* Content */}
<LayoutContent>{children}</LayoutContent>
{footer || null}
</div>
</div>
)
}
export default VerticalLayout
@@ -0,0 +1,40 @@
'use client'
// Third-party Imports
import classnames from 'classnames'
import type { CSSObject } from '@emotion/styled'
// Type Imports
import type { ChildrenType } from '@core/types'
// Util Imports
import { verticalLayoutClasses } from '@layouts/utils/layoutClasses'
// Styled Component Imports
import StyledFooter from '@layouts/styles/vertical/StyledFooter'
type Props = ChildrenType & {
overrideStyles?: CSSObject
}
const Footer = (props: Props) => {
// Props
const { children, overrideStyles } = props
return (
<StyledFooter
overrideStyles={overrideStyles}
className={classnames(
verticalLayoutClasses.footer,
verticalLayoutClasses.footerContentCompact,
verticalLayoutClasses.footerStatic,
verticalLayoutClasses.footerDetached,
'is-full'
)}
>
<div className={verticalLayoutClasses.footerContentWrapper}>{children}</div>
</StyledFooter>
)
}
export default Footer

Some files were not shown because too many files have changed in this diff Show More