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
213 changes: 213 additions & 0 deletions indexer-api-gateway/src/api/services/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2117,5 +2117,218 @@ export class EntityApi extends ApiClient {
);
}
//#endregion

//#region MINT TOKEN SEARCH (Issue #5021)
@ApiOperation({
summary: 'Get mint token documents',
description: 'Returns a list of minting VP documents for searching issued carbon credits. Supports filtering by token, policy, amount range, and date range.',
})
@ApiPaginatedRequest
@ApiPaginatedResponse('Mint token documents', VPGridDTO)
@ApiQuery({
name: 'analytics.tokenId',
description: 'Token identifier to filter by',
example: '0.0.1960',
required: false,
})
@ApiQuery({
name: 'analytics.policyId',
description: 'Policy (methodology) identifier to filter by',
example: '1706823227.586179534',
required: false,
})
@ApiQuery({
name: 'minAmount',
description: 'Minimum token amount',
example: '1000',
required: false,
})
@ApiQuery({
name: 'maxAmount',
description: 'Maximum token amount',
example: '100000',
required: false,
})
@ApiQuery({
name: 'dateFrom',
description: 'Start date filter (consensus timestamp)',
example: '1706823227.000000000',
required: false,
})
@ApiQuery({
name: 'dateTo',
description: 'End date filter (consensus timestamp)',
example: '1709501627.000000000',
required: false,
})
@ApiQuery({
name: 'keywords',
description: 'Keywords to search (JSON array)',
examples: {
geography: {
description: 'Search by geography',
value: '["Brazil"]',
},
},
required: false,
})
@Get('/mint-tokens')
@HttpCode(HttpStatus.OK)
async getMintTokenDocuments(
@Query('pageIndex') pageIndex?: number,
@Query('pageSize') pageSize?: number,
@Query('orderField') orderField?: string,
@Query('orderDir') orderDir?: string,
@Query('keywords') keywords?: string,
@Query('analytics.tokenId') tokenId?: string,
@Query('analytics.policyId') policyId?: string,
@Query('minAmount') minAmount?: string,
@Query('maxAmount') maxAmount?: string,
@Query('dateFrom') dateFrom?: string,
@Query('dateTo') dateTo?: string,
) {
return await this.send(IndexerMessageAPI.GET_MINT_TOKEN_DOCUMENTS, {
pageIndex,
pageSize,
orderField,
orderDir,
keywords,
'analytics.tokenId': tokenId,
'analytics.policyId': policyId,
minAmount,
maxAmount,
dateFrom,
dateTo,
});
}

@ApiOperation({
summary: 'Get mint token filters',
description: 'Returns available filter options (policies, tokens) for the mint token search',
})
@ApiOkResponse({
description: 'Mint token filter options',
})
@ApiInternalServerErrorResponse({
description: 'Internal server error',
type: InternalServerErrorDTO,
})
@Get('/mint-tokens/filters')
@HttpCode(HttpStatus.OK)
async getMintTokenFilters() {
return await this.send(IndexerMessageAPI.GET_MINT_TOKEN_FILTERS, {});
}

@ApiOperation({
summary: 'Get mint token document details',
description: 'Returns details of a specific minting VP document including token and policy information',
})
@ApiOkResponse({
description: 'Mint token document details',
type: VPDetailsDTO,
})
@ApiInternalServerErrorResponse({
description: 'Internal server error',
type: InternalServerErrorDTO,
})
@Get('/mint-tokens/:messageId')
@ApiParam({
name: 'messageId',
description: 'Message identifier (consensus timestamp)',
example: '1706823227.586179534',
})
@HttpCode(HttpStatus.OK)
async getMintTokenDocument(@Param('messageId') messageId: string) {
return await this.send(IndexerMessageAPI.GET_MINT_TOKEN_DOCUMENT, {
messageId,
});
}
//#endregion

//#region PROJECT PRACTICES SEARCH (Issue #5019)
@ApiOperation({
summary: 'Search project practices',
description: 'Search for VC documents (e.g., Monitoring Reports) under the same policy to compare common project practices across methodologies.',
})
@ApiPaginatedRequest
@ApiPaginatedResponse('Project practice documents', VCGridDTO)
@ApiQuery({
name: 'analytics.policyId',
description: 'Policy (methodology) identifier to filter by',
example: '1706823227.586179534',
required: false,
})
@ApiQuery({
name: 'analytics.schemaId',
description: 'Schema identifier to filter by',
example: '1706823227.586179534',
required: false,
})
@ApiQuery({
name: 'analytics.schemaName',
description: 'Schema name to filter by (e.g., "Monitoring Report")',
example: 'Monitoring Report',
required: false,
})
@ApiQuery({
name: 'keywords',
description: 'Keywords to search within document fields (JSON array). Use to find specific methodological decisions.',
examples: {
methodology_choice: {
description: 'Search for a specific methodology choice',
value: '["Use default values"]',
},
},
required: false,
})
@ApiQuery({
name: 'topicId',
description: 'Topic identifier',
example: '0.0.1960',
required: false,
})
@Get('/project-practices')
@HttpCode(HttpStatus.OK)
async getProjectPractices(
@Query('pageIndex') pageIndex?: number,
@Query('pageSize') pageSize?: number,
@Query('orderField') orderField?: string,
@Query('orderDir') orderDir?: string,
@Query('keywords') keywords?: string,
@Query('analytics.policyId') policyId?: string,
@Query('analytics.schemaId') schemaId?: string,
@Query('analytics.schemaName') schemaName?: string,
@Query('topicId') topicId?: string,
) {
return await this.send(IndexerMessageAPI.GET_PROJECT_PRACTICES, {
pageIndex,
pageSize,
orderField,
orderDir,
keywords,
'analytics.policyId': policyId,
'analytics.schemaId': schemaId,
'analytics.schemaName': schemaName,
topicId,
});
}

@ApiOperation({
summary: 'Get project practices filter options',
description: 'Returns available filter options (schema names, policies) for the project practices search',
})
@ApiOkResponse({
description: 'Project practices filter options',
})
@ApiInternalServerErrorResponse({
description: 'Internal server error',
type: InternalServerErrorDTO,
})
@Get('/project-practices/filters')
@HttpCode(HttpStatus.OK)
async getProjectPracticesFilters() {
return await this.send(IndexerMessageAPI.GET_PROJECT_PRACTICES_FILTERS, {});
}
//#endregion
//#endregion
}
11 changes: 11 additions & 0 deletions indexer-common/src/messages/message-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,17 @@ export enum IndexerMessageAPI {
GET_COMPARE_ORIGINAL_POLICY = "INDEXER_API_GET_COMPARE_ORIGINAL_POLICY",
GET_DERIVATIONS = "INDEXER_API_GET_DERIVATIONS",

// #region TOKEN SEARCH (Issue #5021)
GET_MINT_TOKEN_DOCUMENTS = 'INDEXER_API_GET_MINT_TOKEN_DOCUMENTS',
GET_MINT_TOKEN_DOCUMENT = 'INDEXER_API_GET_MINT_TOKEN_DOCUMENT',
GET_MINT_TOKEN_FILTERS = 'INDEXER_API_GET_MINT_TOKEN_FILTERS',
// #endregion

// #region PROJECT PRACTICES SEARCH (Issue #5019)
GET_PROJECT_PRACTICES = 'INDEXER_API_GET_PROJECT_PRACTICES',
GET_PROJECT_PRACTICES_FILTERS = 'INDEXER_API_GET_PROJECT_PRACTICES_FILTERS',
// #endregion

}

export const ARTIFACT_CHUNK_BYTES = Number(process.env.ARTIFACT_CHUNK_BYTES ?? 128 * 1024);
6 changes: 6 additions & 0 deletions indexer-frontend/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ import { FormulaDetailsComponent } from '@views/details/formula-details/formula-
import { PriorityQueueComponent } from '@views/priority-queue/priority-queue.component';
import { SchemasPackagesComponent } from '@views/collections/schemas-packages/schemas-packages.component';
import { SchemasPackageDetailsComponent } from '@views/details/schemas-packages-details/schemas-packages-details.component';
import { MintTokensComponent } from '@views/collections/mint-tokens/mint-tokens.component';
import { ProjectPracticesComponent } from '@views/collections/project-practices/project-practices.component';

export const routes: Routes = [
// _DEV
Expand Down Expand Up @@ -87,6 +89,8 @@ export const routes: Routes = [
{ path: 'statistic-documents', component: StatisticDocumentsComponent },
{ path: 'formulas', component: FormulasComponent },
{ path: 'schemas-packages', component: SchemasPackagesComponent },
{ path: 'mint-tokens', component: MintTokensComponent },
{ path: 'project-practices', component: ProjectPracticesComponent },

//Details
{ path: 'registries/:id', component: RegistryDetailsComponent },
Expand All @@ -109,4 +113,6 @@ export const routes: Routes = [
{ path: 'statistic-documents/:id', component: VcDocumentDetailsComponent },
{ path: 'formulas/:id', component: FormulaDetailsComponent },
{ path: 'schemas-packages/:id', component: SchemasPackageDetailsComponent },
{ path: 'mint-tokens/:id', component: VpDocumentDetailsComponent },
{ path: 'project-practices/:id', component: VcDocumentDetailsComponent },
];
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ export class HeaderComponent {
label: 'header.formulas',
routerLink: '/formulas',
},
{
label: 'header.mint_tokens',
routerLink: '/mint-tokens',
},
{
label: 'header.project_practices',
routerLink: '/project-practices',
},
];

public documentsMenu: MenuItem[] = [
Expand Down
35 changes: 35 additions & 0 deletions indexer-frontend/src/app/services/entities.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,41 @@ export class EntitiesService {
) as any;
}
//#endregion
//#region MINT TOKENS
public getMintTokenDocuments(filters: PageFilters): Observable<Page<VP>> {
const entity = 'mint-tokens';
const options = ApiUtils.getOptions(filters);
return this.http.get<Page<VP>>(`${this.url}/${entity}`, options) as any;
}

public getMintTokenDocument(messageId: string): Observable<VPDetails> {
const entity = 'mint-tokens';
return this.http.get<VPDetails>(
`${this.url}/${entity}/${messageId}`
) as any;
}

public getMintTokenFilters(): Observable<any> {
const entity = 'mint-tokens';
return this.http.get<any>(
`${this.url}/${entity}/filters`
) as any;
}
//#endregion
//#region PROJECT PRACTICES
public getProjectPractices(filters: PageFilters): Observable<Page<VC>> {
const entity = 'project-practices';
const options = ApiUtils.getOptions(filters);
return this.http.get<Page<VC>>(`${this.url}/${entity}`, options) as any;
}

public getProjectPracticesFilters(): Observable<any> {
const entity = 'project-practices';
return this.http.get<any>(
`${this.url}/${entity}/filters`
) as any;
}
//#endregion
//#region VCS
public getVcDocuments(filters: PageFilters): Observable<Page<VC>> {
const entity = 'vc-documents';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<div class="page-content">
<div class="content">
<h1 class="">{{ 'header.mint_tokens' | transloco }}</h1>
<div class="content__table">
<app-table class="collection-container__table"
[loading]="loadingData"
[columns]="columns"
[data]="items"
[pageIndex]="pageIndex"
[pageSize]="pageSize"
[total]="total"
[sortColumn]="orderField"
[sortDirection]="orderDir"
(onPage)="onPage($event)"
(onSort)="onSort($event)">
<div class="filters">
<p-chips class="filters__keywords" [formControl]="keywords"
[placeholder]="'grid.filter.keyword' | transloco" />
<div class="filters__fields">
@for (filter of filters; track $index) {
@if (filter.type === 'input') {
<p-inputGroup>
<p-inputGroupAddon>{{ filter.label | transloco }}</p-inputGroupAddon>
<input pInputText [id]="filter.field" [formControl]="filter.control" />
</p-inputGroup>
}
}
</div>
</div>
</app-table>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.table-body {
.mat-column-tokenAmount {
width: 150px;
min-width: 150px;
max-width: 150px;
}
}
Loading
Loading