Sync service launch links
This commit is contained in:
parent
17e007f49d
commit
c6e1de6345
|
|
@ -231,7 +231,7 @@
|
|||
"subtitle": "Агентная платформа",
|
||||
"description": "Сборка, запуск и мониторинг агентных workflow.",
|
||||
"fullDescription": "NodeDC используется для настройки агентных процессов, визуальной оркестрации, интеграций и runtime-мониторинга.",
|
||||
"url": "https://dev.handhdc.ru",
|
||||
"url": "https://dev.handhdc.ru/sso/launch",
|
||||
"launchUrl": "https://dev.handhdc.ru/sso/launch",
|
||||
"accentColor": "#B5FF5A",
|
||||
"fallbackGradient": "linear-gradient(128deg, rgba(181, 255, 90, 0.84), rgba(37, 58, 36, 0.86) 42%, #0A0D10 82%)",
|
||||
|
|
@ -257,7 +257,7 @@
|
|||
"subtitle": "Операционный слой",
|
||||
"description": "Задачи, контуры предприятия, процессы и AI-функции поверх задачника.",
|
||||
"fullDescription": "Task Manager основан на архитектуре Plane и расширен AI-функциями NODE.DC.",
|
||||
"url": "https://tasks.handhdc.ru",
|
||||
"url": "https://tasks.handhdc.ru/sso/launch",
|
||||
"launchUrl": "https://tasks.handhdc.ru/sso/launch",
|
||||
"accentColor": "#D7C8FF",
|
||||
"fallbackGradient": "linear-gradient(132deg, rgba(215, 200, 255, 0.82), rgba(51, 41, 79, 0.9) 46%, #0B0D10 84%)",
|
||||
|
|
@ -279,7 +279,7 @@
|
|||
"subtitle": "Бухгалтерский ассистент",
|
||||
"description": "Вопросы к 1С, точные выборки и доказательная навигация по данным.",
|
||||
"fullDescription": "Ассистент для бухгалтерских запросов, анализа операций, остатков и документов.",
|
||||
"url": "https://1c.handhdc.ru",
|
||||
"url": "https://1c.handhdc.ru/sso/launch",
|
||||
"launchUrl": "https://1c.handhdc.ru/sso/launch",
|
||||
"accentColor": "#8FD7FF",
|
||||
"fallbackGradient": "linear-gradient(126deg, rgba(143, 215, 255, 0.8), rgba(32, 61, 80, 0.9) 44%, #080B0F 84%)",
|
||||
|
|
@ -301,7 +301,7 @@
|
|||
"subtitle": "Госзакупки и тендеры",
|
||||
"description": "Поиск, анализ и подготовка тендерных решений.",
|
||||
"fullDescription": "Сервис собирает тендерные данные, строит выжимку рисков и помогает подготовить пакет участия.",
|
||||
"url": "https://tender.handhdc.ru",
|
||||
"url": "https://tender.handhdc.ru/sso/launch",
|
||||
"launchUrl": "https://tender.handhdc.ru/sso/launch",
|
||||
"accentColor": "#FFD166",
|
||||
"fallbackGradient": "linear-gradient(135deg, rgba(255, 209, 102, 0.84), rgba(74, 53, 19, 0.92) 42%, #0B0D10 86%)",
|
||||
|
|
@ -327,7 +327,7 @@
|
|||
"subtitle": "3D и пространственные данные",
|
||||
"description": "Просмотр цифровых двойников, карт и объектных сцен.",
|
||||
"fullDescription": "Витрина геометрии, объектов, слоёв и статусов инфраструктуры.",
|
||||
"url": "https://twin.handhdc.ru",
|
||||
"url": "https://launch.dcserve.ru/",
|
||||
"launchUrl": "https://launch.dcserve.ru/",
|
||||
"accentColor": "#76E4F7",
|
||||
"fallbackGradient": "linear-gradient(140deg, rgba(118, 228, 247, 0.82), rgba(23, 69, 87, 0.92) 47%, #080B0F 86%)",
|
||||
|
|
@ -349,7 +349,7 @@
|
|||
"subtitle": "Будущие модули",
|
||||
"description": "Скрытый каталог модулей для root-admin preview.",
|
||||
"fullDescription": "Площадка для будущих цифровых модулей NODE.DC.",
|
||||
"url": "https://dm.handhdc.ru",
|
||||
"url": "https://dm.handhdc.ru/sso/launch",
|
||||
"launchUrl": "https://dm.handhdc.ru/sso/launch",
|
||||
"accentColor": "#FF9AC2",
|
||||
"fallbackGradient": "linear-gradient(135deg, rgba(255, 154, 194, 0.78), rgba(76, 41, 64, 0.9) 44%, #090B0F 86%)",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useEffect, useMemo, useState } from "react";
|
||||
import type { Client } from "../entities/client/types";
|
||||
import type { Invite } from "../entities/invite/types";
|
||||
import { syncServiceLaunchLink } from "../entities/service/links";
|
||||
import type { LauncherServiceView, Service } from "../entities/service/types";
|
||||
import type { SyncStatus } from "../entities/sync/types";
|
||||
import type { ClientGroup, ClientMembership, LauncherUser } from "../entities/user/types";
|
||||
|
|
@ -18,7 +19,7 @@ import { ServiceStage } from "../widgets/service-stage/ServiceStage";
|
|||
import { TopBar } from "../widgets/top-bar/TopBar";
|
||||
|
||||
export function LauncherApp() {
|
||||
const [data, setData] = useState<LauncherData>(initialLauncherData);
|
||||
const [data, setData] = useState<LauncherData>(() => syncLauncherServiceLinks(initialLauncherData));
|
||||
const [activeProfileId, setActiveProfileId] = useState(profileOptions[0].userId);
|
||||
const [activeClientId, setActiveClientId] = useState(profileOptions[0].defaultClientId);
|
||||
const [selectedServiceId, setSelectedServiceId] = useState<string | undefined>();
|
||||
|
|
@ -51,7 +52,7 @@ export function LauncherApp() {
|
|||
loadPersistedLauncherData()
|
||||
.then((persistedData) => {
|
||||
if (isMounted && persistedData) {
|
||||
setData(persistedData);
|
||||
setData(syncLauncherServiceLinks(persistedData));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
|
|
@ -232,11 +233,11 @@ export function LauncherApp() {
|
|||
...current,
|
||||
services: current.services.map((service) =>
|
||||
service.id === serviceId
|
||||
? {
|
||||
? syncServiceLaunchLink({
|
||||
...service,
|
||||
...patch,
|
||||
updatedAt: new Date().toISOString(),
|
||||
}
|
||||
})
|
||||
: service
|
||||
),
|
||||
}));
|
||||
|
|
@ -444,7 +445,7 @@ export function LauncherApp() {
|
|||
subtitle: "Новый сервис",
|
||||
description: "Описание сервиса для витрины.",
|
||||
fullDescription: "Заполните описание, медиа и ссылку запуска в редакторе контента.",
|
||||
url: "https://service.handhdc.ru",
|
||||
url: "https://service.handhdc.ru/sso/launch",
|
||||
launchUrl: "https://service.handhdc.ru/sso/launch",
|
||||
accentColor: "#F7F8F4",
|
||||
fallbackGradient: "linear-gradient(135deg, rgba(247, 248, 244, 0.72), rgba(36, 37, 42, 0.9) 52%, #090B0F 88%)",
|
||||
|
|
@ -529,3 +530,10 @@ export function LauncherApp() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function syncLauncherServiceLinks(data: LauncherData): LauncherData {
|
||||
return {
|
||||
...data,
|
||||
services: data.services.map(syncServiceLaunchLink),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import type { Service } from "./types";
|
||||
|
||||
export function getServiceLaunchLink(service: Pick<Service, "url" | "launchUrl">): string {
|
||||
return service.launchUrl?.trim() || service.url.trim();
|
||||
}
|
||||
|
||||
export function createServiceLaunchLinkPatch(value: string): Pick<Service, "url" | "launchUrl"> {
|
||||
const launchLink = value.trim();
|
||||
|
||||
return {
|
||||
url: launchLink,
|
||||
launchUrl: launchLink || null,
|
||||
};
|
||||
}
|
||||
|
||||
export function syncServiceLaunchLink(service: Service): Service {
|
||||
return {
|
||||
...service,
|
||||
...createServiceLaunchLinkPatch(getServiceLaunchLink(service)),
|
||||
};
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import { computeEffectiveAccess } from "../../entities/access/computeEffectiveAc
|
|||
import type { EffectiveAccessResult, ServiceAccessException, ServiceGrant } from "../../entities/access/types";
|
||||
import type { Client } from "../../entities/client/types";
|
||||
import type { Invite } from "../../entities/invite/types";
|
||||
import { getServiceLaunchLink } from "../../entities/service/links";
|
||||
import type { LauncherServiceView, Service } from "../../entities/service/types";
|
||||
import type { SyncStatus } from "../../entities/sync/types";
|
||||
import type {
|
||||
|
|
@ -217,7 +218,7 @@ export function buildLauncherServices(data: LauncherData, userId: string, active
|
|||
status: service.status,
|
||||
userAccess: effectiveAccess.allowed ? ("allowed" as const) : ("denied" as const),
|
||||
appRole: effectiveAccess.appRole,
|
||||
openUrl: effectiveAccess.openEnabled ? service.launchUrl ?? service.url : null,
|
||||
openUrl: effectiveAccess.openEnabled ? getServiceLaunchLink(service) || null : null,
|
||||
accentColor: service.accentColor,
|
||||
media: {
|
||||
icon: service.iconUrl,
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export const mockServices: Service[] = [
|
|||
description: "Сборка, запуск и мониторинг агентных workflow.",
|
||||
fullDescription:
|
||||
"NodeDC используется для настройки агентных процессов, визуальной оркестрации, интеграций и runtime-мониторинга.",
|
||||
url: "https://dev.handhdc.ru",
|
||||
url: "https://dev.handhdc.ru/sso/launch",
|
||||
launchUrl: "https://dev.handhdc.ru/sso/launch",
|
||||
accentColor: "#B5FF5A",
|
||||
fallbackGradient: "linear-gradient(128deg, rgba(181, 255, 90, 0.84), rgba(37, 58, 36, 0.86) 42%, #0A0D10 82%)",
|
||||
|
|
@ -168,7 +168,7 @@ export const mockServices: Service[] = [
|
|||
subtitle: "Операционный слой",
|
||||
description: "Задачи, контуры предприятия, процессы и AI-функции поверх задачника.",
|
||||
fullDescription: "Task Manager основан на архитектуре Plane и расширен AI-функциями NODE.DC.",
|
||||
url: "https://tasks.handhdc.ru",
|
||||
url: "https://tasks.handhdc.ru/sso/launch",
|
||||
launchUrl: "https://tasks.handhdc.ru/sso/launch",
|
||||
accentColor: "#D7C8FF",
|
||||
fallbackGradient: "linear-gradient(132deg, rgba(215, 200, 255, 0.82), rgba(51, 41, 79, 0.9) 46%, #0B0D10 84%)",
|
||||
|
|
@ -186,7 +186,7 @@ export const mockServices: Service[] = [
|
|||
subtitle: "Бухгалтерский ассистент",
|
||||
description: "Вопросы к 1С, точные выборки и доказательная навигация по данным.",
|
||||
fullDescription: "Ассистент для бухгалтерских запросов, анализа операций, остатков и документов.",
|
||||
url: "https://1c.handhdc.ru",
|
||||
url: "https://1c.handhdc.ru/sso/launch",
|
||||
launchUrl: "https://1c.handhdc.ru/sso/launch",
|
||||
accentColor: "#8FD7FF",
|
||||
fallbackGradient: "linear-gradient(126deg, rgba(143, 215, 255, 0.8), rgba(32, 61, 80, 0.9) 44%, #080B0F 84%)",
|
||||
|
|
@ -204,7 +204,7 @@ export const mockServices: Service[] = [
|
|||
subtitle: "Госзакупки и тендеры",
|
||||
description: "Поиск, анализ и подготовка тендерных решений.",
|
||||
fullDescription: "Сервис собирает тендерные данные, строит выжимку рисков и помогает подготовить пакет участия.",
|
||||
url: "https://tender.handhdc.ru",
|
||||
url: "https://tender.handhdc.ru/sso/launch",
|
||||
launchUrl: "https://tender.handhdc.ru/sso/launch",
|
||||
accentColor: "#FFD166",
|
||||
fallbackGradient: "linear-gradient(135deg, rgba(255, 209, 102, 0.84), rgba(74, 53, 19, 0.92) 42%, #0B0D10 86%)",
|
||||
|
|
@ -222,7 +222,7 @@ export const mockServices: Service[] = [
|
|||
subtitle: "3D и пространственные данные",
|
||||
description: "Просмотр цифровых двойников, карт и объектных сцен.",
|
||||
fullDescription: "Витрина геометрии, объектов, слоёв и статусов инфраструктуры.",
|
||||
url: "https://twin.handhdc.ru",
|
||||
url: "https://twin.handhdc.ru/sso/launch",
|
||||
launchUrl: "https://twin.handhdc.ru/sso/launch",
|
||||
accentColor: "#76E4F7",
|
||||
fallbackGradient: "linear-gradient(140deg, rgba(118, 228, 247, 0.82), rgba(23, 69, 87, 0.92) 47%, #080B0F 86%)",
|
||||
|
|
@ -240,7 +240,7 @@ export const mockServices: Service[] = [
|
|||
subtitle: "Будущие модули",
|
||||
description: "Скрытый каталог модулей для root-admin preview.",
|
||||
fullDescription: "Площадка для будущих цифровых модулей NODE.DC.",
|
||||
url: "https://dm.handhdc.ru",
|
||||
url: "https://dm.handhdc.ru/sso/launch",
|
||||
launchUrl: "https://dm.handhdc.ru/sso/launch",
|
||||
accentColor: "#FF9AC2",
|
||||
fallbackGradient: "linear-gradient(135deg, rgba(255, 154, 194, 0.78), rgba(76, 41, 64, 0.9) 44%, #090B0F 86%)",
|
||||
|
|
@ -259,7 +259,7 @@ export const mockServices: Service[] = [
|
|||
description: "Отключённый сервис для проверки диагностики root-admin.",
|
||||
fullDescription: "Не показывается обычным пользователям, виден root-admin в каталоге.",
|
||||
url: "https://internal.handhdc.ru",
|
||||
launchUrl: null,
|
||||
launchUrl: "https://internal.handhdc.ru",
|
||||
accentColor: "#F97373",
|
||||
fallbackGradient: "linear-gradient(135deg, rgba(249, 115, 115, 0.78), rgba(73, 32, 32, 0.92) 43%, #090B0F 86%)",
|
||||
status: "disabled",
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import {
|
|||
import type { ServiceAppRole } from "../../entities/access/types";
|
||||
import type { Client, ClientStatus, ClientType } from "../../entities/client/types";
|
||||
import type { Invite, InviteStatus } from "../../entities/invite/types";
|
||||
import { createServiceLaunchLinkPatch, getServiceLaunchLink } from "../../entities/service/links";
|
||||
import type { MediaKind, Service, ServiceMediaSource, ServiceStatus } from "../../entities/service/types";
|
||||
import type { SyncState, SyncStatus } from "../../entities/sync/types";
|
||||
import type {
|
||||
|
|
@ -875,7 +876,7 @@ function ServicesSection({
|
|||
<th>Сервис</th>
|
||||
<th>Slug</th>
|
||||
<th>Статус</th>
|
||||
<th>URL</th>
|
||||
<th>Ссылка запуска</th>
|
||||
<th>Authentik</th>
|
||||
<th aria-label="Редактирование" />
|
||||
<th aria-label="Порядок" />
|
||||
|
|
@ -1007,9 +1008,9 @@ function ServiceTableCells({
|
|||
<td>
|
||||
<input
|
||||
className="admin-table-input"
|
||||
value={service.url}
|
||||
onChange={(event) => onUpdateService(service.id, { url: event.target.value })}
|
||||
aria-label={`URL сервиса ${service.title}`}
|
||||
value={getServiceLaunchLink(service)}
|
||||
onChange={(event) => onUpdateService(service.id, createServiceLaunchLinkPatch(event.target.value))}
|
||||
aria-label={`Ссылка запуска сервиса ${service.title}`}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
|
|
@ -1262,7 +1263,10 @@ function ServiceContentModal({
|
|||
<span>
|
||||
<Link2 size={14} /> Ссылка запуска
|
||||
</span>
|
||||
<input value={draft.launchUrl ?? ""} onChange={(event) => update("launchUrl", event.target.value || null)} />
|
||||
<input
|
||||
value={getServiceLaunchLink(draft)}
|
||||
onChange={(event) => setDraft((current) => ({ ...current, ...createServiceLaunchLinkPatch(event.target.value) }))}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<MediaSourceField
|
||||
|
|
@ -1331,6 +1335,7 @@ function ServiceContentModal({
|
|||
subtitle: draft.subtitle,
|
||||
description: draft.description,
|
||||
fullDescription: draft.fullDescription,
|
||||
url: draft.url,
|
||||
launchUrl: draft.launchUrl,
|
||||
coverImageUrl: draft.coverImageUrl,
|
||||
coverMediaKind: draft.coverMediaKind,
|
||||
|
|
|
|||
Loading…
Reference in New Issue