Skip to content
Open
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
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Changelog

## [Unreleased]
### [3.14.2] - 2026-08-13
### Modified
- Updated the credit API to fetch and activate credits from the dashboard instead of for a specific tenant.
### Fixed
- Fixed the exhausted credit color issue in the ID Service dashboard.
### [3.14.0] - 2026-08-07
### Added
- Added a toggle for mobile-assisted verification.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "entity-developer-dashboard",
"version": "3.14.1",
"version": "3.14.2",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --mode production",
Expand Down
212 changes: 73 additions & 139 deletions src/store/mainStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import { RequestHandler, JWTExpiredErrorMessageHandling } from '../utils/utils.j

const { apiServer, studioServer } = config;
const apiServerBaseUrl = sanitizeUrl(apiServer.host) + apiServer.basePath;
const normalizeCredit = (credit) => ({
...credit,
totalCredits: credit?.apiCredit?.total ?? credit?.totalCredits ?? 0,
used: credit?.apiCredit?.used ?? credit?.used ?? 0,
creditScope: credit?.onChainAllowanceScopes ?? credit?.creditScope ?? []
});
Vue.use(Vuex)


Expand Down Expand Up @@ -377,10 +383,10 @@ const mainStore = {
},

setKYCCredits: (state, payload) => {
state.kycCredits = payload
state.kycCredits = Array.isArray(payload) ? payload.map(normalizeCredit) : []
},
setSSICredits: (state, payload) => {
state.ssiCredits = payload
state.ssiCredits = Array.isArray(payload) ? payload.map(normalizeCredit) : []
},
setCompanies: (state, payload) => {
state.companies = payload
Expand Down Expand Up @@ -579,7 +585,7 @@ const mainStore = {
return resp;
},
creditRecharge: async ({ getters }, payload) => {
const url = `${apiServerBaseUrl}/credits/${payload.serviceId}`;
const url = `${apiServerBaseUrl}/app/${payload.serviceId}/credits`;
const resp = await RequestHandler(url, 'POST', payload,
UtilsMixin.methods.getHeader(getters.getAuthToken),
)
Expand Down Expand Up @@ -2699,36 +2705,23 @@ const mainStore = {

},
// - KYC Credit
async fetchKYCCredits({ getters, commit, dispatch }) {

if (!getters.getSelectedService || !getters.getSelectedService.tenantUrl) {
throw new Error('Tenant url is null or empty, service is not selected')
async fetchKYCCredits({ getters, commit }) {
const appId = getters.getSelectedService?.appId;
if (!appId) {
throw new Error('App Id is null or empty, service is not selected');
}
const url = `${sanitizeUrl(getters.getSelectedService.tenantUrl)}/api/v1/credit`;
// const url = `http://localhost:3001/api/v1/credit`;

const authToken = getters.getSelectedService.access_token

const token = await dispatch('getValidToken', {
serviceId: getters.getSelectedService.appId,
grant_type: config.GRANT_TYPES_ENUM.CAVACH_API,
tokenStorageKey: "access_token"
});
const headers = UtilsMixin.methods.getKycServiceHeader(token);
const resp = await fetch(url, {
method: 'GET',
headers
})
const json = await resp.json()
if (!resp.ok || json.error) {
throw new Error(JWTExpiredErrorMessageHandling(json))
}
const url = `${apiServerBaseUrl}/app/${appId}/credits`;
const resp = await RequestHandler(
url,
'GET',
{},
UtilsMixin.methods.getHeader(getters.getAuthToken)
);
const credits = Array.isArray(resp) ? resp : (Array.isArray(resp?.data) ? resp.data : []);

if (json.data) {
commit('setKYCCredits', json.data)
return json.data
}
return []
commit('setKYCCredits', credits);
return credits;
},
async submitComplianceDetail({ getters, dispatch }, payload) {
const { companyId, type, status, reasonDetail, reason, accessToken, serviceId } = payload;
Expand Down Expand Up @@ -2797,43 +2790,25 @@ const mainStore = {
},
// - KYC Credit

activateCredit({ getters, dispatch }, payload) {
return new Promise(function (resolve, reject) {
const { creditId } = payload
{
if (!getters.getSelectedService || !getters.getSelectedService.tenantUrl) {
return reject(new Error('Tenant url is null or empty, service is not selected'))
}

if (!creditId) {
return reject(new Error('Credit Id is null or empty'))
}
const url = `${sanitizeUrl(getters.getSelectedService.tenantUrl)}/api/v1/credit/${creditId}/activate`;
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-kyc-access-token": `${getters.getSelectedService.access_token}`,
"Origin": '*'
}
}
fetch(url, {
...options
})
.then(response => response.json())
.then(json => {
if (json) {
dispatch('fetchKYCCredits')
resolve()
} else {
reject(new Error('Could not register DID for this service'))
}
}).catch(e => {
reject(e)
})
}
})
async activateCredit({ getters, dispatch }, payload) {
const appId = getters.getSelectedService?.appId;
const { creditId } = payload;
if (!appId) {
throw new Error('App Id is null or empty, service is not selected');
}
if (!creditId) {
throw new Error('Credit Id is null or empty');
}

const url = `${apiServerBaseUrl}/app/${appId}/credits/${creditId}/activate`;
const resp = await RequestHandler(
url,
'POST',
{},
UtilsMixin.methods.getHeader(getters.getAuthToken)
);
await dispatch('fetchKYCCredits');
return resp;
},


Expand Down Expand Up @@ -3407,85 +3382,44 @@ const mainStore = {
},

// eslint-disable-next-line
async fetchSSICredits({ getters, commit, dispatch }) {
if (!getters.getSelectedService || !getters.getSelectedService.tenantUrl) {
throw new Error('Tenant url is null or empty, service is not selected')
async fetchSSICredits({ getters, commit }) {
const appId = getters.getSelectedService?.appId;
if (!appId) {
throw new Error('App Id is null or empty, service is not selected');
}
const token = await dispatch('getValidToken', {
serviceId: getters.getSelectedService.appId,
grant_type: config.GRANT_TYPES_ENUM.SSI_API,
tokenStorageKey: "access_token"
})
const url = `${sanitizeUrl(getters.getSelectedService.tenantUrl)}/api/v1/credit`;
const options = {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
"Origin": '*'

}
}
const url = `${apiServerBaseUrl}/app/${appId}/credits`;
const resp = await RequestHandler(
url,
'GET',
{},
UtilsMixin.methods.getHeader(getters.getAuthToken)
);
const credits = Array.isArray(resp) ? resp : (Array.isArray(resp?.data) ? resp.data : []);

const resp = await fetch(url, {
...options
})
const json = await resp.json()
if (!resp.ok || json.error) {
const msg = Array.isArray(json.message) ? json.message.join(', ') : (json.message || json.error || 'Failed to fetch SSI credits');
throw new Error(msg);
}
if (json) {
commit('setSSICredits', json)
return json
}
return []
commit('setSSICredits', credits);
return credits;
},

activateSSICredit({ getters, dispatch }, payload) {
return new Promise(function (resolve, reject) {
const { creditId } = payload
{
if (!getters.getSelectedService || !getters.getSelectedService.tenantUrl) {
return reject(new Error('Tenant url is null or empty, service is not selected'))
}

if (!creditId) {
return reject(new Error('Credit Id is null or empty'))
}
dispatch('getValidToken', {
serviceId: getters.getSelectedService.appId,
grant_type: config.GRANT_TYPES_ENUM.SSI_API,
tokenStorageKey: "access_token"
}).then((token) => {
const url = `${sanitizeUrl(getters.getSelectedService.tenantUrl)}/api/v1/credit/${creditId}/activate`;
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
"Origin": '*'
}
}

return fetch(url, {
...options
})
})
.then(response => response.json())
.then(json => {
if (json) {
dispatch('fetchSSICredits')
resolve()
} else {
reject(new Error('Could not Activate credit for this service'))
}
}).catch(e => {
reject(e)
})
}
})
async activateSSICredit({ getters, dispatch }, payload) {
const appId = getters.getSelectedService?.appId;
const { creditId } = payload;
if (!appId) {
throw new Error('App Id is null or empty, service is not selected');
}
if (!creditId) {
throw new Error('Credit Id is null or empty');
}

const url = `${apiServerBaseUrl}/app/${appId}/credits/${creditId}/activate`;
const resp = await RequestHandler(
url,
'POST',
{},
UtilsMixin.methods.getHeader(getters.getAuthToken)
);
await dispatch('fetchSSICredits');
return resp;
},
// eslint-disable-next-line
async ssiDashboardAllowanceStats({ getters }, payload) {
Expand Down
6 changes: 4 additions & 2 deletions src/views/playground/KYCDashboardCredit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -307,14 +307,16 @@ export default {

renderChart() {
if (!Array.isArray(this.getKYCCredits)) return;
const expired = this.getKYCCredits.every(el => Date.now() > new Date(el.expiresAt));
const used = this.myKYCCredits.allUsedCredits || 0;
const remaining = this.myKYCCredits.allRemainingCredits || 0;
const hasNoRemainingCredits = remaining <= 0;

const dataToRender = (this.getKYCCredits.length === 0 || (used + remaining === 0))
? [0, 1] : [used, remaining];

const colors = expired ? ['#cbd5e1', '#f1f5f9'] : ['#94a3b8', '#3b82f6'];
const colors = hasNoRemainingCredits
? ['#cbd5e1', '#94a3b8']
: ['#94a3b8', '#3b82f6'];

this.doughNutChart?.destroy();
const ctx = document.getElementById('doughNutChat');
Expand Down
17 changes: 10 additions & 7 deletions src/views/playground/SSIDashboardCredit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -852,13 +852,16 @@ export default {
this.startTimer();
const creditsArr = Array.isArray(credits) ? credits : [];
this.ssiCredits = creditsArr;
const credit = creditsArr.filter(each => {
if (each.status == 'Active') {
return each
}
})
if (credit[0]?.credit) {
this.allowance.scope=credit[0].creditScope
const activeCredit = creditsArr.find(each => each.status === 'Active');
if (activeCredit?.onChainAllowance) {
const { amount = 0, denom = 'uhid', usedAmount = 0 } = activeCredit.onChainAllowance;
this.allowance.spend_limit = [{ denom, amount: String(Math.max(amount - usedAmount, 0)) }];
this.allowance.scope = activeCredit.onChainAllowanceScopes || [];
this.allowance.expiration = activeCredit.expiresAt || null;
} else {
this.allowance.spend_limit = [{ denom: 'uhid', amount: '0' }];
this.allowance.scope = [];
this.allowance.expiration = null;
}
this.isLoading = false
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion src/views/sa/components/CreditRecharge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export default {
serviceId: '',
amount: '100',
validityPeriod: '12',
validityPeriodUnit: 'MONTH',
validityPeriodUnit: 'Month',
amountDenom: 'uHID',
}
};
Expand Down
Loading