каркас вроде есть

This commit is contained in:
gorbenko.pavel
2026-07-29 12:30:40 +05:00
parent ad04d9bda4
commit 04f04133f2
114 changed files with 8347 additions and 5143 deletions
@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdminApp = void 0;
var tslib_1 = require("tslib");
var React = tslib_1.__importStar(require("react"));
var AdminApp_module_scss_1 = tslib_1.__importDefault(require("./AdminApp.module.scss"));
var TopNav_1 = require("./TopNav");
var DashboardScreen_1 = require("../screens/DashboardScreen");
var RequestTypesScreen_1 = require("../screens/RequestTypesScreen");
var RoutesScreen_1 = require("../screens/RoutesScreen");
var RouteStepsScreen_1 = require("../screens/RouteStepsScreen");
var ResolveScreen_1 = require("../screens/ResolveScreen");
var AdminApp = function (_a) {
var api = _a.api, currentUserEmail = _a.currentUserEmail;
var _b = React.useState('dashboard'), screen = _b[0], setScreen = _b[1];
var _c = React.useState(), selectedRoute = _c[0], setSelectedRoute = _c[1];
var _d = React.useState(''), selectedRequestTypeName = _d[0], setSelectedRequestTypeName = _d[1];
var navigate = function (key) {
setScreen(key);
};
var openSteps = function (route) {
setSelectedRoute(route);
api.requestTypes.get(route.requestTypeId)
.then(function (rt) { return setSelectedRequestTypeName(rt.name); })
.catch(function () { return setSelectedRequestTypeName("#".concat(route.requestTypeId)); });
setScreen('routeSteps');
};
return (React.createElement("div", { className: AdminApp_module_scss_1.default.shell },
React.createElement(TopNav_1.TopNav, { active: screen, onSelect: navigate }),
React.createElement("div", { className: AdminApp_module_scss_1.default.content },
screen === 'dashboard' && React.createElement(DashboardScreen_1.DashboardScreen, { api: api, onNavigate: navigate }),
screen === 'requestTypes' && React.createElement(RequestTypesScreen_1.RequestTypesScreen, { api: api }),
screen === 'routes' && React.createElement(RoutesScreen_1.RoutesScreen, { api: api, onOpenSteps: openSteps }),
screen === 'routeSteps' && selectedRoute && (React.createElement(RouteStepsScreen_1.RouteStepsScreen, { api: api, route: selectedRoute, requestTypeName: selectedRequestTypeName, onBack: function () { return setScreen('routes'); } })),
screen === 'resolve' && React.createElement(ResolveScreen_1.ResolveScreen, { api: api, currentUserEmail: currentUserEmail }))));
};
exports.AdminApp = AdminApp;
//# sourceMappingURL=AdminApp.js.map
@@ -0,0 +1 @@
{"version":3,"file":"AdminApp.js","sourceRoot":"","sources":["../../../../../src/webparts/approvalAdmin/components/app/AdminApp.tsx"],"names":[],"mappings":";;;;AAAA,mDAA+B;AAC/B,wFAA4C;AAC5C,mCAAkC;AAIlC,8DAA6D;AAC7D,oEAAmE;AACnE,wDAAuD;AACvD,gEAA+D;AAC/D,0DAAyD;AAOlD,IAAM,QAAQ,GAA6B,UAAC,EAAyB;QAAvB,GAAG,SAAA,EAAE,gBAAgB,sBAAA;IAClE,IAAA,KAAsB,KAAK,CAAC,QAAQ,CAAY,WAAW,CAAC,EAA3D,MAAM,QAAA,EAAE,SAAS,QAA0C,CAAC;IAC7D,IAAA,KAAoC,KAAK,CAAC,QAAQ,EAA6B,EAA9E,aAAa,QAAA,EAAE,gBAAgB,QAA+C,CAAC;IAChF,IAAA,KAAwD,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAzE,uBAAuB,QAAA,EAAE,0BAA0B,QAAsB,CAAC;IAEjF,IAAM,QAAQ,GAAG,UAAC,GAAc;QAC9B,SAAS,CAAC,GAAG,CAAC,CAAC;IACjB,CAAC,CAAC;IAEF,IAAM,SAAS,GAAG,UAAC,KAAoB;QACrC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACxB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC;aACtC,IAAI,CAAC,UAAA,EAAE,IAAI,OAAA,0BAA0B,CAAC,EAAE,CAAC,IAAI,CAAC,EAAnC,CAAmC,CAAC;aAC/C,KAAK,CAAC,cAAM,OAAA,0BAA0B,CAAC,WAAI,KAAK,CAAC,aAAa,CAAE,CAAC,EAArD,CAAqD,CAAC,CAAC;QACtE,SAAS,CAAC,YAAY,CAAC,CAAC;IAC1B,CAAC,CAAC;IAEF,OAAO,CACL,6BAAK,SAAS,EAAE,8BAAM,CAAC,KAAK;QAC1B,oBAAC,eAAM,IAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAI;QAC9C,6BAAK,SAAS,EAAE,8BAAM,CAAC,OAAO;YAC3B,MAAM,KAAK,WAAW,IAAI,oBAAC,iCAAe,IAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,GAAI;YAC7E,MAAM,KAAK,cAAc,IAAI,oBAAC,uCAAkB,IAAC,GAAG,EAAE,GAAG,GAAI;YAC7D,MAAM,KAAK,QAAQ,IAAI,oBAAC,2BAAY,IAAC,GAAG,EAAE,GAAG,EAAE,WAAW,EAAE,SAAS,GAAI;YACzE,MAAM,KAAK,YAAY,IAAI,aAAa,IAAI,CAC3C,oBAAC,mCAAgB,IACf,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,aAAa,EACpB,eAAe,EAAE,uBAAuB,EACxC,MAAM,EAAE,cAAM,OAAA,SAAS,CAAC,QAAQ,CAAC,EAAnB,CAAmB,GACjC,CACH;YACA,MAAM,KAAK,SAAS,IAAI,oBAAC,6BAAa,IAAC,GAAG,EAAE,GAAG,EAAE,gBAAgB,EAAE,gBAAgB,GAAI,CACpF,CACF,CACP,CAAC;AACJ,CAAC,CAAC;AApCW,QAAA,QAAQ,YAoCnB","sourcesContent":["import * as React from 'react';\nimport styles from './AdminApp.module.scss';\nimport { TopNav } from './TopNav';\nimport { ScreenKey } from './types';\nimport { ApprovalRouteApi } from '../../services/api';\nimport { TemplateRoute } from '../../models';\nimport { DashboardScreen } from '../screens/DashboardScreen';\nimport { RequestTypesScreen } from '../screens/RequestTypesScreen';\nimport { RoutesScreen } from '../screens/RoutesScreen';\nimport { RouteStepsScreen } from '../screens/RouteStepsScreen';\nimport { ResolveScreen } from '../screens/ResolveScreen';\n\nexport interface IAdminAppProps {\n api: ApprovalRouteApi;\n currentUserEmail?: string;\n}\n\nexport const AdminApp: React.FC<IAdminAppProps> = ({ api, currentUserEmail }) => {\n const [screen, setScreen] = React.useState<ScreenKey>('dashboard');\n const [selectedRoute, setSelectedRoute] = React.useState<TemplateRoute | undefined>();\n const [selectedRequestTypeName, setSelectedRequestTypeName] = React.useState('');\n\n const navigate = (key: ScreenKey): void => {\n setScreen(key);\n };\n\n const openSteps = (route: TemplateRoute): void => {\n setSelectedRoute(route);\n api.requestTypes.get(route.requestTypeId)\n .then(rt => setSelectedRequestTypeName(rt.name))\n .catch(() => setSelectedRequestTypeName(`#${route.requestTypeId}`));\n setScreen('routeSteps');\n };\n\n return (\n <div className={styles.shell}>\n <TopNav active={screen} onSelect={navigate} />\n <div className={styles.content}>\n {screen === 'dashboard' && <DashboardScreen api={api} onNavigate={navigate} />}\n {screen === 'requestTypes' && <RequestTypesScreen api={api} />}\n {screen === 'routes' && <RoutesScreen api={api} onOpenSteps={openSteps} />}\n {screen === 'routeSteps' && selectedRoute && (\n <RouteStepsScreen\n api={api}\n route={selectedRoute}\n requestTypeName={selectedRequestTypeName}\n onBack={() => setScreen('routes')}\n />\n )}\n {screen === 'resolve' && <ResolveScreen api={api} currentUserEmail={currentUserEmail} />}\n </div>\n </div>\n );\n};\n"]}
@@ -0,0 +1,63 @@
:root {
--navy-950: #0A1B30;
--navy-900: #0F2440;
--navy-800: #1B3A5C;
--navy-700: #244B73;
--navy-600: #2D5C8A;
--navy-100: #E8EEF4;
--navy-50: #F4F7FA;
--gold-600: #B8881A;
--gold-500: #D4A01E;
--gold-400: #E5B635;
--gold-300: #F0CC5E;
--gold-100: #FDF5E0;
--gold-50: #FEFBF0;
--gray-900: #111827;
--gray-700: #374151;
--gray-500: #6B7280;
--gray-400: #9CA3AF;
--gray-300: #D1D5DB;
--gray-200: #E5E7EB;
--gray-100: #F3F4F6;
--gray-50: #F9FAFB;
--red-500: #DC2626;
--red-50: #FEF2F2;
--green-600: #15803D;
--green-50: #F0FDF4;
--blue-600: #2563EB;
--blue-50: #EFF6FF;
--amber-700: #854D0E;
--sh-1: 0 1px 2px rgba(15, 36, 64, 0.04), 0 1px 3px rgba(15, 36, 64, 0.06);
--sh-2: 0 4px 16px rgba(15, 36, 64, 0.07);
--sh-3: 0 12px 32px rgba(15, 36, 64, 0.1);
--sh-gold: 0 4px 20px rgba(212, 160, 30, 0.18);
--sh-modal: 0 24px 70px rgba(10, 27, 48, 0.25);
--r-sm: 6px;
--r-md: 10px;
--r-lg: 14px;
}
.shell {
background: linear-gradient(180deg, #F6F8FA 0%, #EEF2F6 100%);
border-radius: 14px;
border: 1px solid #E5E7EB;
overflow: hidden;
box-shadow: 0 1px 2px rgba(15, 36, 64, 0.04), 0 1px 3px rgba(15, 36, 64, 0.06);
color: #111827;
font-family: "Inter", -apple-system, "Segoe UI", sans-serif;
font-size: 14px;
line-height: 1.55;
}
.content {
padding: 28px 30px 36px;
max-width: 1080px;
margin: 0 auto;
}
.centerState {
padding: 60px 30px;
text-align: center;
color: #9CA3AF;
font-size: 13.5px;
}
@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TopNav = void 0;
var tslib_1 = require("tslib");
var React = tslib_1.__importStar(require("react"));
var TopNav_module_scss_1 = tslib_1.__importDefault(require("./TopNav.module.scss"));
var TABS = [
{ key: 'dashboard', label: 'Дашборд' },
{ key: 'requestTypes', label: 'Типы заявок' },
{ key: 'routes', label: 'Маршруты' },
{ key: 'resolve', label: 'Проверка маршрута' }
];
var TopNav = function (_a) {
var active = _a.active, onSelect = _a.onSelect;
return (React.createElement("nav", { className: TopNav_module_scss_1.default.topnav },
React.createElement("div", { className: TopNav_module_scss_1.default.brand },
React.createElement("div", { className: TopNav_module_scss_1.default.mark }, "T"),
React.createElement("div", { className: TopNav_module_scss_1.default.brandText },
"TNSU ",
React.createElement("em", null, "Approval Route"))),
React.createElement("div", { className: TopNav_module_scss_1.default.tabs }, TABS.map(function (tab) { return (React.createElement("button", { key: tab.key, className: "".concat(TopNav_module_scss_1.default.tab, " ").concat((active === tab.key || (active === 'routeSteps' && tab.key === 'routes')) ? TopNav_module_scss_1.default.on : ''), onClick: function () { return onSelect(tab.key); } }, tab.label)); }))));
};
exports.TopNav = TopNav;
//# sourceMappingURL=TopNav.js.map
@@ -0,0 +1 @@
{"version":3,"file":"TopNav.js","sourceRoot":"","sources":["../../../../../src/webparts/approvalAdmin/components/app/TopNav.tsx"],"names":[],"mappings":";;;;AAAA,mDAA+B;AAC/B,oFAA0C;AAQ1C,IAAM,IAAI,GAAW;IACnB,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE;IACtC,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,EAAE;IAC7C,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE;IACpC,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,mBAAmB,EAAE;CAC/C,CAAC;AAOK,IAAM,MAAM,GAA2B,UAAC,EAAoB;QAAlB,MAAM,YAAA,EAAE,QAAQ,cAAA;IAAO,OAAA,CACtE,6BAAK,SAAS,EAAE,4BAAM,CAAC,MAAM;QAC3B,6BAAK,SAAS,EAAE,4BAAM,CAAC,KAAK;YAC1B,6BAAK,SAAS,EAAE,4BAAM,CAAC,IAAI,QAAS;YACpC,6BAAK,SAAS,EAAE,4BAAM,CAAC,SAAS;;gBAAO,iDAAuB,CAAM,CAChE;QACN,6BAAK,SAAS,EAAE,4BAAM,CAAC,IAAI,IACxB,IAAI,CAAC,GAAG,CAAC,UAAA,GAAG,IAAI,OAAA,CACf,gCACE,GAAG,EAAE,GAAG,CAAC,GAAG,EACZ,SAAS,EAAE,UAAG,4BAAM,CAAC,GAAG,cAAI,CAAC,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,KAAK,YAAY,IAAI,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,4BAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAE,EACxH,OAAO,EAAE,cAAM,OAAA,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAjB,CAAiB,IAE/B,GAAG,CAAC,KAAK,CACH,CACV,EARgB,CAQhB,CAAC,CACE,CACF,CACP;AAlBuE,CAkBvE,CAAC;AAlBW,QAAA,MAAM,UAkBjB","sourcesContent":["import * as React from 'react';\nimport styles from './TopNav.module.scss';\nimport { ScreenKey } from './types';\n\ninterface ITab {\n key: ScreenKey;\n label: string;\n}\n\nconst TABS: ITab[] = [\n { key: 'dashboard', label: 'Дашборд' },\n { key: 'requestTypes', label: 'Типы заявок' },\n { key: 'routes', label: 'Маршруты' },\n { key: 'resolve', label: 'Проверка маршрута' }\n];\n\nexport interface ITopNavProps {\n active: ScreenKey;\n onSelect: (key: ScreenKey) => void;\n}\n\nexport const TopNav: React.FC<ITopNavProps> = ({ active, onSelect }) => (\n <nav className={styles.topnav}>\n <div className={styles.brand}>\n <div className={styles.mark}>T</div>\n <div className={styles.brandText}>TNSU <em>Approval Route</em></div>\n </div>\n <div className={styles.tabs}>\n {TABS.map(tab => (\n <button\n key={tab.key}\n className={`${styles.tab} ${(active === tab.key || (active === 'routeSteps' && tab.key === 'routes')) ? styles.on : ''}`}\n onClick={() => onSelect(tab.key)}\n >\n {tab.label}\n </button>\n ))}\n </div>\n </nav>\n);\n"]}
@@ -0,0 +1,112 @@
:root {
--navy-950: #0A1B30;
--navy-900: #0F2440;
--navy-800: #1B3A5C;
--navy-700: #244B73;
--navy-600: #2D5C8A;
--navy-100: #E8EEF4;
--navy-50: #F4F7FA;
--gold-600: #B8881A;
--gold-500: #D4A01E;
--gold-400: #E5B635;
--gold-300: #F0CC5E;
--gold-100: #FDF5E0;
--gold-50: #FEFBF0;
--gray-900: #111827;
--gray-700: #374151;
--gray-500: #6B7280;
--gray-400: #9CA3AF;
--gray-300: #D1D5DB;
--gray-200: #E5E7EB;
--gray-100: #F3F4F6;
--gray-50: #F9FAFB;
--red-500: #DC2626;
--red-50: #FEF2F2;
--green-600: #15803D;
--green-50: #F0FDF4;
--blue-600: #2563EB;
--blue-50: #EFF6FF;
--amber-700: #854D0E;
--sh-1: 0 1px 2px rgba(15, 36, 64, 0.04), 0 1px 3px rgba(15, 36, 64, 0.06);
--sh-2: 0 4px 16px rgba(15, 36, 64, 0.07);
--sh-3: 0 12px 32px rgba(15, 36, 64, 0.1);
--sh-gold: 0 4px 20px rgba(212, 160, 30, 0.18);
--sh-modal: 0 24px 70px rgba(10, 27, 48, 0.25);
--r-sm: 6px;
--r-md: 10px;
--r-lg: 14px;
}
.topnav {
background: linear-gradient(135deg, #0A1B30 0%, #1B3A5C 85%);
padding: 0 22px;
display: flex;
align-items: center;
height: 54px;
box-shadow: 0 1px 0 rgba(212, 160, 30, 0.35);
border-radius: 10px 10px 0 0;
flex-wrap: wrap;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
margin-right: 28px;
flex-shrink: 0;
}
.mark {
width: 30px;
height: 30px;
border-radius: 7px;
background: linear-gradient(180deg, #E5B635, #B8881A);
display: flex;
align-items: center;
justify-content: center;
color: #0A1B30;
font-weight: 800;
font-size: 13px;
font-family: "Lora", Georgia, serif;
}
.brandText {
color: #fff;
font-weight: 600;
font-size: 13px;
letter-spacing: 0.4px;
white-space: nowrap;
}
.brandText em {
font-style: normal;
color: #E5B635;
}
.tabs {
display: flex;
height: 100%;
flex-wrap: wrap;
}
.tab {
padding: 0 16px;
border: none;
background: 0;
color: rgba(255, 255, 255, 0.5);
font-size: 13px;
font-weight: 500;
cursor: pointer;
font-family: inherit;
transition: 0.18s;
height: 54px;
border-bottom: 2px solid transparent;
letter-spacing: 0.2px;
}
.tab:hover {
color: rgba(255, 255, 255, 0.85);
}
.tab.on {
color: #F0CC5E;
border-bottom-color: #E5B635;
}
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/webparts/approvalAdmin/components/app/types.ts"],"names":[],"mappings":"","sourcesContent":["export type ScreenKey = 'dashboard' | 'requestTypes' | 'routes' | 'routeSteps' | 'resolve';\n"]}