diff --git a/CHANGELOG.md b/CHANGELOG.md
index 83187b91e..bebe3620c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+### 📌 **dev 25w21 (5/19/2025 - 5/26/2025)**
+- **Account Settings**: Renamed the folder from `Account` to `Account Settings` for better clarity.
+- **Code**: Module route configs are now stored in a separate file in each module directory and loaded dynamically in the routing mechanism.
+- **Code**: Massive mechanism refactor to load module routes dynamically.
+- **Code**: Renamed `layout` folder to `routes` for better clarity.
+
### 📌 **dev 25w20 (5/12/2025 - 5/19/2025)**
- **Wallet**: Fixed a bug where the order of the transactions doesn't update when the user update the date of a transaction.
- **Wallet**: Fixed a critical bug where the page will crash when user tries to load the dashboard.
diff --git a/src/apps/Achievements/config.tsx b/src/apps/Achievements/config.tsx
new file mode 100644
index 000000000..db29a4c3d
--- /dev/null
+++ b/src/apps/Achievements/config.tsx
@@ -0,0 +1,13 @@
+import { IconAward } from '@tabler/icons-react'
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Achievements',
+ icon: ,
+ routes: {
+ achievements: lazy(() => import('.'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/BooksLibrary/config.tsx b/src/apps/BooksLibrary/config.tsx
new file mode 100644
index 000000000..645b0da99
--- /dev/null
+++ b/src/apps/BooksLibrary/config.tsx
@@ -0,0 +1,12 @@
+import { ModuleConfig } from '@core/routes/interfaces/routes_interfaces'
+import { lazy } from 'react'
+
+export default {
+ name: 'Books Library',
+ icon: 'tabler:books',
+ provider: lazy(() => import('./providers/BooksLibraryProvider')),
+ routes: {
+ '': lazy(() => import('.'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/Calendar/config.tsx b/src/apps/Calendar/config.tsx
new file mode 100644
index 000000000..71d061bbf
--- /dev/null
+++ b/src/apps/Calendar/config.tsx
@@ -0,0 +1,13 @@
+import { IconCalendar } from '@tabler/icons-react'
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Calendar',
+ icon: ,
+ routes: {
+ calendar: lazy(() => import('.'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/CodeTime/config.tsx b/src/apps/CodeTime/config.tsx
new file mode 100644
index 000000000..f45510986
--- /dev/null
+++ b/src/apps/CodeTime/config.tsx
@@ -0,0 +1,11 @@
+import { IconCode } from '@tabler/icons-react'
+import { lazy } from 'react'
+
+export default {
+ name: 'Code Time',
+ icon: ,
+ routes: {
+ 'code-time': lazy(() => import('.'))
+ },
+ togglable: true
+}
diff --git a/src/apps/CurrencyConverter/config.tsx b/src/apps/CurrencyConverter/config.tsx
new file mode 100644
index 000000000..eff999c5b
--- /dev/null
+++ b/src/apps/CurrencyConverter/config.tsx
@@ -0,0 +1,12 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Currency Converter',
+ icon: 'tabler:currency-dollar',
+ routes: {
+ 'currency-converter': lazy(() => import('.'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/GuitarTabs/config.tsx b/src/apps/GuitarTabs/config.tsx
new file mode 100644
index 000000000..3891d17d4
--- /dev/null
+++ b/src/apps/GuitarTabs/config.tsx
@@ -0,0 +1,12 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Guitar Tabs',
+ icon: 'mingcute:guitar-line',
+ routes: {
+ 'guitar-tabs': lazy(() => import('.'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/IdeaBox/config.tsx b/src/apps/IdeaBox/config.tsx
new file mode 100644
index 000000000..7a88b861b
--- /dev/null
+++ b/src/apps/IdeaBox/config.tsx
@@ -0,0 +1,15 @@
+import { IconBulb } from '@tabler/icons-react'
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Idea Box',
+ provider: lazy(() => import('./providers/IdeaBoxProvider')),
+ icon: ,
+ routes: {
+ '': lazy(() => import('./pages/Containers')),
+ ':id/*': lazy(() => import('./pages/Ideas'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/MomentVault/config.tsx b/src/apps/MomentVault/config.tsx
new file mode 100644
index 000000000..6790d745d
--- /dev/null
+++ b/src/apps/MomentVault/config.tsx
@@ -0,0 +1,14 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Moment Vault',
+ icon: 'tabler:history',
+ hasAI: true,
+ routes: {
+ 'moment-vault': lazy(() => import('.'))
+ },
+ togglable: true,
+ requiredAPIKeys: ['openai']
+} satisfies ModuleConfig
diff --git a/src/apps/Movies/config.tsx b/src/apps/Movies/config.tsx
new file mode 100644
index 000000000..614c86eb1
--- /dev/null
+++ b/src/apps/Movies/config.tsx
@@ -0,0 +1,13 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Movies',
+ icon: 'tabler:movie',
+ routes: {
+ movies: lazy(() => import('.'))
+ },
+ togglable: true,
+ requiredAPIKeys: ['tmdb']
+} satisfies ModuleConfig
diff --git a/src/apps/Music/config.tsx b/src/apps/Music/config.tsx
new file mode 100644
index 000000000..f0c06b167
--- /dev/null
+++ b/src/apps/Music/config.tsx
@@ -0,0 +1,12 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Music',
+ icon: 'tabler:music',
+ routes: {
+ music: lazy(() => import('.'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/Passwords/config.tsx b/src/apps/Passwords/config.tsx
new file mode 100644
index 000000000..a5788a72d
--- /dev/null
+++ b/src/apps/Passwords/config.tsx
@@ -0,0 +1,13 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Passwords',
+ icon: 'tabler:key',
+ provider: lazy(() => import('./providers/PasswordsProvider')),
+ routes: {
+ '': lazy(() => import('.'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/RailwayMap/config.tsx b/src/apps/RailwayMap/config.tsx
new file mode 100644
index 000000000..5dc611278
--- /dev/null
+++ b/src/apps/RailwayMap/config.tsx
@@ -0,0 +1,13 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Railway Map',
+ icon: 'uil:subway',
+ provider: lazy(() => import('./providers/RailwayMapProvider')),
+ routes: {
+ '': lazy(() => import('.'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/Sudoku/config.tsx b/src/apps/Sudoku/config.tsx
new file mode 100644
index 000000000..f3073af63
--- /dev/null
+++ b/src/apps/Sudoku/config.tsx
@@ -0,0 +1,12 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Sudoku',
+ icon: 'uil:table',
+ routes: {
+ sudoku: lazy(() => import('.'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/TodoList/config.tsx b/src/apps/TodoList/config.tsx
new file mode 100644
index 000000000..1048c3c3e
--- /dev/null
+++ b/src/apps/TodoList/config.tsx
@@ -0,0 +1,15 @@
+import { IconListCheck } from '@tabler/icons-react'
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Todo List',
+ icon: ,
+ routes: {
+ 'todo-list': lazy(() => import('@apps/TodoList'))
+ },
+ togglable: true,
+ hasAI: true,
+ requiredAPIKeys: ['groq']
+} satisfies ModuleConfig
diff --git a/src/apps/VirtualWardrobe/config.tsx b/src/apps/VirtualWardrobe/config.tsx
new file mode 100644
index 000000000..02db0442f
--- /dev/null
+++ b/src/apps/VirtualWardrobe/config.tsx
@@ -0,0 +1,27 @@
+import { lazy } from 'react'
+import { Navigate } from 'react-router'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Virtual Wardrobe',
+ icon: 'tabler:shirt',
+ subsection: [
+ {
+ name: 'Virtual Wardrobe Clothes',
+ icon: 'tabler:shirt',
+ path: 'clothes'
+ },
+ {
+ name: 'Virtual Wardrobe Outfits',
+ icon: 'tabler:layout',
+ path: 'outfits'
+ }
+ ],
+ routes: {
+ 'virtual-wardrobe': () => ,
+ 'virtual-wardrobe/clothes': lazy(() => import('./pages/Clothes')),
+ 'virtual-wardrobe/outfits': lazy(() => import('./pages/Outfits'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/Wallet/config.tsx b/src/apps/Wallet/config.tsx
new file mode 100644
index 000000000..29c99ba87
--- /dev/null
+++ b/src/apps/Wallet/config.tsx
@@ -0,0 +1,40 @@
+import {
+ IconArrowsExchange,
+ IconBook,
+ IconCurrencyDollar,
+ IconDashboard,
+ IconFileText,
+ IconWallet
+} from '@tabler/icons-react'
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Wallet',
+ icon: ,
+ hasAI: true,
+ subsection: [
+ { name: 'Dashboard', icon: , path: '' },
+ {
+ name: 'Transactions',
+ icon: ,
+ path: 'transactions'
+ },
+ { name: 'Assets', icon: , path: 'assets' },
+ { name: 'Ledgers', icon: , path: 'ledgers' },
+ {
+ name: 'Financial Statements',
+ icon: ,
+ path: 'statements'
+ }
+ ],
+ routes: {
+ wallet: lazy(() => import('./pages/Dashboard')),
+ 'wallet/transactions': lazy(() => import('./pages/Transactions')),
+ 'wallet/assets': lazy(() => import('./pages/Assets')),
+ 'wallet/ledgers': lazy(() => import('./pages/Ledgers')),
+ 'wallet/statements': lazy(() => import('./pages/Statements'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/apps/Wishlist/config.tsx b/src/apps/Wishlist/config.tsx
new file mode 100644
index 000000000..eaa492f81
--- /dev/null
+++ b/src/apps/Wishlist/config.tsx
@@ -0,0 +1,14 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Wishlist',
+ icon: 'tabler:heart',
+ routes: {
+ wishlist: lazy(() => import('./pages/WishlistList')),
+ 'wishlist/:id': lazy(() => import('./pages/WishlistEntries'))
+ },
+ togglable: true,
+ hasAI: true
+} satisfies ModuleConfig
diff --git a/src/apps/YoutubeSummarizer/config.tsx b/src/apps/YoutubeSummarizer/config.tsx
new file mode 100644
index 000000000..8036d91d7
--- /dev/null
+++ b/src/apps/YoutubeSummarizer/config.tsx
@@ -0,0 +1,14 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Youtube Summarizer',
+ icon: 'tabler:brand-youtube',
+ routes: {
+ 'youtube-summarizer': lazy(() => import('.'))
+ },
+ togglable: true,
+ requiredAPIKeys: ['groq'],
+ hasAI: true
+} satisfies ModuleConfig
diff --git a/src/apps/YoutubeVideos/config.tsx b/src/apps/YoutubeVideos/config.tsx
new file mode 100644
index 000000000..9ec5d2014
--- /dev/null
+++ b/src/apps/YoutubeVideos/config.tsx
@@ -0,0 +1,12 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../core/routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Youtube Videos',
+ icon: 'tabler:brand-youtube',
+ routes: {
+ 'youtube-videos': lazy(() => import('.'))
+ },
+ togglable: true
+} satisfies ModuleConfig
diff --git a/src/core/App.tsx b/src/core/App.tsx
index 41050ddef..588a721ba 100644
--- a/src/core/App.tsx
+++ b/src/core/App.tsx
@@ -22,7 +22,7 @@ import '@lifeforge/ui/dist/index.css'
import './i18n'
import './index.css'
-import AppRouter from './layout/index.tsx'
+import AppRouter from './routes/index.tsx'
dayjs.extend(duration)
dayjs.extend(isBetween)
diff --git a/src/core/Routes.tsx b/src/core/Routes.tsx
deleted file mode 100644
index 1360c2bf0..000000000
--- a/src/core/Routes.tsx
+++ /dev/null
@@ -1,382 +0,0 @@
-import {
- IconArrowsExchange,
- IconAward,
- IconBook,
- IconBooks,
- IconBulb,
- IconCalendar,
- IconCode,
- IconCurrencyDollar,
- IconDashboard,
- IconFileText,
- IconHeart,
- IconHistory,
- IconInfoCircle,
- IconKey,
- IconLayout,
- IconListCheck,
- IconMovie,
- IconMusic,
- IconPalette,
- IconPassword,
- IconPlug,
- IconServer,
- IconShirt,
- IconUserCog,
- IconWallet
-} from '@tabler/icons-react'
-import { lazy } from 'react'
-import { Navigate } from 'react-router'
-
-import { RouteCategory } from './layout/interfaces/routes_interfaces'
-
-export const ROUTES: RouteCategory[] = [
- {
- title: '',
- items: [
- {
- name: 'Dashboard',
- icon: ,
- routes: {
- dashboard: lazy(() => import('./pages/Dashboard'))
- },
- togglable: false
- }
- ]
- },
- {
- title: 'Productivity',
- items: [
- {
- name: 'Idea Box',
- provider: lazy(() => import('@apps/IdeaBox/providers/IdeaBoxProvider')),
- icon: ,
- routes: {
- '': lazy(() => import('@apps/IdeaBox/pages/Containers')),
- ':id/*': lazy(() => import('@apps/IdeaBox/pages/Ideas'))
- },
- togglable: true
- },
- {
- name: 'Todo List',
- icon: ,
- routes: {
- 'todo-list': lazy(() => import('@apps/TodoList'))
- },
- togglable: true,
- hasAI: true,
- requiredAPIKeys: ['groq']
- },
- {
- name: 'Calendar',
- icon: ,
- routes: {
- calendar: lazy(() => import('@apps/Calendar'))
- },
- togglable: true
- },
- {
- name: 'Code Time',
- icon: ,
- routes: {
- 'code-time': lazy(() => import('@apps/CodeTime'))
- },
- togglable: true
- }
- ]
- },
- {
- title: 'Study',
- items: []
- },
- {
- title: 'Lifestyle',
- items: [
- {
- name: 'Moment Vault',
- icon: ,
- hasAI: true,
- routes: {
- 'moment-vault': lazy(() => import('@apps/MomentVault'))
- },
- togglable: true,
- requiredAPIKeys: ['openai']
- },
- {
- name: 'Achievements',
- icon: ,
- routes: {
- achievements: lazy(() => import('@apps/Achievements'))
- },
- togglable: true
- },
- {
- name: 'Virtual Wardrobe',
- icon: ,
- subsection: [
- {
- name: 'Virtual Wardrobe Clothes',
- icon: ,
- path: 'clothes'
- },
- {
- name: 'Virtual Wardrobe Outfits',
- icon: ,
- path: 'outfits'
- }
- ],
- routes: {
- 'virtual-wardrobe': () => ,
- 'virtual-wardrobe/clothes': lazy(
- () => import('@apps/VirtualWardrobe/pages/Clothes')
- ),
- 'virtual-wardrobe/outfits': lazy(
- () => import('@apps/VirtualWardrobe/pages/Outfits')
- )
- },
- togglable: true
- },
- {
- name: 'Movies',
- icon: ,
- routes: {
- movies: lazy(() => import('@apps/Movies'))
- },
- togglable: true,
- requiredAPIKeys: ['tmdb']
- }
- ]
- },
- {
- title: 'Finance',
- items: [
- {
- name: 'Wallet',
- icon: ,
- hasAI: true,
- subsection: [
- { name: 'Dashboard', icon: , path: '' },
- {
- name: 'Transactions',
- icon: ,
- path: 'transactions'
- },
- { name: 'Assets', icon: , path: 'assets' },
- { name: 'Ledgers', icon: , path: 'ledgers' },
- {
- name: 'Financial Statements',
- icon: ,
- path: 'statements'
- }
- ],
- routes: {
- wallet: lazy(() => import('@apps/Wallet/pages/Dashboard')),
- 'wallet/transactions': lazy(
- () => import('@apps/Wallet/pages/Transactions')
- ),
- 'wallet/assets': lazy(() => import('@apps/Wallet/pages/Assets')),
- 'wallet/ledgers': lazy(() => import('@apps/Wallet/pages/Ledgers')),
- 'wallet/statements': lazy(
- () => import('@apps/Wallet/pages/Statements')
- )
- },
- togglable: true
- },
- {
- name: 'Wishlist',
- icon: ,
- routes: {
- wishlist: lazy(() => import('@apps/Wishlist/pages/WishlistList')),
- 'wishlist/:id': lazy(
- () => import('@apps/Wishlist/pages/WishlistEntries')
- )
- },
- togglable: true,
- hasAI: true
- }
- ]
- },
- {
- title: 'Storage',
- items: [
- {
- name: 'Books Library',
- icon: ,
- provider: lazy(
- () => import('@apps/BooksLibrary/providers/BooksLibraryProvider')
- ),
- routes: {
- '': lazy(() => import('@apps/BooksLibrary'))
- },
- togglable: true
- },
- {
- name: 'Music',
- icon: ,
- routes: {
- music: lazy(() => import('@apps/Music'))
- },
- togglable: true
- },
- {
- name: 'Guitar Tabs',
- icon: 'mingcute:guitar-line',
- routes: {
- 'guitar-tabs': lazy(() => import('@apps/GuitarTabs'))
- },
- togglable: true
- },
- {
- name: 'Youtube Videos',
- icon: 'tabler:brand-youtube',
- routes: {
- 'youtube-videos': lazy(() => import('@apps/YoutubeVideos'))
- },
- togglable: true
- }
- ]
- },
- {
- title: 'Confidential',
- items: [
- {
- name: 'Passwords',
- icon: ,
- provider: lazy(
- () => import('@apps/Passwords/providers/PasswordsProvider')
- ),
- routes: {
- '': lazy(() => import('@apps/Passwords'))
- },
- togglable: true
- },
- {
- name: 'API Keys',
- icon: ,
- routes: {
- 'api-keys': lazy(() => import('./pages/APIKeys'))
- },
- togglable: false
- }
- ]
- },
- {
- title: 'Information',
- items: [
- {
- name: 'Railway Map',
- icon: 'uil:subway',
- provider: lazy(
- () => import('@apps/RailwayMap/providers/RailwayMapProvider')
- ),
- routes: {
- '': lazy(() => import('@apps/RailwayMap'))
- },
- togglable: true
- }
- ]
- },
- {
- title: 'Utilities',
- items: [
- {
- name: 'Sudoku',
- icon: 'uil:table',
- routes: {
- sudoku: lazy(() => import('@apps/Sudoku'))
- },
- togglable: true
- },
- {
- name: 'Currency Converter',
- icon: ,
- routes: {
- 'currency-converter': lazy(() => import('@apps/CurrencyConverter'))
- },
- togglable: true
- },
- {
- name: 'Youtube Summarizer',
- icon: 'tabler:brand-youtube',
- routes: {
- 'youtube-summarizer': lazy(() => import('@apps/YoutubeSummarizer'))
- },
- togglable: true,
- requiredAPIKeys: ['groq'],
- hasAI: true
- }
- ]
- },
- {
- title: 'Settings',
- items: [
- {
- name: 'Personalization',
- icon: ,
- routes: {
- personalization: lazy(() => import('./pages/Personalization'))
- },
- togglable: false
- },
- {
- name: 'Modules',
- icon: ,
- routes: {
- modules: lazy(() => import('@apps/Modules'))
- },
- togglable: false
- },
- {
- name: 'Server Status',
- icon: ,
- routes: {
- 'server-status': lazy(() => import('./pages/ServerStatus'))
- },
- togglable: false
- }
- ]
- },
- {
- title: 'sso',
- items: [
- {
- name: 'Localization Manager',
- icon: 'mingcute:translate-line',
- routes: {
- 'localization-manager': lazy(
- () => import('./pages/LocalizationManager')
- )
- },
- togglable: false
- }
- ]
- },
- {
- title: '',
- items: [
- {
- name: 'Documentation',
- icon: ,
- routes: {
- documentation: () => {
- window.location.href =
- 'https://docs.lifeforge.melvinchia.dev/getting-started/introduction'
- return
- }
- },
- togglable: false
- },
- {
- name: 'Account Settings',
- icon: ,
- routes: {
- account: lazy(() => import('./pages/Account'))
- },
- togglable: false,
- hidden: true
- }
- ]
- }
-]
-
-export default ROUTES
diff --git a/src/core/pages/Account/components/AvatarColumn.tsx b/src/core/pages/AccountSettings/components/AvatarColumn.tsx
similarity index 100%
rename from src/core/pages/Account/components/AvatarColumn.tsx
rename to src/core/pages/AccountSettings/components/AvatarColumn.tsx
diff --git a/src/core/pages/Account/components/OrdinaryColumn.tsx b/src/core/pages/AccountSettings/components/OrdinaryColumn.tsx
similarity index 100%
rename from src/core/pages/Account/components/OrdinaryColumn.tsx
rename to src/core/pages/AccountSettings/components/OrdinaryColumn.tsx
diff --git a/src/core/pages/Account/components/PasswordColumn.tsx b/src/core/pages/AccountSettings/components/PasswordColumn.tsx
similarity index 100%
rename from src/core/pages/Account/components/PasswordColumn.tsx
rename to src/core/pages/AccountSettings/components/PasswordColumn.tsx
diff --git a/src/core/pages/Account/components/TwoFAColumn.tsx b/src/core/pages/AccountSettings/components/TwoFAColumn.tsx
similarity index 100%
rename from src/core/pages/Account/components/TwoFAColumn.tsx
rename to src/core/pages/AccountSettings/components/TwoFAColumn.tsx
diff --git a/src/core/pages/AccountSettings/config.tsx b/src/core/pages/AccountSettings/config.tsx
new file mode 100644
index 000000000..26f3f52cb
--- /dev/null
+++ b/src/core/pages/AccountSettings/config.tsx
@@ -0,0 +1,13 @@
+import { ModuleConfig } from '@core/routes/interfaces/routes_interfaces'
+import { IconUserCog } from '@tabler/icons-react'
+import { lazy } from 'react'
+
+export default {
+ name: 'Account Settings',
+ icon: ,
+ routes: {
+ account: lazy(() => import('.'))
+ },
+ togglable: false,
+ hidden: true
+} satisfies ModuleConfig
diff --git a/src/core/pages/Account/index.tsx b/src/core/pages/AccountSettings/index.tsx
similarity index 94%
rename from src/core/pages/Account/index.tsx
rename to src/core/pages/AccountSettings/index.tsx
index 2501deeb9..46e2b1f8c 100644
--- a/src/core/pages/Account/index.tsx
+++ b/src/core/pages/AccountSettings/index.tsx
@@ -7,7 +7,7 @@ import PasswordColumn from './components/PasswordColumn'
import TwoFAColumn from './components/TwoFAColumn'
import { AccountSettingsModals } from './modals'
-function Account() {
+function AccountSettings() {
useModalsEffect(AccountSettingsModals)
return (
@@ -35,4 +35,4 @@ function Account() {
)
}
-export default Account
+export default AccountSettings
diff --git a/src/core/pages/Account/modals/DisableTwoFAModal.tsx b/src/core/pages/AccountSettings/modals/DisableTwoFAModal.tsx
similarity index 100%
rename from src/core/pages/Account/modals/DisableTwoFAModal.tsx
rename to src/core/pages/AccountSettings/modals/DisableTwoFAModal.tsx
diff --git a/src/core/pages/Account/modals/EnableTwoFAModal/components/OTPConfirmScreen.tsx b/src/core/pages/AccountSettings/modals/EnableTwoFAModal/components/OTPConfirmScreen.tsx
similarity index 100%
rename from src/core/pages/Account/modals/EnableTwoFAModal/components/OTPConfirmScreen.tsx
rename to src/core/pages/AccountSettings/modals/EnableTwoFAModal/components/OTPConfirmScreen.tsx
diff --git a/src/core/pages/Account/modals/EnableTwoFAModal/components/QRCodeDisplay.tsx b/src/core/pages/AccountSettings/modals/EnableTwoFAModal/components/QRCodeDisplay.tsx
similarity index 100%
rename from src/core/pages/Account/modals/EnableTwoFAModal/components/QRCodeDisplay.tsx
rename to src/core/pages/AccountSettings/modals/EnableTwoFAModal/components/QRCodeDisplay.tsx
diff --git a/src/core/pages/Account/modals/EnableTwoFAModal/components/TwoFAEnableProcedure.tsx b/src/core/pages/AccountSettings/modals/EnableTwoFAModal/components/TwoFAEnableProcedure.tsx
similarity index 100%
rename from src/core/pages/Account/modals/EnableTwoFAModal/components/TwoFAEnableProcedure.tsx
rename to src/core/pages/AccountSettings/modals/EnableTwoFAModal/components/TwoFAEnableProcedure.tsx
diff --git a/src/core/pages/Account/modals/EnableTwoFAModal/index.tsx b/src/core/pages/AccountSettings/modals/EnableTwoFAModal/index.tsx
similarity index 100%
rename from src/core/pages/Account/modals/EnableTwoFAModal/index.tsx
rename to src/core/pages/AccountSettings/modals/EnableTwoFAModal/index.tsx
diff --git a/src/core/pages/Account/modals/ModifyModal.tsx b/src/core/pages/AccountSettings/modals/ModifyModal.tsx
similarity index 100%
rename from src/core/pages/Account/modals/ModifyModal.tsx
rename to src/core/pages/AccountSettings/modals/ModifyModal.tsx
diff --git a/src/core/pages/Account/modals/index.ts b/src/core/pages/AccountSettings/modals/index.ts
similarity index 100%
rename from src/core/pages/Account/modals/index.ts
rename to src/core/pages/AccountSettings/modals/index.ts
diff --git a/src/core/pages/Dashboard/config.tsx b/src/core/pages/Dashboard/config.tsx
new file mode 100644
index 000000000..9000172be
--- /dev/null
+++ b/src/core/pages/Dashboard/config.tsx
@@ -0,0 +1,12 @@
+import { ModuleConfig } from '@core/routes/interfaces/routes_interfaces'
+import { IconDashboard } from '@tabler/icons-react'
+import { lazy } from 'react'
+
+export default {
+ name: 'Dashboard',
+ icon: ,
+ routes: {
+ dashboard: lazy(() => import('.'))
+ },
+ togglable: false
+} satisfies ModuleConfig
diff --git a/src/core/pages/Documentation/config.tsx b/src/core/pages/Documentation/config.tsx
new file mode 100644
index 000000000..b3372a848
--- /dev/null
+++ b/src/core/pages/Documentation/config.tsx
@@ -0,0 +1,16 @@
+import { ModuleConfig } from '@core/routes/interfaces/routes_interfaces'
+import { IconInfoCircle } from '@tabler/icons-react'
+import { Navigate } from 'react-router'
+
+export default {
+ name: 'Documentation',
+ icon: ,
+ routes: {
+ documentation: () => {
+ window.location.href =
+ 'https://docs.lifeforge.melvinchia.dev/getting-started/introduction'
+ return
+ }
+ },
+ togglable: false
+} as ModuleConfig
diff --git a/src/core/pages/LocalizationManager/config.tsx b/src/core/pages/LocalizationManager/config.tsx
new file mode 100644
index 000000000..db786cf55
--- /dev/null
+++ b/src/core/pages/LocalizationManager/config.tsx
@@ -0,0 +1,12 @@
+import { lazy } from 'react'
+
+import { ModuleConfig } from '../../routes/interfaces/routes_interfaces'
+
+export default {
+ name: 'Localization Manager',
+ icon: 'mingcute:translate-line',
+ routes: {
+ 'localization-manager': lazy(() => import('.'))
+ },
+ togglable: false
+} satisfies ModuleConfig
diff --git a/src/apps/Modules/ModuleItem.tsx b/src/core/pages/Modules/components/ModuleItem.tsx
similarity index 95%
rename from src/apps/Modules/ModuleItem.tsx
rename to src/core/pages/Modules/components/ModuleItem.tsx
index 0b782b6e9..f1040a919 100644
--- a/src/apps/Modules/ModuleItem.tsx
+++ b/src/core/pages/Modules/components/ModuleItem.tsx
@@ -1,3 +1,4 @@
+import { ModuleConfig } from '@core/routes/interfaces/routes_interfaces'
import { Icon } from '@iconify/react'
import clsx from 'clsx'
import _ from 'lodash'
@@ -8,14 +9,12 @@ import { Switch } from '@lifeforge/ui'
import useComponentBg from '@hooks/useComponentBg'
-import { RouteItem } from '../../core/layout/interfaces/routes_interfaces'
-
function ModuleItem({
module,
enabled,
toggleModule
}: {
- module: RouteItem
+ module: ModuleConfig
enabled: boolean
toggleModule: (moduleName: string) => void
}) {
diff --git a/src/core/pages/Modules/config.tsx b/src/core/pages/Modules/config.tsx
new file mode 100644
index 000000000..73fa9e2e6
--- /dev/null
+++ b/src/core/pages/Modules/config.tsx
@@ -0,0 +1,11 @@
+import { ModuleConfig } from '@core/routes/interfaces/routes_interfaces'
+import { lazy } from 'react'
+
+export default {
+ name: 'Modules',
+ icon: 'tabler:plug',
+ routes: {
+ modules: lazy(() => import('.'))
+ },
+ togglable: false
+} satisfies ModuleConfig
diff --git a/src/apps/Modules/index.tsx b/src/core/pages/Modules/index.tsx
similarity index 94%
rename from src/apps/Modules/index.tsx
rename to src/core/pages/Modules/index.tsx
index 7cc4a36f2..689f9d71d 100644
--- a/src/apps/Modules/index.tsx
+++ b/src/core/pages/Modules/index.tsx
@@ -1,3 +1,4 @@
+import ROUTES from '@core/routes/Routes'
import _ from 'lodash'
import { useTranslation } from 'react-i18next'
import { toast } from 'react-toastify'
@@ -6,9 +7,8 @@ import { LoadingScreen, ModuleHeader, ModuleWrapper } from '@lifeforge/ui'
import fetchAPI from '@utils/fetchAPI'
-import ROUTES from '../../core/Routes'
-import { useAuth } from '../../core/pages/Auth/providers/AuthProvider'
-import ModuleItem from './ModuleItem'
+import { useAuth } from '../Auth/providers/AuthProvider'
+import ModuleItem from './components/ModuleItem'
function Modules() {
const { t } = useTranslation('common.sidebar')
diff --git a/src/apps/Modules/interfaces/module_interfaces.ts b/src/core/pages/Modules/interfaces/module_interfaces.ts
similarity index 100%
rename from src/apps/Modules/interfaces/module_interfaces.ts
rename to src/core/pages/Modules/interfaces/module_interfaces.ts
diff --git a/src/core/pages/Personalization/config.tsx b/src/core/pages/Personalization/config.tsx
new file mode 100644
index 000000000..fa0f29e01
--- /dev/null
+++ b/src/core/pages/Personalization/config.tsx
@@ -0,0 +1,12 @@
+import { ModuleConfig } from '@core/routes/interfaces/routes_interfaces'
+import { IconPalette } from '@tabler/icons-react'
+import { lazy } from 'react'
+
+export default {
+ name: 'Personalization',
+ icon: ,
+ routes: {
+ personalization: lazy(() => import('.'))
+ },
+ togglable: false
+} satisfies ModuleConfig
diff --git a/src/core/pages/ServerStatus/config.tsx b/src/core/pages/ServerStatus/config.tsx
new file mode 100644
index 000000000..d2ca9733e
--- /dev/null
+++ b/src/core/pages/ServerStatus/config.tsx
@@ -0,0 +1,12 @@
+import { ModuleConfig } from '@core/routes/interfaces/routes_interfaces'
+import { IconServer } from '@tabler/icons-react'
+import { lazy } from 'react'
+
+export default {
+ name: 'Server Status',
+ icon: ,
+ routes: {
+ 'server-status': lazy(() => import('.'))
+ },
+ togglable: false
+} satisfies ModuleConfig
diff --git a/src/core/routes/Routes.tsx b/src/core/routes/Routes.tsx
new file mode 100644
index 000000000..593dcde8d
--- /dev/null
+++ b/src/core/routes/Routes.tsx
@@ -0,0 +1,43 @@
+import { ModuleCategory } from './interfaces/routes_interfaces'
+import RouteItems from './routes.json'
+
+export const ROUTES: ModuleCategory[] = []
+
+const modules = import.meta.glob([
+ '../../apps/**/config.tsx',
+ '../pages/**/config.tsx'
+])
+
+function resolveAlias(path: string): string {
+ if (path.startsWith('@apps')) {
+ return path.replace('@apps', '../../apps')
+ }
+ if (path.startsWith('@core')) {
+ return path.replace('@core', '../pages')
+ }
+
+ return path
+}
+
+for (const route of RouteItems) {
+ const { title, items } = route
+
+ const importPromises = items.map(async item => {
+ const resolved = `${resolveAlias(item)}/config.tsx`
+ const importer = modules[resolved]
+
+ if (!importer) throw new Error(`Module not found: ${resolved}`)
+
+ const mod = (await importer()) as { default: any }
+ return mod.default
+ })
+
+ const awaitedRoutes = await Promise.all(importPromises)
+
+ ROUTES.push({
+ title,
+ items: awaitedRoutes
+ })
+}
+
+export default ROUTES
diff --git a/src/core/layout/components/ChildRoutesRenderer.tsx b/src/core/routes/components/ChildRoutesRenderer.tsx
similarity index 89%
rename from src/core/layout/components/ChildRoutesRenderer.tsx
rename to src/core/routes/components/ChildRoutesRenderer.tsx
index 5da6fba1b..001aef424 100644
--- a/src/core/layout/components/ChildRoutesRenderer.tsx
+++ b/src/core/routes/components/ChildRoutesRenderer.tsx
@@ -3,7 +3,7 @@ import { Route } from 'react-router'
import { LoadingScreen, ModalManager } from '@lifeforge/ui'
-import { RouteItem } from '../interfaces/routes_interfaces'
+import { ModuleConfig } from '../../routes/interfaces/routes_interfaces'
import APIKeyStatusProvider from '../providers/APIKeyStatusProvider'
function ChildRoutesRenderer({
@@ -12,7 +12,7 @@ function ChildRoutesRenderer({
APIKeys = [],
t
}: {
- routes: RouteItem['routes']
+ routes: ModuleConfig['routes']
t: any
isNested?: boolean
APIKeys?: string[]
diff --git a/src/core/layout/components/Layout.tsx b/src/core/routes/components/Layout.tsx
similarity index 100%
rename from src/core/layout/components/Layout.tsx
rename to src/core/routes/components/Layout.tsx
diff --git a/src/core/layout/components/MainRoutesRenderer.tsx b/src/core/routes/components/MainRoutesRenderer.tsx
similarity index 92%
rename from src/core/layout/components/MainRoutesRenderer.tsx
rename to src/core/routes/components/MainRoutesRenderer.tsx
index 283ff636d..5f43d4bda 100644
--- a/src/core/layout/components/MainRoutesRenderer.tsx
+++ b/src/core/routes/components/MainRoutesRenderer.tsx
@@ -1,14 +1,14 @@
+import MainApplication from '@core/routes/components/Layout'
import _ from 'lodash'
import { useTranslation } from 'react-i18next'
import { Route, Routes } from 'react-router'
import { NotFoundScreen } from '@lifeforge/ui'
-import ROUTES from '../../Routes'
import Auth from '../../pages/Auth'
import { useAuth } from '../../pages/Auth/providers/AuthProvider'
+import ROUTES from '../Routes'
import ChildRoutesRenderer from './ChildRoutesRenderer'
-import MainApplication from './Layout'
function MainRoutesRenderer() {
const { t } = useTranslation('common.misc')
@@ -29,6 +29,8 @@ function MainRoutesRenderer() {
? (() => {
const Provider: React.FC = item.provider
+ console.log('Provider', Provider)
+
return (
{
diff --git a/src/core/layout/index.tsx b/src/core/routes/index.tsx
similarity index 94%
rename from src/core/layout/index.tsx
rename to src/core/routes/index.tsx
index d3695b63a..effc3b18c 100644
--- a/src/core/layout/index.tsx
+++ b/src/core/routes/index.tsx
@@ -1,3 +1,4 @@
+import MainRoutesRenderer from '@core/routes/components/MainRoutesRenderer'
import { useEffect } from 'react'
import { useLocation, useNavigate } from 'react-router'
@@ -12,7 +13,6 @@ import { useModalsEffect } from '@lifeforge/ui'
import Auth from '../pages/Auth'
import { useAuth } from '../pages/Auth/providers/AuthProvider'
-import MainRoutesRenderer from './components/MainRoutesRenderer'
import useAuthEffect from './hooks/useAuthEffect'
import useTitleEffect from './hooks/useTitleEffect'
diff --git a/src/core/layout/interfaces/routes_interfaces.ts b/src/core/routes/interfaces/routes_interfaces.ts
similarity index 84%
rename from src/core/layout/interfaces/routes_interfaces.ts
rename to src/core/routes/interfaces/routes_interfaces.ts
index 5a77c5cf6..74febeccf 100644
--- a/src/core/layout/interfaces/routes_interfaces.ts
+++ b/src/core/routes/interfaces/routes_interfaces.ts
@@ -1,4 +1,4 @@
-export interface RouteItem {
+export interface ModuleConfig {
name: string
icon: React.ReactElement | string
provider?:
@@ -20,7 +20,7 @@ export interface RouteItem {
hidden?: boolean
}
-export interface RouteCategory {
+export interface ModuleCategory {
title: string
- items: RouteItem[]
+ items: ModuleConfig[]
}
diff --git a/src/core/layout/providers/APIKeyStatusProvider.tsx b/src/core/routes/providers/APIKeyStatusProvider.tsx
similarity index 92%
rename from src/core/layout/providers/APIKeyStatusProvider.tsx
rename to src/core/routes/providers/APIKeyStatusProvider.tsx
index 3def7e86b..15a3d2174 100644
--- a/src/core/layout/providers/APIKeyStatusProvider.tsx
+++ b/src/core/routes/providers/APIKeyStatusProvider.tsx
@@ -1,9 +1,9 @@
+import MissingAPIKeyScreen from '@core/routes/components/MissingAPIKeyScreen'
+
import { ModuleWrapper, QueryWrapper } from '@lifeforge/ui'
import useAPIQuery from '@hooks/useAPIQuery'
-import MissingAPIKeyScreen from '../components/MissingAPIKeyScreen'
-
function APIKeyStatusProvider({
APIKeys,
children
diff --git a/src/core/routes/routes.json b/src/core/routes/routes.json
new file mode 100644
index 000000000..e4af0d938
--- /dev/null
+++ b/src/core/routes/routes.json
@@ -0,0 +1,83 @@
+[
+ {
+ "title": "",
+ "items": [
+ "@core/Dashboard"
+ ]
+ },
+ {
+ "title": "Productivity",
+ "items": [
+ "@apps/IdeaBox",
+ "@apps/TodoList",
+ "@apps/Calendar",
+ "@apps/CodeTime"
+ ]
+ },
+ {
+ "title": "Lifestyle",
+ "items": [
+ "@apps/MomentVault",
+ "@apps/VirtualWardrobe",
+ "@apps/Movies",
+ "@apps/Achievements"
+ ]
+ },
+ {
+ "title": "Finance",
+ "items": [
+ "@apps/Wallet",
+ "@apps/Wishlist"
+ ]
+ },
+ {
+ "title": "Storage",
+ "items": [
+ "@apps/BooksLibrary",
+ "@apps/Music",
+ "@apps/GuitarTabs",
+ "@apps/YoutubeVideos"
+ ]
+ },
+ {
+ "title": "Confidential",
+ "items": [
+ "@apps/Passwords"
+ ]
+ },
+ {
+ "title": "Information",
+ "items": [
+ "@apps/RailwayMap"
+ ]
+ },
+ {
+ "title": "Utilities",
+ "items": [
+ "@apps/Sudoku",
+ "@apps/CurrencyConverter",
+ "@apps/YoutubeSummarizer"
+ ]
+ },
+ {
+ "title": "Settings",
+ "items": [
+ "@core/Personalization",
+ "@core/Modules",
+ "@core/ServerStatus"
+ ]
+ },
+ {
+ "title": "sso",
+ "items": [
+ "@core/LocalizationManager"
+ ]
+ },
+ {
+ "title": "",
+ "items": [
+ "@core/Documentation",
+ "@core/AccountSettings"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
index 15e79e5d9..f52264a23 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,10 +2,16 @@
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "lib": [
+ "ES2020",
+ "DOM",
+ "DOM.Iterable"
+ ],
"module": "ESNext",
"skipLibCheck": true,
- "types": ["node"],
+ "types": [
+ "node"
+ ],
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
@@ -19,19 +25,38 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"paths": {
- "@components/*": ["./src/components/*"],
- "@providers/*": ["./src/core/providers/*"],
- "@hooks/*": ["./src/core/hooks/*"],
- "@interfaces/*": ["./src/core/interfaces/*"],
- "@utils/*": ["./src/core/utils/*"],
- "@apps/*": ["./src/apps/*"],
- "@security/*": ["./src/core/security/*"]
+ "@components/*": [
+ "./src/components/*"
+ ],
+ "@providers/*": [
+ "./src/core/providers/*"
+ ],
+ "@hooks/*": [
+ "./src/core/hooks/*"
+ ],
+ "@interfaces/*": [
+ "./src/core/interfaces/*"
+ ],
+ "@utils/*": [
+ "./src/core/utils/*"
+ ],
+ "@apps/*": [
+ "./src/apps/*"
+ ],
+ "@security/*": [
+ "./src/core/security/*"
+ ],
+ "@core/*": [
+ "./src/core/*"
+ ],
}
},
- "include": ["./src/**/*"],
+ "include": [
+ "./src/**/*"
+ ],
"references": [
{
"path": "./tsconfig.node.json"
}
]
-}
+}
\ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
index f3e6db7c0..3e7beeb3b 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -10,6 +10,17 @@ const ReactCompilerConfig = {
}
}
+export const alias = {
+ '@components': path.resolve(__dirname, './src/components'),
+ '@providers': path.resolve(__dirname, './src/core/providers'),
+ '@hooks': path.resolve(__dirname, './src/core/hooks'),
+ '@interfaces': path.resolve(__dirname, './src/core/interfaces'),
+ '@utils': path.resolve(__dirname, './src/core/utils'),
+ '@apps': path.resolve(__dirname, './src/apps'),
+ '@security': path.resolve(__dirname, './src/core/security'),
+ '@core': path.resolve(__dirname, './src/core')
+}
+
export default defineConfig({
envDir: path.resolve(__dirname, './env'),
plugins: [
@@ -30,15 +41,7 @@ export default defineConfig({
}
},
resolve: {
- alias: {
- '@components': path.resolve(__dirname, './src/components'),
- '@providers': path.resolve(__dirname, './src/core/providers'),
- '@hooks': path.resolve(__dirname, './src/core/hooks'),
- '@interfaces': path.resolve(__dirname, './src/core/interfaces'),
- '@utils': path.resolve(__dirname, './src/core/utils'),
- '@apps': path.resolve(__dirname, './src/apps'),
- '@security': path.resolve(__dirname, './src/core/security')
- }
+ alias
},
build: {
target: 'esnext',