feat: add ex-register-box component and Account API endpoint#66
Conversation
Introduces a new Stencil web component, `ExRegisterBox`, which provides a complete UI for user registration. This component includes: - Email, password, and confirm password input fields. - Password visibility toggling. - Password confirmation validation. - An event for form submission (`exSubmit`). - An event for navigating to the sign-in page (`exSignIn`). - A slot for integrating social login buttons. - Full integration with the existing theming system. The component is exposed in the `@expressthat-auth/ui-react` package and demonstrated in the example `apps/web` application. The `@expressthat-auth/api-client` is added as a dev dependency to the `ui` package, anticipating its use in future component logic.
The confirm password input's `onExChange` handler was not correctly capturing its value, preventing it from updating the component's state. This commit adds the necessary handler to bind the input's value and removes a redundant `onExRightButtonClick` definition.
- Add AccountController with register endpoint - Add ex-register-box Stencil component wired to API - Fix ex-register-box submit button (shadow DOM form workaround) - Allow all CORS in development environment - Add main entry to packages/ui/package.json for Stencil dist - Update stencil.config.ts and web page Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Connected to Huly®: AUTH-64 |
There was a problem hiding this comment.
Pull request overview
Adds an end-to-end registration path across the Stencil UI library, ASP.NET API, and the Next.js web app for local testing.
Changes:
- Introduces a new
ex-register-boxStencil component and exports it from@expressthat-auth/ui. - Adds a new
POST /api/accountregistration endpoint (stub) and enables dev CORS. - Updates the web home page to render the new register box for manual verification.
Reviewed changes
Copilot reviewed 11 out of 13 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Adds workspace dependency on @expressthat-auth/api-client and esbuild for packages/ui. |
| packages/ui/stencil.config.ts | Adds a Rollup plugin (esbuild-based) to transpile external TS workspace imports. |
| packages/ui/src/index.ts | Exports the new EXRegisterBox. |
| packages/ui/src/components/ex-register-box/readme.md | Auto-generated docs for the new component. |
| packages/ui/src/components/ex-register-box/ex-register-box.tsx | Implements the registration UI, validation, events, and API call. |
| packages/ui/src/components/ex-register-box/ex-register-box.css | Base host styling for the new component. |
| packages/ui/src/components/ex-input/readme.md | Updates component usage graph to include ex-register-box. |
| packages/ui/src/components/ex-button/readme.md | Updates component usage graph to include ex-register-box. |
| packages/ui/src/components.d.ts | Updates generated Stencil typings for the new component/events. |
| packages/ui/package.json | Adds api-client + esbuild dev deps and sets a main entry. |
| apps/web/src/app/page.tsx | Renders ExRegisterBox for local testing and switches to @expressthat-auth/ui-react/next. |
| apps/api/Program.cs | Adds a dev-only default CORS policy and enables it in development. |
| apps/api/Controllers/AccountController.cs | Adds AccountController with Register action (stub response). |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
| this.client.account.register({ | ||
| email: this.email, | ||
| password: this.password, | ||
| }) .then((response) => { | ||
| console.log(`Registration successful: Email: ${response.email}, User ID: ${response.id}`); | ||
| }) | ||
| .catch((error) => { | ||
| console.error("Registration failed:", error); | ||
| }); |
There was a problem hiding this comment.
The promise chain formatting has a stray space before .then (}) .then(...)) and inconsistent indentation, which will likely fail Biome formatting checks. Please run the formatter (or adjust spacing/line breaks) so the chain is consistently formatted.
| this.client.account.register({ | |
| email: this.email, | |
| password: this.password, | |
| }) .then((response) => { | |
| console.log(`Registration successful: Email: ${response.email}, User ID: ${response.id}`); | |
| }) | |
| .catch((error) => { | |
| console.error("Registration failed:", error); | |
| }); | |
| this.client.account | |
| .register({ | |
| email: this.email, | |
| password: this.password, | |
| }) | |
| .then((response) => { | |
| console.log(`Registration successful: Email: ${response.email}, User ID: ${response.id}`); | |
| }) | |
| .catch((error) => { | |
| console.error("Registration failed:", error); | |
| }); |
| "type": "module", | ||
| "main": "dist/index.cjs.js", | ||
| "module": "dist/components/index.js", | ||
| "collection": "dist/collection/collection-manifest.json", |
There was a problem hiding this comment.
main points to dist/index.cjs.js, but this package is "type": "module" and your exports["."] doesn’t provide a require condition. If dist/index.cjs.js is actually CommonJS output, Node/tooling may treat it as ESM (because the extension is still .js) and/or ignore it due to exports, leading to confusing resolution behavior. Consider either removing main, renaming the CJS entry to a real .cjs file, and/or adding an explicit exports["."].require/default mapping to the correct artifact.
| console.log("Submitting registration form with email:", this.email); | ||
| e?.preventDefault(); |
There was a problem hiding this comment.
Avoid logging user identifiers from auth flows in a shared component library. console.log here will leak email addresses into browser logs (and could end up in remote log collectors). Please remove these logs or gate them behind an explicit debug flag.
| this.client.account.register({ | ||
| email: this.email, | ||
| password: this.password, | ||
| }) .then((response) => { | ||
| console.log(`Registration successful: Email: ${response.email}, User ID: ${response.id}`); | ||
| }) | ||
| .catch((error) => { | ||
| console.error("Registration failed:", error); | ||
| }); | ||
|
|
||
| this.exSubmit.emit({ email: this.email, password: this.password }); | ||
| }; |
There was a problem hiding this comment.
exSubmit is emitted immediately after starting the API request, even if the request fails. This conflicts with the docstring (“on successful submission”) and can lead to consumers treating registration as successful when it isn’t. Emit the event only after the request resolves successfully (or emit a separate exError/exSubmitting state), and consider surfacing the server response (e.g. id, email) instead of re-emitting the password.
| </div> | ||
|
|
||
| <div style={{ marginTop: "0.5rem" }}> | ||
| <ex-button label="Create account" type="button" variant="primary" {...theme} onExClick={this.handleSubmit} /> |
There was a problem hiding this comment.
The submit button is rendered as type="button" and manually calls handleSubmit. This differs from ex-login-box (which uses type="submit") and hurts keyboard behavior (Enter key submission) and form semantics. Prefer type="submit" on the <ex-button> and rely on the form’s onSubmit handler instead of wiring onExClick.
| <ex-button label="Create account" type="button" variant="primary" {...theme} onExClick={this.handleSubmit} /> | |
| <ex-button label="Create account" type="submit" variant="primary" {...theme} /> |
| builder.Services.AddCors(options => | ||
| { | ||
| options.AddDefaultPolicy(policy => | ||
| policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); | ||
| }); |
There was a problem hiding this comment.
The PR description says CORS is added “to allow requests from the local Vite/Next dev servers”, but the implementation uses AllowAnyOrigin() which allows any origin in development. If the intent is to only allow local dev UIs, configure explicit WithOrigins(...) entries (and add AllowCredentials() if you expect cookie-based auth).
| @@ -0,0 +1,20 @@ | |||
| using Microsoft.AspNetCore.Authorization; | |||
There was a problem hiding this comment.
using Microsoft.AspNetCore.Authorization; is unused in this controller (there’s no [Authorize]/[AllowAnonymous] usage). Please remove the unused using to keep the file clean, or add the intended attribute if authorization behavior was meant to be explicit.
| using Microsoft.AspNetCore.Authorization; |
| const externalTsPlugin: Plugin = { | ||
| name: "external-ts", | ||
| async transform(code, id) { | ||
| if (id.endsWith(".ts") && !id.includes("/ui/src/")) { |
There was a problem hiding this comment.
The Rollup plugin excludes internal sources via !id.includes("/ui/src/"), which is path-separator dependent and will fail on Windows (paths use \\). That can cause the plugin to transpile this package’s own .ts modules unexpectedly. Use a separator-agnostic check (e.g., normalize the path, or check id.includes(path.join("packages","ui","src") + path.sep)), and consider narrowing the match to only the workspace packages you actually need to transpile.
| if (id.endsWith(".ts") && !id.includes("/ui/src/")) { | |
| const normalizedId = id.replace(/\\/g, "/"); | |
| if (id.endsWith(".ts") && !normalizedId.includes("/ui/src/")) { |
| @State() private showConfirmPassword: boolean = false; | ||
| @State() private confirmError: string = ""; | ||
|
|
||
| private client = createExpressThatAuthClient("http://localhost:3001"); |
There was a problem hiding this comment.
createExpressThatAuthClient("http://localhost:3001") hardcodes a dev-only base URL inside a reusable UI component. This will break in non-local environments and makes SSR builds environment-dependent. Consider taking the base URL as a prop (or reading from a global config) and defaulting to same-origin (e.g. new URL("/", window.location.origin) on the client) where appropriate.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Adds a registration flow end-to-end: a new Stencil
ex-register-boxUI component, anAccountControllerAPI endpoint, and dev CORS configuration to wire them together.Changes
packages/ui–ex-register-boxStencil componentEXRegisterBoxcomponent (ex-register-boxtag) with email, password, and confirm-password fieldsexSubmitevent with{ email, password }on successful submissionexSignInevent when the "Sign in" link is clickedPOST /api/accountvia the generated@expressthat-auth/api-clientThemeBasefor consistent themingpackages/ui/src/index.tsapps/api–AccountControllerPOST /api/accountendpoint (Registeraction){ email, password }, returns{ id, email }(stub implementation)apps/api– Dev CORSProgram.csto allow requests from the local Vite/Next dev servers during developmentapps/webpage.tsxto renderex-register-boxfor local testingTesting
pnpm --filter api build) and regenerate the client if neededapps/apiandapps/webviapnpm devand verify the register box renders and submits