Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/v0-behavior.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: V0 behavior checks
on:
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
behavior:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '22'
cache: npm
- name: Install locked dependencies without lifecycle hooks
run: npm ci --ignore-scripts --legacy-peer-deps --no-audit --no-fund
- name: Run focused behavior and HTTP routing tests
run: npm run test:v0
- name: Generate local environment modules without credentials
env:
CI: 'false'
run: node tools/set-env
- name: Check application types and templates
run: node_modules/.bin/ngc --noEmit -p apps/36-blocks/tsconfig.app.json
80 changes: 8 additions & 72 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,75 +1,11 @@
# proxy-ui (36 Blocks frontend)
# Working on proxy-ui

An Nx monorepo (Angular 21, standalone components, NgRx, Tailwind v4) with two apps:
- `apps/36-blocks`: the customer dashboard and the marketing site.
- `apps/36-blocks-widget`: an embeddable auth widget, built into one script (proxy-auth.js) that customer sites load and call through `window.initVerification(config)`.
Owns the Angular public website, operator dashboard and embeddable authentication widget. Backend APIs, persistence and session policy belong to sibling `user-proxy`; connected applications own business authorization.

Both apps are client-only and call a backend "proxy" API that lives in another repo.
Start with the canonical [work state](../user-proxy/docs/WORK-STATE.md), [architecture](../user-proxy/docs/ARCHITECTURE.md), [capabilities](../user-proxy/docs/CAPABILITIES.md), [development](../user-proxy/docs/DEVELOPMENT.md), [security](../user-proxy/docs/SECURITY-OPERATIONS.md) and [design](../user-proxy/docs/DESIGN.md). Preserve existing README contribution rules and DESIGNGUIDE.md. Read widget integration/event guides before changing the custom element contract.

## Commands

| Task | Command |
|---|---|
| Install | `npm ci --legacy-peer-deps` (Node 20; peer deps conflict without the flag) |
| Env setup | `cp .env.example .env` and fill in the values. `tools/set-env.js` generates the git-ignored env-variables.ts for each app ([docs/environment.md](docs/environment.md)) |
| Run dashboard | `npm start` (port 4200) |
| Run widget alone | `npm run start:proxy-auth` (also port 4200) |
| Build widget into dashboard assets | `npm run build:proxy-auth` |
| Full build | `npm run build:test`, `npm run build:stage` or `npm run build:prod`. Plain `npm run build` mixes a prod widget with a local dashboard |
| Format | `npm run format:code`. Prettier also runs on pre-commit through husky and lint-staged |

`npm test` and `npm run lint` are not reliable at this commit: there is no test target on the dashboard, and `nx workspace-lint` is gone in Nx 21. Check a change by building the affected app: `npx nx build 36-blocks` or `npx nx build 36-blocks-widget` (run `node tools/set-env` first).

## Read before you change

- Anything → [docs/architecture.md](docs/architecture.md), then [docs/conventions.md](docs/conventions.md)
- The widget's public API, events or build → [docs/modules/widget-embed.md](docs/modules/widget-embed.md) and [docs/api.md](docs/api.md#widget-public-api-host-page--widget)
- Widget login, register or OTP → [docs/modules/widget-auth.md](docs/modules/widget-auth.md), [docs/flows/widget-verification.md](docs/flows/widget-verification.md)
- Widget account screens → [docs/modules/widget-account.md](docs/modules/widget-account.md)
- Dashboard login, token or guards → [docs/modules/app-shell.md](docs/modules/app-shell.md), [docs/flows/dashboard-login.md](docs/flows/dashboard-login.md)
- Blocks (features) wizard → [docs/modules/panel-features.md](docs/modules/panel-features.md), [docs/flows/feature-configuration.md](docs/flows/feature-configuration.md)
- Users, logs, billing, dashboard, layout → [docs/modules/panel-ops.md](docs/modules/panel-ops.md)
- `libs/**`, HTTP, the interceptor → [docs/modules/shared-libs.md](docs/modules/shared-libs.md)
- Routes, state, components, styling → [docs/ui/](docs/README.md#ui)
- A common change (page, backend call, block tab, widget key or screen, env value, lib, release) → [docs/recipes.md](docs/recipes.md)

## Rules

1. Never rename or remove any part of the widget's public contract:
- `window.initVerification` and the `proxy-auth` tag
- `window.__proxyAuth` and the `openAddUserDialog` event
- the `userProxyContainer` id and the assets/proxy-auth/proxy-auth.js path
- any value in `libs/constant/src/verification/verification.ts`

Customer sites depend on all of these. Add new values instead ([docs/api.md](docs/api.md)).
2. No dynamic `import()` in `apps/36-blocks-widget`. `build-elements.js` leaves the `chunk-*` file it creates out of the single-file bundle, so the bundle breaks on host pages.
3. A new `initVerification` config key that must not be sent to the backend goes into `RESERVED_KEYS` in `apps/36-blocks-widget/src/app/init-verification.ts`. Every other key is POSTed as `otherData` ([recipe](docs/recipes.md#add-an-initverification-config-key)).
4. Put widget CSS in component styles. `apps/36-blocks-widget/src/styles.scss` is also injected into the host page's `<head>`. Build overlays with `WidgetPortalService` / `WidgetDialogService`.
5. Set and clear the dashboard token only through `AuthService.setTokenSync` / `clearTokenSync`. They keep the `authToken` cookie and the in-memory `IToken` in sync. Guards read both, HttpWrapper reads memory, and ErrorInterceptor reads only the cookie ([docs/modules/app-shell.md](docs/modules/app-shell.md)).
6. A dashboard endpoint called without a session must be on the ErrorInterceptor allow-list. Otherwise it is silently dropped (`EMPTY`) ([docs/modules/shared-libs.md](docs/modules/shared-libs.md)).
7. A 200 can still be an error. Check `res.hasError` (dashboard) or the per-endpoint success field (widget) as well as using `catchError` ([docs/conventions.md](docs/conventions.md#http-and-errors)).
8. A block has two ids: numeric `id` for feature CRUD, OAuth and scopes, and string `reference_id` for the widget, billing and the Users/roles screens. Passing the wrong one fails silently ([docs/glossary.md](docs/glossary.md)).
9. In the widget, copy `OtpService.options` before adding per-call headers. It is one shared mutable object, and its headers leak into later calls.
10. The widget must not import `@proxy/services/proxy/*`, `@proxy/services/httpWrapper` or the login service. They need `AngularFireAuth` and `IToken`, which only the dashboard provides.
11. Add dashboard pages to `apps/36-blocks/src/app/panel/panel.routes.ts`. `apps/36-blocks/src/app/panel/layout/layout.routes.ts` is dead (nothing imports it).
12. Never send a client id. The current client is stored on the server against the token, and switching client is `POST switchclient` followed by a full page reload.
13. Everything `tools/set-env.js` writes ends up in a public browser bundle. Never add a real server-side secret there. When you add a key, also add it to `.env.example`.
14. Keep the `setTimeout(…, 100)` before block create/update in `create-feature.component.ts`. The payload is filled in by promises (file reads, chip lists) ([docs/modules/panel-features.md](docs/modules/panel-features.md)).
15. Branch flow is `<card>-clean` → `stage` → `production`; the README's `development`/`main` flow is stale. PRs into `stage` get **no CI build**, so build locally first. Land any revert on `stage` too ([docs/conventions.md](docs/conventions.md#branching-and-release)).

## Where things are

- `apps/36-blocks/src/app/website/`: public marketing pages, login and register
- `apps/36-blocks/src/app/panel/`: the logged-in `/app` dashboard pages, each with a `<page>.store.ts` ComponentStore
- `apps/36-blocks/src/app/core/`: the root NgRx store, the auth initializer, client registration
- `apps/36-blocks-widget/src/app/init-verification.ts`: the widget's global API
- `apps/36-blocks-widget/src/app/otp/`: widget screens, the `otp` store, `OtpService`
- `apps/36-blocks-widget/build-elements.js`: post-build step that produces the single proxy-auth.js
- `libs/`: shared code, imported only through the `@proxy/...` aliases in `tsconfig.base.json`
- `tools/set-env.js`, `tools/postbuild.js`: env generation and the version.json writer
- `apps/shared/scss/`: global SCSS and Tailwind entry shared by both apps

## Docs map

See [docs/README.md](docs/README.md) for every doc, when to read it, and the open questions for humans.
When you change behaviour that a doc describes, update that doc in the same change and run `/ai-docs update`.
- Inspect status/diff; preserve unrelated changes. New application code uses TypeScript and existing standalone Angular patterns. Use Angular CLI scaffolding when practical; never change architecture solely to satisfy scaffolding.
- Install from lock using `npm ci --ignore-scripts --legacy-peer-deps`; generate ignored env modules with `node tools/set-env`. Review environment base URLs before starting: local config defaults to a remote test API.
- Run `node --test tools/tests/*.test.cjs`, affected builds and `git diff --check`. Existing Nx/Jest targets have known gaps: report exact commands and limitations in canonical work state.
- Keep error, denied, empty and loading states distinct; provide retry. Never render request data as HTML or log/store tokens. Client visibility does not replace server authorization.
- Update the single backend-owned capability matrix and work state alongside changes. Product/architecture guidance is linked, not duplicated here. Public claims require evidence; do not invent SLAs, guarantees or future functional controls.
16 changes: 2 additions & 14 deletions DESIGNGUIDE.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
# Proxy UI Design Guide

## Setup Guide
## Setup

1. Clone https://github.com/Walkover-Web-Solution/proxy-UI.git.

2. This project requires Node version 14+, install Node from [here](https://nodejs.org/en/).

3. Go inside the cloned directory `proxy-UI` in any terminal and run these following commands in the terminal to install dependency and start local serve.

4. Run `npm i` for installation of dependencies.

5. Serving the project locally:
* For proxy: Run `npm start` to local serve the project.
* For Admin Panel: Run `npm run start:admin` to local serve the project.
* For chat-widget: Run `npm run start:chat-widget` to local serve the project.
* For otp-provider: Run `npm run start:otp-provider` to local serve the project.
Use the current [README](README.md) and canonical [development guide](../user-proxy/docs/DEVELOPMENT.md) for runtime, install and startup commands. Legacy admin/chat/OTP application commands formerly listed here no longer describe the supplied workspace. The following styling guidance remains reference material; use the canonical [design standards](../user-proxy/docs/DESIGN.md) for product behavior.

## Prerequisites
It is important to have a thorough understanding of **HTML**, **CSS** and its pre-processor **SASS** to contribute to this project. If unaware of any of the above then go through their documention first.
Expand Down
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@

1. Clone https://github.com/Walkover-Web-Solution/proxy-ui.git.

2. This project requires Node version 14+, install Node from [here](https://nodejs.org/en/).
2. This is Angular 21.2 / Nx 21.6 / TypeScript 5.9. Use a compatible Node runtime; see the [canonical development guide](../user-proxy/docs/DEVELOPMENT.md). Node 14 is unsupported.

3. Go inside the cloned directory `proxy-ui` in any terminal and run these following commands in the terminal to install dependency and start local serve.

4. Run `npm i` for installation of dependencies.
4. Run `npm ci --ignore-scripts --legacy-peer-deps` to install the locked dependencies. Review the development guide before enabling install hooks.

5. Run `npm start` to local serve the project.
5. Configure approved local environment values, run `node tools/set-env`, then `npm start`. The checked-in local API URL points to a remote test service; change it before exercising mutations.

Read [AGENTS.md](AGENTS.md), [current work state](../user-proxy/docs/WORK-STATE.md), [architecture](../user-proxy/docs/ARCHITECTURE.md) and the authoritative [capability matrix](../user-proxy/docs/CAPABILITIES.md). The API is the sibling `user-proxy` repository. `node --test tools/tests/*.test.cjs` runs focused behavior tests; legacy Nx/Jest targets have known gaps recorded in the work state.

The existing contribution and style guidance below remains applicable. Use the canonical development and design guides where obsolete runtime/module instructions conflict with the current standalone Angular architecture.

## Contributing Guide

Expand Down Expand Up @@ -344,3 +348,9 @@ We have a server [Marlin](marlin.phone91.com) [for Prod] & [sailfish](sailfish.p

If client origin is widget (origin = `chat`) [will implement for all in v2], we have an option to start meet from `client-card-component`.
For selected client we use `call-session-creds` API to fetch `room` & `jwt` to init socket connection and Jitsi meet room with Jitsi Options. [Jitsi Reference Doc](https://developer.8x8.com/jaas/docs/iframe-api-integration)

## Production integration — 29 October 2026

The V0 changes are reconciled with production commit `2b1deb17`. Production deliberately uses a browser-only build and removed `ProjectGuard`; this merge preserves both decisions and the newer production navigation, OAuth/client-secret features, and compact website spacing. The original feature branch's SSR and project-guard changes/tests are therefore not part of this production integration.

Validation: 23 applicable `npm run test:v0` checks, Angular type/template checking, and `npm run build:prod` passed, including the widget bundle and post-build packaging. Build warnings remain; this does not certify configured Firebase/provider login. Activity and Login as require the companion backend API/migrations from [user-proxy PR #1476](https://github.com/Walkover-Web-Solution/user-proxy/pull/1476) before live use. See the canonical [work state](../user-proxy/docs/WORK-STATE.md) for remaining integration gates.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class RootEffects {
switchMap((req) => {
return this.rootService.getProjects().pipe(
map((res) => {
if (res.hasError) {
if (res.hasError || !Array.isArray(res.data?.data)) {
this.showError(res.errors);
return rootActions.getAllProjectsError();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface IRootState {
//All project
allProjects: IPaginatedResponse<IProjects[]>;
projectInProcess: boolean;
projectLoadFailed: boolean;
}

export const initialState: IRootState = {
Expand All @@ -33,6 +34,7 @@ export const initialState: IRootState = {
clientsInProcess: false,
switchClientSuccess: false,
projectInProcess: false,
projectLoadFailed: false,
allProjects: null,
};

Expand Down Expand Up @@ -121,20 +123,23 @@ const _rootReducer = createReducer(
return {
...state,
projectInProcess: true,
projectLoadFailed: false,
};
}),
on(rootActions.getAllProjectSuccess, (state, { response }) => {
return {
...state,
allProjects: response,
projectInProcess: false,
projectLoadFailed: false,
};
}),
on(rootActions.getAllProjectsError, (state) => {
return {
...state,
allProjects: null,
projectInProcess: false,
projectLoadFailed: true,
};
})
);
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,10 @@ export const selectProjectInProcess = createSelector(
selectRootState,
(rootState: IRootState) => rootState.projectInProcess
);

/** One selector keeps project data and its request outcome consistent for guards. */
export const selectProjectAccessState = createSelector(selectRootState, (state: IRootState) => ({
projects: state.allProjects,
loading: state.projectInProcess,
failed: state.projectLoadFailed,
}));
Loading
Loading