Gate Finance behind a super-admin business module.

Add finance to the modules picker and hide sidebar, home tile, and customer invoice actions when it is disabled.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-11 15:23:56 +03:30
co-authored by Cursor
parent 917b840ee5
commit a50685ec69
6 changed files with 22 additions and 11 deletions
+1
View File
@@ -142,6 +142,7 @@ export function Sidebar() {
icon: Wallet, icon: Wallet,
labelKey: 'nav.finance', labelKey: 'nav.finance',
basePath: '/finance', basePath: '/finance',
moduleId: 'finance',
children: [ children: [
{ labelKey: 'nav.finance.overview', to: '/finance' }, { labelKey: 'nav.finance.overview', to: '/finance' },
{ labelKey: 'nav.finance.invoices', to: '/invoices' }, { labelKey: 'nav.finance.invoices', to: '/invoices' },
+16 -10
View File
@@ -12,6 +12,7 @@ import { Pagination } from '../components/Pagination'
import { ToggleSwitch } from '../components/ToggleSwitch' import { ToggleSwitch } from '../components/ToggleSwitch'
import { Tooltip } from '../components/Tooltip' import { Tooltip } from '../components/Tooltip'
import { useAuth } from '../context/AuthContext' import { useAuth } from '../context/AuthContext'
import { useTenantBranding } from '../context/TenantBrandingContext'
import { useToast } from '../context/ToastContext' import { useToast } from '../context/ToastContext'
import { useT } from '../i18n/useT' import { useT } from '../i18n/useT'
import type { BusinessMessageKey } from '../i18n/messages' import type { BusinessMessageKey } from '../i18n/messages'
@@ -26,6 +27,7 @@ import {
type CustomerAccessFilter, type CustomerAccessFilter,
type CustomersListResponse, type CustomersListResponse,
} from '../services/customerService' } from '../services/customerService'
import { hasBusinessModule } from '../utils/businessModules'
import { formatIrtPrice } from '../utils/irtPrice' import { formatIrtPrice } from '../utils/irtPrice'
import { textLocaleAttrs } from '../utils/textLocale' import { textLocaleAttrs } from '../utils/textLocale'
import filterStyles from '../components/ListFiltersPanel.module.css' import filterStyles from '../components/ListFiltersPanel.module.css'
@@ -74,7 +76,9 @@ export function CustomersPage() {
const navigate = useNavigate() const navigate = useNavigate()
const { locale } = useLocale() const { locale } = useLocale()
const { user: authUser } = useAuth() const { user: authUser } = useAuth()
const { enabledModules } = useTenantBranding()
const { showToast } = useToast() const { showToast } = useToast()
const financeEnabled = hasBusinessModule(enabledModules, 'finance')
const [data, setData] = useState<CustomersListResponse | null>(null) const [data, setData] = useState<CustomersListResponse | null>(null)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [error, setError] = useState('') const [error, setError] = useState('')
@@ -537,16 +541,18 @@ export function CustomersPage() {
<Pencil size={15} /> <Pencil size={15} />
</button> </button>
</Tooltip> </Tooltip>
<Tooltip label={t('customers.invoices')}> {financeEnabled ? (
<button <Tooltip label={t('customers.invoices')}>
type="button" <button
className={styles.actionBtn} type="button"
onClick={() => handleViewInvoices(customer)} className={styles.actionBtn}
aria-label={t('customers.invoices')} onClick={() => handleViewInvoices(customer)}
> aria-label={t('customers.invoices')}
<Receipt size={15} /> >
</button> <Receipt size={15} />
</Tooltip> </button>
</Tooltip>
) : null}
<Tooltip label={t('customers.sms')}> <Tooltip label={t('customers.sms')}>
<button <button
type="button" type="button"
+1
View File
@@ -75,6 +75,7 @@ const sections: {
descKey: 'home.card.finance.desc', descKey: 'home.card.finance.desc',
linkKey: 'home.card.finance.link', linkKey: 'home.card.finance.link',
href: '/finance', href: '/finance',
moduleId: 'finance',
}, },
{ {
icon: FileText, icon: FileText,
@@ -6,6 +6,7 @@ export const BUSINESS_DASHBOARD_MODULE_IDS = [
'blog', 'blog',
'warehouse', 'warehouse',
'videos', 'videos',
'finance',
] as const ] as const
/** Optional customer-dashboard modules. Always-on: home, profile, addresses, orders, favorites. */ /** Optional customer-dashboard modules. Always-on: home, profile, addresses, orders, favorites. */
@@ -6,6 +6,7 @@ export const BUSINESS_DASHBOARD_MODULE_IDS = [
'blog', 'blog',
'warehouse', 'warehouse',
'videos', 'videos',
'finance',
] as const ] as const
/** Optional customer-dashboard modules. Always-on: home, profile, addresses, orders, favorites. */ /** Optional customer-dashboard modules. Always-on: home, profile, addresses, orders, favorites. */
@@ -32,6 +33,7 @@ export const BUSINESS_DASHBOARD_MODULE_LABELS: Record<
blog: 'Blog', blog: 'Blog',
warehouse: 'Warehouse', warehouse: 'Warehouse',
videos: 'Videos', videos: 'Videos',
finance: 'Finance',
} }
export const CUSTOMER_MODULE_LABELS: Record<CustomerModuleId, string> = { export const CUSTOMER_MODULE_LABELS: Record<CustomerModuleId, string> = {
+1 -1
View File
@@ -272,7 +272,7 @@ Products accept optional `brandId` on create/update.
### Business settings ### Business settings
- `GET/PATCH /businesses/:businessId/settings``branding`, `dashboard`, `store`, `modules` - `GET/PATCH /businesses/:businessId/settings``branding`, `dashboard`, `store`, `modules`
- `modules.enabled`: optional modules — business: `products`, `store`, `portfolio`, `blog`, `warehouse`, `videos`; customer: `customer_products`. Missing key → all business modules enabled (legacy); customer modules stay opt-in. Super-admin only for modules PATCH. - `modules.enabled`: optional modules — business: `products`, `store`, `portfolio`, `blog`, `warehouse`, `videos`, `finance`; customer: `customer_products`. Missing key → all business modules enabled (legacy); customer modules stay opt-in. Super-admin only for modules PATCH.
- `modules.charts`: two home chart slots (`none`, `orders_30d`, `customers_joined_1y`, `blog_views_30d`, `products_added_1y`); `none` hides that slot; defaults orders + customers. - `modules.charts`: two home chart slots (`none`, `orders_30d`, `customers_joined_1y`, `blog_views_30d`, `products_added_1y`); `none` hides that slot; defaults orders + customers.
- Always-on (not in modules list): business — customers, website, profile, settings, home; customer — home, profile, addresses, orders, favorites - Always-on (not in modules list): business — customers, website, profile, settings, home; customer — home, profile, addresses, orders, favorites
- Tenant public: `GET /tenants/:host` includes `enabledModules` + `homeCharts` - Tenant public: `GET /tenants/:host` includes `enabledModules` + `homeCharts`