improvement: billing and report tabs turned invisible. removed from permissions dialog too. this is temporary.

This commit is contained in:
2026-07-14 03:17:57 +03:30
parent d383a4abc3
commit 8de15ecdb5
5 changed files with 63 additions and 3 deletions

View File

@@ -0,0 +1,44 @@
/**
* Tabs hidden from navigation and staff permission UI until features ship.
* Routes, pages, and backend permissions stay in place — flip flags to re-enable.
*/
export const HIDDEN_TAB_FEATURE_FLAGS = {
billing: true,
reports: true,
} as const;
const HIDDEN_PATHS: readonly string[] = [
...(HIDDEN_TAB_FEATURE_FLAGS.billing ? (['/billing'] as const) : []),
...(HIDDEN_TAB_FEATURE_FLAGS.reports ? (['/reports'] as const) : []),
];
const HIDDEN_PERMISSIONS: readonly string[] = [
...(HIDDEN_TAB_FEATURE_FLAGS.billing
? (['TAB_BILLING_READ', 'TAB_BILLING_EDIT'] as const)
: []),
...(HIDDEN_TAB_FEATURE_FLAGS.reports
? (['TAB_REPORTS_READ', 'TAB_REPORTS_EDIT'] as const)
: []),
];
const HIDDEN_PATH_SET = new Set<string>(HIDDEN_PATHS);
const HIDDEN_PERMISSION_SET = new Set<string>(HIDDEN_PERMISSIONS);
export function isHiddenTabPath(path: string): boolean {
return HIDDEN_PATH_SET.has(path);
}
export function isHiddenTabPermission(permission: string): boolean {
return HIDDEN_PERMISSION_SET.has(permission);
}
/** Keep hidden-tab grants when saving staff permissions the UI no longer shows. */
export function preserveHiddenTabPermissions(
fromForm: string[],
existing?: string[] | null,
): string[] {
if (!existing?.length) return fromForm;
const preserved = existing.filter((p) => isHiddenTabPermission(p));
if (!preserved.length) return fromForm;
return [...new Set([...fromForm, ...preserved])];
}

View File

@@ -1,4 +1,5 @@
import type { Organization } from '@/types/organization'; import type { Organization } from '@/types/organization';
import { isHiddenTabPath } from '@/components/shared/hidden-tabs';
export type OrgTypeName = 'CLINIC' | 'LAB'; export type OrgTypeName = 'CLINIC' | 'LAB';
@@ -64,6 +65,10 @@ export function canAccessDashboardRoute(org: Organization | null, pathname: stri
const route = getRouteConfigForPath(pathname); const route = getRouteConfigForPath(pathname);
if (!route) return true; if (!route) return true;
if (isHiddenTabPath(route.prefix)) {
return false;
}
if (!isRouteAllowedForOrgType(pathname, org.type)) { if (!isRouteAllowedForOrgType(pathname, org.type)) {
return false; return false;
} }
@@ -89,6 +94,7 @@ export function firstAccessibleDashboardPath(org: Organization | null): string {
for (const route of DASHBOARD_ROUTES) { for (const route of DASHBOARD_ROUTES) {
if (!route.orgTypes.includes(org.type)) continue; if (!route.orgTypes.includes(org.type)) continue;
if (isHiddenTabPath(route.prefix)) continue;
if (route.prefix === '/appointments') { if (route.prefix === '/appointments') {
if (canAccessAppointmentsSection(org)) return route.prefix; if (canAccessAppointmentsSection(org)) return route.prefix;
continue; continue;

View File

@@ -4,6 +4,7 @@
*/ */
import type { OrgTypeName } from '@/components/shared/permissions'; import type { OrgTypeName } from '@/components/shared/permissions';
import { isHiddenTabPermission } from '@/components/shared/hidden-tabs';
export const STAFF_FEATURE_GROUPS = [ export const STAFF_FEATURE_GROUPS = [
{ labelKey: 'featureToday', read: 'TAB_TODAY_READ', edit: 'TAB_TODAY_EDIT', orgTypes: ['CLINIC', 'LAB'] as const }, { labelKey: 'featureToday', read: 'TAB_TODAY_READ', edit: 'TAB_TODAY_EDIT', orgTypes: ['CLINIC', 'LAB'] as const },
@@ -24,8 +25,9 @@ export type OrgType = OrgTypeName | null | undefined;
type StaffFeaturesTranslate = (key: string) => string; type StaffFeaturesTranslate = (key: string) => string;
export function staffFeatureGroupsForOrgType(organizationType: OrgType) { export function staffFeatureGroupsForOrgType(organizationType: OrgType) {
if (!organizationType) return [...STAFF_FEATURE_GROUPS]; const visible = STAFF_FEATURE_GROUPS.filter((g) => !isHiddenTabPermission(g.read));
return STAFF_FEATURE_GROUPS.filter((g) => if (!organizationType) return [...visible];
return visible.filter((g) =>
(g.orgTypes as readonly OrgTypeName[]).includes(organizationType), (g.orgTypes as readonly OrgTypeName[]).includes(organizationType),
); );
} }

View File

@@ -27,6 +27,7 @@ import {
canViewTasks, canViewTasks,
canViewTab, canViewTab,
} from '@/components/shared/permissions'; } from '@/components/shared/permissions';
import { isHiddenTabPath } from '@/components/shared/hidden-tabs';
import { import {
counterpartOrganizationType, counterpartOrganizationType,
organizationTypeIcon, organizationTypeIcon,
@@ -82,6 +83,9 @@ function Sidebar({ mobileOpen = false, onClose }: SidebarProps) {
const visibleMenu = useMemo( const visibleMenu = useMemo(
() => () =>
menu.filter((item) => { menu.filter((item) => {
if (isHiddenTabPath(item.path)) {
return false;
}
if (!orgType || !item.orgTypes.includes(orgType)) { if (!orgType || !item.orgTypes.includes(orgType)) {
return false; return false;
} }

View File

@@ -9,6 +9,7 @@ import {
canEditStaff, canEditStaff,
canViewStaff, canViewStaff,
} from '@/components/shared/permissions'; } from '@/components/shared/permissions';
import { preserveHiddenTabPermissions } from '@/components/shared/hidden-tabs';
import { import {
permissionNamesFromFeatureState, permissionNamesFromFeatureState,
emptyFeaturePermissionState, emptyFeaturePermissionState,
@@ -464,7 +465,10 @@ export function StaffPage() {
await staffApi.updateMember(editing.id, { await staffApi.updateMember(editing.id, {
name: editName.trim(), name: editName.trim(),
permissionNames: permissionNamesFromFeatureState(editPerms), permissionNames: preserveHiddenTabPermissions(
permissionNamesFromFeatureState(editPerms),
editing.permissions,
),
}); });
toast.showSuccess(t('successMemberUpdated')); toast.showSuccess(t('successMemberUpdated'));