Implement internationalization for account settings and search components. Added translation support for various UI elements, including billing, notifications, and user dropdowns. Refactored search suggestions and no result messages to utilize localized text. Enhanced user experience by ensuring all relevant components display text based on selected language.

This commit is contained in:
2026-02-19 17:41:37 +03:30
parent 0844100613
commit 25344a8738
14 changed files with 646 additions and 415 deletions
+223 -1
View File
@@ -6,6 +6,8 @@
"close": "بستن"
},
"login": {
"title": "ورود",
"description": "ورود به حساب کاربری",
"welcome": "خوش آمدید به {templateName}! 👋🏻",
"phoneStep": "شماره موبایل خود را برای دریافت کد OTP وارد کنید",
"otpStep": "کد OTP ارسال شده به موبایل خود را وارد کنید",
@@ -211,6 +213,226 @@
"confirmDelete": "لطفاً تایید کنید که می‌خواهید حساب را حذف کنید",
"saveChanges": "ذخیره تغییرات",
"saving": "در حال ذخیره...",
"reset": "بازنشانی"
"reset": "بازنشانی",
"security": {
"changePassword": "تغییر رمز عبور",
"currentPassword": "رمز عبور فعلی",
"newPassword": "رمز عبور جدید",
"confirmNewPassword": "تأیید رمز عبور جدید",
"passwordRequirements": "الزامات رمز عبور",
"passwordReq1": "حداقل ۸ کاراکتر - هر چه بیشتر بهتر",
"passwordReq2": "حداقل یک حرف کوچک و یک حرف بزرگ",
"passwordReq3": "حداقل یک عدد، نماد یا کاراکتر فاصله",
"createApiKey": "ایجاد کلید API",
"apiKeyType": "نوع کلید API را انتخاب کنید",
"apiKeyName": "نام کلید API",
"createKey": "ایجاد کلید",
"fullControl": "کنترل کامل",
"modify": "تغییر",
"readExecute": "خواندن و اجرا",
"listFolderContents": "فهرست محتویات پوشه",
"readOnly": "فقط خواندن",
"readWrite": "خواندن و نوشتن"
},
"billing": {
"currentPlan": "پلن فعلی",
"yourPlanBasic": "پلن فعلی شما پایه است",
"simpleStart": "شروع ساده برای همه",
"activeUntil": "فعال تا {date}",
"subscriptionExpiry": "اعلان انقضای اشتراک ارسال می‌شود",
"perMonth": "{amount} در ماه",
"popular": "محبوب",
"standardPlan": "پلن استاندارد برای کسب‌وکارهای کوچک و متوسط",
"attention": "توجه شما را می‌خواهیم!",
"planRequiresUpdate": "پلن شما نیاز به بروزرسانی دارد",
"days": "روزها",
"daysProgress": "{current} از {total} روز",
"daysRemaining": "{count} روز تا بروزرسانی پلن باقی مانده",
"upgradePlan": "ارتقای پلن",
"cancelSubscription": "لغو اشتراک",
"billingAddress": "آدرس صورتحساب",
"companyName": "نام شرکت",
"billingEmail": "ایمیل صورتحساب",
"taxId": "شناسه مالیاتی",
"vatNumber": "شماره ارزش افزوده",
"mobileNumber": "شماره موبایل",
"country": "کشور",
"selectCountry": "انتخاب کشور",
"australia": "استرالیا",
"canada": "کانادا",
"france": "فرانسه",
"unitedKingdom": "بریتانیا",
"unitedStates": "آمریکا",
"state": "ایالت",
"zipCode": "کد پستی",
"discard": "انصراف"
},
"notifications": {
"recentDevices": "دستگاه‌های اخیر",
"permissionRequest": "برای نمایش اعلان‌ها به مجوز مرورگر نیاز داریم",
"requestPermission": "درخواست مجوز",
"type": "نوع",
"whenToSend": "چه زمانی اعلان ارسال شود؟",
"onlyWhenOnline": "فقط وقتی آنلاین هستم",
"anytime": "هر زمان",
"discard": "انصراف"
}
},
"search": {
"placeholder": "جستجو ⌘K",
"toNavigate": "برای ناوبری",
"toOpen": "برای باز کردن",
"toClose": "برای بستن",
"noResult": "نتیجه‌ای برای «{searchValue}» یافت نشد",
"trySearching": "جستجو برای",
"popularSearches": "جستجوهای پرطرفدار",
"sections": {
"dashboards": "داشبوردها",
"frontPages": "صفحات فرانت",
"apps": "اپلیکیشن‌ها",
"pages": "صفحات",
"formsAndCharts": "فرم‌ها و چارت‌ها",
"formsAndTables": "فرم‌ها و جداول",
"charts": "چارت‌ها",
"others": "سایر",
"foundation": "پایه",
"components": "کامپوننت‌ها"
},
"items": {
"analyticsDashboard": "داشبورد تحلیل",
"ecommerceDashboard": "داشبورد فروشگاه",
"academyDashboard": "داشبورد آکادمی",
"logisticsDashboard": "داشبورد لجستیک",
"landingFront": "صفحه اصلی فرانت",
"pricingFront": "قیمت‌گذاری فرانت",
"paymentFront": "پرداخت فرانت",
"checkoutFront": "تسویه فرانت",
"helpCenterFront": "مرکز راهنمایی فرانت",
"ecommerceDashboardApp": "فروشگاه - داشبورد",
"ecommerceProductList": "فروشگاه - فهرست محصولات",
"ecommerceAddProduct": "فروشگاه - افزودن محصول",
"ecommerceProductCategory": "فروشگاه - دسته‌بندی محصولات",
"ecommerceOrderList": "فروشگاه - فهرست سفارشات",
"ecommerceOrderDetails": "فروشگاه - جزئیات سفارش",
"ecommerceCustomerList": "فروشگاه - فهرست مشتریان",
"ecommerceCustomerDetails": "فروشگاه - جزئیات مشتری",
"ecommerceManageReviews": "فروشگاه - مدیریت نظرات",
"ecommerceReferrals": "فروشگاه - معرفی‌ها",
"ecommerceSettings": "فروشگاه - تنظیمات",
"academyDashboardApp": "آکادمی - داشبورد",
"academyMyCourses": "آکادمی - دوره‌های من",
"academyCourseDetails": "آکادمی - جزئیات دوره",
"logisticsDashboardApp": "لجستیک - داشبورد",
"logisticsFleet": "لجستیک - ناوگان",
"email": "ایمیل",
"chat": "چت",
"calendar": "تقویم",
"kanban": "کانبان",
"todo": "وظایف",
"invoiceList": "فهرست فاکتور",
"invoicePreview": "پیش‌نمایش فاکتور",
"invoiceAdd": "افزودن فاکتور",
"invoiceEdit": "ویرایش فاکتور",
"userList": "فهرست کاربران",
"userView": "مشاهده کاربر",
"roles": "نقش‌ها",
"rolesPermissions": "نقش‌ها و دسترسی‌ها",
"permissions": "دسترسی‌ها",
"userProfile": "پروفایل کاربر",
"formLayouts": "چیدمان فرم",
"formValidation": "اعتبارسنجی فرم",
"formWizard": "ویزارد فرم",
"apexCharts": "چارت Apex",
"analytics": "تحلیل‌ها",
"todo": "وظایف",
"accountSettings": "تنظیمات حساب",
"faq": "سوالات متداول",
"pricing": "قیمت‌گذاری",
"comingSoon": "به زودی",
"underMaintenance": "در حال تعمیر",
"pageNotFound404": "صفحه یافت نشد - 404",
"notAuthorized401": "غیرمجاز - 401",
"loginV1": "ورود نسخه 1",
"loginV2": "ورود نسخه 2",
"registerV1": "ثبت نام نسخه 1",
"registerV2": "ثبت نام نسخه 2",
"registerMultiSteps": "ثبت نام چند مرحله‌ای",
"forgotPasswordV1": "فراموشی رمز نسخه 1",
"forgotPasswordV2": "فراموشی رمز نسخه 2",
"resetPasswordV1": "بازنشانی رمز نسخه 1",
"resetPasswordV2": "بازنشانی رمز نسخه 2",
"verifyEmailV1": "تایید ایمیل نسخه 1",
"verifyEmailV2": "تایید ایمیل نسخه 2",
"twoStepsV1": "دو مرحله‌ای نسخه 1",
"twoStepsV2": "دو مرحله‌ای نسخه 2",
"wizardCheckout": "ویزارد تسویه",
"propertyListing": "فهرست املاک",
"createDeal": "ایجاد معامله",
"dialogExamples": "مثال‌های دیالوگ",
"basic": "پایه",
"advanced": "پیشرفته",
"statistics": "آمار",
"charts": "چارت‌ها",
"actions": "عملیات",
"reactTable": "جدول React",
"recharts": "Recharts",
"menuExamples": "مثال‌های منو",
"typography": "تایپوگرافی",
"colors": "رنگ‌ها",
"shadows": "سایه‌ها",
"icons": "آیکون‌ها",
"accordion": "آکاردئون",
"alerts": "هشدارها",
"avatars": "آواتارها",
"badges": "نشان‌ها",
"buttons": "دکمه‌ها",
"buttonGroup": "گروه دکمه",
"chips": "تراشه‌ها",
"dialogs": "دیالوگ‌ها",
"list": "فهرست",
"menu": "منو",
"pagination": "صفحه‌بندی",
"progress": "پیشرفت",
"ratings": "امتیازات",
"snackbar": "اسنک بار",
"swiper": "اسلایدر",
"tabs": "تب‌ها",
"timeline": "خط زمانی",
"toasts": "اعلان‌ها",
"moreComponents": "کامپوننت‌های بیشتر",
"textField": "فیلد متن",
"select": "انتخاب",
"checkbox": "چک‌باکس",
"radio": "رادیو",
"customInputs": "ورودی‌های سفارشی",
"textarea": "ناحیه متن",
"autocomplete": "خودتکمیل",
"dateTimePickers": "انتخابگر تاریخ و زمان",
"switch": "سوئیچ",
"fileUploader": "آپلود فایل",
"editor": "ویرایشگر",
"slider": "اسلایدر",
"muiTables": "جداول MUI"
}
},
"theme": {
"colorPalette": "پالت رنگ",
"previewMode": "حالت پیش‌نمایش",
"light": "روشن",
"dark": "تاریک",
"lightMode": "حالت روشن",
"darkMode": "حالت تاریک",
"main": "اصلی",
"palettes": {
"purple": "پالت بنفش",
"teal": "پالت فیروزه‌ای",
"orange": "پالت نارنجی",
"pink": "پالت صورتی",
"blue": "پالت آبی"
}
},
"userDropdown": {
"myProfile": "پروفایل من",
"logout": "خروج"
}
}
@@ -1,5 +1,6 @@
// Next Imports
import type { Metadata } from 'next'
import { getTranslations } from 'next-intl/server'
// Component Imports
import Login from '@views/Login'
@@ -7,9 +8,13 @@ import Login from '@views/Login'
// Server Action Imports
import { getServerMode } from '@core/utils/serverHelpers'
export const metadata: Metadata = {
title: 'Login',
description: 'Login to your account'
export async function generateMetadata(): Promise<Metadata> {
const t = await getTranslations('login')
return {
title: t('title'),
description: t('description')
}
}
const LoginPage = async () => {
@@ -2,6 +2,7 @@
// React Imports
import { useRef, useState, useMemo } from 'react'
import { useTranslations } from 'next-intl'
// MUI Imports
import Tooltip from '@mui/material/Tooltip'
@@ -26,6 +27,8 @@ import { useSettings } from '@core/hooks/useSettings'
import type { SystemMode } from '@core/types'
const ThemeColorDropdown = () => {
const t = useTranslations('theme')
// States
const [open, setOpen] = useState(false)
const [tooltipOpen, setTooltipOpen] = useState(false)
@@ -84,7 +87,7 @@ const ThemeColorDropdown = () => {
return (
<>
<Tooltip
title='Color Palette'
title={t('colorPalette')}
onOpen={() => setTooltipOpen(true)}
onClose={() => setTooltipOpen(false)}
open={open ? false : tooltipOpen ? true : false}
@@ -112,7 +115,7 @@ const ThemeColorDropdown = () => {
{/* Mode Preview Selection */}
<Box className='pis-4 pie-4 pbs-2 pbe-2'>
<Typography variant='caption' className='text-textSecondary uppercase'>
Preview Mode
{t('previewMode')}
</Typography>
<Box className='flex gap-2 mts-2'>
<MenuItem
@@ -122,7 +125,7 @@ const ThemeColorDropdown = () => {
sx={{ minHeight: 'auto', py: 1 }}
>
<i className='tabler-sun text-sm' />
<span className='text-sm'>Light</span>
<span className='text-sm'>{t('light')}</span>
</MenuItem>
<MenuItem
className='flex-1 gap-2 rounded'
@@ -131,7 +134,7 @@ const ThemeColorDropdown = () => {
sx={{ minHeight: 'auto', py: 1 }}
>
<i className='tabler-moon-stars text-sm' />
<span className='text-sm'>Dark</span>
<span className='text-sm'>{t('dark')}</span>
</MenuItem>
</Box>
</Box>
@@ -153,7 +156,7 @@ const ThemeColorDropdown = () => {
<Box
className='is-8 bs-8 rounded-full border border-border flex items-center justify-center'
style={{ backgroundColor: palette.light.main }}
title='Light Mode'
title={t('lightMode')}
>
<i className='tabler-sun text-[10px] text-white' style={{ opacity: 0.7 }} />
</Box>
@@ -161,28 +164,28 @@ const ThemeColorDropdown = () => {
<Box
className='is-8 bs-8 rounded-full border border-border flex items-center justify-center'
style={{ backgroundColor: palette.dark.main }}
title='Dark Mode'
title={t('darkMode')}
>
<i className='tabler-moon-stars text-[10px] text-white' style={{ opacity: 0.7 }} />
</Box>
</Box>
<Box className='flex flex-col flex-1'>
<span className='text-sm font-medium'>{palette.name}</span>
<span className='text-sm font-medium'>{t(`palettes.${palette.paletteKey}`)}</span>
<Box className='flex items-center gap-1 mts-1'>
<Box
className='is-4 bs-4 rounded-full border border-border'
style={{ backgroundColor: colors.main }}
title='Main'
title={t('main')}
/>
<Box
className='is-4 bs-4 rounded-full border border-border'
style={{ backgroundColor: colors.light }}
title='Light'
title={t('light')}
/>
<Box
className='is-4 bs-4 rounded-full border border-border'
style={{ backgroundColor: colors.dark }}
title='Dark'
title={t('dark')}
/>
</Box>
</Box>
@@ -6,6 +6,7 @@ import type { MouseEvent } from 'react'
// Next Imports
import { useRouter } from 'next/navigation'
import { useTranslations } from 'next-intl'
// MUI Imports
import { styled } from '@mui/material/styles'
@@ -38,6 +39,8 @@ const BadgeContentSpan = styled('span')({
})
const UserDropdown = () => {
const t = useTranslations('userDropdown')
// States
const [open, setOpen] = useState(false)
@@ -127,7 +130,7 @@ const UserDropdown = () => {
<Divider className='mlb-1' />
<MenuItem className='mli-2 gap-3' onClick={e => handleDropdownClose(e, '/pages/account-settings')}>
<i className='tabler-user' />
<Typography color='text.primary'>My Profile</Typography>
<Typography color='text.primary'>{t('myProfile')}</Typography>
</MenuItem>
<div className='flex items-center plb-2 pli-3'>
<Button
@@ -139,7 +142,7 @@ const UserDropdown = () => {
onClick={handleUserLogout}
sx={{ '& .MuiButton-endIcon': { marginInlineStart: 1.5 } }}
>
Logout
{t('logout')}
</Button>
</div>
</MenuList>
@@ -1,13 +1,16 @@
'use client'
// Next Imports
import Link from 'next/link'
import { useTranslations } from 'next-intl'
// Third-party Imports
import classnames from 'classnames'
type DefaultSuggestionsType = {
sectionLabel: string
sectionLabelKey: string
items: {
label: string
labelKey: string
href: string
icon?: string
}[]
@@ -15,89 +18,40 @@ type DefaultSuggestionsType = {
const defaultSuggestions: DefaultSuggestionsType[] = [
{
sectionLabel: 'Popular Searches',
sectionLabelKey: 'popularSearches',
items: [
{
label: 'Analytics',
href: '/dashboards/analytics',
icon: 'tabler-trending-up'
},
{
label: 'eCommerce',
href: '/dashboards/ecommerce',
icon: 'tabler-shopping-cart'
},
{
label: 'User List',
href: '/apps/user/list',
icon: 'tabler-file-description'
}
{ labelKey: 'analytics', href: '/dashboards/analytics', icon: 'tabler-trending-up' },
{ labelKey: 'ecommerceDashboard', href: '/dashboards/ecommerce', icon: 'tabler-shopping-cart' },
{ labelKey: 'userList', href: '/apps/user/list', icon: 'tabler-file-description' }
]
},
{
sectionLabel: 'Apps',
sectionLabelKey: 'apps',
items: [
{
label: 'Calendar',
href: '/apps/calendar',
icon: 'tabler-calendar'
},
{
label: 'Invoice List',
href: '/apps/invoice/list',
icon: 'tabler-file-info'
},
{
label: 'User List',
href: '/apps/user/list',
icon: 'tabler-file-invoice'
},
{
label: 'Roles & Permissions',
href: '/apps/roles',
icon: 'tabler-lock'
}
{ labelKey: 'calendar', href: '/apps/calendar', icon: 'tabler-calendar' },
{ labelKey: 'invoiceList', href: '/apps/invoice/list', icon: 'tabler-file-info' },
{ labelKey: 'userList', href: '/apps/user/list', icon: 'tabler-file-invoice' },
{ labelKey: 'rolesPermissions', href: '/apps/roles', icon: 'tabler-lock' }
]
},
{
sectionLabel: 'Pages',
items: [
{
label: 'User Profile',
href: '/pages/user-profile',
icon: 'tabler-user'
}
]
sectionLabelKey: 'pages',
items: [{ labelKey: 'userProfile', href: '/pages/user-profile', icon: 'tabler-user' }]
},
{
sectionLabel: 'Forms & Charts',
sectionLabelKey: 'formsAndCharts',
items: [
{
label: 'Form Layouts',
href: '/forms/form-layouts',
icon: 'tabler-layout'
},
{
label: 'Form Validation',
href: '/forms/form-validation',
icon: 'tabler-checkup-list'
},
{
label: 'Form Wizard',
href: '/forms/form-wizard',
icon: 'tabler-git-merge'
},
{
label: 'Apex Charts',
href: '/charts/apex-charts',
icon: 'tabler-chart-ppf'
}
{ labelKey: 'formLayouts', href: '/forms/form-layouts', icon: 'tabler-layout' },
{ labelKey: 'formValidation', href: '/forms/form-validation', icon: 'tabler-checkup-list' },
{ labelKey: 'formWizard', href: '/forms/form-wizard', icon: 'tabler-git-merge' },
{ labelKey: 'apexCharts', href: '/charts/apex-charts', icon: 'tabler-chart-ppf' }
]
}
]
const DefaultSuggestions = ({ setOpen }: { setOpen: (value: boolean) => void }) => {
const t = useTranslations('search')
return (
<div className='flex grow flex-wrap gap-x-[48px] gap-y-8 plb-14 pli-16 overflow-y-auto overflow-x-hidden bs-full'>
{defaultSuggestions.map((section, index) => (
@@ -106,7 +60,9 @@ const DefaultSuggestions = ({ setOpen }: { setOpen: (value: boolean) => void })
className='flex flex-col justify-center overflow-x-hidden gap-4 basis-full sm:basis-[calc((100%-3rem)/2)]'
>
<p className='text-xs leading-[1.16667] uppercase text-textDisabled tracking-[0.8px]'>
{section.sectionLabel}
{section.sectionLabelKey === 'popularSearches'
? t('popularSearches')
: t(`sections.${section.sectionLabelKey}`)}
</p>
<ul className='flex flex-col gap-4'>
{section.items.map((item, i) => (
@@ -117,7 +73,7 @@ const DefaultSuggestions = ({ setOpen }: { setOpen: (value: boolean) => void })
onClick={() => setOpen(false)}
>
{item.icon && <i className={classnames(item.icon, 'flex text-xl')} />}
<p className='text-[15px] leading-[1.4667] truncate'>{item.label}</p>
<p className='text-[15px] leading-[1.4667] truncate'>{t(`items.${item.labelKey}`)}</p>
</Link>
</li>
))}
@@ -1,36 +1,32 @@
'use client'
// Next Imports
import Link from 'next/link'
import { useTranslations } from 'next-intl'
// Third-party Imports
import classnames from 'classnames'
type NoResultData = {
label: string
labelKey: string
href: string
icon: string
}
const noResultData: NoResultData[] = [
{
label: 'Analytics',
href: '/dashboards/analytics',
icon: 'tabler-chart-pie-2'
},
{
label: 'User Profile',
href: '/pages/user-profile',
icon: 'tabler-user'
},
{ labelKey: 'analytics', href: '/dashboards/analytics', icon: 'tabler-chart-pie-2' },
{ labelKey: 'userProfile', href: '/pages/user-profile', icon: 'tabler-user' }
]
const NoResult = ({ searchValue, setOpen }: { searchValue: string; setOpen: (value: boolean) => void }) => {
const t = useTranslations('search')
return (
<div className='flex items-center justify-center grow flex-wrap plb-14 pli-16 overflow-y-auto overflow-x-hidden bs-full'>
<div className='flex flex-col items-center'>
<i className='tabler-file-alert text-[64px] mbe-2.5' />
<p className='text-lg font-medium leading-[1.55556] mbe-11'>{`No result for "${searchValue}"`}</p>
<p className='text-[15px] leading-[1.4667] mbe-4 text-textDisabled'>Try searching for</p>
<p className='text-lg font-medium leading-[1.55556] mbe-11'>{t('noResult', { searchValue })}</p>
<p className='text-[15px] leading-[1.4667] mbe-4 text-textDisabled'>{t('trySearching')}</p>
<ul className='flex flex-col self-start gap-[18px]'>
{noResultData.map((item, index) => (
<li key={index} className='flex items-center'>
@@ -40,7 +36,7 @@ const NoResult = ({ searchValue, setOpen }: { searchValue: string; setOpen: (val
onClick={() => setOpen(false)}
>
<i className={classnames(item.icon, 'text-xl')} />
<p className='text-[15px] leading-[1.4667] truncate'>{item.label}</p>
<p className='text-[15px] leading-[1.4667] truncate'>{t(`items.${item.labelKey}`)}</p>
</Link>
</li>
))}
+36 -27
View File
@@ -1,11 +1,12 @@
'use client'
// React Imports
import { useEffect, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import type { ReactNode } from 'react'
// Next Imports
import { useRouter, usePathname } from 'next/navigation'
import { useTranslations } from 'next-intl'
// MUI Imports
import IconButton from '@mui/material/IconButton'
@@ -52,28 +53,6 @@ type SearchItemProps = {
onSelect?: () => void
}
// Transform the data to group items by their sections
const transformedData = data.reduce((acc: Section[], item) => {
const existingSection = acc.find(section => section.title === item.section)
const newItem = {
id: item.id,
name: item.name,
url: item.url,
excludeLang: item.excludeLang,
icon: item.icon,
shortcut: item.shortcut
}
if (existingSection) {
existingSection.items.push(newItem)
} else {
acc.push({ title: item.section, items: [newItem] })
}
return acc
}, [])
// SearchItem Component for introduce the shortcut keys
const SearchItem = ({ children, shortcut, value, currentPath, url, onSelect = () => {} }: SearchItemProps) => {
return (
@@ -108,6 +87,8 @@ const getFilteredResults = (sections: Section[]) => {
// Footer component for the search menu
const CommandFooter = () => {
const t = useTranslations('search')
return (
<div cmdk-footer=''>
<div className='flex items-center gap-1'>
@@ -117,23 +98,25 @@ const CommandFooter = () => {
<kbd>
<i className='tabler-arrow-down text-base' />
</kbd>
<span>to navigate</span>
<span>{t('toNavigate')}</span>
</div>
<div className='flex items-center gap-1'>
<kbd>
<i className='tabler-corner-down-left text-base' />
</kbd>
<span>to open</span>
<span>{t('toOpen')}</span>
</div>
<div className='flex items-center gap-1'>
<kbd>esc</kbd>
<span>to close</span>
<span>{t('toClose')}</span>
</div>
</div>
)
}
const NavSearch = () => {
const t = useTranslations('search')
// States
const [open, setOpen] = useState(false)
const [searchValue, setSearchValue] = useState('')
@@ -144,6 +127,32 @@ const NavSearch = () => {
const { settings } = useSettings()
const { isBreakpointReached } = useVerticalNav()
// Transform the data to group items by their sections (with translations)
const transformedData = useMemo(() => {
return data.reduce((acc: Section[], item) => {
const sectionTitle = t(`sections.${item.sectionKey}`)
const itemName = t(`items.${item.nameKey}`)
const existingSection = acc.find(section => section.title === sectionTitle)
const newItem: Item = {
id: item.id,
name: itemName,
url: item.url,
excludeLang: item.excludeLang,
icon: item.icon,
shortcut: item.shortcut
}
if (existingSection) {
existingSection.items.push(newItem)
} else {
acc.push({ title: sectionTitle, items: [newItem] })
}
return acc
}, [])
}, [t])
// When an item is selected from the search results
const onSearchItemSelect = (item: Item) => {
item.url.startsWith('http')
@@ -214,7 +223,7 @@ const NavSearch = () => {
<IconButton className='text-textPrimary' onClick={() => setOpen(true)}>
<i className='tabler-search text-2xl' />
</IconButton>
<div className='whitespace-nowrap select-none text-textDisabled'>Search K</div>
<div className='whitespace-nowrap select-none text-textDisabled'>{t('placeholder')}</div>
</div>
)}
<CommandDialog open={open} onOpenChange={setOpen}>
+12
View File
@@ -6,7 +6,9 @@ export type ColorPalette = {
export type PrimaryColorConfig = {
id: string
paletteKey: string
name: string
main?: string
light: ColorPalette
dark: ColorPalette
}
@@ -15,7 +17,9 @@ export type PrimaryColorConfig = {
const primaryColorConfig: PrimaryColorConfig[] = [
{
id: 'palette-1',
paletteKey: 'purple',
name: 'Purple Palette',
main: '#7367F0',
light: {
main: '#7367F0',
light: '#8F85F3',
@@ -29,7 +33,9 @@ const primaryColorConfig: PrimaryColorConfig[] = [
},
{
id: 'palette-2',
paletteKey: 'teal',
name: 'Teal Palette',
main: '#0D9394',
light: {
main: '#0D9394',
light: '#4EB0B1',
@@ -43,7 +49,9 @@ const primaryColorConfig: PrimaryColorConfig[] = [
},
{
id: 'palette-3',
paletteKey: 'orange',
name: 'Orange Palette',
main: '#FFAB1D',
light: {
main: '#FFAB1D',
light: '#FFC25A',
@@ -57,7 +65,9 @@ const primaryColorConfig: PrimaryColorConfig[] = [
},
{
id: 'palette-4',
paletteKey: 'pink',
name: 'Pink Palette',
main: '#EB3D63',
light: {
main: '#EB3D63',
light: '#F0718D',
@@ -71,7 +81,9 @@ const primaryColorConfig: PrimaryColorConfig[] = [
},
{
id: 'palette-5',
paletteKey: 'blue',
name: 'Blue Palette',
main: '#2092EC',
light: {
main: '#2092EC',
light: '#5CAFF1',
+223 -223
View File
File diff suppressed because it is too large Load Diff
@@ -2,6 +2,7 @@
// React Imports
import { useState } from 'react'
import { useTranslations } from 'next-intl'
// MUI Imports
import Grid from '@mui/material/Grid2'
@@ -16,32 +17,35 @@ import InputAdornment from '@mui/material/InputAdornment'
import CustomTextField from '@core/components/mui/TextField'
const Address = () => {
const t = useTranslations('accountSettings')
const tBilling = useTranslations('accountSettings.billing')
// States
const [state, setState] = useState('')
return (
<Card>
<CardHeader title='Billing Address' />
<CardHeader title={tBilling('billingAddress')} />
<CardContent>
<form>
<Grid container spacing={6}>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField fullWidth label='Company Name' variant='outlined' placeholder='Pixinvent' />
<CustomTextField fullWidth label={tBilling('companyName')} variant='outlined' placeholder='Pixinvent' />
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField fullWidth label='Billing Email' variant='outlined' placeholder='john.doe@example.com' />
<CustomTextField fullWidth label={tBilling('billingEmail')} variant='outlined' placeholder='john.doe@example.com' />
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField fullWidth label='TAX ID' variant='outlined' placeholder='Enter TAX ID' />
<CustomTextField fullWidth label={tBilling('taxId')} variant='outlined' placeholder='Enter TAX ID' />
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField fullWidth label='VAT Number' variant='outlined' placeholder='Enter VAT Number' />
<CustomTextField fullWidth label={tBilling('vatNumber')} variant='outlined' placeholder='Enter VAT Number' />
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField
fullWidth
type='number'
label='Mobile Number'
label={tBilling('mobileNumber')}
placeholder='202 555 0111'
slotProps={{
input: {
@@ -51,28 +55,28 @@ const Address = () => {
/>
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField select fullWidth label='Country' value={state} onChange={e => setState(e.target.value)}>
<MenuItem value=''>Select Country</MenuItem>
<MenuItem value='australia'>Australia</MenuItem>
<MenuItem value='canada'>Canada</MenuItem>
<MenuItem value='france'>France</MenuItem>
<MenuItem value='united-kingdom'>United Kingdom</MenuItem>
<MenuItem value='united-states'>United States</MenuItem>
<CustomTextField select fullWidth label={tBilling('country')} value={state} onChange={e => setState(e.target.value)}>
<MenuItem value=''>{tBilling('selectCountry')}</MenuItem>
<MenuItem value='australia'>{tBilling('australia')}</MenuItem>
<MenuItem value='canada'>{tBilling('canada')}</MenuItem>
<MenuItem value='france'>{tBilling('france')}</MenuItem>
<MenuItem value='united-kingdom'>{tBilling('unitedKingdom')}</MenuItem>
<MenuItem value='united-states'>{tBilling('unitedStates')}</MenuItem>
</CustomTextField>
</Grid>
<Grid size={{ xs: 12 }}>
<CustomTextField fullWidth label='Billing Address' variant='outlined' placeholder='Billing Address' />
<CustomTextField fullWidth label={tBilling('billingAddress')} variant='outlined' placeholder={tBilling('billingAddress')} />
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField fullWidth label='State' variant='outlined' placeholder='California' />
<CustomTextField fullWidth label={tBilling('state')} variant='outlined' placeholder='California' />
</Grid>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField fullWidth type='number' label='Zip Code' variant='outlined' placeholder='231465' />
<CustomTextField fullWidth type='number' label={tBilling('zipCode')} variant='outlined' placeholder='231465' />
</Grid>
<Grid size={{ xs: 12 }} className='flex gap-4 flex-wrap'>
<Button variant='contained'>Save Changes</Button>
<Button variant='contained'>{t('saveChanges')}</Button>
<Button variant='tonal' type='reset' color='secondary' onClick={() => setState('')}>
Discard
{tBilling('discard')}
</Button>
</Grid>
</Grid>
@@ -1,3 +1,7 @@
'use client'
import { useTranslations } from 'next-intl'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
@@ -21,6 +25,9 @@ import UpgradePlan from '@components/dialogs/upgrade-plan'
import OpenDialogOnElementClick from '@components/dialogs/OpenDialogOnElementClick'
const CurrentPlan = ({ data }: { data?: PricingPlanType[] }) => {
const t = useTranslations('accountSettings')
const tBilling = useTranslations('accountSettings.billing')
const buttonProps = (children: string, color: ThemeColor, variant: ButtonProps['variant']): ButtonProps => ({
children,
variant,
@@ -29,60 +36,60 @@ const CurrentPlan = ({ data }: { data?: PricingPlanType[] }) => {
return (
<Card>
<CardHeader title='Current Plan' />
<CardHeader title={tBilling('currentPlan')} />
<CardContent>
<Grid container spacing={6}>
<Grid size={{ xs: 12, md: 6 }} className='flex flex-col gap-6'>
<div className='flex flex-col gap-1'>
<Typography color='text.primary' className='font-medium'>
Your Current Plan is Basic
{tBilling('yourPlanBasic')}
</Typography>
<Typography>A simple start for everyone</Typography>
<Typography>{tBilling('simpleStart')}</Typography>
</div>
<div className='flex flex-col gap-1'>
<Typography color='text.primary' className='font-medium'>
Active until Dec 09, 2021
{tBilling('activeUntil', { date: 'Dec 09, 2021' })}
</Typography>
<Typography>We will send you a notification upon Subscription expiration</Typography>
<Typography>{tBilling('subscriptionExpiry')}</Typography>
</div>
<div className='flex flex-col gap-1'>
<div className='flex items-center gap-1.5'>
<Typography color='text.primary' className='font-medium'>
$199 Per Month
{tBilling('perMonth', { amount: '$199' })}
</Typography>
<Chip color='primary' variant='tonal' label='Popular' size='small' />
<Chip color='primary' variant='tonal' label={tBilling('popular')} size='small' />
</div>
<Typography>Standard plan for small to medium businesses</Typography>
<Typography>{tBilling('standardPlan')}</Typography>
</div>
</Grid>
<Grid size={{ xs: 12, md: 6 }} className='flex flex-col gap-6'>
<Alert severity='warning'>
<AlertTitle>We need your attention!</AlertTitle>
Your plan requires update
<AlertTitle>{tBilling('attention')}</AlertTitle>
{tBilling('planRequiresUpdate')}
</Alert>
<div className='flex flex-col gap-1'>
<div className='flex items-center justify-between'>
<Typography color='text.primary' className='font-medium'>
Days
{tBilling('days')}
</Typography>
<Typography color='text.primary' className='font-medium'>
12 of 30 Days
{tBilling('daysProgress', { current: '12', total: '30' })}
</Typography>
</div>
<LinearProgress variant='determinate' value={20} />
<Typography variant='body2'>18 days remaining until your plan requires update</Typography>
<Typography variant='body2'>{tBilling('daysRemaining', { count: '18' })}</Typography>
</div>
</Grid>
<Grid size={{ xs: 12 }} className='flex gap-4 flex-wrap'>
<OpenDialogOnElementClick
element={Button}
elementProps={buttonProps('Upgrade Plan', 'primary', 'contained')}
elementProps={buttonProps(tBilling('upgradePlan'), 'primary', 'contained')}
dialog={UpgradePlan}
dialogProps={{ data: data }}
/>
<OpenDialogOnElementClick
element={Button}
elementProps={buttonProps('Cancel Subscription', 'error', 'tonal')}
elementProps={buttonProps(tBilling('cancelSubscription'), 'error', 'tonal')}
dialog={ConfirmationDialog}
dialogProps={{ type: 'unsubscribe' }}
/>
@@ -1,5 +1,7 @@
'use client'
import { useTranslations } from 'next-intl'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
@@ -54,14 +56,17 @@ const tableData: TableDataType[] = [
]
const Notifications = () => {
const t = useTranslations('accountSettings')
const tNotif = useTranslations('accountSettings.notifications')
return (
<Card>
<CardHeader
title='Recent Devices'
title={tNotif('recentDevices')}
subheader={
<>
We need permission from your browser to show notifications.
<Link className='text-primary'> Request Permission</Link>
{tNotif('permissionRequest')}
<Link className='text-primary'> {tNotif('requestPermission')}</Link>
</>
}
/>
@@ -70,8 +75,8 @@ const Notifications = () => {
<table className={tableStyles.table}>
<thead>
<tr>
<th>Type</th>
<th>Email</th>
<th>{tNotif('type')}</th>
<th>{t('email')}</th>
<th>Browser</th>
<th>App</th>
</tr>
@@ -97,20 +102,20 @@ const Notifications = () => {
</table>
</div>
<CardContent>
<Typography className='mbe-6 font-medium'>When should we send you notifications?</Typography>
<Typography className='mbe-6 font-medium'>{tNotif('whenToSend')}</Typography>
<Grid container spacing={6}>
<Grid size={{ xs: 12, sm: 6, md: 4 }}>
<CustomTextField select fullWidth defaultValue='online'>
<MenuItem value='online'>Only when I&#39;m online</MenuItem>
<MenuItem value='anytime'>Anytime</MenuItem>
<MenuItem value='online'>{tNotif('onlyWhenOnline')}</MenuItem>
<MenuItem value='anytime'>{tNotif('anytime')}</MenuItem>
</CustomTextField>
</Grid>
<Grid size={{ xs: 12 }} className='flex gap-4 flex-wrap'>
<Button variant='contained' type='submit'>
Save Changes
{t('saveChanges')}
</Button>
<Button variant='tonal' color='secondary' type='reset'>
Discard
{tNotif('discard')}
</Button>
</Grid>
</Grid>
@@ -2,6 +2,7 @@
// React Imports
import { useState } from 'react'
import { useTranslations } from 'next-intl'
// MUI Imports
import Card from '@mui/material/Card'
@@ -17,6 +18,9 @@ import Button from '@mui/material/Button'
import CustomTextField from '@core/components/mui/TextField'
const ChangePasswordCard = () => {
const t = useTranslations('accountSettings')
const tSecurity = useTranslations('accountSettings.security')
// States
const [isCurrentPasswordShown, setIsCurrentPasswordShown] = useState(false)
const [isConfirmPasswordShown, setIsConfirmPasswordShown] = useState(false)
@@ -28,14 +32,14 @@ const ChangePasswordCard = () => {
return (
<Card>
<CardHeader title='Change Password' />
<CardHeader title={tSecurity('changePassword')} />
<CardContent>
<form>
<Grid container spacing={6}>
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField
fullWidth
label='Current Password'
label={tSecurity('currentPassword')}
type={isCurrentPasswordShown ? 'text' : 'password'}
placeholder='············'
slotProps={{
@@ -60,7 +64,7 @@ const ChangePasswordCard = () => {
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField
fullWidth
label='New Password'
label={tSecurity('newPassword')}
type={isNewPasswordShown ? 'text' : 'password'}
placeholder='············'
slotProps={{
@@ -83,7 +87,7 @@ const ChangePasswordCard = () => {
<Grid size={{ xs: 12, sm: 6 }}>
<CustomTextField
fullWidth
label='Confirm New Password'
label={tSecurity('confirmNewPassword')}
type={isConfirmPasswordShown ? 'text' : 'password'}
placeholder='············'
slotProps={{
@@ -104,26 +108,26 @@ const ChangePasswordCard = () => {
/>
</Grid>
<Grid size={{ xs: 12 }} className='flex flex-col gap-4'>
<Typography variant='h6'>Password Requirements:</Typography>
<Typography variant='h6'>{tSecurity('passwordRequirements')}</Typography>
<div className='flex flex-col gap-4'>
<div className='flex items-center gap-2.5'>
<i className='tabler-circle-filled text-[8px]' />
Minimum 8 characters long - the more, the better
{tSecurity('passwordReq1')}
</div>
<div className='flex items-center gap-2.5'>
<i className='tabler-circle-filled text-[8px]' />
At least one lowercase & one uppercase character
{tSecurity('passwordReq2')}
</div>
<div className='flex items-center gap-2.5'>
<i className='tabler-circle-filled text-[8px]' />
At least one number, symbol, or whitespace character
{tSecurity('passwordReq3')}
</div>
</div>
</Grid>
<Grid size={{ xs: 12 }} className='flex gap-4'>
<Button variant='contained'>Save Changes</Button>
<Button variant='contained'>{t('saveChanges')}</Button>
<Button variant='tonal' type='reset' color='secondary'>
Reset
{t('reset')}
</Button>
</Grid>
</Grid>
@@ -1,5 +1,7 @@
'use client'
import { useTranslations } from 'next-intl'
// MUI Imports
import Card from '@mui/material/Card'
import CardHeader from '@mui/material/CardHeader'
@@ -12,24 +14,27 @@ import MenuItem from '@mui/material/MenuItem'
import CustomTextField from '@core/components/mui/TextField'
const CreateApiKey = () => {
const t = useTranslations('accountSettings')
const tSecurity = useTranslations('accountSettings.security')
return (
<Card>
<CardHeader title='Create an API Key' />
<CardHeader title={tSecurity('createApiKey')} />
<CardContent className='!pb-0'>
<Grid container spacing={6}>
<Grid size={{ xs: 12, md: 6 }}>
<form className='flex justify-end items-end bs-full flex-col gap-5 pbe-6'>
<CustomTextField select fullWidth label='Choose the API key type you want to create' defaultValue=''>
<MenuItem value='full-control'>Full Control</MenuItem>
<MenuItem value='modify'>Modify</MenuItem>
<MenuItem value='read-execute'>Read & Execute</MenuItem>
<MenuItem value='list-folder-contents'>List Folder Contents</MenuItem>
<MenuItem value='read-only'>Read Only</MenuItem>
<MenuItem value='read-write'>Read & Write</MenuItem>
<CustomTextField select fullWidth label={tSecurity('apiKeyType')} defaultValue=''>
<MenuItem value='full-control'>{tSecurity('fullControl')}</MenuItem>
<MenuItem value='modify'>{tSecurity('modify')}</MenuItem>
<MenuItem value='read-execute'>{tSecurity('readExecute')}</MenuItem>
<MenuItem value='list-folder-contents'>{tSecurity('listFolderContents')}</MenuItem>
<MenuItem value='read-only'>{tSecurity('readOnly')}</MenuItem>
<MenuItem value='read-write'>{tSecurity('readWrite')}</MenuItem>
</CustomTextField>
<CustomTextField label='Name the API key' placeholder='Server key 1' fullWidth />
<CustomTextField label={tSecurity('apiKeyName')} placeholder='Server key 1' fullWidth />
<Button variant='contained' fullWidth>
Create Key
{tSecurity('createKey')}
</Button>
</form>
</Grid>