diff --git a/Changelog.md b/Changelog.md
index 9d012ecf..e8e4dc3c 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -7,6 +7,7 @@
### Added
- Added an inline DNS TXT record guide and domain verification controls to App Configuration.
+- Added direct URL access to the General, Domain Verification, and Issuer Configuration tabs.
### Changed
diff --git a/src/App.vue b/src/App.vue
index 68d065d1..c6dec817 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -454,7 +454,6 @@ color: #1a1a2e !important;
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.03em;
- text-transform: uppercase;
border-radius: 14px;
}
@@ -586,7 +585,7 @@ color: #1a1a2e !important;
- DEV
+ Development
diff --git a/src/store/mainStore.js b/src/store/mainStore.js
index b357fcda..1796c987 100644
--- a/src/store/mainStore.js
+++ b/src/store/mainStore.js
@@ -728,7 +728,7 @@ const mainStore = {
resolve(json)
}
}).catch((e) => {
- reject(new Error(`while updating an app ${e}`))
+ reject(new Error(`${e}`))
})
})
},
@@ -936,7 +936,7 @@ const mainStore = {
resolve(json)
}
}).catch((e) => {
- reject(new Error(`while updating an app ${e}`))
+ reject(new Error(`${e}`))
})
})
},
@@ -963,7 +963,7 @@ const mainStore = {
}
})
.catch(e => {
- reject(new Error(`while updating an app ${e}`));
+ reject(new Error(`${e}`));
});
});
},
diff --git a/src/views/ServiceConfig.vue b/src/views/ServiceConfig.vue
index 65da896b..dde85101 100644
--- a/src/views/ServiceConfig.vue
+++ b/src/views/ServiceConfig.vue
@@ -112,7 +112,7 @@
{{ displayDomainVerified ? '✓ Verified' : 'Unverified' }}
-
Confirm ownership of the domain used by this application.
+ Verify domain ownership to unlock Production access.
Switching environments can affect credentials, integrations, and available services.
+ Your domain must be verified before moving to Production.
+ The environment change will be applied when you save.
Cancel
Save environment
@@ -354,7 +356,6 @@
.domain-summary { width: 100%; }
.domain-summary strong { font-size: 13px; }
.card-footer-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 22px; padding-top: 18px; border-top: 1px solid #e8edf3; }
-.domain-footer-actions { margin-top: 16px; padding-top: 0; border-top: 0; }
.verification-badge, .inline-verified, .key-type { display: inline-flex; align-items: center; border-radius: 14px; font-size: 11px; font-weight: 700; }
.verification-badge { padding: 5px 10px; }
.verified, .inline-verified { background: #eaf8f0; color: #218459; }
@@ -366,6 +367,7 @@
.environment-toggle button:disabled { cursor: default; opacity: 1; }
.environment-help { margin: 14px 0 0; color: #7b8798; font-size: 11px; }
.pending-message { margin: 12px 0 0; color: #218459; font-size: 12px; }
+.production-requirement { margin: 10px 0 0; padding: 9px 10px; border-radius: 6px; background: #fff8e8; color: #8a611d; font-size: 11px; line-height: 1.45; }
.identity-fields { display: grid; gap: 22px; margin-top: 24px; }
.key-type { margin-top: 7px; padding: 4px 8px; background: #f0f4ff; color: #315fba; }
.dns-record { display: flex; align-items: center; justify-content: space-between; gap: 15px; padding: 14px; border: 1px solid #dfe6ef; border-radius: 7px; background: #f8fafc; }
@@ -483,6 +485,13 @@ export default {
.map(value => normalizeCorsOrigin(value))
.filter(Boolean)
.filter((origin, index, all) => all.indexOf(origin) === index);
+ this.activeTab = this.isValidTab(this.$route.query.tab) ? this.$route.query.tab : 'general';
+ },
+ watch: {
+ '$route.query.tab'(tab) {
+ if (this.isEditing || this.isEditingDomain) return;
+ this.activeTab = this.isValidTab(tab) ? tab : 'general';
+ },
},
methods: {
...mapActions('mainStore', [
@@ -496,9 +505,21 @@ export default {
return serviceId ? this.getAppsWithSSIServices.find(item => item.appId === serviceId) : null;
},
async selectTab(tab) {
+ if (!this.isValidTab(tab)) return;
this.activeTab = tab;
+ if (this.$route.query.tab !== tab) {
+ await this.$router.replace({
+ query: {
+ ...this.$route.query,
+ tab,
+ },
+ });
+ }
if (tab === 'issuer' && this.isEditing && this.editingSection === 'issuer') await this.prepareIdentityEditor();
},
+ isValidTab(tab) {
+ return this.tabs.some(item => item.value === tab);
+ },
async prepareIdentityEditor() {
await this.ensureWidgetConfigsLoaded();
if (!this.associatedSSIServiceDIDs.length) await this.fetchDIDs();