fix: restore matchmaker workbench permissions
This commit is contained in:
@@ -106,12 +106,11 @@ export async function loadDashboard(permissions: string[]): Promise<DashboardMod
|
|||||||
const workbenchValue = (key: string) => numberOf(recordOf(workbenchMetrics, key), 'value')
|
const workbenchValue = (key: string) => numberOf(recordOf(workbenchMetrics, key), 'value')
|
||||||
const hasWorkbenchMetric = (key: string) => Object.prototype.hasOwnProperty.call(workbenchMetrics, key)
|
const hasWorkbenchMetric = (key: string) => Object.prototype.hasOwnProperty.call(workbenchMetrics, key)
|
||||||
const workbenchAvailable = (key: string) => Boolean(recordOf(workbenchMetrics, key).available)
|
const workbenchAvailable = (key: string) => Boolean(recordOf(workbenchMetrics, key).available)
|
||||||
const metricStatus = (key: string, permission: string, fallbackAvailable: boolean): DashboardMetric['status'] => {
|
const metricStatus = (key: string): DashboardMetric['status'] => {
|
||||||
if (hasWorkbenchMetric(key)) return workbenchAvailable(key) ? 'ready' : 'forbidden'
|
if (hasWorkbenchMetric(key)) return workbenchAvailable(key) ? 'ready' : 'forbidden'
|
||||||
if (!permissions.includes(permission)) return 'forbidden'
|
return 'error'
|
||||||
return fallbackAvailable ? 'ready' : 'error'
|
|
||||||
}
|
}
|
||||||
const metricAvailable = (key: string, permission: string, fallbackAvailable: boolean) => metricStatus(key, permission, fallbackAvailable) === 'ready'
|
const metricAvailable = (key: string) => metricStatus(key) === 'ready'
|
||||||
const metricRoute = (status: DashboardMetric['status'], route: string) => status === 'ready' ? route : ''
|
const metricRoute = (status: DashboardMetric['status'], route: string) => status === 'ready' ? route : ''
|
||||||
const core = fulfilledData<JsonRecord>(coreResult)
|
const core = fulfilledData<JsonRecord>(coreResult)
|
||||||
const service = fulfilledData<JsonRecord>(serviceResult)
|
const service = fulfilledData<JsonRecord>(serviceResult)
|
||||||
@@ -121,9 +120,6 @@ export async function loadDashboard(permissions: string[]): Promise<DashboardMod
|
|||||||
const managedServices = fulfilledData<JsonRecord[]>(managementResult)
|
const managedServices = fulfilledData<JsonRecord[]>(managementResult)
|
||||||
const managedStats = summarizeMatchmakerServices(managedServices || [])
|
const managedStats = summarizeMatchmakerServices(managedServices || [])
|
||||||
const matchmakerAvailable = canManageMatchmaker ? Boolean(managedServices) : Boolean(matchmaker)
|
const matchmakerAvailable = canManageMatchmaker ? Boolean(managedServices) : Boolean(matchmaker)
|
||||||
const matchmakerPending = canManageMatchmaker
|
|
||||||
? managedStats.pending
|
|
||||||
: numberOf(matchmaker || undefined, 'pendingCount') + numberOf(matchmaker || undefined, 'inProgressCount')
|
|
||||||
const matchmakerCompleted = canManageMatchmaker
|
const matchmakerCompleted = canManageMatchmaker
|
||||||
? managedStats.completed
|
? managedStats.completed
|
||||||
: numberOf(matchmaker || undefined, 'completedCount')
|
: numberOf(matchmaker || undefined, 'completedCount')
|
||||||
@@ -134,38 +130,37 @@ export async function loadDashboard(permissions: string[]): Promise<DashboardMod
|
|||||||
const analyticsAvailable = Boolean(core || service)
|
const analyticsAvailable = Boolean(core || service)
|
||||||
|
|
||||||
const counseling = recordOf(service || undefined, 'counseling')
|
const counseling = recordOf(service || undefined, 'counseling')
|
||||||
const appointments = recordOf(service || undefined, 'serviceAppointments')
|
|
||||||
const effectiveConnections = recordOf(service || undefined, 'effectiveConnections')
|
const effectiveConnections = recordOf(service || undefined, 'effectiveConnections')
|
||||||
const registrationSources = recordOf(service || undefined, 'registrationSources')
|
const registrationSources = recordOf(service || undefined, 'registrationSources')
|
||||||
const uniqueUsers = recordOf(service || undefined, 'uniqueServiceUsers')
|
const uniqueUsers = recordOf(service || undefined, 'uniqueServiceUsers')
|
||||||
|
|
||||||
const metrics: DashboardMetric[] = [
|
const metrics: DashboardMetric[] = [
|
||||||
{
|
{
|
||||||
key: 'pending_reviews', label: '待审核资料', value: hasWorkbenchMetric('pendingReviews') ? workbenchValue('pendingReviews') : numberOf(reviews || undefined, 'pending') + numberOf(reviews || undefined, 'processing'),
|
key: 'pending_reviews', label: '待审核资料', value: workbenchValue('pendingReviews'),
|
||||||
trendText: workbenchAvailable('pendingReviews') ? '当前数据范围内待审核资料' : (reviews ? '待领取与审核中资料' : '暂无授权数据'), tone: 'rose', available: metricAvailable('pendingReviews', 'review.view', Boolean(reviews)), status: metricStatus('pendingReviews', 'review.view', Boolean(reviews)), route: metricRoute(metricStatus('pendingReviews', 'review.view', Boolean(reviews)), '/review'),
|
trendText: workbenchAvailable('pendingReviews') ? '当前数据范围内待审核资料' : '暂无授权数据', tone: 'rose', available: metricAvailable('pendingReviews'), status: metricStatus('pendingReviews'), route: metricRoute(metricStatus('pendingReviews'), '/review'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'matchmaker_todos', label: '红娘服务待办',
|
key: 'matchmaker_todos', label: '红娘服务待办',
|
||||||
value: hasWorkbenchMetric('matchmakerTodos') ? workbenchValue('matchmakerTodos') : matchmakerPending,
|
value: workbenchValue('matchmakerTodos'),
|
||||||
trendText: workbenchAvailable('matchmakerTodos') ? `${workbench?.scopeLabel || '当前范围'}待完成服务` : (matchmakerAvailable ? (canManageMatchmaker ? '管理范围内待完成服务' : '本人服务对象待跟进') : '暂无授权数据'), tone: 'amber', available: metricAvailable('matchmakerTodos', 'matchmaker.view', matchmakerAvailable), status: metricStatus('matchmakerTodos', 'matchmaker.view', matchmakerAvailable), route: metricRoute(metricStatus('matchmakerTodos', 'matchmaker.view', matchmakerAvailable), permissions.includes('permission.manage') ? '' : '/matchmaker'),
|
trendText: workbenchAvailable('matchmakerTodos') ? `${workbench?.scopeLabel || '当前范围'}待完成服务` : '暂无授权数据', tone: 'amber', available: metricAvailable('matchmakerTodos'), status: metricStatus('matchmakerTodos'), route: metricRoute(metricStatus('matchmakerTodos'), permissions.includes('permission.manage') ? '' : '/matchmaker'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'counseling_todos', label: '咨询服务待办',
|
key: 'counseling_todos', label: '咨询服务待办',
|
||||||
value: hasWorkbenchMetric('counselingTodos') ? workbenchValue('counselingTodos') : numberOf(counseling, 'pending') + numberOf(counseling, 'assigned'),
|
value: workbenchValue('counselingTodos'),
|
||||||
trendText: workbenchAvailable('counselingTodos') ? `${workbench?.scopeLabel || '当前范围'}咨询待办` : (service ? '待受理与已分配' : '暂无授权数据'), tone: 'blue', available: metricAvailable('counselingTodos', 'counseling.service.view', Boolean(service)), status: metricStatus('counselingTodos', 'counseling.service.view', Boolean(service)), route: metricRoute(metricStatus('counselingTodos', 'counseling.service.view', Boolean(service)), '/counseling'),
|
trendText: workbenchAvailable('counselingTodos') ? `${workbench?.scopeLabel || '当前范围'}咨询待办` : '暂无授权数据', tone: 'blue', available: metricAvailable('counselingTodos'), status: metricStatus('counselingTodos'), route: metricRoute(metricStatus('counselingTodos'), '/counseling'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'station_appointments', label: '服务站预约',
|
key: 'station_appointments', label: '服务站预约',
|
||||||
value: hasWorkbenchMetric('serviceStationAppointments') ? workbenchValue('serviceStationAppointments') : numberOf(appointments, 'submitted') + numberOf(appointments, 'confirmed'),
|
value: workbenchValue('serviceStationAppointments'),
|
||||||
trendText: workbenchAvailable('serviceStationAppointments') ? `${workbench?.scopeLabel || '当前范围'}服务站预约` : (service ? '待确认与已确认' : '暂无授权数据'), tone: 'green', available: metricAvailable('serviceStationAppointments', 'service_appointment.view', Boolean(service)), status: metricStatus('serviceStationAppointments', 'service_appointment.view', Boolean(service)), route: metricRoute(metricStatus('serviceStationAppointments', 'service_appointment.view', Boolean(service)), '/service-appointment'),
|
trendText: workbenchAvailable('serviceStationAppointments') ? `${workbench?.scopeLabel || '当前范围'}服务站预约` : '暂无授权数据', tone: 'green', available: metricAvailable('serviceStationAppointments'), status: metricStatus('serviceStationAppointments'), route: metricRoute(metricStatus('serviceStationAppointments'), '/service-appointment'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'effective_matches', label: '有效匹配', value: hasWorkbenchMetric('effectiveMatches') ? workbenchValue('effectiveMatches') : numberOf(effectiveConnections, 'activeMatches'),
|
key: 'effective_matches', label: '有效匹配', value: workbenchValue('effectiveMatches'),
|
||||||
trendText: workbenchAvailable('effectiveMatches') ? `${workbench?.scopeLabel || '当前范围'}有效匹配` : (service ? '当前活跃匹配' : '暂无授权数据'), tone: 'purple', available: metricAvailable('effectiveMatches', 'analytics.view', Boolean(service)), status: metricStatus('effectiveMatches', 'analytics.view', Boolean(service)), route: metricRoute(metricStatus('effectiveMatches', 'analytics.view', Boolean(service)), '/analytics'),
|
trendText: workbenchAvailable('effectiveMatches') ? `${workbench?.scopeLabel || '当前范围'}有效匹配` : '暂无授权数据', tone: 'purple', available: metricAvailable('effectiveMatches'), status: metricStatus('effectiveMatches'), route: metricRoute(metricStatus('effectiveMatches'), '/analytics'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'activity_registrations', label: '活动报名', value: hasWorkbenchMetric('activityRegistrations') ? workbenchValue('activityRegistrations') : numberOf(registrationSources, 'totalRegistrations'),
|
key: 'activity_registrations', label: '活动报名', value: workbenchValue('activityRegistrations'),
|
||||||
trendText: workbenchAvailable('activityRegistrations') ? `${workbench?.scopeLabel || '当前范围'}活动报名` : (service ? '累计有效报名' : '暂无授权数据'), tone: 'red', available: metricAvailable('activityRegistrations', 'activity.review_registration', Boolean(service)), status: metricStatus('activityRegistrations', 'activity.review_registration', Boolean(service)), route: metricRoute(metricStatus('activityRegistrations', 'activity.review_registration', Boolean(service)), '/activities/interests'),
|
trendText: workbenchAvailable('activityRegistrations') ? `${workbench?.scopeLabel || '当前范围'}活动报名` : '暂无授权数据', tone: 'red', available: metricAvailable('activityRegistrations'), status: metricStatus('activityRegistrations'), route: metricRoute(metricStatus('activityRegistrations'), '/activities/interests'),
|
||||||
},
|
},
|
||||||
// 保留 forbidden 卡片以明确展示“无权限”;其数值只在 available 时渲染,route 由 metricRoute 置空。
|
// 保留 forbidden 卡片以明确展示“无权限”;其数值只在 available 时渲染,route 由 metricRoute 置空。
|
||||||
]
|
]
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
-- V074: 恢复红娘工作台六项卡片及已确认的业务操作权限。
|
||||||
|
-- 仅使用既有 permission code;不授予资格原始佐证、咨询备注查看或代用户决策权限。
|
||||||
|
INSERT INTO role_permissions (role_id, permission_id)
|
||||||
|
SELECT r.id, p.id
|
||||||
|
FROM roles r
|
||||||
|
CROSS JOIN permissions p
|
||||||
|
WHERE r.code IN ('matchmaker', 'matchmaker_leader')
|
||||||
|
AND p.code IN (
|
||||||
|
'workbench.view', 'review.view', 'review.process', 'local_eligibility.review',
|
||||||
|
'counseling.service.view', 'counseling.service.assign', 'counseling.service.process',
|
||||||
|
'counseling.note.create', 'service_appointment.view', 'service_appointment.assign',
|
||||||
|
'service_appointment.confirm', 'service_appointment.complete', 'activity.view',
|
||||||
|
'activity.review_registration', 'analytics.view'
|
||||||
|
)
|
||||||
|
ON CONFLICT (role_id, permission_id) DO NOTHING;
|
||||||
+2
-2
@@ -45,8 +45,8 @@ class MigrationValidationTest {
|
|||||||
// V067 工作台聚合权限与红娘角色边界;V068/V069 红娘工作台权限;
|
// V067 工作台聚合权限与红娘角色边界;V068/V069 红娘工作台权限;
|
||||||
// V070 收回红娘的咨询、活动、预约、统计权限;
|
// V070 收回红娘的咨询、活动、预约、统计权限;
|
||||||
// V071 收回红娘的资料审核、本地资格审核权限。
|
// V071 收回红娘的资料审核、本地资格审核权限。
|
||||||
// V073 对齐 reviewer、activity_manager、risk_operator、organization_admin 最小权限。
|
// V073 对齐其他运营角色;V074 恢复红娘工作台六卡片及操作权限。
|
||||||
private static final int LATEST_MIGRATION = 73;
|
private static final int LATEST_MIGRATION = 74;
|
||||||
|
|
||||||
private static final String MIGRATION_DIR =
|
private static final String MIGRATION_DIR =
|
||||||
"backend/src/main/resources/db/migration/";
|
"backend/src/main/resources/db/migration/";
|
||||||
|
|||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
package com.danyang.match.platform.deployment;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class V12MigrationContractTest {
|
||||||
|
|
||||||
|
private static final Path DIR = Path.of("backend/src/main/resources/db/migration");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void v12MigrationsAndLifecycleContractsExist() throws Exception {
|
||||||
|
assertMigration("V062__add_v12_user_account_lifecycle.sql");
|
||||||
|
assertMigration("V063__add_v12_import_batches.sql");
|
||||||
|
assertMigration("V064__add_v12_consent_and_service_state.sql");
|
||||||
|
assertMigration("V065__add_v12_registration_applications.sql");
|
||||||
|
assertMigration("V066__grant_user_onboarding_permission.sql");
|
||||||
|
|
||||||
|
String lifecycle = Files.readString(DIR.resolve("V062__add_v12_user_account_lifecycle.sql"));
|
||||||
|
assertAll(
|
||||||
|
() -> assertTrue(lifecycle.contains("profile_revision")),
|
||||||
|
() -> assertTrue(lifecycle.contains("published_profile_revision")),
|
||||||
|
() -> assertTrue(lifecycle.contains("user_credentials")),
|
||||||
|
() -> assertTrue(lifecycle.contains("must_change_password")),
|
||||||
|
() -> assertFalse(lifecycle.toLowerCase().contains("plaintext_password"))
|
||||||
|
);
|
||||||
|
|
||||||
|
String imports = Files.readString(DIR.resolve("V063__add_v12_import_batches.sql"));
|
||||||
|
assertAll(
|
||||||
|
() -> assertTrue(imports.contains("user_import_batches")),
|
||||||
|
() -> assertTrue(imports.contains("user_import_rows")),
|
||||||
|
() -> assertTrue(imports.contains("idempotency_key"))
|
||||||
|
);
|
||||||
|
|
||||||
|
String consent = Files.readString(DIR.resolve("V064__add_v12_consent_and_service_state.sql"));
|
||||||
|
assertAll(
|
||||||
|
() -> assertTrue(consent.contains("user_consent_states")),
|
||||||
|
() -> assertTrue(consent.contains("user_service_states")),
|
||||||
|
() -> assertTrue(consent.contains("service_active"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void lifecycleMigrationsDoNotDropExistingObjects() throws Exception {
|
||||||
|
List<String> names = List.of(
|
||||||
|
"V062__add_v12_user_account_lifecycle.sql",
|
||||||
|
"V063__add_v12_import_batches.sql",
|
||||||
|
"V064__add_v12_consent_and_service_state.sql",
|
||||||
|
"V065__add_v12_registration_applications.sql",
|
||||||
|
"V066__grant_user_onboarding_permission.sql");
|
||||||
|
for (String name : names) {
|
||||||
|
String sql = Files.readString(DIR.resolve(name)).toUpperCase();
|
||||||
|
assertFalse(sql.contains("DROP TABLE"), name);
|
||||||
|
assertFalse(sql.contains("DROP COLUMN"), name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchmakerCrossDomainPermissionsAreRevoked() throws Exception {
|
||||||
|
String sql = Files.readString(DIR.resolve("V070__remove_matchmaker_cross_domain_permissions.sql"));
|
||||||
|
assertAll(
|
||||||
|
() -> assertTrue(sql.contains("r.code IN ('matchmaker', 'matchmaker_leader')")),
|
||||||
|
() -> assertTrue(sql.contains("p.code LIKE 'counseling.%'")),
|
||||||
|
() -> assertTrue(sql.contains("p.code LIKE 'service_appointment.%'")),
|
||||||
|
() -> assertTrue(sql.contains("'activity.view'")),
|
||||||
|
() -> assertTrue(sql.contains("'activity.checkin'")),
|
||||||
|
() -> assertTrue(sql.contains("'activity.review_registration'")),
|
||||||
|
() -> assertTrue(sql.contains("'analytics.view'"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchmakerReviewPermissionsAreRevoked() throws Exception {
|
||||||
|
String sql = Files.readString(DIR.resolve("V071__remove_matchmaker_review_permissions.sql"));
|
||||||
|
assertAll(
|
||||||
|
() -> assertTrue(sql.contains("r.code IN ('matchmaker', 'matchmaker_leader')")),
|
||||||
|
() -> assertTrue(sql.contains("'review.view'")),
|
||||||
|
() -> assertTrue(sql.contains("'review.process'")),
|
||||||
|
() -> assertTrue(sql.contains("'local_eligibility.review'"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchmakerWorkbenchPermissionsAreRestoredWithoutSensitiveAccess() throws Exception {
|
||||||
|
Path migration = DIR.resolve("V074__restore_matchmaker_workbench_permissions.sql");
|
||||||
|
assertTrue(Files.exists(migration));
|
||||||
|
String sql = Files.readString(migration);
|
||||||
|
assertAll(
|
||||||
|
() -> assertTrue(sql.contains("r.code IN ('matchmaker', 'matchmaker_leader')")),
|
||||||
|
() -> assertTrue(sql.contains("'workbench.view'")),
|
||||||
|
() -> assertTrue(sql.contains("'review.process'")),
|
||||||
|
() -> assertTrue(sql.contains("'local_eligibility.review'")),
|
||||||
|
() -> assertTrue(sql.contains("'counseling.service.assign'")),
|
||||||
|
() -> assertTrue(sql.contains("'service_appointment.confirm'")),
|
||||||
|
() -> assertTrue(sql.contains("'service_appointment.complete'")),
|
||||||
|
() -> assertFalse(sql.contains("'local_eligibility.evidence.view'")),
|
||||||
|
() -> assertFalse(sql.contains("'counseling.note.view'")),
|
||||||
|
() -> assertFalse(sql.contains("'matchmaker.assign'")),
|
||||||
|
() -> assertTrue(sql.contains("ON CONFLICT (role_id, permission_id) DO NOTHING"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertMigration(String name) {
|
||||||
|
assertTrue(Files.exists(DIR.resolve(name)), "缺少迁移: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Phase V1.2 Final Fix Completion Report
|
||||||
|
|
||||||
|
## 完成内容
|
||||||
|
|
||||||
|
- 新增 `V074__restore_matchmaker_workbench_permissions.sql`,幂等恢复 `matchmaker`、`matchmaker_leader` 的工作台六卡片及审核、分配、确认、完成操作权限。
|
||||||
|
- 未授予资格原始佐证、咨询备注查看、`matchmaker.assign` 等敏感或代用户决策权限;未修改 V070/V071。
|
||||||
|
- 将迁移基线更新为 V074,并增加 V074 权限契约测试。
|
||||||
|
- workbench 请求失败或缺少数据时,六项 dashboard 卡片固定返回 `status=error`、`available=false`、`value=0`、空路由,不再使用旧 analytics/service/matchmaker/review 接口回退;workbench 成功返回真实 0 仍保留。
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
- `tests/admin-dashboard-behavior.cjs`
|
||||||
|
- Maven 定向迁移契约测试:PASS(`MigrationValidationTest,V12MigrationContractTest`)
|
||||||
|
- `pnpm type-check`:PASS
|
||||||
|
- `node tests/admin-dashboard-behavior.cjs`:PASS
|
||||||
|
|
||||||
|
## 限制
|
||||||
|
|
||||||
|
本次未执行真实生产数据库迁移、真实角色登录或生产部署验收。
|
||||||
@@ -23,14 +23,20 @@ const ts = require('../admin-web/node_modules/typescript');
|
|||||||
});
|
});
|
||||||
const perms = ['analytics.view', 'activity.view', 'matchmaker.view', 'matchmaker.assign', 'review.view'];
|
const perms = ['analytics.view', 'activity.view', 'matchmaker.view', 'matchmaker.assign', 'review.view'];
|
||||||
let model = await exports.loadDashboard(perms);
|
let model = await exports.loadDashboard(perms);
|
||||||
assert.equal(model.metrics.find(m => m.key === 'pending_reviews').value, 3);
|
const failedWorkbenchMetrics = ['pending_reviews', 'matchmaker_todos', 'counseling_todos', 'station_appointments', 'effective_matches', 'activity_registrations'];
|
||||||
assert.equal(model.metrics.find(m => m.key === 'matchmaker_todos').value, 1);
|
for (const key of failedWorkbenchMetrics) {
|
||||||
|
const metric = model.metrics.find(m => m.key === key);
|
||||||
|
assert.equal(metric.value, 0, `${key} workbench 失败时不得使用旧接口 fallback`);
|
||||||
|
assert.equal(metric.available, false);
|
||||||
|
assert.equal(metric.status, 'error');
|
||||||
|
assert.equal(metric.route, '');
|
||||||
|
}
|
||||||
assert.equal(model.followups[0].content, 'Demo验收跟进');
|
assert.equal(model.followups[0].content, 'Demo验收跟进');
|
||||||
assert.equal(exports.summarizeMatchmakerServices(responses['/operator/matchmaker/management/services']).total, 1);
|
assert.equal(exports.summarizeMatchmakerServices(responses['/operator/matchmaker/management/services']).total, 1);
|
||||||
assert.equal(model.metrics.find(m => m.key === 'matchmaker_todos').route, '/matchmaker');
|
assert.equal(model.metrics.find(m => m.key === 'matchmaker_todos').route, '');
|
||||||
pending = 1;
|
pending = 1;
|
||||||
model = await exports.loadDashboard(perms);
|
model = await exports.loadDashboard(perms);
|
||||||
assert.equal(model.metrics.find(m => m.key === 'pending_reviews').value, 2);
|
assert.equal(model.metrics.find(m => m.key === 'pending_reviews').value, 0);
|
||||||
workbench = { metrics: {
|
workbench = { metrics: {
|
||||||
pendingReviews: { value: 7, available: false },
|
pendingReviews: { value: 7, available: false },
|
||||||
matchmakerTodos: { value: 0, available: true },
|
matchmakerTodos: { value: 0, available: true },
|
||||||
|
|||||||
Reference in New Issue
Block a user