@kuzntsv/uikit 1.0.101 → 1.0.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/kuzntsv-uikit-interceptors.mjs +13 -6
- package/fesm2022/kuzntsv-uikit-interceptors.mjs.map +1 -1
- package/fesm2022/kuzntsv-uikit-ui.mjs +4 -4
- package/fesm2022/kuzntsv-uikit-ui.mjs.map +1 -1
- package/fesm2022/kuzntsv-uikit.mjs +3 -1
- package/fesm2022/kuzntsv-uikit.mjs.map +1 -1
- package/package.json +1 -1
- package/types/kuzntsv-uikit.d.ts +10 -0
|
@@ -11,6 +11,8 @@ import { MatSnackBar } from "@angular/material/snack-bar";
|
|
|
11
11
|
const UIKIT_CONFIG = new InjectionToken("UIKIT_CONFIG");
|
|
12
12
|
const HIDE_ROLE_MARKER = "HIDE";
|
|
13
13
|
const ADMIN_ROLE = "ADMIN";
|
|
14
|
+
const X_SKIP_NOTIFICATION = "X-Skip-Notification";
|
|
15
|
+
const X_SKIP_LOADER = "X-Skip-Loader";
|
|
14
16
|
const API_NAME = new HttpContextToken(() => "");
|
|
15
17
|
const BYPASS_AUTH = new HttpContextToken(() => false);
|
|
16
18
|
const DEFAULT_TIMEOUT = new InjectionToken("DEFAULT_TIMEOUT", {
|
|
@@ -462,6 +464,6 @@ function formatZodError(error, contextName) {
|
|
|
462
464
|
return ` • Ошибка ${path ? `в "${path}"` : "в корне"}: ${issue.expected ? `ожидалось "${issue.expected}"` : ""}${issue.received ? `, получено "${issue.received}"` : ""}. Сообщение: ${issue.message}`;
|
|
463
465
|
}).join("\n")}\n`;
|
|
464
466
|
}
|
|
465
|
-
export { ADMIN_ROLE, API_NAME, APP_ENVIRONMENT, AuthEventService, AuthService, BYPASS_AUTH, DEFAULT_TIMEOUT, HIDE_ROLE_MARKER, LoaderService, NotificationService, REQUEST_TIMEOUT, RoleService, SKIP_LOADER, SKIP_NOTIFICATION, SignalSelectionModel, UIKIT_CONFIG, UIKIT_NOTIFICATION_TOKEN, User, UserService, dateToString, formatZodError, hasArrayIntersection };
|
|
467
|
+
export { ADMIN_ROLE, API_NAME, APP_ENVIRONMENT, AuthEventService, AuthService, BYPASS_AUTH, DEFAULT_TIMEOUT, HIDE_ROLE_MARKER, LoaderService, NotificationService, REQUEST_TIMEOUT, RoleService, SKIP_LOADER, SKIP_NOTIFICATION, SignalSelectionModel, UIKIT_CONFIG, UIKIT_NOTIFICATION_TOKEN, User, UserService, X_SKIP_LOADER, X_SKIP_NOTIFICATION, dateToString, formatZodError, hasArrayIntersection };
|
|
466
468
|
|
|
467
469
|
//# sourceMappingURL=kuzntsv-uikit.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kuzntsv-uikit.mjs","names":[],"sources":["../../src/config/lib-config.ts","../../src/constants/auth.constants.ts","../../src/http/http-context-tokens.ts","../../src/models/interfaces/environment.interface.ts","../../src/models/interfaces/notification-service.interface.ts","../../src/services/auth-event.service.ts","../../src/services/auth-user.service.ts","../../src/services/auth.service.ts","../../src/utils/array.utils.ts","../../src/services/auth-role.service.ts","../../src/shared/progress-spinner/progress-spinner.component.ts","../../src/services/loader.service.ts","../../src/services/notification.service.ts","../../src/utils/date.utils.ts","../../src/utils/signal-selection-model.ts","../../src/utils/zod-error-formatter.ts"],"sourcesContent":["import { InjectionToken } from \"@angular/core\";\r\n\r\nexport interface UikitConfig {\r\n apiUrls: {\r\n default: string;\r\n auth: string;\r\n [key: string]: string;\r\n };\r\n auth: {\r\n disabled?: boolean; // true = авторизация полностью отключена\r\n userStorageKey: string; // Например, 'app_user_jwt'\r\n tokenRoute: string; // Например, '/jwt'\r\n // refreshTokenStorageKey: string; // Ключ для Refresh токена (например, 'refresh_token')\r\n // refreshEndpoint: string; // Относительный путь (например, '/auth/refresh')\r\n loginRoute: string; // Например, '/auth/login'\r\n publicUrls: string[]; // Относительные пути, которые не требуют токена (например, '/login', '/assets')\r\n };\r\n}\r\n\r\nexport const UIKIT_CONFIG = new InjectionToken<UikitConfig>('UIKIT_CONFIG');\r\n","/**\r\n * Специальный маркер-роль в массиве ограничений, \r\n * который полностью скрывает элемент от обычных пользователей.\r\n */\r\nexport const HIDE_ROLE_MARKER = 'HIDE';\r\n\r\n/**\r\n * Системная роль Администратора, которая игнорирует любые ограничения прав.\r\n */\r\nexport const ADMIN_ROLE = 'ADMIN';\r\n","import { HttpContextToken } from '@angular/common/http';\r\nimport { inject, InjectionToken } from '@angular/core';\r\n\r\n// Токен, в который мы будем передавать строку-идентификатор нужного API\r\n// По умолчанию пустая строка означает \"использовать дефолтное (основное) API\"\r\nexport const API_NAME = new HttpContextToken<string>(() => '');\r\n\r\n// Иногда внутри сервиса нужно сделать один специфический запрос (например, стянуть публичный JSON-файл конфигурации), не добавляя его глобально в publicUrls.\r\n// Используя подход с HttpContext, мы можем добавить в библиотеку маркер пропуска авторизации:\r\n// А в коде приложения вызывать так:\r\n// this.http.get('/assets/config.json', { \r\n// context: new HttpContext().set(BYPASS_AUTH, true) \r\n// });\r\nexport const BYPASS_AUTH = new HttpContextToken<boolean>(() => false);\r\n\r\n// DI-токен, который переопределяем в appConfig\r\nexport const DEFAULT_TIMEOUT = new InjectionToken<number>('DEFAULT_TIMEOUT', {\r\n providedIn: 'root',\r\n factory: () => 10000 // Запасной дефолт (10 сек), если в appConfig ничего не указали\r\n});\r\n\r\n// Если для каких-то тяжелых запросов (например, загрузка больших файлов или генерация отчетов) \r\n// вам нужно увеличить таймаут, вы можете передавать его значение прямо в запросе через HttpContext.\r\nexport const REQUEST_TIMEOUT = new HttpContextToken<number>(() => inject(DEFAULT_TIMEOUT));\r\n\r\n// Передать в токене значение true, для запросов (например, автосохранение черновика \r\n// или периодический опрос сервера), при которых показывать блокирующий спиннер пользователю не нужно.\r\nexport const SKIP_LOADER = new HttpContextToken<boolean>(() => false);\r\n\r\n// Передайте true для запросов, ошибки которых (например, 404 при проверке уникальности)\r\n// не должны приводить к показу глобального нотификатора.\r\nexport const SKIP_NOTIFICATION = new HttpContextToken<boolean>(() => false);\r\n","import { InjectionToken } from \"@angular/core\";\r\n\r\nexport interface AppEnvironment {\r\n appVersion: string;\r\n production: boolean;\r\n \r\n // Объявляем словарь адресов. \r\n // Ключи default и auth — обязательные, остальные — любые строки.\r\n apiUrls: {\r\n default: string;\r\n auth: string;\r\n [key: string]: string; // <-- Это позволяет писать billing, analytics, crm и т.д.\r\n };\r\n};\r\n\r\nexport const APP_ENVIRONMENT = new InjectionToken<AppEnvironment>('ApplicationEnvironment');","import { InjectionToken } from \"@angular/core\";\r\n\r\n/**\r\n * Сервис уведомлений.\r\n */\r\nexport interface INotificationService {\r\n /**\r\n * Показ всплывашки с сообщением.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n openSnackBar(message: string, action: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о успехе.\r\n *\r\n * @param message сообщение\r\n * @param duration задержка в миллисекундах\r\n */\r\n success(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о информации.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n info(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о сообщения помощи.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n helper(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о предупреждении.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n warning(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление об ошибке.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n error(message: string, duration?: number): void;\r\n}\r\n\r\nexport const UIKIT_NOTIFICATION_TOKEN = new InjectionToken<INotificationService>('UikitNotificationToken');\r\n","import { Injectable } from '@angular/core';\r\nimport { Subject } from 'rxjs';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class AuthEventService {\r\n // Ошибки авторизации\r\n private readonly authErrorSource = new Subject<void>();\r\n\r\n // Стрим для прослушивания ошибок авторизации (экспортируется наружу)\r\n readonly authError$ = this.authErrorSource.asObservable();\r\n\r\n /** Вызывается в интерцепторе при 401 ошибке */\r\n notify401(): void {\r\n this.authErrorSource.next();\r\n }\r\n}","import { inject, Service } from '@angular/core';\r\nimport { UIKIT_CONFIG } from '../config';\r\n/**\r\n * Сущность пользователя из API\r\n */\r\nexport class User {\r\n id: number = 0;\r\n username: string = '';\r\n firstName: string = '';\r\n lastName: string = '';\r\n prof: string = '';\r\n depart: number = 0;\r\n area: string = '';\r\n tabnum: string = '';\r\n mail: string = '';\r\n phone: string = '';\r\n roles: string = '';\r\n rolesArray: string[] = [];\r\n roleList: string[] = [];\r\n idHuman: string = '';\r\n token: string = '';\r\n // Время действия токена\r\n expires_in: number = 0;\r\n}\r\n@Service()\r\n/**\r\n * Сервис для работы с объектом в local Storage\r\n */\r\nexport class UserService {\r\n private user: User;\r\n\r\n private config = inject(UIKIT_CONFIG);\r\n\r\n constructor() {\r\n const saved = localStorage.getItem(this.config.auth.userStorageKey);\r\n this.user = saved ? JSON.parse(saved) : new User();\r\n }\r\n\r\n get dept(): number {\r\n return this.user?.depart;\r\n }\r\n\r\n /** Получить данные о пользователе */\r\n get userData(): User {\r\n return this.user;\r\n }\r\n\r\n /** Изменить данные пользователя в localStorage */\r\n set userData(user: User) {\r\n this.user = user;\r\n localStorage.setItem(this.config.auth.userStorageKey, JSON.stringify(user));\r\n }\r\n\r\n /** Удаляет данные о пользователе из localStorage. */\r\n removeUser() {\r\n localStorage.removeItem(this.config.auth.userStorageKey);\r\n }\r\n}\r\n","import { DestroyRef, Injectable, inject, signal } from '@angular/core';\r\nimport { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';\r\nimport { Router } from '@angular/router';\r\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\n\r\nimport { AuthEventService } from './auth-event.service';\r\nimport { User, UserService } from './auth-user.service';\r\nimport { UIKIT_CONFIG } from '../config';\r\nimport { UIKIT_NOTIFICATION_TOKEN } from '../models';\r\nimport { catchError, Observable, tap, throwError } from 'rxjs';\r\n\r\n// interface RefreshResponse {\r\n// accessToken: string;\r\n// refreshToken: string;\r\n// }\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class AuthService {\r\n // Хранит роли пользователя\r\n private readonly _roles = signal<string[]>([]);\r\n // Состояние авторизации\r\n private readonly _loggedIn = signal<boolean>(false);\r\n\r\n readonly roles = this._roles.asReadonly();\r\n readonly loggedIn = this._loggedIn.asReadonly();\r\n\r\n // Заголовки для URL-encoded формы\r\n private readonly defaultOptions = {\r\n headers: new HttpHeaders().set(\r\n 'content-type',\r\n 'application/x-www-form-urlencoded',\r\n ),\r\n };\r\n\r\n private readonly notificationService = inject(UIKIT_NOTIFICATION_TOKEN);\r\n private readonly userService = inject(UserService);\r\n private readonly eventService = inject(AuthEventService);\r\n private readonly destroyRef = inject(DestroyRef);\r\n private readonly http = inject(HttpClient);\r\n private readonly config = inject(UIKIT_CONFIG);\r\n // Внедряем роутер опционально, чтобы библиотека не падала, если роутера нет в приложении\r\n private readonly router = inject(Router, { optional: true });\r\n\r\n constructor() {\r\n const initialUser = this.userService.userData as User | null;\r\n\r\n if (initialUser?.token) {\r\n this._loggedIn.set(true);\r\n this._roles.set(initialUser.roleList ?? []);\r\n }\r\n\r\n // Слушаем шину событий: если интерцептор поймал 401, сбрасываем сессию\r\n this.eventService.authError$\r\n .pipe(takeUntilDestroyed(this.destroyRef))\r\n .subscribe(() => this.logout());\r\n }\r\n\r\n /**\r\n * Запрос авторизации\r\n * Возвращает Observable, позволяя компоненту реагировать на статус запроса.\r\n */\r\n login(username: string, password: string, idCas: number): Observable<User> {\r\n let params = new HttpParams()\r\n .set('username', username)\r\n .set('password', password); // HttpParams закодирует спецсимволы сам!\r\n\r\n if (idCas) {\r\n params = params.set('idcas', idCas.toString());\r\n }\r\n\r\n const url = `${this.config.apiUrls.auth}${this.config.auth.tokenRoute}`;\r\n\r\n return this.http\r\n .post<User>(url, params.toString(), this.defaultOptions)\r\n .pipe(\r\n tap((data) => this.successLoginAction(data)),\r\n catchError((error) => {\r\n console.log('Login error:', error);\r\n return throwError(() => error);\r\n }),\r\n );\r\n }\r\n\r\n /** Aвторизация по пропуску */\r\n loginByPermit(permit: string, idCas: number): Observable<User> {\r\n let params = new HttpParams().set('permit', permit);\r\n if (idCas) {\r\n params = params.set('idcas', idCas.toString());\r\n }\r\n\r\n const url = `${this.config.apiUrls.auth}${this.config.auth.tokenRoute}`;\r\n\r\n return this.http.post<User>(url, params.toString(), this.defaultOptions).pipe(\r\n tap((data) => this.successLoginAction(data)),\r\n catchError((error) => {\r\n console.log('Permit login error:', error); \r\n return throwError(() => error);\r\n }),\r\n );\r\n }\r\n\r\n logout(): void {\r\n // Убирает(удаляет) пользователя из локального хранилища когда он разлогинивается\r\n this.userService.removeUser();\r\n this._loggedIn.set(false);\r\n this._roles.set([]);\r\n\r\n // localStorage.removeItem(this.config.auth.userStorageKey);\r\n // localStorage.removeItem(this.config.auth.refreshTokenStorageKey);\r\n // ЛОГИКА СОХРАНЕНИЯ URL ПРИ РАЗЛОГИНЕ (Ваш улучшенный кусок кода)\r\n if (this.router) {\r\n const currentUrl = this.router.url;\r\n\r\n // Проверяем, не находимся ли мы уже на странице логина, чтобы избежать зацикливания\r\n if (currentUrl.includes(this.config.auth.loginRoute)) {\r\n return;\r\n }\r\n\r\n // Безопасное формирование query-параметров через Router\r\n this.router.navigate([this.config.auth.loginRoute], {\r\n queryParams: { returnUrl: encodeURIComponent(currentUrl) },\r\n });\r\n }\r\n }\r\n\r\n /** При успешной авторизации */\r\n private successLoginAction(user: User) {\r\n this.userService.userData = user;\r\n this._loggedIn.set(true);\r\n this._roles.set(user.roleList ?? []);\r\n }\r\n\r\n // // Метод отправляет старый refresh-токен на сервер\r\n // refreshToken(): Observable<RefreshResponse> {\r\n // const refreshToken = localStorage.getItem(this.config.auth.refreshTokenStorageKey);\r\n\r\n // if (!refreshToken) {\r\n // this.logout();\r\n // return throwError(() => new Error('No refresh token available'));\r\n // }\r\n\r\n // // Собираем абсолютный урл (берем default baseUrl + эндпоинт рефреша)\r\n // const baseUrl = this.config.apiUrls.default.replace(/\\/\\$/, '');\r\n // const url = `${baseUrl}${this.config.auth.refreshEndpoint}`;\r\n\r\n // // Передаем токен (в зависимости от вашего бэкенда: в body или в куках/заголовках)\r\n // return this.http.post<RefreshResponse>(url, { refreshToken }).pipe(\r\n // tap(response => {\r\n // // Сохраняем новые токены в локалсторадж\r\n // localStorage.setItem(this.config.auth.tokenStorageKey, response.accessToken);\r\n // localStorage.setItem(this.config.auth.refreshTokenStorageKey, response.refreshToken);\r\n // }),\r\n // catchError(err => {\r\n // // Если даже рефреш-токен невалиден (истек) — принудительный разлогин\r\n // this.logout();\r\n // return throwError(() => err);\r\n // })\r\n // );\r\n // }\r\n}\r\n","/**\r\n * Проверяет, пересекаются ли два массива (есть ли хотя бы один общий элемент).\r\n */\r\nexport function hasArrayIntersection<T>(arrayA: T[], arrayB: T[]): boolean {\r\n if (!arrayA || !arrayB) return false;\r\n return arrayA.some(item => arrayB.includes(item));\r\n}\r\n","import { inject, Injectable } from '@angular/core';\r\n\r\nimport { AuthService } from './auth.service';\r\nimport { hasArrayIntersection } from '../utils/array.utils';\r\nimport { ADMIN_ROLE, HIDE_ROLE_MARKER } from '../constants';\r\n\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\n/** Вычисление разрешений по ролям */ \r\nexport class RoleService {\r\n private readonly authService = inject(AuthService);\r\n\r\n /**\r\n * Проверяет, заблокирован ли доступ к элементу на основе списка ограничений.\r\n * Возвращает true, если у пользователя есть запрещенная роль или передан маркер 'HIDE'.\r\n * Администратор (ADMIN) всегда игнорирует любые запреты и маркер 'HIDE'.\r\n */\r\n public hasForbiddenRole(forbiddenRoles: string[]): boolean {\r\n if (!forbiddenRoles || forbiddenRoles.length === 0) return false;\r\n\r\n const userRoles = this.authService.roles() ?? [];\r\n\r\n // Если пользователь ADMIN — он игнорирует списки запретов\r\n if (userRoles.includes(ADMIN_ROLE)) return false;\r\n\r\n // Если в условиях зашит маркер 'HIDE' — скрываем элемент от обычного пользователя\r\n if (forbiddenRoles.includes(HIDE_ROLE_MARKER)) return true;\r\n\r\n // В остальных случаях проверяем стандартное пересечение ролей\r\n return hasArrayIntersection(userRoles, forbiddenRoles);\r\n }\r\n\r\n /**\r\n * Проверяет, есть ли у пользователя хотя бы одна из разрешенных ролей.\r\n * ADMIN всегда возвращает true.\r\n */\r\npublic hasAnyRole(allowedRoles: string[]): boolean {\r\n const userRoles = this.authService.roles() ?? [];\r\n if (userRoles.includes(ADMIN_ROLE)) return true;\r\n \r\n return hasArrayIntersection(userRoles, allowedRoles);\r\n}\r\n}\r\n","import { ChangeDetectionStrategy, Component } from '@angular/core';\r\nimport { MatProgressSpinner } from '@angular/material/progress-spinner';\r\n\r\n@Component({\r\n selector: 'ngx-uik-loader',\r\n template: `\r\n <div class=\"loading-indicator\">\r\n <mat-progress-spinner mode=\"indeterminate\" aria-label=\"Загрузка...\"/>\r\n </div>\r\n `,\r\n styles: [\r\n `\r\n /* Разместить по центру */\r\n .loading-indicator {\r\n position: fixed;\r\n z-index: 999;\r\n height: 2em;\r\n width: 2em;\r\n overflow: initial;\r\n margin: auto;\r\n inset: 0; /* 2. Современное CSS-свойство взамен top/left/bottom/right */\r\n }\r\n\r\n /* Заблокировать оверлей */\r\n .loading-indicator::before {\r\n content: '';\r\n display: block;\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.3);\r\n }\r\n `,\r\n ],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [MatProgressSpinner],\r\n})\r\nexport class ProgressSpinnerComponent {}\r\n","import { Overlay, OverlayRef } from '@angular/cdk/overlay';\r\nimport { ComponentPortal } from '@angular/cdk/portal';\r\nimport { inject, OnDestroy, Service } from '@angular/core';\r\n\r\nimport { ProgressSpinnerComponent } from '../shared/progress-spinner';\r\n\r\n@Service()\r\nexport class LoaderService implements OnDestroy {\r\n private overlayRef: OverlayRef | null = null;\r\n private readonly overlay = inject(Overlay);\r\n\r\n // Счетчик для контроля параллельных HTTP-запросов\r\n private activeRequests = 0;\r\n\r\n public show(): void {\r\n this.activeRequests++;\r\n\r\n if (!this.overlayRef) {\r\n this.overlayRef = this.overlay.create({\r\n hasBackdrop: false, // Настройки бэкдропа теперь берет на себя стилистика лоадера\r\n positionStrategy: this.overlay\r\n .position()\r\n .global()\r\n .centerHorizontally()\r\n .centerVertically(),\r\n });\r\n }\r\n if (!this.overlayRef.hasAttached()) {\r\n const spinnerPortal = new ComponentPortal(ProgressSpinnerComponent);\r\n this.overlayRef.attach(spinnerPortal);\r\n }\r\n }\r\n\r\n public hide(): void {\r\n if (this.activeRequests > 0) {\r\n this.activeRequests--;\r\n }\r\n // Скрываем спиннер только тогда, когда завершился ПОСЛЕДНИЙ активный запрос\r\n if (this.activeRequests === 0 && this.overlayRef && this.overlayRef.hasAttached()) {\r\n this.overlayRef.detach();\r\n }\r\n }\r\n\r\n /**\r\n * Принудительный сброс (например, при смене роута или критической ошибке)\r\n */\r\n public reset(): void {\r\n this.activeRequests = 0;\r\n if (this.overlayRef && this.overlayRef.hasAttached()) {\r\n this.overlayRef.detach();\r\n }\r\n }\r\n\r\n ngOnDestroy(): void {\r\n if (this.overlayRef) {\r\n this.overlayRef.dispose(); // Полностью удаляет элемент оверлея из DOM\r\n }\r\n }\r\n}\r\n","import { inject, Service } from '@angular/core';\r\nimport { MatSnackBar } from '@angular/material/snack-bar';\r\n\r\nimport { INotificationService } from '../models';\r\n\r\n// Сервис для вывода сообщений(ошибки, успеха)\r\n@Service()\r\nexport class NotificationService implements INotificationService {\r\n private readonly duration = 5000;\r\n private readonly _snackBar = inject(MatSnackBar);\r\n\r\n openSnackBar(message: string, action: string, duration: number = this.duration) {\r\n this._snackBar.open(message, action, {\r\n duration,\r\n panelClass: ['red-snackbar'],\r\n });\r\n }\r\n\r\n success(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n info(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n helper(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n warning(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n error(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n}\r\n","/**\r\n * Преобразует дату в локальную строковую дату (Europe/Samara).\r\n * Чистая функция, независимая от контекста Angular.\r\n */\r\nexport function dateToString(\r\n date: string | number | Date | null | undefined,\r\n): string {\r\n if (!date) return '';\r\n\r\n return new Date(date).toLocaleString('ru-RU', {\r\n timeZone: 'Europe/Samara',\r\n });\r\n}\r\n","import { computed, signal, type Signal } from '@angular/core';\r\n\r\nexport class SignalSelectionModel<T> {\r\n private readonly _selectedSet = signal<Set<T>>(new Set());\r\n\r\n // Свойства-сигналы для эффективной работы в Angular шаблонах\r\n readonly selected: Signal<T[]> = computed(() => Array.from(this._selectedSet()));\r\n readonly isEmpty = computed(() => this._selectedSet().size === 0);\r\n readonly hasValue = computed(() => this._selectedSet().size > 0);\r\n\r\n constructor(\r\n public readonly multiple = false, // Поле multiple как в CDK\r\n initialValues: T[] = [],\r\n private _compareWith?: (o1: T, o2: T) => boolean // Поддержка сравнения по ID (как в MatSelect)\r\n ) {\r\n if (initialValues.length > 0) {\r\n this.select(...initialValues);\r\n }\r\n }\r\n\r\n // Проверка наличия элемента с учетом кастомного сравнения\r\n isSelected(value: T): boolean {\r\n const set = this._selectedSet();\r\n if (!this._compareWith) {\r\n return set.has(value);\r\n }\r\n \r\n for (const item of set) {\r\n if (this._compareWith(item, value)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n select(...values: T[]): void {\r\n this._selectedSet.update(set => {\r\n const newSet = new Set(set);\r\n \r\n if (!this.multiple) {\r\n newSet.clear();\r\n if (values.length > 0) {\r\n this._addWithCompare(newSet, values[values.length - 1]);\r\n }\r\n } else {\r\n values.forEach(v => this._addWithCompare(newSet, v));\r\n }\r\n return newSet;\r\n });\r\n }\r\n\r\n deselect(...values: T[]): void {\r\n if (values.length === 0) return; // Ранний выход, если массив пустой\r\n\r\n this._selectedSet.update(set => {\r\n const newSet = new Set(set);\r\n if (this._compareWith) {\r\n const toDelete = new Set<T>(); // Сюда складываем кандидатов на удаление\r\n \r\n for (const value of values) {\r\n for (const current of newSet) {\r\n if (this._compareWith(current, value)) {\r\n toDelete.add(current);\r\n }\r\n }\r\n }\r\n // Удаляем только после того, как все циклы по newSet завершены\r\n toDelete.forEach(item => newSet.delete(item));\r\n } else {\r\n values.forEach(v => newSet.delete(v));\r\n }\r\n return newSet;\r\n });\r\n }\r\n\r\n toggle(value: T): void {\r\n this.isSelected(value) ? this.deselect(value) : this.select(value);\r\n }\r\n\r\n clear(): void {\r\n if (this._selectedSet().size === 0) return; \r\n this._selectedSet.set(new Set());\r\n }\r\n\r\n // Вспомогательный метод для корректного добавления объектов\r\n private _addWithCompare(set: Set<T>, value: T): void {\r\n if (this._compareWith) {\r\n let duplicate: T | undefined;\r\n for (const current of set) {\r\n if (this._compareWith(current, value)) {\r\n duplicate = current;\r\n break;\r\n }\r\n }\r\n if (duplicate !== undefined) {\r\n set.delete(duplicate);\r\n }\r\n }\r\n set.add(value);\r\n }\r\n}\r\n","import { ZodError } from 'zod';\r\n\r\nexport function formatZodError(error: ZodError, contextName: string): string {\r\n const issuesText = error.issues\r\n .map((issue) => {\r\n // Форматируем путь: [0, \"user\"] -> \"[0] -> user\"\r\n const path = issue.path\r\n .map((p) => (typeof p === 'number' ? `[${p}]` : p))\r\n .join(' -> ');\r\n\r\n const location = path ? `в \"${path}\"` : 'в корне';\r\n const expected = (issue as any).expected\r\n ? `ожидалось \"${(issue as any).expected}\"`\r\n : '';\r\n const received = (issue as any).received\r\n ? `, получено \"${(issue as any).received}\"`\r\n : '';\r\n\r\n return ` • Ошибка ${location}: ${expected}${received}. Сообщение: ${issue.message}`;\r\n })\r\n .join('\\n');\r\n\r\n return `\\n[Zod Валидация] Ошибка парсинга для ресурса: ${contextName}\\n${issuesText}\\n`;\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"kuzntsv-uikit.mjs","names":[],"sources":["../../src/config/lib-config.ts","../../src/constants/auth.constants.ts","../../src/constants/http-headers.constants.ts","../../src/http/http-context-tokens.ts","../../src/models/interfaces/environment.interface.ts","../../src/models/interfaces/notification-service.interface.ts","../../src/services/auth-event.service.ts","../../src/services/auth-user.service.ts","../../src/services/auth.service.ts","../../src/utils/array.utils.ts","../../src/services/auth-role.service.ts","../../src/shared/progress-spinner/progress-spinner.component.ts","../../src/services/loader.service.ts","../../src/services/notification.service.ts","../../src/utils/date.utils.ts","../../src/utils/signal-selection-model.ts","../../src/utils/zod-error-formatter.ts"],"sourcesContent":["import { InjectionToken } from \"@angular/core\";\r\n\r\nexport interface UikitConfig {\r\n apiUrls: {\r\n default: string;\r\n auth: string;\r\n [key: string]: string;\r\n };\r\n auth: {\r\n disabled?: boolean; // true = авторизация полностью отключена\r\n userStorageKey: string; // Например, 'app_user_jwt'\r\n tokenRoute: string; // Например, '/jwt'\r\n // refreshTokenStorageKey: string; // Ключ для Refresh токена (например, 'refresh_token')\r\n // refreshEndpoint: string; // Относительный путь (например, '/auth/refresh')\r\n loginRoute: string; // Например, '/auth/login'\r\n publicUrls: string[]; // Относительные пути, которые не требуют токена (например, '/login', '/assets')\r\n };\r\n}\r\n\r\nexport const UIKIT_CONFIG = new InjectionToken<UikitConfig>('UIKIT_CONFIG');\r\n","/**\r\n * Специальный маркер-роль в массиве ограничений, \r\n * который полностью скрывает элемент от обычных пользователей.\r\n */\r\nexport const HIDE_ROLE_MARKER = 'HIDE';\r\n\r\n/**\r\n * Системная роль Администратора, которая игнорирует любые ограничения прав.\r\n */\r\nexport const ADMIN_ROLE = 'ADMIN';\r\n","/**\r\n * Специальный маркер-роль в массиве ограничений, \r\n * который полностью скрывает элемент от обычных пользователей.\r\n */\r\nexport const X_SKIP_NOTIFICATION = 'X-Skip-Notification';\r\n\r\n/**\r\n * Специальный маркер-роль в массиве ограничений, \r\n * который полностью скрывает элемент от обычных пользователей.\r\n */\r\nexport const X_SKIP_LOADER = 'X-Skip-Loader';","import { HttpContextToken } from '@angular/common/http';\r\nimport { inject, InjectionToken } from '@angular/core';\r\n\r\n// Токен, в который мы будем передавать строку-идентификатор нужного API\r\n// По умолчанию пустая строка означает \"использовать дефолтное (основное) API\"\r\nexport const API_NAME = new HttpContextToken<string>(() => '');\r\n\r\n// Иногда внутри сервиса нужно сделать один специфический запрос (например, стянуть публичный JSON-файл конфигурации), не добавляя его глобально в publicUrls.\r\n// Используя подход с HttpContext, мы можем добавить в библиотеку маркер пропуска авторизации:\r\n// А в коде приложения вызывать так:\r\n// this.http.get('/assets/config.json', { \r\n// context: new HttpContext().set(BYPASS_AUTH, true) \r\n// });\r\nexport const BYPASS_AUTH = new HttpContextToken<boolean>(() => false);\r\n\r\n// DI-токен, который переопределяем в appConfig\r\nexport const DEFAULT_TIMEOUT = new InjectionToken<number>('DEFAULT_TIMEOUT', {\r\n providedIn: 'root',\r\n factory: () => 10000 // Запасной дефолт (10 сек), если в appConfig ничего не указали\r\n});\r\n\r\n// Если для каких-то тяжелых запросов (например, загрузка больших файлов или генерация отчетов) \r\n// вам нужно увеличить таймаут, вы можете передавать его значение прямо в запросе через HttpContext.\r\nexport const REQUEST_TIMEOUT = new HttpContextToken<number>(() => inject(DEFAULT_TIMEOUT));\r\n\r\n// Передать в токене значение true, для запросов (например, автосохранение черновика \r\n// или периодический опрос сервера), при которых показывать блокирующий спиннер пользователю не нужно.\r\nexport const SKIP_LOADER = new HttpContextToken<boolean>(() => false);\r\n\r\n// Передайте true для запросов, ошибки которых (например, 404 при проверке уникальности)\r\n// не должны приводить к показу глобального нотификатора.\r\nexport const SKIP_NOTIFICATION = new HttpContextToken<boolean>(() => false);\r\n","import { InjectionToken } from \"@angular/core\";\r\n\r\nexport interface AppEnvironment {\r\n appVersion: string;\r\n production: boolean;\r\n \r\n // Объявляем словарь адресов. \r\n // Ключи default и auth — обязательные, остальные — любые строки.\r\n apiUrls: {\r\n default: string;\r\n auth: string;\r\n [key: string]: string; // <-- Это позволяет писать billing, analytics, crm и т.д.\r\n };\r\n};\r\n\r\nexport const APP_ENVIRONMENT = new InjectionToken<AppEnvironment>('ApplicationEnvironment');","import { InjectionToken } from \"@angular/core\";\r\n\r\n/**\r\n * Сервис уведомлений.\r\n */\r\nexport interface INotificationService {\r\n /**\r\n * Показ всплывашки с сообщением.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n openSnackBar(message: string, action: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о успехе.\r\n *\r\n * @param message сообщение\r\n * @param duration задержка в миллисекундах\r\n */\r\n success(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о информации.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n info(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о сообщения помощи.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n helper(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление о предупреждении.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n warning(message: string, duration?: number): void;\r\n\r\n /**\r\n * Уведомление об ошибке.\r\n *\r\n * @param message сообщение.\r\n * @param duration задержка в миллисекундах.\r\n */\r\n error(message: string, duration?: number): void;\r\n}\r\n\r\nexport const UIKIT_NOTIFICATION_TOKEN = new InjectionToken<INotificationService>('UikitNotificationToken');\r\n","import { Injectable } from '@angular/core';\r\nimport { Subject } from 'rxjs';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class AuthEventService {\r\n // Ошибки авторизации\r\n private readonly authErrorSource = new Subject<void>();\r\n\r\n // Стрим для прослушивания ошибок авторизации (экспортируется наружу)\r\n readonly authError$ = this.authErrorSource.asObservable();\r\n\r\n /** Вызывается в интерцепторе при 401 ошибке */\r\n notify401(): void {\r\n this.authErrorSource.next();\r\n }\r\n}","import { inject, Service } from '@angular/core';\r\nimport { UIKIT_CONFIG } from '../config';\r\n/**\r\n * Сущность пользователя из API\r\n */\r\nexport class User {\r\n id: number = 0;\r\n username: string = '';\r\n firstName: string = '';\r\n lastName: string = '';\r\n prof: string = '';\r\n depart: number = 0;\r\n area: string = '';\r\n tabnum: string = '';\r\n mail: string = '';\r\n phone: string = '';\r\n roles: string = '';\r\n rolesArray: string[] = [];\r\n roleList: string[] = [];\r\n idHuman: string = '';\r\n token: string = '';\r\n // Время действия токена\r\n expires_in: number = 0;\r\n}\r\n@Service()\r\n/**\r\n * Сервис для работы с объектом в local Storage\r\n */\r\nexport class UserService {\r\n private user: User;\r\n\r\n private config = inject(UIKIT_CONFIG);\r\n\r\n constructor() {\r\n const saved = localStorage.getItem(this.config.auth.userStorageKey);\r\n this.user = saved ? JSON.parse(saved) : new User();\r\n }\r\n\r\n get dept(): number {\r\n return this.user?.depart;\r\n }\r\n\r\n /** Получить данные о пользователе */\r\n get userData(): User {\r\n return this.user;\r\n }\r\n\r\n /** Изменить данные пользователя в localStorage */\r\n set userData(user: User) {\r\n this.user = user;\r\n localStorage.setItem(this.config.auth.userStorageKey, JSON.stringify(user));\r\n }\r\n\r\n /** Удаляет данные о пользователе из localStorage. */\r\n removeUser() {\r\n localStorage.removeItem(this.config.auth.userStorageKey);\r\n }\r\n}\r\n","import { DestroyRef, Injectable, inject, signal } from '@angular/core';\r\nimport { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';\r\nimport { Router } from '@angular/router';\r\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\n\r\nimport { AuthEventService } from './auth-event.service';\r\nimport { User, UserService } from './auth-user.service';\r\nimport { UIKIT_CONFIG } from '../config';\r\nimport { UIKIT_NOTIFICATION_TOKEN } from '../models';\r\nimport { catchError, Observable, tap, throwError } from 'rxjs';\r\n\r\n// interface RefreshResponse {\r\n// accessToken: string;\r\n// refreshToken: string;\r\n// }\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class AuthService {\r\n // Хранит роли пользователя\r\n private readonly _roles = signal<string[]>([]);\r\n // Состояние авторизации\r\n private readonly _loggedIn = signal<boolean>(false);\r\n\r\n readonly roles = this._roles.asReadonly();\r\n readonly loggedIn = this._loggedIn.asReadonly();\r\n\r\n // Заголовки для URL-encoded формы\r\n private readonly defaultOptions = {\r\n headers: new HttpHeaders().set(\r\n 'content-type',\r\n 'application/x-www-form-urlencoded',\r\n ),\r\n };\r\n\r\n private readonly notificationService = inject(UIKIT_NOTIFICATION_TOKEN);\r\n private readonly userService = inject(UserService);\r\n private readonly eventService = inject(AuthEventService);\r\n private readonly destroyRef = inject(DestroyRef);\r\n private readonly http = inject(HttpClient);\r\n private readonly config = inject(UIKIT_CONFIG);\r\n // Внедряем роутер опционально, чтобы библиотека не падала, если роутера нет в приложении\r\n private readonly router = inject(Router, { optional: true });\r\n\r\n constructor() {\r\n const initialUser = this.userService.userData as User | null;\r\n\r\n if (initialUser?.token) {\r\n this._loggedIn.set(true);\r\n this._roles.set(initialUser.roleList ?? []);\r\n }\r\n\r\n // Слушаем шину событий: если интерцептор поймал 401, сбрасываем сессию\r\n this.eventService.authError$\r\n .pipe(takeUntilDestroyed(this.destroyRef))\r\n .subscribe(() => this.logout());\r\n }\r\n\r\n /**\r\n * Запрос авторизации\r\n * Возвращает Observable, позволяя компоненту реагировать на статус запроса.\r\n */\r\n login(username: string, password: string, idCas: number): Observable<User> {\r\n let params = new HttpParams()\r\n .set('username', username)\r\n .set('password', password); // HttpParams закодирует спецсимволы сам!\r\n\r\n if (idCas) {\r\n params = params.set('idcas', idCas.toString());\r\n }\r\n\r\n const url = `${this.config.apiUrls.auth}${this.config.auth.tokenRoute}`;\r\n\r\n return this.http\r\n .post<User>(url, params.toString(), this.defaultOptions)\r\n .pipe(\r\n tap((data) => this.successLoginAction(data)),\r\n catchError((error) => {\r\n console.log('Login error:', error);\r\n return throwError(() => error);\r\n }),\r\n );\r\n }\r\n\r\n /** Aвторизация по пропуску */\r\n loginByPermit(permit: string, idCas: number): Observable<User> {\r\n let params = new HttpParams().set('permit', permit);\r\n if (idCas) {\r\n params = params.set('idcas', idCas.toString());\r\n }\r\n\r\n const url = `${this.config.apiUrls.auth}${this.config.auth.tokenRoute}`;\r\n\r\n return this.http.post<User>(url, params.toString(), this.defaultOptions).pipe(\r\n tap((data) => this.successLoginAction(data)),\r\n catchError((error) => {\r\n console.log('Permit login error:', error); \r\n return throwError(() => error);\r\n }),\r\n );\r\n }\r\n\r\n logout(): void {\r\n // Убирает(удаляет) пользователя из локального хранилища когда он разлогинивается\r\n this.userService.removeUser();\r\n this._loggedIn.set(false);\r\n this._roles.set([]);\r\n\r\n // localStorage.removeItem(this.config.auth.userStorageKey);\r\n // localStorage.removeItem(this.config.auth.refreshTokenStorageKey);\r\n // ЛОГИКА СОХРАНЕНИЯ URL ПРИ РАЗЛОГИНЕ (Ваш улучшенный кусок кода)\r\n if (this.router) {\r\n const currentUrl = this.router.url;\r\n\r\n // Проверяем, не находимся ли мы уже на странице логина, чтобы избежать зацикливания\r\n if (currentUrl.includes(this.config.auth.loginRoute)) {\r\n return;\r\n }\r\n\r\n // Безопасное формирование query-параметров через Router\r\n this.router.navigate([this.config.auth.loginRoute], {\r\n queryParams: { returnUrl: encodeURIComponent(currentUrl) },\r\n });\r\n }\r\n }\r\n\r\n /** При успешной авторизации */\r\n private successLoginAction(user: User) {\r\n this.userService.userData = user;\r\n this._loggedIn.set(true);\r\n this._roles.set(user.roleList ?? []);\r\n }\r\n\r\n // // Метод отправляет старый refresh-токен на сервер\r\n // refreshToken(): Observable<RefreshResponse> {\r\n // const refreshToken = localStorage.getItem(this.config.auth.refreshTokenStorageKey);\r\n\r\n // if (!refreshToken) {\r\n // this.logout();\r\n // return throwError(() => new Error('No refresh token available'));\r\n // }\r\n\r\n // // Собираем абсолютный урл (берем default baseUrl + эндпоинт рефреша)\r\n // const baseUrl = this.config.apiUrls.default.replace(/\\/\\$/, '');\r\n // const url = `${baseUrl}${this.config.auth.refreshEndpoint}`;\r\n\r\n // // Передаем токен (в зависимости от вашего бэкенда: в body или в куках/заголовках)\r\n // return this.http.post<RefreshResponse>(url, { refreshToken }).pipe(\r\n // tap(response => {\r\n // // Сохраняем новые токены в локалсторадж\r\n // localStorage.setItem(this.config.auth.tokenStorageKey, response.accessToken);\r\n // localStorage.setItem(this.config.auth.refreshTokenStorageKey, response.refreshToken);\r\n // }),\r\n // catchError(err => {\r\n // // Если даже рефреш-токен невалиден (истек) — принудительный разлогин\r\n // this.logout();\r\n // return throwError(() => err);\r\n // })\r\n // );\r\n // }\r\n}\r\n","/**\r\n * Проверяет, пересекаются ли два массива (есть ли хотя бы один общий элемент).\r\n */\r\nexport function hasArrayIntersection<T>(arrayA: T[], arrayB: T[]): boolean {\r\n if (!arrayA || !arrayB) return false;\r\n return arrayA.some(item => arrayB.includes(item));\r\n}\r\n","import { inject, Injectable } from '@angular/core';\r\n\r\nimport { AuthService } from './auth.service';\r\nimport { hasArrayIntersection } from '../utils/array.utils';\r\nimport { ADMIN_ROLE, HIDE_ROLE_MARKER } from '../constants';\r\n\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\n/** Вычисление разрешений по ролям */ \r\nexport class RoleService {\r\n private readonly authService = inject(AuthService);\r\n\r\n /**\r\n * Проверяет, заблокирован ли доступ к элементу на основе списка ограничений.\r\n * Возвращает true, если у пользователя есть запрещенная роль или передан маркер 'HIDE'.\r\n * Администратор (ADMIN) всегда игнорирует любые запреты и маркер 'HIDE'.\r\n */\r\n public hasForbiddenRole(forbiddenRoles: string[]): boolean {\r\n if (!forbiddenRoles || forbiddenRoles.length === 0) return false;\r\n\r\n const userRoles = this.authService.roles() ?? [];\r\n\r\n // Если пользователь ADMIN — он игнорирует списки запретов\r\n if (userRoles.includes(ADMIN_ROLE)) return false;\r\n\r\n // Если в условиях зашит маркер 'HIDE' — скрываем элемент от обычного пользователя\r\n if (forbiddenRoles.includes(HIDE_ROLE_MARKER)) return true;\r\n\r\n // В остальных случаях проверяем стандартное пересечение ролей\r\n return hasArrayIntersection(userRoles, forbiddenRoles);\r\n }\r\n\r\n /**\r\n * Проверяет, есть ли у пользователя хотя бы одна из разрешенных ролей.\r\n * ADMIN всегда возвращает true.\r\n */\r\npublic hasAnyRole(allowedRoles: string[]): boolean {\r\n const userRoles = this.authService.roles() ?? [];\r\n if (userRoles.includes(ADMIN_ROLE)) return true;\r\n \r\n return hasArrayIntersection(userRoles, allowedRoles);\r\n}\r\n}\r\n","import { ChangeDetectionStrategy, Component } from '@angular/core';\r\nimport { MatProgressSpinner } from '@angular/material/progress-spinner';\r\n\r\n@Component({\r\n selector: 'ngx-uik-loader',\r\n template: `\r\n <div class=\"loading-indicator\">\r\n <mat-progress-spinner mode=\"indeterminate\" aria-label=\"Загрузка...\"/>\r\n </div>\r\n `,\r\n styles: [\r\n `\r\n /* Разместить по центру */\r\n .loading-indicator {\r\n position: fixed;\r\n z-index: 999;\r\n height: 2em;\r\n width: 2em;\r\n overflow: initial;\r\n margin: auto;\r\n inset: 0; /* 2. Современное CSS-свойство взамен top/left/bottom/right */\r\n }\r\n\r\n /* Заблокировать оверлей */\r\n .loading-indicator::before {\r\n content: '';\r\n display: block;\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n width: 100%;\r\n height: 100%;\r\n background-color: rgba(0, 0, 0, 0.3);\r\n }\r\n `,\r\n ],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [MatProgressSpinner],\r\n})\r\nexport class ProgressSpinnerComponent {}\r\n","import { Overlay, OverlayRef } from '@angular/cdk/overlay';\r\nimport { ComponentPortal } from '@angular/cdk/portal';\r\nimport { inject, OnDestroy, Service } from '@angular/core';\r\n\r\nimport { ProgressSpinnerComponent } from '../shared/progress-spinner';\r\n\r\n@Service()\r\nexport class LoaderService implements OnDestroy {\r\n private overlayRef: OverlayRef | null = null;\r\n private readonly overlay = inject(Overlay);\r\n\r\n // Счетчик для контроля параллельных HTTP-запросов\r\n private activeRequests = 0;\r\n\r\n public show(): void {\r\n this.activeRequests++;\r\n\r\n if (!this.overlayRef) {\r\n this.overlayRef = this.overlay.create({\r\n hasBackdrop: false, // Настройки бэкдропа теперь берет на себя стилистика лоадера\r\n positionStrategy: this.overlay\r\n .position()\r\n .global()\r\n .centerHorizontally()\r\n .centerVertically(),\r\n });\r\n }\r\n if (!this.overlayRef.hasAttached()) {\r\n const spinnerPortal = new ComponentPortal(ProgressSpinnerComponent);\r\n this.overlayRef.attach(spinnerPortal);\r\n }\r\n }\r\n\r\n public hide(): void {\r\n if (this.activeRequests > 0) {\r\n this.activeRequests--;\r\n }\r\n // Скрываем спиннер только тогда, когда завершился ПОСЛЕДНИЙ активный запрос\r\n if (this.activeRequests === 0 && this.overlayRef && this.overlayRef.hasAttached()) {\r\n this.overlayRef.detach();\r\n }\r\n }\r\n\r\n /**\r\n * Принудительный сброс (например, при смене роута или критической ошибке)\r\n */\r\n public reset(): void {\r\n this.activeRequests = 0;\r\n if (this.overlayRef && this.overlayRef.hasAttached()) {\r\n this.overlayRef.detach();\r\n }\r\n }\r\n\r\n ngOnDestroy(): void {\r\n if (this.overlayRef) {\r\n this.overlayRef.dispose(); // Полностью удаляет элемент оверлея из DOM\r\n }\r\n }\r\n}\r\n","import { inject, Service } from '@angular/core';\r\nimport { MatSnackBar } from '@angular/material/snack-bar';\r\n\r\nimport { INotificationService } from '../models';\r\n\r\n// Сервис для вывода сообщений(ошибки, успеха)\r\n@Service()\r\nexport class NotificationService implements INotificationService {\r\n private readonly duration = 5000;\r\n private readonly _snackBar = inject(MatSnackBar);\r\n\r\n openSnackBar(message: string, action: string, duration: number = this.duration) {\r\n this._snackBar.open(message, action, {\r\n duration,\r\n panelClass: ['red-snackbar'],\r\n });\r\n }\r\n\r\n success(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n info(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n helper(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n warning(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n error(message: string, duration: number = this.duration) {\r\n this.openSnackBar(message, 'Закрыть', duration);\r\n }\r\n\r\n}\r\n","/**\r\n * Преобразует дату в локальную строковую дату (Europe/Samara).\r\n * Чистая функция, независимая от контекста Angular.\r\n */\r\nexport function dateToString(\r\n date: string | number | Date | null | undefined,\r\n): string {\r\n if (!date) return '';\r\n\r\n return new Date(date).toLocaleString('ru-RU', {\r\n timeZone: 'Europe/Samara',\r\n });\r\n}\r\n","import { computed, signal, type Signal } from '@angular/core';\r\n\r\nexport class SignalSelectionModel<T> {\r\n private readonly _selectedSet = signal<Set<T>>(new Set());\r\n\r\n // Свойства-сигналы для эффективной работы в Angular шаблонах\r\n readonly selected: Signal<T[]> = computed(() => Array.from(this._selectedSet()));\r\n readonly isEmpty = computed(() => this._selectedSet().size === 0);\r\n readonly hasValue = computed(() => this._selectedSet().size > 0);\r\n\r\n constructor(\r\n public readonly multiple = false, // Поле multiple как в CDK\r\n initialValues: T[] = [],\r\n private _compareWith?: (o1: T, o2: T) => boolean // Поддержка сравнения по ID (как в MatSelect)\r\n ) {\r\n if (initialValues.length > 0) {\r\n this.select(...initialValues);\r\n }\r\n }\r\n\r\n // Проверка наличия элемента с учетом кастомного сравнения\r\n isSelected(value: T): boolean {\r\n const set = this._selectedSet();\r\n if (!this._compareWith) {\r\n return set.has(value);\r\n }\r\n \r\n for (const item of set) {\r\n if (this._compareWith(item, value)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n select(...values: T[]): void {\r\n this._selectedSet.update(set => {\r\n const newSet = new Set(set);\r\n \r\n if (!this.multiple) {\r\n newSet.clear();\r\n if (values.length > 0) {\r\n this._addWithCompare(newSet, values[values.length - 1]);\r\n }\r\n } else {\r\n values.forEach(v => this._addWithCompare(newSet, v));\r\n }\r\n return newSet;\r\n });\r\n }\r\n\r\n deselect(...values: T[]): void {\r\n if (values.length === 0) return; // Ранний выход, если массив пустой\r\n\r\n this._selectedSet.update(set => {\r\n const newSet = new Set(set);\r\n if (this._compareWith) {\r\n const toDelete = new Set<T>(); // Сюда складываем кандидатов на удаление\r\n \r\n for (const value of values) {\r\n for (const current of newSet) {\r\n if (this._compareWith(current, value)) {\r\n toDelete.add(current);\r\n }\r\n }\r\n }\r\n // Удаляем только после того, как все циклы по newSet завершены\r\n toDelete.forEach(item => newSet.delete(item));\r\n } else {\r\n values.forEach(v => newSet.delete(v));\r\n }\r\n return newSet;\r\n });\r\n }\r\n\r\n toggle(value: T): void {\r\n this.isSelected(value) ? this.deselect(value) : this.select(value);\r\n }\r\n\r\n clear(): void {\r\n if (this._selectedSet().size === 0) return; \r\n this._selectedSet.set(new Set());\r\n }\r\n\r\n // Вспомогательный метод для корректного добавления объектов\r\n private _addWithCompare(set: Set<T>, value: T): void {\r\n if (this._compareWith) {\r\n let duplicate: T | undefined;\r\n for (const current of set) {\r\n if (this._compareWith(current, value)) {\r\n duplicate = current;\r\n break;\r\n }\r\n }\r\n if (duplicate !== undefined) {\r\n set.delete(duplicate);\r\n }\r\n }\r\n set.add(value);\r\n }\r\n}\r\n","import { ZodError } from 'zod';\r\n\r\nexport function formatZodError(error: ZodError, contextName: string): string {\r\n const issuesText = error.issues\r\n .map((issue) => {\r\n // Форматируем путь: [0, \"user\"] -> \"[0] -> user\"\r\n const path = issue.path\r\n .map((p) => (typeof p === 'number' ? `[${p}]` : p))\r\n .join(' -> ');\r\n\r\n const location = path ? `в \"${path}\"` : 'в корне';\r\n const expected = (issue as any).expected\r\n ? `ожидалось \"${(issue as any).expected}\"`\r\n : '';\r\n const received = (issue as any).received\r\n ? `, получено \"${(issue as any).received}\"`\r\n : '';\r\n\r\n return ` • Ошибка ${location}: ${expected}${received}. Сообщение: ${issue.message}`;\r\n })\r\n .join('\\n');\r\n\r\n return `\\n[Zod Валидация] Ошибка парсинга для ресурса: ${contextName}\\n${issuesText}\\n`;\r\n}\r\n"],"mappings":"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"}
|
package/package.json
CHANGED
package/types/kuzntsv-uikit.d.ts
CHANGED
|
@@ -27,6 +27,16 @@ export declare const HIDE_ROLE_MARKER = "HIDE";
|
|
|
27
27
|
* Системная роль Администратора, которая игнорирует любые ограничения прав.
|
|
28
28
|
*/
|
|
29
29
|
export declare const ADMIN_ROLE = "ADMIN";
|
|
30
|
+
/**
|
|
31
|
+
* Специальный маркер-роль в массиве ограничений,
|
|
32
|
+
* который полностью скрывает элемент от обычных пользователей.
|
|
33
|
+
*/
|
|
34
|
+
export declare const X_SKIP_NOTIFICATION = "X-Skip-Notification";
|
|
35
|
+
/**
|
|
36
|
+
* Специальный маркер-роль в массиве ограничений,
|
|
37
|
+
* который полностью скрывает элемент от обычных пользователей.
|
|
38
|
+
*/
|
|
39
|
+
export declare const X_SKIP_LOADER = "X-Skip-Loader";
|
|
30
40
|
export declare const API_NAME: HttpContextToken<string>;
|
|
31
41
|
export declare const BYPASS_AUTH: HttpContextToken<boolean>;
|
|
32
42
|
export declare const DEFAULT_TIMEOUT: InjectionToken<number>;
|