diff --git a/structures-frontend-next/components.d.ts b/structures-frontend-next/components.d.ts index 992413a82..de52b8b4a 100644 --- a/structures-frontend-next/components.d.ts +++ b/structures-frontend-next/components.d.ts @@ -22,6 +22,7 @@ declare module 'vue' { CrudTable: typeof import('./src/components/CrudTable.vue')['default'] DashboardWidgetCard: typeof import('./src/components/DashboardWidgetCard.vue')['default'] DataTable: typeof import('primevue/datatable')['default'] + Dialog: typeof import('primevue/dialog')['default'] EmptyState: typeof import('./src/components/EmptyState.vue')['default'] EnumNode: typeof import('./src/components/nodes/EnumNode.vue')['default'] ERTable: typeof import('./src/components/modals/ERTable.vue')['default'] @@ -34,6 +35,7 @@ declare module 'vue' { Menu: typeof import('primevue/menu')['default'] NewProjectSidebar: typeof import('./src/components/NewProjectSidebar.vue')['default'] ObjectNode: typeof import('./src/components/nodes/ObjectNode.vue')['default'] + OpenAPIModal: typeof import('./src/components/modals/OpenAPIModal.vue')['default'] Paginator: typeof import('primevue/paginator')['default'] Password: typeof import('primevue/password')['default'] Popover: typeof import('primevue/popover')['default'] diff --git a/structures-frontend-next/public/scalar-ui.html b/structures-frontend-next/public/scalar-ui.html index 791f3a8e0..f0e5cb46b 100644 --- a/structures-frontend-next/public/scalar-ui.html +++ b/structures-frontend-next/public/scalar-ui.html @@ -13,10 +13,7 @@
- - - diff --git a/structures-frontend-next/src/assets/login-page-logo-new.svg b/structures-frontend-next/src/assets/login-page-logo-new.svg new file mode 100644 index 000000000..d9c747c9b --- /dev/null +++ b/structures-frontend-next/src/assets/login-page-logo-new.svg @@ -0,0 +1,17 @@ + diff --git a/structures-frontend-next/src/assets/login-page-symbol-new.svg b/structures-frontend-next/src/assets/login-page-symbol-new.svg new file mode 100644 index 000000000..08a15de81 --- /dev/null +++ b/structures-frontend-next/src/assets/login-page-symbol-new.svg @@ -0,0 +1,42 @@ + diff --git a/structures-frontend-next/src/assets/login-page-symbol.svg b/structures-frontend-next/src/assets/login-page-symbol.svg new file mode 100644 index 000000000..08a15de81 --- /dev/null +++ b/structures-frontend-next/src/assets/login-page-symbol.svg @@ -0,0 +1,42 @@ + diff --git a/structures-frontend-next/src/assets/mcp.svg b/structures-frontend-next/src/assets/mcp.svg new file mode 100644 index 000000000..ba9ec2547 --- /dev/null +++ b/structures-frontend-next/src/assets/mcp.svg @@ -0,0 +1,4 @@ + diff --git a/structures-frontend-next/src/components/CrudTable.vue b/structures-frontend-next/src/components/CrudTable.vue index 2647d60b0..a71c72440 100644 --- a/structures-frontend-next/src/components/CrudTable.vue +++ b/structures-frontend-next/src/components/CrudTable.vue @@ -60,7 +60,8 @@ class CrudTable extends Vue { @Prop({ default: 'No items yet' }) emptyStateText!: string @Prop({ default: '' }) search!: string @Prop({ default: true }) showPagination!: boolean - @Prop({ default: true }) enableRowHover!: boolean; + @Prop({ default: true }) enableRowHover!: boolean + @Prop({ default: 10 }) defaultPageSize!: number getRowClass() { return { @@ -78,7 +79,7 @@ class CrudTable extends Vue { searchText: string | null = ""; options = { page: 0, - rows: 9, + rows: 10, first: 0, sortField: "", sortOrder: 1 as 1 | -1, @@ -108,10 +109,22 @@ class CrudTable extends Vue { return this.enableViewSwitcher && this.activeView === "column"; } + get paginationOptions(): number[] { + const options = [5, 10, 20, 50]; + if (!options.includes(this.defaultPageSize)) { + options.push(this.defaultPageSize); + options.sort((a, b) => a - b); + } + return options; + } + mounted() { const urlSearch = (this.$route.query.search as string) || '' this.loading = true - this.initialSearchCompleted = false + this.initialSearchCompleted = false + + this.options.rows = this.defaultPageSize; + if (urlSearch) { this.searchText = urlSearch; } @@ -352,6 +365,7 @@ export default toNative(CrudTable);You can try again or use an alternative login method:
-Initializing...
@@ -233,42 +221,35 @@ import { StructuresStates } from "@/states/index" } }) export default class Login extends Vue { - // Use the authentication service private auth = new AuthenticationService(); - // Form data get login() { return this.auth.login; } set login(value: string) { this.auth.login = value; } get password() { return this.auth.password; } set password(value: string) { this.auth.password = value; } - // State from service get state() { return this.auth.state; } get shouldShowLoginForm() { return this.auth.shouldShowLoginForm; } get isLoginValid() { return this.auth.isLoginValid; } get isPasswordValid() { return this.auth.isPasswordValid; } get currentOidcError() { return this.auth.currentOidcError; } - // Computed properties for template get showPassword() { return this.state.showPassword; } set showPassword(value: boolean) { this.auth.updateState({ showPassword: value }); } - // Always show the form immediately - no initialization wait get isInitialized() { - return true; // Always ready to show email form + return true; } - // These will be loaded when needed private _isConfigLoaded: boolean = false; - private _isBasicAuthEnabled: boolean = true; // Default assumption - assume basic auth is available + private _isBasicAuthEnabled: boolean = true; get isConfigLoaded() { return this._isConfigLoaded; } get isBasicAuthEnabled() { return this._isBasicAuthEnabled; } - // Debug method to check current state get debugInfo() { return { emailEntered: this.state?.emailEntered, @@ -283,21 +264,17 @@ export default class Login extends Vue { private userState: IUserState = StructuresStates.getUserState() async mounted() { - // Focus the email input immediately - no waiting for initialization this.$nextTick(() => { this.focusEmailInput(); }); - // Load basic config in background (non-blocking) this.loadBasicConfig(); - // Check if we're returning from an OIDC login with an error if (this.$route.query.error) { this.handleOidcError(); return; } - // Check if we're returning from an OIDC login if (this.$route.query.code && this.$route.query.state) { this.handleOidcCallback(); } @@ -305,7 +282,6 @@ export default class Login extends Vue { private async loadBasicConfig() { console.log('Loading basic config...'); - // Load basic configuration in the background without blocking UI try { this._isBasicAuthEnabled = await this.auth.checkBasicAuthEnabled(); this._isConfigLoaded = true; @@ -361,13 +337,11 @@ export default class Login extends Vue { if (isRetryable) { this.auth.showRetryOption(oidcError); - // Keep current email and provider info for retry this.auth.updateState({ emailEntered: true, showPassword: false }); } else { - // For non-retryable errors, reset to email input this.auth.resetToEmail(); } } @@ -386,13 +360,10 @@ export default class Login extends Vue { const { referer, provider } = stateInfo; const userManager = await createUserManager(provider); - // Complete the OIDC login const user = await userManager.signinRedirectCallback(); - // Store the user info in your state management await this.userState.handleOidcLogin(user); - // Redirect to the original destination or default const redirectPath = referer || '/applications'; await CONTINUUM_UI.navigate(redirectPath); } catch (error: unknown) { @@ -403,7 +374,6 @@ export default class Login extends Vue { this.displayAlert('OIDC callback failed'); } - // Reset form to email input after OIDC callback error this.auth.resetToEmail(); } finally { this.auth.setOidcCallbackLoading(false); @@ -441,7 +411,6 @@ export default class Login extends Vue { this.displayAlert('Unknown login error') } - // Reset form to email input after authentication error this.auth.resetToEmail(); } finally { this.auth.setLoading(false); @@ -469,42 +438,24 @@ export default class Login extends Vue { this.auth.setLoading(true); try { - const authMethod = await this.auth.determineAuthMethod(this.login); - console.log('Auth method determined:', authMethod); // Debug log + console.log('Proceeding to password step for email:', this.login); + this.auth.updateState({ + emailEntered: true, + showPassword: true, + matchedProvider: null, + providerDisplayName: '', + showRetryOption: false, + showErrorDetails: false + }); - if (authMethod.shouldUseOidc && authMethod.matchedProvider) { - // Automatically redirect to OIDC login when provider is found - console.log('OIDC provider found, automatically redirecting to:', authMethod.matchedProvider); - console.log('About to call handleOidcLogin...'); - // Don't show provider selection UI - just redirect directly - await this.handleOidcLogin(authMethod.matchedProvider); - console.log('handleOidcLogin completed, returning early'); - return; // Exit early to prevent any further UI updates - } else if (authMethod.fallbackToBasicAuth) { - // Only show password if explicitly marked for fallback (no OIDC providers or no match found) - console.log('Falling back to basic auth - no OIDC provider found'); - this.auth.resetToPassword(null, ''); - } else { - // OIDC is enabled but something went wrong - reset form and show error - console.log('OIDC enabled but no provider found - resetting form'); - this.auth.resetToEmail(); - this.displayAlert('Unable to determine authentication method. Please try again or contact support.'); - } + this.$nextTick(() => { + this.focusPasswordInput(); + }); } catch (error) { - console.error('Error determining authentication method:', error); - // OIDC failed - reset form and show error, don't fallback to password - console.log('OIDC error occurred - resetting form and showing error'); - this.auth.resetToEmail(); - this.displayAlert('Authentication service error. Please try again or contact support.'); + console.error('Error in email submit:', error); + this.displayAlert('Error processing email. Please try again.'); } finally { this.auth.setLoading(false); - - // Focus password input when it becomes visible - if (this.state.showPassword) { - this.$nextTick(() => { - this.focusPasswordInput(); - }); - } } } @@ -532,7 +483,6 @@ export default class Login extends Vue { private usePasswordInstead() { this.auth.clearRetryOption(); - // Explicitly show password input when user chooses to use password instead of OIDC this.auth.updateState({ emailEntered: true, showPassword: true, @@ -582,12 +532,7 @@ export default class Login extends Vue { private async handleOidcLogin(provider: string) { console.log('handleOidcLogin called with provider:', provider); console.log('Current loading state:', this.state.loading); - - // Don't check loading state here since we're already in a loading context from handleEmailSubmit - // if (this.state.loading) { - // console.log('Loading is true, returning early'); - // return; - // } + console.log('Starting OIDC flow (loading already set)...'); @@ -596,20 +541,16 @@ export default class Login extends Vue { const userManager = await createUserManager(provider); console.log('User manager created successfully'); - // Create state object with provider and referer information console.log('Creating OIDC state...'); const state = await this.auth.createOidcState(this.referer, provider); console.log('OIDC state created:', state); - // Start OIDC login with login_hint to pre-fill email const signinOptions: any = { state }; - // Add login_hint if we have an email to pre-fill if (this.login) { signinOptions.login_hint = this.login; console.log('Added login_hint:', this.login); - // Some providers also support domain_hint for better UX const emailDomain = this.login.split('@')[1]; if (emailDomain) { signinOptions.domain_hint = emailDomain; @@ -625,10 +566,8 @@ export default class Login extends Vue { console.error('OIDC login failed:', error); this.displayAlert(`OIDC login failed: ${error instanceof Error ? error.message : 'Unknown error'}`); - // Reset form state to allow user to try again this.auth.resetToEmail(); } - // Note: Loading state is managed by the calling handleEmailSubmit method } } \ No newline at end of file diff --git a/structures-frontend-next/src/states/IApplicationState.ts b/structures-frontend-next/src/states/IApplicationState.ts index ff9ff152b..0ba7ffbb5 100644 --- a/structures-frontend-next/src/states/IApplicationState.ts +++ b/structures-frontend-next/src/states/IApplicationState.ts @@ -97,7 +97,6 @@ class ApplicationState implements IApplicationState { public async loadAllApplications(): Promise