feat(app): wire dashboard administration flows

This commit is contained in:
2026-05-07 15:35:37 +00:00
parent e9eb9b4172
commit 91f06ccae1
91 changed files with 4087 additions and 5863 deletions
+42 -55
View File
@@ -14,8 +14,6 @@ import {
import '@uptime.link/webwidget';
import '@design.estate/dees-catalog';
import { DeesForm, DeesFormSubmit, DeesInputText } from '@design.estate/dees-catalog';
import { IdpState } from '../states/idp.state.js';
declare global {
@@ -146,7 +144,7 @@ export class IdpLoginPrompt extends DeesElement {
return false;
}
const loginForm = this.shadowRoot.querySelector('#loginForm') as DeesForm | null;
const loginForm = this.shadowRoot.querySelector('#loginForm') as plugins.idpCatalog.IdpForm | null;
loginForm?.setStatus('pending', 'preparing application authorization...');
this.oidcConsentError = '';
@@ -177,7 +175,7 @@ export class IdpLoginPrompt extends DeesElement {
}
const idpState = await IdpState.getSingletonInstance();
const loginForm = this.shadowRoot.querySelector('#loginForm') as DeesForm | null;
const loginForm = this.shadowRoot.querySelector('#loginForm') as plugins.idpCatalog.IdpForm | null;
loginForm?.setStatus('pending', 'authorizing application...');
this.oidcConsentError = '';
@@ -233,7 +231,7 @@ export class IdpLoginPrompt extends DeesElement {
margin: 0;
}
dees-form {
idp-form {
display: flex;
flex-direction: column;
gap: 16px;
@@ -318,25 +316,6 @@ export class IdpLoginPrompt extends DeesElement {
gap: 12px;
}
.consent-button {
border: none;
border-radius: 999px;
padding: 12px 18px;
font: inherit;
cursor: pointer;
}
.consent-button-secondary {
background: rgba(255, 255, 255, 0.08);
color: var(--foreground);
}
.consent-button-primary {
background: linear-gradient(135deg, #9b7bff, #5fd1ff);
color: #0a0a0a;
font-weight: 600;
}
.consent-error {
color: #ff9a9a;
font-size: 14px;
@@ -370,16 +349,16 @@ export class IdpLoginPrompt extends DeesElement {
</div>
${this.oidcConsentError ? html`<div class="consent-error">${this.oidcConsentError}</div>` : null}
<div class="consent-actions">
<button
class="consent-button consent-button-secondary"
<idp-button
variant="outline"
@click=${() => {
this.redirectOidcError('access_denied');
}}
>
Cancel
</button>
<button
class="consent-button consent-button-primary"
</idp-button>
<idp-button
variant="accent"
@click=${async () => {
const idpState = await IdpState.getSingletonInstance();
const jwt = await idpState.idpClient.getJwt();
@@ -391,7 +370,7 @@ export class IdpLoginPrompt extends DeesElement {
}}
>
Allow and continue
</button>
</idp-button>
</div>
</div>
</idp-centercontainer>
@@ -404,29 +383,31 @@ export class IdpLoginPrompt extends DeesElement {
<h2>Sign in to your account</h2>
<p>Enter your credentials to continue</p>
</div>
<dees-form
<idp-form
id="loginForm"
@formData=${(eventArg) => {
@idp-submit=${(eventArg: CustomEvent<plugins.idpCatalog.IIdpFormSubmitEventDetail>) => {
this.login({
emailAddress: eventArg.detail.data.emailAddress,
passwordArg: eventArg.detail.data.password,
emailAddress: String(eventArg.detail.data.emailAddress || ''),
passwordArg: String(eventArg.detail.data.password || ''),
});
}}
>
<dees-input-text
<idp-input
id="loginEmailInput"
.required=${true}
key="emailAddress"
required
name="emailAddress"
label="Email or Username"
></dees-input-text>
<dees-input-text
.id=${'loginPasswordInput'}
.key=${'password'}
.label=${'Password'}
.isPasswordBool=${true}
></dees-input-text>
<dees-form-submit id="loginSubmitButton"></dees-form-submit>
</dees-form>
autocomplete="username"
></idp-input>
<idp-input
id="loginPasswordInput"
name="password"
label="Password"
type="password"
autocomplete="current-password"
></idp-input>
<idp-form-submit id="loginSubmitButton"></idp-form-submit>
</idp-form>
<div class="form-footer">
Don't have an account?
<a @click=${async () => {
@@ -441,9 +422,9 @@ export class IdpLoginPrompt extends DeesElement {
public async firstUpdated() {
await this.domtoolsPromise;
const idpState = await IdpState.getSingletonInstance();
const loginForm = this.shadowRoot.querySelector('#loginForm') as DeesForm;
const loginPasswordInput = loginForm.querySelector('#loginPasswordInput') as DeesInputText;
const loginSubmitButton = loginForm.querySelector('#loginSubmitButton') as DeesFormSubmit;
const loginForm = this.shadowRoot.querySelector('#loginForm') as plugins.idpCatalog.IdpForm;
const loginPasswordInput = loginForm.querySelector('#loginPasswordInput') as plugins.idpCatalog.IdpInput;
const loginSubmitButton = loginForm.querySelector('#loginSubmitButton') as plugins.idpCatalog.IdpFormSubmit;
const oidcContext = this.getOidcAuthorizationContext();
const setButtonText = async () => {
if (loginPasswordInput.value) {
@@ -452,7 +433,7 @@ export class IdpLoginPrompt extends DeesElement {
loginSubmitButton.text = 'Send magic link (or enter password)';
}
};
loginForm.changeSubject.subscribe(() => {
loginForm.addEventListener('idp-input-change', () => {
void setButtonText();
});
await setButtonText();
@@ -470,17 +451,19 @@ export class IdpLoginPrompt extends DeesElement {
await this.handleOidcAfterLogin(jwt);
}
}
} else if (await idpState.idpClient.determineLoginStatus(false)) {
idpState.domtools.router.pushUrl('/dash/overview');
}
}
private login = async (valueArg: { emailAddress: string; passwordArg: string }) => {
const loginSubmitButton = this.shadowRoot.querySelector(
'#loginSubmitButton'
) as plugins.deesCatalog.DeesFormSubmit;
) as plugins.idpCatalog.IdpFormSubmit;
loginSubmitButton.disabled = true;
const idpState = await IdpState.getSingletonInstance();
const loginForm = this.shadowRoot.querySelector('#loginForm') as DeesForm;
const loginForm = this.shadowRoot.querySelector('#loginForm') as plugins.idpCatalog.IdpForm;
const loginRequestWithUsernameAndPassword =
idpState.idpClient.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmailOrUsernameAndPassword>(
'loginWithEmailOrUsernameAndPassword'
@@ -512,7 +495,7 @@ export class IdpLoginPrompt extends DeesElement {
loginForm.setStatus('success', 'obtained jwt.');
const oidcHandled = await this.handleOidcAfterLogin(jwt);
if (!oidcHandled) {
idpState.domtools.router.pushUrl('/account');
idpState.domtools.router.pushUrl('/dash/overview');
}
} else {
loginForm.setStatus('error', 'something went wrong');
@@ -522,8 +505,12 @@ export class IdpLoginPrompt extends DeesElement {
loginForm.setStatus('pending', 'sending magic link...');
const response = await loginRequestWithEmail.fire({
email: valueArg.emailAddress,
}).catch((err) => {
const message = err?.errorText || err?.message || 'Could not send the magic link. Please try again.';
loginForm.setStatus('error', message);
return null;
});
if (response.status === 'ok') {
if (response?.status === 'ok') {
loginForm.setStatus('success', 'Please check your email!');
}
}
@@ -547,7 +534,7 @@ export class IdpLoginPrompt extends DeesElement {
}
public async focus() {
(this.shadowRoot.querySelector('#loginEmailInput') as plugins.deesCatalog.DeesInputText).focus();
(this.shadowRoot.querySelector('#loginEmailInput') as plugins.idpCatalog.IdpInput).focus();
}
public async show() {