fix production trial issue
Build & Push / Pipeline Tests (push) Successful in 1m49s
Test / Type Check (all packages) (push) Successful in 55s
Build & Push / Build & Push Docker Image (push) Failing after 5m31s
Test / API Unit Tests (push) Successful in 1m20s
Test / Homepage Unit Tests (push) Successful in 48s
Test / Carplace Unit Tests (push) Successful in 45s
Test / Admin Unit Tests (push) Successful in 44s
Test / Dashboard Unit Tests (push) Successful in 46s
Test / API Integration Tests (push) Successful in 1m8s
Build & Push / Pipeline Tests (push) Successful in 1m49s
Test / Type Check (all packages) (push) Successful in 55s
Build & Push / Build & Push Docker Image (push) Failing after 5m31s
Test / API Unit Tests (push) Successful in 1m20s
Test / Homepage Unit Tests (push) Successful in 48s
Test / Carplace Unit Tests (push) Successful in 45s
Test / Admin Unit Tests (push) Successful in 44s
Test / Dashboard Unit Tests (push) Successful in 46s
Test / API Integration Tests (push) Successful in 1m8s
This commit is contained in:
@@ -375,4 +375,55 @@ describe('menu.service', () => {
|
||||
'settings',
|
||||
])
|
||||
})
|
||||
|
||||
it('adds baseline routes when a full-access trial menu only exposes a small recovery menu', async () => {
|
||||
vi.mocked(prisma.employee.findUniqueOrThrow).mockResolvedValue({
|
||||
id: 'employee_1',
|
||||
role: 'OWNER',
|
||||
isActive: true,
|
||||
companyId: 'company_1',
|
||||
} as never)
|
||||
|
||||
vi.mocked(prisma.company.findUniqueOrThrow).mockResolvedValue({
|
||||
id: 'company_1',
|
||||
name: 'Atlas Cars',
|
||||
status: 'TRIALING',
|
||||
subscription: { plan: 'STARTER', status: 'TRIALING' },
|
||||
} as never)
|
||||
|
||||
vi.mocked(prisma.menuItem.findMany).mockResolvedValue([
|
||||
{
|
||||
id: 'item_notifications',
|
||||
systemKey: 'notifications',
|
||||
label: 'Notifications',
|
||||
itemType: 'INTERNAL_PAGE',
|
||||
routeOrUrl: '/notifications',
|
||||
icon: 'Bell',
|
||||
parentId: null,
|
||||
openInNewTab: false,
|
||||
isRequired: false,
|
||||
isActive: true,
|
||||
displayOrder: 120,
|
||||
roleVisibilities: [{ role: 'OWNER' }],
|
||||
subscriptionAssignments: [{ plan: 'STARTER', displayOrder: 120, isActive: true }],
|
||||
companyAssignments: [],
|
||||
},
|
||||
] as never)
|
||||
|
||||
const result = await getEmployeeMenu('employee_1')
|
||||
|
||||
expect(result.subscriptionStatus).toBe('TRIALING')
|
||||
expect(result.subscriptionAccessLevel).toBe('full')
|
||||
expect(result.items.map((menuItem) => menuItem.systemKey)).toEqual([
|
||||
'dashboard',
|
||||
'reservations',
|
||||
'contracts',
|
||||
'fleet',
|
||||
'customers',
|
||||
'reports',
|
||||
'billing',
|
||||
'settings',
|
||||
'notifications',
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -621,6 +621,46 @@ function buildBaselineEmployeeMenu(role: EmployeeRole) {
|
||||
}))
|
||||
}
|
||||
|
||||
function routeKey(routeOrUrl: string | null) {
|
||||
return routeOrUrl === '/' ? '/' : routeOrUrl?.replace(/\/+$/, '')
|
||||
}
|
||||
|
||||
function hasOnlyRecoveryOrUtilityRoutes(items: ReturnType<typeof buildMenuTree>) {
|
||||
const internalRoutes = items
|
||||
.filter((item) => item.itemType === 'INTERNAL_PAGE')
|
||||
.map((item) => routeKey(item.routeOrUrl))
|
||||
.filter((route): route is string => Boolean(route))
|
||||
|
||||
if (internalRoutes.length === 0) return false
|
||||
|
||||
return internalRoutes.every((route) => (
|
||||
route === '/' ||
|
||||
route === '/notifications' ||
|
||||
MENU_RECOVERY_ROUTES.has(route)
|
||||
))
|
||||
}
|
||||
|
||||
function mergeWithBaselineEmployeeMenu(items: ReturnType<typeof buildMenuTree>, role: EmployeeRole) {
|
||||
const merged = [...items]
|
||||
const seenRoutes = new Set(
|
||||
merged
|
||||
.filter((item) => item.itemType === 'INTERNAL_PAGE')
|
||||
.map((item) => routeKey(item.routeOrUrl))
|
||||
.filter(Boolean),
|
||||
)
|
||||
const seenSystemKeys = new Set(merged.map((item) => item.systemKey).filter(Boolean))
|
||||
|
||||
for (const baselineItem of buildBaselineEmployeeMenu(role)) {
|
||||
const key = routeKey(baselineItem.routeOrUrl)
|
||||
if (seenRoutes.has(key) || seenSystemKeys.has(baselineItem.systemKey)) continue
|
||||
merged.push(baselineItem)
|
||||
seenRoutes.add(key)
|
||||
seenSystemKeys.add(baselineItem.systemKey)
|
||||
}
|
||||
|
||||
return sortByDisplayOrder(merged)
|
||||
}
|
||||
|
||||
export async function previewCompanyMenu(input: MenuPreviewInput) {
|
||||
const context = await getMenuEvaluationContext(input.companyId, input.role)
|
||||
return {
|
||||
@@ -677,6 +717,8 @@ export async function getEmployeeMenu(employeeId: string) {
|
||||
const resolvedItems =
|
||||
context.subscriptionAccessLevel === 'full' && employee.isActive && !hasFeatureMenuRoute(menuItems)
|
||||
? buildBaselineEmployeeMenu(employee.role)
|
||||
: context.subscriptionAccessLevel === 'full' && employee.isActive && hasOnlyRecoveryOrUtilityRoutes(menuItems)
|
||||
? mergeWithBaselineEmployeeMenu(menuItems, employee.role)
|
||||
: menuItems
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user