improvement/ux-overhaul up #61

Merged
rameen merged 24 commits from improvement/ux-overhaul into master 2026-07-14 22:46:19 +03:30
5 changed files with 63 additions and 3 deletions
Showing only changes of commit 8de15ecdb5 - Show all commits

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

View File

@@ -4,6 +4,7 @@
*/
import type { OrgTypeName } from '@/components/shared/permissions';
import { isHiddenTabPermission } from '@/components/shared/hidden-tabs';
export const STAFF_FEATURE_GROUPS = [
{ 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;
export function staffFeatureGroupsForOrgType(organizationType: OrgType) {
if (!organizationType) return [...STAFF_FEATURE_GROUPS];
return STAFF_FEATURE_GROUPS.filter((g) =>
const visible = STAFF_FEATURE_GROUPS.filter((g) => !isHiddenTabPermission(g.read));
if (!organizationType) return [...visible];
return visible.filter((g) =>
(g.orgTypes as readonly OrgTypeName[]).includes(organizationType),
);
}

View File

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

View File

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