mirror of
https://github.com/MAKS-IT-COM/maksit-webui.git
synced 2026-08-15 09:58:09 +02:00
(feature): release as unified npm package @maks-it.com/webui, deepDelta fixes
Some checks failed
Storybook tests / storybook-tests (push) Has been cancelled
Some checks failed
Storybook tests / storybook-tests (push) Has been cancelled
This commit is contained in:
parent
b74e9250a7
commit
25ae18bab6
@ -2,6 +2,7 @@
|
|||||||
"$schema": "homelab-maksit-skills-manifest-v1",
|
"$schema": "homelab-maksit-skills-manifest-v1",
|
||||||
"skillsRoot": "E:\\Users\\maksym\\source\\repos\\private\\homelab\\ai\\skills",
|
"skillsRoot": "E:\\Users\\maksym\\source\\repos\\private\\homelab\\ai\\skills",
|
||||||
"skills": [
|
"skills": [
|
||||||
|
"common/maksit-webui",
|
||||||
"common/react-typescript",
|
"common/react-typescript",
|
||||||
"common/maksit-repo-maintenance",
|
"common/maksit-repo-maintenance",
|
||||||
"local-ollama"
|
"local-ollama"
|
||||||
|
|||||||
@ -6,8 +6,13 @@ alwaysApply: true
|
|||||||
|
|
||||||
# MaksIT skills (maksit-webui)
|
# MaksIT skills (maksit-webui)
|
||||||
|
|
||||||
1. `E:\Users\maksym\source\repos\private\homelab\ai\skills\common\react-typescript\SKILL.md`
|
1. `E:\Users\maksym\source\repos\private\homelab\ai\skills\common\maksit-webui\SKILL.md` — `@maks-it.com/webui` ↔ **MaksIT.Core** + **maksit-identity-hub** + product backends (PATCH, OAuth, ACL)
|
||||||
2. `E:\Users\maksym\source\repos\private\homelab\ai\skills\common\maksit-repo-maintenance\SKILL.md`
|
2. `E:\Users\maksym\source\repos\private\homelab\ai\skills\common\react-typescript\SKILL.md`
|
||||||
3. `E:\Users\maksym\source\repos\private\homelab\ai\skills\local-ollama\SKILL.md` — local Ollama offload (`@local-ollama`)
|
3. `E:\Users\maksym\source\repos\private\homelab\ai\skills\common\maksit-repo-maintenance\SKILL.md`
|
||||||
|
4. `E:\Users\maksym\source\repos\private\homelab\ai\skills\local-ollama\SKILL.md` — `@local-ollama`
|
||||||
|
|
||||||
|
Before changing `src/` library source: read **maksit-webui** skill; grep **maksit-core** and **maksit-identity-hub** as needed. Run `npm run build` in `src/` after source changes.
|
||||||
|
|
||||||
|
Shortcuts: `c:\Users\maksym\Desktop\maksit-core.lnk`, `c:\Users\maksym\Desktop\maksit-identity-hub.lnk`
|
||||||
|
|
||||||
Manifest: `.cursor/maksit-skills.json`.
|
Manifest: `.cursor/maksit-skills.json`.
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -267,6 +267,8 @@ __pycache__/
|
|||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
coverage/
|
coverage/
|
||||||
|
src/coverage/
|
||||||
|
src/dist-test-dts/
|
||||||
storybook-static/
|
storybook-static/
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
"typescript.tsdk": "src/node_modules/typescript/lib",
|
"typescript.tsdk": "src/node_modules/typescript/lib",
|
||||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||||
|
"terminal.integrated.cwd": "${workspaceFolder}/src",
|
||||||
|
"npm.packageManager": "npm"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
# Agent instructions (maksit-webui)
|
# Agent instructions (maksit-webui)
|
||||||
|
|
||||||
|
Read **[maksit-webui](E:\Users\maksym\source\repos\private\homelab\ai\skills\common\maksit-webui\SKILL.md)** before changing `src/` library source or shared npm packages. It covers alignment with **MaksIT.Core**, **maksit-identity-hub**, **maksit-results**, and product backends.
|
||||||
|
|
||||||
Complementary skills (no precedence):
|
Complementary skills (no precedence):
|
||||||
|
|
||||||
| Skill | Path |
|
| Skill | Path |
|
||||||
|-------|------|
|
|-------|------|
|
||||||
|
| maksit-webui | [SKILL.md](E:\Users\maksym\source\repos\private\homelab\ai\skills\common\maksit-webui\SKILL.md) |
|
||||||
| react-typescript | [SKILL.md](E:\Users\maksym\source\repos\private\homelab\ai\skills\common\react-typescript\SKILL.md) |
|
| react-typescript | [SKILL.md](E:\Users\maksym\source\repos\private\homelab\ai\skills\common\react-typescript\SKILL.md) |
|
||||||
| maksit-repo-maintenance | [SKILL.md](E:\Users\maksym\source\repos\private\homelab\ai\skills\common\maksit-repo-maintenance\SKILL.md) |
|
| maksit-repo-maintenance | [SKILL.md](E:\Users\maksym\source\repos\private\homelab\ai\skills\common\maksit-repo-maintenance\SKILL.md) |
|
||||||
(E:\Users\maksym\source\repos\private\homelab\ai\skills\common\maksit-repo-maintenance\SKILL.md) |
|
|
||||||
| local-ollama | [SKILL.md](E:\Users\maksym\source\repos\private\homelab\ai\skills\local-ollama\SKILL.md) |
|
| local-ollama | [SKILL.md](E:\Users\maksym\source\repos\private\homelab\ai\skills\local-ollama\SKILL.md) |
|
||||||
|
|
||||||
Manifest: `.cursor/maksit-skills.json`.
|
Manifest: `.cursor/maksit-skills.json`.
|
||||||
|
|
||||||
|
**Shortcuts:** `c:\Users\maksym\Desktop\maksit-core.lnk`, `c:\Users\maksym\Desktop\maksit-identity-hub.lnk`
|
||||||
|
|
||||||
|
**Build rule:** after editing `src/` library source (`contracts/`, `core/`, `components/`), run `npm run build` in `src/` before typechecking consumers.
|
||||||
|
|||||||
24
CHANGELOG.md
24
CHANGELOG.md
@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.4.0] - 2026-07-07
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Breaking:** merged `@maks-it.com/webui-contracts`, `@maks-it.com/webui-core`, and `@maks-it.com/webui-components` into a single published package **`@maks-it.com/webui`**. Host apps install one package and import everything from `@maks-it.com/webui` (see `assets/docs/NPM_CONSUMPTION.md` migration notes).
|
||||||
|
- **Breaking:** `src/` is now the npm package root — `src/package.json` publishes as `@maks-it.com/webui`; npm workspaces and `src/packages/*` nesting removed.
|
||||||
|
- Source layout: `contracts/`, `core/`, and `components/` live directly under `src/` (internal organization only; no subpath exports).
|
||||||
|
- Release pipeline (`utils/engines/release/scriptSettings.json`) publishes one package; `NpmReleaseVersion.syncWorkspaceVersions` disabled.
|
||||||
|
- Jest, Vitest, and Storybook configs updated for the flat `src/` layout (`@webui/*` aliases point at `src/{contracts,core,components}`).
|
||||||
|
- VS Code/Cursor: TypeScript SDK and integrated terminal default to `src/`.
|
||||||
|
- `src/coverage/` explicitly gitignored; generated coverage is no longer tracked (README badges remain under `assets/badges/`).
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- `@maks-it.com/webui-contracts`, `@maks-it.com/webui-core`, and `@maks-it.com/webui-components` as separate npm packages (superseded by `@maks-it.com/webui`).
|
||||||
|
- `src/packages/` workspace layout and per-package `package.json` files.
|
||||||
|
- Repo-root `tsconfig.json` and stray `node_modules/` (tooling and dependencies live under `src/` only).
|
||||||
|
|
||||||
|
## [0.3.5] - 2026-07-06
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `ENTITY_SCOPES_ARRAY_POLICY`: synthetic identity is now `{entityId}-{entityType}` (not including `scope`), so adding an application scope alongside an existing organization scope produces a correct `AddToCollection` delta, and permission changes on id-less rows update in place via `_deltaId`.
|
||||||
|
|
||||||
## [0.3.4] - 2026-07-06
|
## [0.3.4] - 2026-07-06
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
18
README.md
18
README.md
@ -4,15 +4,13 @@
|
|||||||
|
|
||||||
Shared React UI library for **maksit-certs-ui** and **maksit-vault** WebUI apps.
|
Shared React UI library for **maksit-certs-ui** and **maksit-vault** WebUI apps.
|
||||||
|
|
||||||
## Packages
|
## Package
|
||||||
|
|
||||||
| npm package | Description |
|
| npm package | Description |
|
||||||
|-------------|-------------|
|
|-------------|-------------|
|
||||||
| `@maks-it.com/webui-contracts` | Shared TypeScript contracts (paging, gallery types, patch ops, scopes) |
|
| `@maks-it.com/webui` | Contracts, utilities (`deepDelta`, hooks, HTTP, SignalR), and React components |
|
||||||
| `@maks-it.com/webui-core` | Utilities (`deepDelta`, enum helpers, ACL parsers) and `useFormState` |
|
|
||||||
| `@maks-it.com/webui-components` | React components, layout, editors, DataTable, auth shell |
|
|
||||||
|
|
||||||
Source lives under `src/` (npm workspaces). Release automation lives under `utils/` (from [maksit-repoutils](https://github.com/MAKS-IT-COM/maksit-repoutils)).
|
Source lives under `src/` (`@maks-it.com/webui` — `contracts/`, `core/`, `components/`). Release automation lives under `utils/`.
|
||||||
|
|
||||||
## Local development
|
## Local development
|
||||||
|
|
||||||
@ -24,7 +22,7 @@ npm test
|
|||||||
npm run storybook
|
npm run storybook
|
||||||
```
|
```
|
||||||
|
|
||||||
**Storybook** (`npm run storybook`) runs a local catalog of `@maks-it.com/webui-components` with Tailwind, React Router, autodocs, a11y checks, and **Vitest component tests** (testing widget + `npm run test-storybook`). Stories live under `src/stories/components/` (mirroring component folders); see `src/stories/README.md` for story conventions and testing.
|
**Storybook** (`npm run storybook`) runs a local component catalog with Tailwind, React Router, autodocs, a11y checks, and **Vitest component tests** (testing widget + `npm run test-storybook`). Stories live under `src/stories/components/`; see `src/stories/README.md`.
|
||||||
|
|
||||||
Tests and coverage badges: **`utils\Invoke-TestEngine.bat`** (plugin config in `utils\engines\test\scriptSettings.json`; uses `NpmJestTest`).
|
Tests and coverage badges: **`utils\Invoke-TestEngine.bat`** (plugin config in `utils\engines\test\scriptSettings.json`; uses `NpmJestTest`).
|
||||||
|
|
||||||
@ -38,21 +36,21 @@ Configured plugins (see `utils\engines\release\scriptSettings.json`):
|
|||||||
|
|
||||||
| Plugin | Role |
|
| Plugin | Role |
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| `NpmReleaseVersion` | Read semver from `src/package.json`; sync `packages/*/package.json` |
|
| `NpmReleaseVersion` | Read semver from `src/package.json` |
|
||||||
| `NpmBuild` | `npm ci` + `npm run build` |
|
| `NpmBuild` | `npm ci` + `npm run build` |
|
||||||
| `ReleasePublishGuard` | Branch/tag checks before publish |
|
| `ReleasePublishGuard` | Branch/tag checks before publish |
|
||||||
| `GitHub` | GitHub release (optional; set `GitHub` env var) |
|
| `GitHub` | GitHub release (optional; set `GitHub` env var) |
|
||||||
| `NpmPublish` | Publish workspace packages in dependency order |
|
| `NpmPublish` | Publish `@maks-it.com/webui` |
|
||||||
|
|
||||||
Refresh shared utils from repoutils: **`utils\Update-RepoUtils.bat`**.
|
Refresh shared utils from repoutils: **`utils\Update-RepoUtils.bat`**.
|
||||||
|
|
||||||
## Consume in product repos
|
## Consume in product repos
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install @maks-it.com/webui-contracts @maks-it.com/webui-core @maks-it.com/webui-components
|
npm install @maks-it.com/webui
|
||||||
```
|
```
|
||||||
|
|
||||||
Wrap the app with `WebUiProvider` and pass axios/redux adapters — see [assets/docs/NPM_CONSUMPTION.md](assets/docs/NPM_CONSUMPTION.md).
|
See [assets/docs/NPM_CONSUMPTION.md](assets/docs/NPM_CONSUMPTION.md).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="147.5" height="20" role="img" aria-label="Branch Coverage: 78%">
|
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="20" role="img" aria-label="Branch Coverage: 78.2%">
|
||||||
<title>Branch Coverage: 78%</title>
|
<title>Branch Coverage: 78.2%</title>
|
||||||
<linearGradient id="s" x2="0" y2="100%">
|
<linearGradient id="s" x2="0" y2="100%">
|
||||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||||
<stop offset="1" stop-opacity=".1"/>
|
<stop offset="1" stop-opacity=".1"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<clipPath id="r">
|
<clipPath id="r">
|
||||||
<rect width="147.5" height="20" rx="3" fill="#fff"/>
|
<rect width="150" height="20" rx="3" fill="#fff"/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<g clip-path="url(#r)">
|
<g clip-path="url(#r)">
|
||||||
<rect width="107.5" height="20" fill="#555"/>
|
<rect width="107.5" height="20" fill="#555"/>
|
||||||
<rect x="107.5" width="40" height="20" fill="#97ca00"/>
|
<rect x="107.5" width="42.5" height="20" fill="#97ca00"/>
|
||||||
<rect width="147.5" height="20" fill="url(#s)"/>
|
<rect width="150" height="20" fill="url(#s)"/>
|
||||||
</g>
|
</g>
|
||||||
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
|
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
|
||||||
<text aria-hidden="true" x="53.75" y="15" fill="#010101" fill-opacity=".3">Branch Coverage</text>
|
<text aria-hidden="true" x="53.75" y="15" fill="#010101" fill-opacity=".3">Branch Coverage</text>
|
||||||
<text x="53.75" y="14" fill="#fff">Branch Coverage</text>
|
<text x="53.75" y="14" fill="#fff">Branch Coverage</text>
|
||||||
<text aria-hidden="true" x="127.5" y="15" fill="#010101" fill-opacity=".3">78%</text>
|
<text aria-hidden="true" x="128.75" y="15" fill="#010101" fill-opacity=".3">78.2%</text>
|
||||||
<text x="127.5" y="14" fill="#fff">78%</text>
|
<text x="128.75" y="14" fill="#fff">78.2%</text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1,5 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="137" height="20" role="img" aria-label="Line Coverage: 82.2%">
|
<svg xmlns="http://www.w3.org/2000/svg" width="137" height="20" role="img" aria-label="Line Coverage: 82.5%">
|
||||||
<title>Line Coverage: 82.2%</title>
|
<title>Line Coverage: 82.5%</title>
|
||||||
<linearGradient id="s" x2="0" y2="100%">
|
<linearGradient id="s" x2="0" y2="100%">
|
||||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||||
<stop offset="1" stop-opacity=".1"/>
|
<stop offset="1" stop-opacity=".1"/>
|
||||||
@ -15,7 +15,7 @@
|
|||||||
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
|
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
|
||||||
<text aria-hidden="true" x="47.25" y="15" fill="#010101" fill-opacity=".3">Line Coverage</text>
|
<text aria-hidden="true" x="47.25" y="15" fill="#010101" fill-opacity=".3">Line Coverage</text>
|
||||||
<text x="47.25" y="14" fill="#fff">Line Coverage</text>
|
<text x="47.25" y="14" fill="#fff">Line Coverage</text>
|
||||||
<text aria-hidden="true" x="115.75" y="15" fill="#010101" fill-opacity=".3">82.2%</text>
|
<text aria-hidden="true" x="115.75" y="15" fill="#010101" fill-opacity=".3">82.5%</text>
|
||||||
<text x="115.75" y="14" fill="#fff">82.2%</text>
|
<text x="115.75" y="14" fill="#fff">82.5%</text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1,5 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="20" role="img" aria-label="Method Coverage: 62.4%">
|
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="20" role="img" aria-label="Method Coverage: 58.2%">
|
||||||
<title>Method Coverage: 62.4%</title>
|
<title>Method Coverage: 58.2%</title>
|
||||||
<linearGradient id="s" x2="0" y2="100%">
|
<linearGradient id="s" x2="0" y2="100%">
|
||||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||||
<stop offset="1" stop-opacity=".1"/>
|
<stop offset="1" stop-opacity=".1"/>
|
||||||
@ -9,13 +9,13 @@
|
|||||||
</clipPath>
|
</clipPath>
|
||||||
<g clip-path="url(#r)">
|
<g clip-path="url(#r)">
|
||||||
<rect width="107.5" height="20" fill="#555"/>
|
<rect width="107.5" height="20" fill="#555"/>
|
||||||
<rect x="107.5" width="42.5" height="20" fill="#97ca00"/>
|
<rect x="107.5" width="42.5" height="20" fill="#a4a61d"/>
|
||||||
<rect width="150" height="20" fill="url(#s)"/>
|
<rect width="150" height="20" fill="url(#s)"/>
|
||||||
</g>
|
</g>
|
||||||
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
|
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="11">
|
||||||
<text aria-hidden="true" x="53.75" y="15" fill="#010101" fill-opacity=".3">Method Coverage</text>
|
<text aria-hidden="true" x="53.75" y="15" fill="#010101" fill-opacity=".3">Method Coverage</text>
|
||||||
<text x="53.75" y="14" fill="#fff">Method Coverage</text>
|
<text x="53.75" y="14" fill="#fff">Method Coverage</text>
|
||||||
<text aria-hidden="true" x="128.75" y="15" fill="#010101" fill-opacity=".3">62.4%</text>
|
<text aria-hidden="true" x="128.75" y="15" fill="#010101" fill-opacity=".3">58.2%</text>
|
||||||
<text x="128.75" y="14" fill="#fff">62.4%</text>
|
<text x="128.75" y="14" fill="#fff">58.2%</text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1,16 +1,29 @@
|
|||||||
# Consuming @maks-it.com/webui-* in Certs UI / Vault
|
# Consuming `@maks-it.com/webui` in Certs UI / Vault
|
||||||
|
|
||||||
Install:
|
Install:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install @maks-it.com/webui-contracts @maks-it.com/webui-core @maks-it.com/webui-components
|
npm install @maks-it.com/webui
|
||||||
|
npm install react react-dom react-router-dom lucide-react @tanstack/react-table react-virtualized axios @microsoft/signalr zod
|
||||||
|
```
|
||||||
|
|
||||||
|
Example imports:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import {
|
||||||
|
WebUiProvider,
|
||||||
|
Loader,
|
||||||
|
Authorization,
|
||||||
|
DataTable,
|
||||||
|
createColumns,
|
||||||
|
deepDelta,
|
||||||
|
PatchOperation,
|
||||||
|
} from '@maks-it.com/webui'
|
||||||
```
|
```
|
||||||
|
|
||||||
Wrap the app:
|
Wrap the app:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import { WebUiProvider, Loader, Authorization } from '@maks-it.com/webui-components'
|
|
||||||
|
|
||||||
<WebUiProvider
|
<WebUiProvider
|
||||||
api={{
|
api={{
|
||||||
getData: (url) => getData(url),
|
getData: (url) => getData(url),
|
||||||
@ -39,9 +52,27 @@ import { WebUiProvider, Loader, Authorization } from '@maks-it.com/webui-compone
|
|||||||
</WebUiProvider>
|
</WebUiProvider>
|
||||||
```
|
```
|
||||||
|
|
||||||
## API differences vs copied local folders
|
## Migration from `@maks-it.com/webui-*`
|
||||||
|
|
||||||
|
Replace three package installs with one:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- npm install @maks-it.com/webui-contracts @maks-it.com/webui-core @maks-it.com/webui-components
|
||||||
|
+ npm install @maks-it.com/webui
|
||||||
|
```
|
||||||
|
|
||||||
|
Update imports:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
- import { PatchOperation } from '@maks-it.com/webui-contracts'
|
||||||
|
- import { deepDelta } from '@maks-it.com/webui-core'
|
||||||
|
- import { DataTable } from '@maks-it.com/webui-components'
|
||||||
|
+ import { PatchOperation, deepDelta, DataTable } from '@maks-it.com/webui'
|
||||||
|
```
|
||||||
|
|
||||||
|
## API notes
|
||||||
|
|
||||||
- `RemoteSelectBoxComponent`: use `searchRoute` (absolute API path string) instead of `apiRoute: ApiRoutes`.
|
- `RemoteSelectBoxComponent`: use `searchRoute` (absolute API path string) instead of `apiRoute: ApiRoutes`.
|
||||||
- `SecretComponent`: pass `generateSecretRoute` when `enableGenerate` is true.
|
- `SecretComponent`: pass `generateSecretRoute` when `enableGenerate` is true.
|
||||||
- ACL: generic `parseAclEntry` / `parseAclEntries` from `@maks-it.com/webui-core`; per-app entity maps and `parse*AclEntries` live in each WebUI project (`models/acl.ts`).
|
- ACL: generic `parseAclEntry` / `parseAclEntries` from `@maks-it.com/webui`; per-app entity maps and `parse*AclEntries` live in each WebUI project (`models/acl.ts`).
|
||||||
- Identity request types and Zod schemas (`LoginRequest` + `LoginRequestSchema`, `LogoutRequest` + `LogoutRequestSchema`, `RefreshTokenRequest` + `RefreshTokenRequestSchema`) live in `@maks-it.com/webui-contracts`.
|
- Identity request types and Zod schemas (`LoginRequest` + `LoginRequestSchema`, `LogoutRequest` + `LogoutRequestSchema`, `RefreshTokenRequest` + `RefreshTokenRequestSchema`) are exported from `@maks-it.com/webui`.
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
# Publishing `@maks-it.com/webui-*` to npm
|
# Publishing `@maks-it.com/webui` to npm
|
||||||
|
|
||||||
Packages are published under the **`@maks-it.com` scope** to [registry.npmjs.org](https://registry.npmjs.org), managed from the [maks-it.com npm org](https://www.npmjs.com/settings/maks-it.com/packages).
|
The library is published under the **`@maks-it.com` scope** to [registry.npmjs.org](https://registry.npmjs.org), managed from the [maks-it.com npm org](https://www.npmjs.com/settings/maks-it.com/packages).
|
||||||
|
|
||||||
Published packages:
|
Published package:
|
||||||
|
|
||||||
| Package | npm |
|
| Package | npm |
|
||||||
|---------|-----|
|
|---------|-----|
|
||||||
| `@maks-it.com/webui-contracts` | https://www.npmjs.com/package/@maks-it.com/webui-contracts |
|
| `@maks-it.com/webui` | https://www.npmjs.com/package/@maks-it.com/webui |
|
||||||
| `@maks-it.com/webui-core` | https://www.npmjs.com/package/@maks-it.com/webui-core |
|
|
||||||
| `@maks-it.com/webui-components` | https://www.npmjs.com/package/@maks-it.com/webui-components |
|
> **Note:** `@maks-it.com/webui-contracts`, `@maks-it.com/webui-core`, and `@maks-it.com/webui-components` were merged into `@maks-it.com/webui` starting at **0.4.0**. Migrate host apps to the single package.
|
||||||
|
|
||||||
## One-time npm setup
|
## One-time npm setup
|
||||||
|
|
||||||
1. Sign in at https://www.npmjs.com/ with the **maks-it.com** org account.
|
1. Sign in at https://www.npmjs.com/ with the **maks-it.com** org account.
|
||||||
2. Confirm the **`@maks-it.com` scope** exists under [Packages](https://www.npmjs.com/settings/maks-it.com/packages). The org name `maks-it.com` is the npm scope for scoped packages.
|
2. Confirm the **`@maks-it.com` scope** exists under [Packages](https://www.npmjs.com/settings/maks-it.com/packages).
|
||||||
3. Create an **Automation** token (recommended) or Granular Access token with **Publish** on `@maks-it.com/*`:
|
3. Create an **Automation** token (recommended) or Granular Access token with **Publish** on `@maks-it.com/*`:
|
||||||
- https://www.npmjs.com/settings/maks-it.com/tokens
|
- https://www.npmjs.com/settings/maks-it.com/tokens
|
||||||
4. Store the token for release tooling:
|
4. Store the token for release tooling:
|
||||||
@ -23,9 +23,9 @@ Published packages:
|
|||||||
//registry.npmjs.org/:_authToken=YOUR_TOKEN
|
//registry.npmjs.org/:_authToken=YOUR_TOKEN
|
||||||
```
|
```
|
||||||
|
|
||||||
Scoped packages must use **`--access public`** (already configured in each package `publishConfig`).
|
Scoped packages must use **`--access public`** (already configured in package `publishConfig`).
|
||||||
|
|
||||||
## Manual first publish (0.1.0)
|
## Manual publish
|
||||||
|
|
||||||
From the repo root:
|
From the repo root:
|
||||||
|
|
||||||
@ -33,19 +33,13 @@ From the repo root:
|
|||||||
cd src
|
cd src
|
||||||
npm ci
|
npm ci
|
||||||
npm run build
|
npm run build
|
||||||
npm publish -w @maks-it.com/webui-contracts --access public
|
npm publish -w @maks-it.com/webui --access public
|
||||||
npm publish -w @maks-it.com/webui-core --access public
|
|
||||||
npm publish -w @maks-it.com/webui-components --access public
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Order matters: **contracts → core → components**.
|
|
||||||
|
|
||||||
Verify:
|
Verify:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
npm view @maks-it.com/webui-contracts version
|
npm view @maks-it.com/webui version
|
||||||
npm view @maks-it.com/webui-core version
|
|
||||||
npm view @maks-it.com/webui-components version
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Release pipeline (recommended)
|
## Release pipeline (recommended)
|
||||||
@ -53,19 +47,17 @@ npm view @maks-it.com/webui-components version
|
|||||||
Use **`utils\Invoke-ReleasePackage-Single.bat`** (or `pwsh utils\engines\release\Invoke-ReleasePackage.ps1`):
|
Use **`utils\Invoke-ReleasePackage-Single.bat`** (or `pwsh utils\engines\release\Invoke-ReleasePackage.ps1`):
|
||||||
|
|
||||||
1. Bump version in `src/package.json` (or tag drives `NpmReleaseVersion`).
|
1. Bump version in `src/package.json` (or tag drives `NpmReleaseVersion`).
|
||||||
2. Tag `HEAD` with exact semver, e.g. `git tag v0.2.0 && git push origin v0.2.0`.
|
2. Tag `HEAD` with exact semver, e.g. `git tag v0.4.0 && git push origin v0.4.0`.
|
||||||
3. Set `$env:Npm` and run the release engine.
|
3. Set `$env:Npm` and run the release engine.
|
||||||
|
|
||||||
`utils\engines\release\scriptSettings.json` runs `NpmReleaseVersion`, `NpmBuild`, `ReleasePublishGuard`, optional `GitHub`, then `NpmPublish` in dependency order.
|
`utils\engines\release\scriptSettings.json` runs `NpmReleaseVersion`, `NpmBuild`, `ReleasePublishGuard`, optional `GitHub`, then `NpmPublish`.
|
||||||
|
|
||||||
## After publish — Certs UI / Vault
|
## After publish — Certs UI / Vault
|
||||||
|
|
||||||
In each WebUI app (`MaksIT.WebUI/package.json`):
|
In each WebUI app (`MaksIT.WebUI/package.json`):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"@maks-it.com/webui-contracts": "^0.1.0",
|
"@maks-it.com/webui": "^0.4.0"
|
||||||
"@maks-it.com/webui-core": "^0.1.0",
|
|
||||||
"@maks-it.com/webui-components": "^0.1.0"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then refresh the lockfile:
|
Then refresh the lockfile:
|
||||||
|
|||||||
6
src/.gitignore
vendored
Normal file
6
src/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
coverage/
|
||||||
|
dist/
|
||||||
|
dist-test-dts/
|
||||||
|
storybook-static/
|
||||||
|
*.tsbuildinfo
|
||||||
|
*.tgz
|
||||||
@ -9,6 +9,12 @@ const srcDir = join(storybookDir, '..')
|
|||||||
const config: StorybookConfig = {
|
const config: StorybookConfig = {
|
||||||
stories: ['../stories/**/*.stories.@(ts|tsx)'],
|
stories: ['../stories/**/*.stories.@(ts|tsx)'],
|
||||||
staticDirs: ['../public'],
|
staticDirs: ['../public'],
|
||||||
|
typescript: {
|
||||||
|
reactDocgen: 'react-docgen-typescript',
|
||||||
|
reactDocgenTypescriptOptions: {
|
||||||
|
tsconfigPath: join(srcDir, 'tsconfig.json'),
|
||||||
|
},
|
||||||
|
},
|
||||||
addons: [
|
addons: [
|
||||||
getAbsolutePath("@storybook/addon-docs"),
|
getAbsolutePath("@storybook/addon-docs"),
|
||||||
getAbsolutePath("@storybook/addon-a11y"),
|
getAbsolutePath("@storybook/addon-a11y"),
|
||||||
@ -29,9 +35,9 @@ const config: StorybookConfig = {
|
|||||||
...config.resolve,
|
...config.resolve,
|
||||||
alias: {
|
alias: {
|
||||||
...(config.resolve?.alias as Record<string, string> | undefined),
|
...(config.resolve?.alias as Record<string, string> | undefined),
|
||||||
'@webui/components': join(srcDir, 'packages/components/src'),
|
'@webui/components': join(srcDir, 'components'),
|
||||||
'@webui/contracts': join(srcDir, 'packages/contracts/src'),
|
'@webui/contracts': join(srcDir, 'contracts'),
|
||||||
'@webui/core': join(srcDir, 'packages/core/src'),
|
'@webui/core': join(srcDir, 'core'),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../tsconfig.base.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"noEmit": true,
|
|
||||||
"declaration": false,
|
|
||||||
"declarationMap": false,
|
|
||||||
"rootDir": "..",
|
|
||||||
"paths": {
|
|
||||||
"@webui/components": ["../packages/components/src"],
|
|
||||||
"@webui/contracts": ["../packages/contracts/src"],
|
|
||||||
"@webui/core": ["../packages/core/src"],
|
|
||||||
"@webui/components/*": ["../packages/components/src/*"],
|
|
||||||
"@webui/contracts/*": ["../packages/contracts/src/*"],
|
|
||||||
"@webui/core/*": ["../packages/core/src/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"./**/*",
|
|
||||||
"../stories/**/*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
54
src/README.md
Normal file
54
src/README.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# @maks-it.com/webui
|
||||||
|
|
||||||
|
Shared TypeScript contracts, utilities, hooks, and React components for MaksIT WebUI apps (Certs UI, Vault WebUI, CICD, and related products).
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @maks-it.com/webui
|
||||||
|
npm install react react-dom react-router-dom lucide-react @tanstack/react-table react-virtualized axios @microsoft/signalr zod
|
||||||
|
```
|
||||||
|
|
||||||
|
Peer dependencies must be installed in the host app.
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
| Area | Examples |
|
||||||
|
|------|----------|
|
||||||
|
| Contracts | `PagedRequest`, `PatchOperation`, `LoginRequestSchema`, OAuth DTOs |
|
||||||
|
| Core | `deepDelta`, `useFormState`, `createWebUiHttpClient`, `useWebUiHub`, ACL parsers |
|
||||||
|
| Components | `DataTable`, `Layout`, editors, `ExternalLoginButtons`, `IdpLoginCallbackHandler` |
|
||||||
|
|
||||||
|
## Example — imports
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import {
|
||||||
|
PatchOperation,
|
||||||
|
deepDelta,
|
||||||
|
deltaHasOperations,
|
||||||
|
useFormState,
|
||||||
|
DataTable,
|
||||||
|
createColumns,
|
||||||
|
} from '@maks-it.com/webui'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example — PATCH delta
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { deepDelta, deltaHasOperations, ENTITY_SCOPES_ARRAY_POLICY } from '@maks-it.com/webui'
|
||||||
|
|
||||||
|
const delta = deepDelta(formState, backupState, {
|
||||||
|
arrays: { entityScopes: ENTITY_SCOPES_ARRAY_POLICY },
|
||||||
|
})
|
||||||
|
if (deltaHasOperations(delta)) {
|
||||||
|
await api.patch('/resource', delta)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Repository
|
||||||
|
|
||||||
|
[github.com/MAKS-IT-COM/maksit-webui](https://github.com/MAKS-IT-COM/maksit-webui) — `src/`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useMemo, useRef, useEffect } from 'react'
|
import React, { useState, useMemo, useRef, useEffect } from 'react'
|
||||||
import { AutoSizer, MultiGrid, GridCellProps } from 'react-virtualized'
|
import { AutoSizer, MultiGrid, GridCellProps } from 'react-virtualized'
|
||||||
|
|
||||||
import { mapPagedToDataTable, type DataTablePageView, type PagedResponse } from '@maks-it.com/webui-core'
|
import { mapPagedToDataTable, type DataTablePageView, type PagedResponse } from '@webui/core'
|
||||||
import { Plus, Trash2, Edit } from 'lucide-react'
|
import { Plus, Trash2, Edit } from 'lucide-react'
|
||||||
import { debounce, colSpanClass, type GridColSpan } from '../../functions'
|
import { debounce, colSpanClass, type GridColSpan } from '../../functions'
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { formatISODateString } from '@maks-it.com/webui-core'
|
import { formatISODateString } from '@webui/core'
|
||||||
|
|
||||||
interface NormalLabelProps {
|
interface NormalLabelProps {
|
||||||
type: 'normal'
|
type: 'normal'
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import type { SearchEntityScopeEntry } from '@maks-it.com/webui-contracts'
|
import type { SearchEntityScopeEntry } from '@webui/contracts'
|
||||||
|
|
||||||
export interface EntityScopesSummaryProps<TScopeEntityType extends number = number> {
|
export interface EntityScopesSummaryProps<TScopeEntityType extends number = number> {
|
||||||
entries: SearchEntityScopeEntry<TScopeEntityType>[]
|
entries: SearchEntityScopeEntry<TScopeEntityType>[]
|
||||||
@ -6,7 +6,7 @@ import {
|
|||||||
getDaysInMonth,
|
getDaysInMonth,
|
||||||
addMonths,
|
addMonths,
|
||||||
subMonths,
|
subMonths,
|
||||||
} from '@maks-it.com/webui-core'
|
} from '@webui/core'
|
||||||
import { CircleX } from 'lucide-react'
|
import { CircleX } from 'lucide-react'
|
||||||
import { ButtonComponent } from './ButtonComponent'
|
import { ButtonComponent } from './ButtonComponent'
|
||||||
import { TextBoxComponent } from './TextBoxComponent'
|
import { TextBoxComponent } from './TextBoxComponent'
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { useState, useCallback, ChangeEvent, useEffect, useRef } from 'react'
|
import { useState, useCallback, ChangeEvent, useEffect, useRef } from 'react'
|
||||||
import type { PagedRequest } from '@maks-it.com/webui-contracts'
|
import type { PagedRequest } from '@webui/contracts'
|
||||||
import type { SearchResponseBase } from '@maks-it.com/webui-contracts'
|
import type { SearchResponseBase } from '@webui/contracts'
|
||||||
import { deepEqual } from '@maks-it.com/webui-core'
|
import { deepEqual } from '@webui/core'
|
||||||
import type { GridColSpan } from '../../functions'
|
import type { GridColSpan } from '../../functions'
|
||||||
import { SelectBoxComponent } from './SelectBoxComponent'
|
import { SelectBoxComponent } from './SelectBoxComponent'
|
||||||
|
|
||||||
59
src/components/oauth/ExternalLoginButtons.tsx
Normal file
59
src/components/oauth/ExternalLoginButtons.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import type { FC, ReactNode } from 'react'
|
||||||
|
import { LoginProviderExternal } from '@webui/contracts'
|
||||||
|
|
||||||
|
export interface ExternalLoginProviderConfig {
|
||||||
|
provider: LoginProviderExternal
|
||||||
|
label: string
|
||||||
|
iconSrc?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExternalLoginButtonsProps {
|
||||||
|
providers: ExternalLoginProviderConfig[]
|
||||||
|
onSelect: (provider: LoginProviderExternal) => void
|
||||||
|
className?: string
|
||||||
|
renderButton?: (props: {
|
||||||
|
provider: LoginProviderExternal
|
||||||
|
label: string
|
||||||
|
iconSrc?: string
|
||||||
|
onClick: () => void
|
||||||
|
}) => ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultRenderButton = ({
|
||||||
|
label,
|
||||||
|
iconSrc,
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
label: string
|
||||||
|
iconSrc?: string
|
||||||
|
onClick: () => void
|
||||||
|
}) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="w-full flex items-center justify-center gap-2 rounded border border-gray-300 bg-white px-4 py-2 text-sm hover:bg-gray-50"
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{iconSrc && <img src={iconSrc} alt="" className="h-5 w-5" />}
|
||||||
|
<span>{label}</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
|
||||||
|
const ExternalLoginButtons: FC<ExternalLoginButtonsProps> = ({
|
||||||
|
providers,
|
||||||
|
onSelect,
|
||||||
|
className,
|
||||||
|
renderButton = defaultRenderButton,
|
||||||
|
}) => (
|
||||||
|
<div className={className ?? 'flex flex-col gap-2'}>
|
||||||
|
{providers.map(({ provider, label, iconSrc }) =>
|
||||||
|
renderButton({
|
||||||
|
provider,
|
||||||
|
label,
|
||||||
|
iconSrc,
|
||||||
|
onClick: () => onSelect(provider),
|
||||||
|
}),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export { ExternalLoginButtons }
|
||||||
14
src/components/oauth/IdpLoginCallbackHandler.tsx
Normal file
14
src/components/oauth/IdpLoginCallbackHandler.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import type { FC } from 'react'
|
||||||
|
import { useIdpLoginCallback, type UseIdpLoginCallbackOptions } from '@webui/core'
|
||||||
|
|
||||||
|
export type IdpLoginCallbackHandlerProps = UseIdpLoginCallbackOptions
|
||||||
|
|
||||||
|
const IdpLoginCallbackHandler: FC<IdpLoginCallbackHandlerProps> = (props) => {
|
||||||
|
useIdpLoginCallback(props)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export { IdpLoginCallbackHandler }
|
||||||
|
export type { LoginExternalExchangeResponse } from '@webui/contracts'
|
||||||
|
/** @deprecated Use `LoginExternalExchangeResponse`. */
|
||||||
|
export type { IdpRedeemLoginResponse } from '@webui/contracts'
|
||||||
11
src/components/oauth/MailboxOAuthReturnHandler.tsx
Normal file
11
src/components/oauth/MailboxOAuthReturnHandler.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import type { FC } from 'react'
|
||||||
|
import { useMailboxOAuthReturn, type UseMailboxOAuthReturnOptions } from '@webui/core'
|
||||||
|
|
||||||
|
export type MailboxOAuthReturnHandlerProps = UseMailboxOAuthReturnOptions
|
||||||
|
|
||||||
|
const MailboxOAuthReturnHandler: FC<MailboxOAuthReturnHandlerProps> = (props) => {
|
||||||
|
useMailboxOAuthReturn(props)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export { MailboxOAuthReturnHandler }
|
||||||
15
src/components/oauth/index.ts
Normal file
15
src/components/oauth/index.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export {
|
||||||
|
ExternalLoginButtons,
|
||||||
|
type ExternalLoginButtonsProps,
|
||||||
|
type ExternalLoginProviderConfig,
|
||||||
|
} from './ExternalLoginButtons'
|
||||||
|
|
||||||
|
export {
|
||||||
|
IdpLoginCallbackHandler,
|
||||||
|
type IdpLoginCallbackHandlerProps,
|
||||||
|
} from './IdpLoginCallbackHandler'
|
||||||
|
|
||||||
|
export {
|
||||||
|
MailboxOAuthReturnHandler,
|
||||||
|
type MailboxOAuthReturnHandlerProps,
|
||||||
|
} from './MailboxOAuthReturnHandler'
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import { boolean, intersection, number, object, record, string, type ZodType } from 'zod'
|
import { boolean, intersection, number, object, string, type ZodType } from 'zod'
|
||||||
import type { RequestModelBase } from './RequestModelBase'
|
import type { RequestModelBase } from './RequestModelBase'
|
||||||
import { RequestModelBaseSchema } from './RequestModelBase'
|
import { RequestModelBaseSchema } from './RequestModelBase'
|
||||||
|
|
||||||
|
/** Matches server `MaksIT.Core.Webapi.Models.PagedRequest` (camelCase JSON). */
|
||||||
export interface PagedRequest extends RequestModelBase {
|
export interface PagedRequest extends RequestModelBase {
|
||||||
pageSize?: number
|
pageSize?: number
|
||||||
pageNumber?: number
|
pageNumber?: number
|
||||||
filters?: string
|
filters?: string
|
||||||
collectionFilters?: Record<string, string>
|
|
||||||
sortBy?: string
|
sortBy?: string
|
||||||
isAscending?: boolean
|
isAscending?: boolean
|
||||||
}
|
}
|
||||||
@ -17,7 +17,6 @@ export const PagedRequestSchema: ZodType<PagedRequest> = intersection(
|
|||||||
pageSize: number().optional(),
|
pageSize: number().optional(),
|
||||||
pageNumber: number().optional(),
|
pageNumber: number().optional(),
|
||||||
filters: string().optional(),
|
filters: string().optional(),
|
||||||
collectionFilters: record(string(), string()).optional(),
|
|
||||||
sortBy: string().optional(),
|
sortBy: string().optional(),
|
||||||
isAscending: boolean().optional(),
|
isAscending: boolean().optional(),
|
||||||
})
|
})
|
||||||
11
src/contracts/SearchResponseBase.ts
Normal file
11
src/contracts/SearchResponseBase.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import type { ResponseModelBase } from './ResponseModelBase'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal id + display label for remote select / paged search helpers.
|
||||||
|
* Not a MaksIT.Core type — product `Search*Response` models add domain fields
|
||||||
|
* (e.g. `username` on users, `description` on API keys). Map to `name` in the UI layer.
|
||||||
|
*/
|
||||||
|
export interface SearchResponseBase extends ResponseModelBase {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
}
|
||||||
6
src/contracts/TrngResponse.ts
Normal file
6
src/contracts/TrngResponse.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import type { ResponseModelBase } from './ResponseModelBase'
|
||||||
|
|
||||||
|
/** Matches `GET api/secret/generatesecret` anonymous response `{ secret }` (e.g. MaksIT.Vault). */
|
||||||
|
export interface TrngResponse extends ResponseModelBase {
|
||||||
|
secret: string
|
||||||
|
}
|
||||||
14
src/contracts/identity/logout/LogoutRequest.ts
Normal file
14
src/contracts/identity/logout/LogoutRequest.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { boolean, object, type ZodType } from 'zod'
|
||||||
|
import type { RequestModelBase } from '../../RequestModelBase'
|
||||||
|
import { RequestModelBaseSchema } from '../../RequestModelBase'
|
||||||
|
|
||||||
|
/** Matches product `Models/Identity/Logout/LogoutRequest` (e.g. MaksIT.CertsUI). */
|
||||||
|
export interface LogoutRequest extends RequestModelBase {
|
||||||
|
logoutFromAllDevices?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LogoutRequestSchema: ZodType<LogoutRequest> = RequestModelBaseSchema.and(
|
||||||
|
object({
|
||||||
|
logoutFromAllDevices: boolean().optional(),
|
||||||
|
})
|
||||||
|
)
|
||||||
9
src/contracts/oauth/EmailProtocol.ts
Normal file
9
src/contracts/oauth/EmailProtocol.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Mailbox email protocol flags for multi-step OAuth UI.
|
||||||
|
* FiPlan/legacy FE shape — not in MaksIT.IdentityHub.Contracts.
|
||||||
|
*/
|
||||||
|
export enum EmailProtocol {
|
||||||
|
Smtp = 0,
|
||||||
|
Pop3 = 1,
|
||||||
|
Imap = 2,
|
||||||
|
}
|
||||||
6
src/contracts/oauth/IdpRedeemLoginResponse.ts
Normal file
6
src/contracts/oauth/IdpRedeemLoginResponse.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import type { LoginExternalExchangeResponse } from './LoginExternalExchangeResponse'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link LoginExternalExchangeResponse}. Legacy FiPlan.Auth snake_case shape removed.
|
||||||
|
*/
|
||||||
|
export type IdpRedeemLoginResponse = LoginExternalExchangeResponse
|
||||||
16
src/contracts/oauth/LoginExternalExchangeResponse.ts
Normal file
16
src/contracts/oauth/LoginExternalExchangeResponse.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import type { ResponseModelBase } from '../ResponseModelBase'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response from Identity Hub `POST login-external/redeem`.
|
||||||
|
* Align with `MaksIT.IdentityHub.Contracts.LoginExternalExchangeResponse`.
|
||||||
|
*/
|
||||||
|
export interface LoginExternalExchangeResponse extends ResponseModelBase {
|
||||||
|
provider: string
|
||||||
|
subject: string
|
||||||
|
email: string
|
||||||
|
idToken: string
|
||||||
|
accessToken: string
|
||||||
|
refreshToken?: string
|
||||||
|
expiresAt?: string
|
||||||
|
postLogin?: string
|
||||||
|
}
|
||||||
23
src/contracts/oauth/LoginProviderExternal.ts
Normal file
23
src/contracts/oauth/LoginProviderExternal.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* External identity provider for Identity Hub login (`login-external/challenge`).
|
||||||
|
* Values match `MaksIT.IdentityHub.Engine.LoginProviderExternal` names (`Microsoft`, `Google`).
|
||||||
|
*/
|
||||||
|
export enum LoginProviderExternal {
|
||||||
|
Microsoft = 'Microsoft',
|
||||||
|
Google = 'Google',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toAuthProviderName = (provider: LoginProviderExternal): string => provider
|
||||||
|
|
||||||
|
export const tryParseLoginProviderExternal = (
|
||||||
|
value: string | null | undefined,
|
||||||
|
): LoginProviderExternal | undefined => {
|
||||||
|
if (!value)
|
||||||
|
return undefined
|
||||||
|
|
||||||
|
const normalized = value.trim()
|
||||||
|
if (normalized === LoginProviderExternal.Google || normalized === LoginProviderExternal.Microsoft)
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
8
src/contracts/oauth/MailboxOAuthPending.ts
Normal file
8
src/contracts/oauth/MailboxOAuthPending.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import type { EmailProtocol } from './EmailProtocol'
|
||||||
|
|
||||||
|
/** sessionStorage payload during multi-protocol mailbox OAuth (FE-only). */
|
||||||
|
export interface MailboxOAuthPending {
|
||||||
|
mailboxId: number
|
||||||
|
protocols: EmailProtocol[]
|
||||||
|
returnPath: string
|
||||||
|
}
|
||||||
7
src/contracts/oauth/MsalViewModel.ts
Normal file
7
src/contracts/oauth/MsalViewModel.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import type { EmailProtocol } from './EmailProtocol'
|
||||||
|
|
||||||
|
/** FiPlan/legacy mailbox redeem payload — not Identity Hub `LoginExternalExchangeResponse`. */
|
||||||
|
export interface MsalViewModel {
|
||||||
|
code: string
|
||||||
|
emailProtocol: EmailProtocol
|
||||||
|
}
|
||||||
18
src/contracts/oauth/RedeemLoginExternalRequest.ts
Normal file
18
src/contracts/oauth/RedeemLoginExternalRequest.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { object, string, type ZodType } from 'zod'
|
||||||
|
import type { RequestModelBase } from '../RequestModelBase'
|
||||||
|
import { RequestModelBaseSchema } from '../RequestModelBase'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Body for Identity Hub `POST login-external/redeem`.
|
||||||
|
* Align with `MaksIT.IdentityHub.Contracts.RedeemLoginExternalRequest`.
|
||||||
|
*/
|
||||||
|
export interface RedeemLoginExternalRequest extends RequestModelBase {
|
||||||
|
code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RedeemLoginExternalRequestSchema: ZodType<RedeemLoginExternalRequest> =
|
||||||
|
RequestModelBaseSchema.and(
|
||||||
|
object({
|
||||||
|
code: string().min(1),
|
||||||
|
}),
|
||||||
|
)
|
||||||
20
src/contracts/oauth/RefreshMailboxAccessTokenRequest.ts
Normal file
20
src/contracts/oauth/RefreshMailboxAccessTokenRequest.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { object, string, type ZodType } from 'zod'
|
||||||
|
import type { RequestModelBase } from '../RequestModelBase'
|
||||||
|
import { RequestModelBaseSchema } from '../RequestModelBase'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Body for Identity Hub `POST login-external/refresh-mailbox`.
|
||||||
|
* Align with `MaksIT.IdentityHub.Contracts.RefreshMailboxAccessTokenRequest`.
|
||||||
|
*/
|
||||||
|
export interface RefreshMailboxAccessTokenRequest extends RequestModelBase {
|
||||||
|
provider: string
|
||||||
|
refreshToken: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RefreshMailboxAccessTokenRequestSchema: ZodType<RefreshMailboxAccessTokenRequest> =
|
||||||
|
RequestModelBaseSchema.and(
|
||||||
|
object({
|
||||||
|
provider: string().min(1),
|
||||||
|
refreshToken: string().min(1),
|
||||||
|
}),
|
||||||
|
)
|
||||||
9
src/contracts/oauth/RefreshMailboxAccessTokenResponse.ts
Normal file
9
src/contracts/oauth/RefreshMailboxAccessTokenResponse.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import type { ResponseModelBase } from '../ResponseModelBase'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response from Identity Hub `POST login-external/refresh-mailbox`.
|
||||||
|
* Align with `MaksIT.IdentityHub.Contracts.RefreshMailboxAccessTokenResponse`.
|
||||||
|
*/
|
||||||
|
export interface RefreshMailboxAccessTokenResponse extends ResponseModelBase {
|
||||||
|
accessToken: string
|
||||||
|
}
|
||||||
9
src/contracts/oauth/ServerAuthenticationMethod.ts
Normal file
9
src/contracts/oauth/ServerAuthenticationMethod.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Mailbox server authentication method (OAuth2 vs login).
|
||||||
|
* FiPlan/legacy FE shape — not in MaksIT.IdentityHub.Contracts.
|
||||||
|
*/
|
||||||
|
export enum ServerAuthenticationMethod {
|
||||||
|
Default = 0,
|
||||||
|
Login = 1,
|
||||||
|
OAuth2 = 6,
|
||||||
|
}
|
||||||
37
src/contracts/oauth/index.ts
Normal file
37
src/contracts/oauth/index.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
export {
|
||||||
|
LoginProviderExternal,
|
||||||
|
toAuthProviderName,
|
||||||
|
tryParseLoginProviderExternal,
|
||||||
|
} from './LoginProviderExternal'
|
||||||
|
|
||||||
|
export { EmailProtocol } from './EmailProtocol'
|
||||||
|
|
||||||
|
export { ServerAuthenticationMethod } from './ServerAuthenticationMethod'
|
||||||
|
|
||||||
|
export type { MsalViewModel } from './MsalViewModel'
|
||||||
|
|
||||||
|
export type { LoginExternalExchangeResponse } from './LoginExternalExchangeResponse'
|
||||||
|
|
||||||
|
export type { RedeemLoginExternalRequest } from './RedeemLoginExternalRequest'
|
||||||
|
export { RedeemLoginExternalRequestSchema } from './RedeemLoginExternalRequest'
|
||||||
|
|
||||||
|
export type { RefreshMailboxAccessTokenRequest } from './RefreshMailboxAccessTokenRequest'
|
||||||
|
export { RefreshMailboxAccessTokenRequestSchema } from './RefreshMailboxAccessTokenRequest'
|
||||||
|
|
||||||
|
export type { RefreshMailboxAccessTokenResponse } from './RefreshMailboxAccessTokenResponse'
|
||||||
|
|
||||||
|
/** @deprecated Use `LoginExternalExchangeResponse`. */
|
||||||
|
export type { IdpRedeemLoginResponse } from './IdpRedeemLoginResponse'
|
||||||
|
|
||||||
|
export type { MailboxOAuthPending } from './MailboxOAuthPending'
|
||||||
|
|
||||||
|
export {
|
||||||
|
MAILBOX_OAUTH_STORAGE_KEY,
|
||||||
|
MAILBOX_OAUTH_QUERY_FLAG,
|
||||||
|
MAILBOX_OAUTH_PROCESSING_KEY_PREFIX,
|
||||||
|
IDP_PROVIDER_STORAGE_KEY,
|
||||||
|
IDENTITY_HUB_ROUTE_CHALLENGE,
|
||||||
|
IDENTITY_HUB_ROUTE_REDEEM,
|
||||||
|
IDENTITY_HUB_ROUTE_REFRESH_MAILBOX,
|
||||||
|
IDENTITY_HUB_MAILBOX_PURPOSE,
|
||||||
|
} from './oauthConstants'
|
||||||
19
src/contracts/oauth/oauthConstants.ts
Normal file
19
src/contracts/oauth/oauthConstants.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/** sessionStorage key while a multi-protocol mailbox OAuth flow is in progress. */
|
||||||
|
export const MAILBOX_OAUTH_STORAGE_KEY = 'mailboxOAuth_pending'
|
||||||
|
|
||||||
|
/** Query flag on the OAuth redirect landing URL (`?mailboxOAuth=1`). */
|
||||||
|
export const MAILBOX_OAUTH_QUERY_FLAG = 'mailboxOAuth'
|
||||||
|
|
||||||
|
/** sessionStorage prefix to dedupe concurrent code redemption for the same authorization code. */
|
||||||
|
export const MAILBOX_OAUTH_PROCESSING_KEY_PREFIX = 'mailboxOAuth_processing_'
|
||||||
|
|
||||||
|
/** sessionStorage key for the last external login provider (optional diagnostics). */
|
||||||
|
export const IDP_PROVIDER_STORAGE_KEY = 'ext_provider'
|
||||||
|
|
||||||
|
/** Identity Hub route segments (append to hub base URL). */
|
||||||
|
export const IDENTITY_HUB_ROUTE_CHALLENGE = 'login-external/challenge'
|
||||||
|
export const IDENTITY_HUB_ROUTE_REDEEM = 'login-external/redeem'
|
||||||
|
export const IDENTITY_HUB_ROUTE_REFRESH_MAILBOX = 'login-external/refresh-mailbox'
|
||||||
|
|
||||||
|
/** Query value for mailbox OAuth challenge (`?purpose=mailbox`). Matches `HttpExternalLoginSessionContext`. */
|
||||||
|
export const IDENTITY_HUB_MAILBOX_PURPOSE = 'mailbox'
|
||||||
@ -1,4 +1,7 @@
|
|||||||
/** One entity scope line in search results (entity + CRUD permission flags). */
|
/**
|
||||||
|
* One entity scope line in search results (entity + CRUD permission flags).
|
||||||
|
* Align with product `Models/Shared/Search/SearchEntityScopeEntry.cs` (certs-ui, vault).
|
||||||
|
*/
|
||||||
export interface SearchEntityScopeEntry<TScopeEntityType = number> {
|
export interface SearchEntityScopeEntry<TScopeEntityType = number> {
|
||||||
scopeEntityType: TScopeEntityType
|
scopeEntityType: TScopeEntityType
|
||||||
entityId: string
|
entityId: string
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user