feat(onboarding+modules): чеклист новой org, MedSchedule за флагом, удаление Capacitor

Шаги 1.9 и 1.10 плана production-готовности:
- OnboardingChecklist (форма→коллеги→задача) на главной, фикс EmptyState в Tasks
- organizations.medschedule_enabled (миграция 0081), гейт модуля по флагу
- Capacitor-стек удалён (5 зависимостей, push-notifications.ts, capacitor.config)
- check/lint/build чисто, vitest 96/96
This commit is contained in:
2026-09-08 00:27:17 +03:00
parent 960f28d3de
commit b1a3461c33
16 changed files with 213 additions and 921 deletions

View File

@@ -2,6 +2,7 @@ import { Router } from "express";
import type { Express, Request, Response, NextFunction } from "express";
import type { Response as ExpressResponse } from "express";
import { authenticateToken, type AuthenticatedRequest } from "../middleware/auth.middleware";
import { storage } from "../storage";
import { medScheduleStorage } from "./storage";
@@ -25,12 +26,17 @@ function requireMedOrganization() {
// MedSchedule API работает в контексте org medschedule, а не текущей org пользователя
req.organizationId = medOrgId;
// Доступ разрешён либо членам org medschedule, либо администраторам приложения
if (req.user && req.user.organizationId !== medOrgId && req.user.appRole !== "admin") {
return res.status(403).json({
success: false,
error: "Этот раздел доступен только пользователям MedSchedule",
});
// Доступ: у организации пользователя включён флаг medschedule_enabled
// (миграция 0081) либо пользователь — администратор приложения
// (admin-override, как в доступе к модулям GPS/Финансы).
if (req.user && req.user.appRole !== "admin") {
const userOrg = await storage.getOrganization(req.user.organizationId);
if (!userOrg?.medscheduleEnabled) {
return res.status(403).json({
success: false,
error: "Модуль MedSchedule не подключён для вашей организации",
});
}
}
next();

View File

@@ -212,7 +212,8 @@ export function registerAuthCoreRoutes(router: Router): void {
name: user.organization.name,
slug: user.organization.slug,
displayName: user.organization.displayName,
activeSkinId: user.organization.activeSkinId
activeSkinId: user.organization.activeSkinId,
medscheduleEnabled: user.organization.medscheduleEnabled ?? false
} : null
}
});