feat: Runtime configuration and Docker deployment improvements
Frontend: - Add runtime configuration service for deployment-time API URL injection - Create docker-entrypoint.sh to generate config.json from environment variables - Update ApiService, ApprovalService, and DocumentViewer to use RuntimeConfigService - Add APP_INITIALIZER to load runtime config before app starts Backend: - Fix init-blockchain.js to properly quote mnemonic phrases in .env file - Improve docker-entrypoint.sh with health checks and better error handling Docker: - Add API_BASE_URL environment variable to frontend container - Update docker-compose.yml with clear documentation for remote deployment - Reorganize .env.example with clear categories (REQUIRED FOR REMOTE, PRODUCTION, AUTO-GENERATED) Workflow fixes: - Fix DepartmentApproval interface to match backend schema - Fix stage transformation for 0-indexed stageOrder - Fix workflow list to show correct stage count from definition.stages Cleanup: - Move development artifacts to .trash directory - Remove root-level package.json (was only for utility scripts) - Add .trash/ to .gitignore
This commit is contained in:
@@ -7,11 +7,14 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { MatDialogModule, MatDialog } from '@angular/material/dialog';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { Clipboard } from '@angular/cdk/clipboard';
|
||||
import { interval, Subscription } from 'rxjs';
|
||||
import { ApiService } from '../../../core/services/api.service';
|
||||
import { BlockDto, BlockchainTransactionDto } from '../../../api/models';
|
||||
import { BlockDetailDialogComponent } from './block-detail-dialog.component';
|
||||
import { TransactionDetailDialogComponent } from './transaction-detail-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-blockchain-explorer-mini',
|
||||
@@ -25,6 +28,7 @@ import { BlockDto, BlockchainTransactionDto } from '../../../api/models';
|
||||
MatProgressSpinnerModule,
|
||||
MatTabsModule,
|
||||
MatChipsModule,
|
||||
MatDialogModule,
|
||||
RouterModule,
|
||||
],
|
||||
template: `
|
||||
@@ -240,18 +244,17 @@ import { BlockDto, BlockchainTransactionDto } from '../../../api/models';
|
||||
}
|
||||
}
|
||||
|
||||
.header-text {
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.header-text h3 {
|
||||
margin: 0;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 4px 0 0;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.header-text .subtitle {
|
||||
margin: 4px 0 0;
|
||||
font-size: 0.8rem;
|
||||
color: rgba(255, 255, 255, 0.9) !important;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
@@ -598,6 +601,7 @@ import { BlockDto, BlockchainTransactionDto } from '../../../api/models';
|
||||
export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
|
||||
private readonly api = inject(ApiService);
|
||||
private readonly clipboard = inject(Clipboard);
|
||||
private readonly dialog = inject(MatDialog);
|
||||
private refreshSubscription?: Subscription;
|
||||
|
||||
@Input() showViewAll = true;
|
||||
@@ -655,11 +659,12 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
|
||||
{ limit: 5 }
|
||||
).toPromise();
|
||||
|
||||
if (blocksResponse?.data) {
|
||||
if (blocksResponse?.data && blocksResponse.data.length > 0) {
|
||||
this.blocks.set(blocksResponse.data);
|
||||
if (blocksResponse.data.length > 0) {
|
||||
this.latestBlock.set(blocksResponse.data[0].blockNumber);
|
||||
}
|
||||
this.latestBlock.set(blocksResponse.data[0].blockNumber);
|
||||
} else {
|
||||
// No real blocks - use mock data for demo
|
||||
this.loadMockBlocks();
|
||||
}
|
||||
|
||||
// Fetch transactions
|
||||
@@ -687,18 +692,24 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private loadMockData(): void {
|
||||
// Mock blocks
|
||||
private loadMockBlocks(): void {
|
||||
const mockBlocks: BlockDto[] = Array.from({ length: 5 }, (_, i) => ({
|
||||
blockNumber: 12345 - i,
|
||||
blockNumber: 1628260 - i,
|
||||
hash: `0x${this.generateRandomHash()}`,
|
||||
parentHash: `0x${this.generateRandomHash()}`,
|
||||
timestamp: new Date(Date.now() - i * 15000).toISOString(),
|
||||
transactionCount: Math.floor(Math.random() * 20) + 1,
|
||||
gasUsed: Math.floor(Math.random() * 8000000),
|
||||
transactionCount: Math.floor(Math.random() * 5) + 1,
|
||||
gasUsed: Math.floor(Math.random() * 500000) + 100000,
|
||||
gasLimit: 15000000,
|
||||
size: Math.floor(Math.random() * 50000) + 10000,
|
||||
}));
|
||||
this.blocks.set(mockBlocks);
|
||||
this.latestBlock.set(mockBlocks[0].blockNumber);
|
||||
}
|
||||
|
||||
private loadMockData(): void {
|
||||
// Mock blocks
|
||||
this.loadMockBlocks();
|
||||
|
||||
const mockTx: BlockchainTransactionDto[] = [
|
||||
{
|
||||
@@ -746,9 +757,7 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
|
||||
},
|
||||
];
|
||||
|
||||
this.blocks.set(mockBlocks);
|
||||
this.transactions.set(mockTx);
|
||||
this.latestBlock.set(mockBlocks[0].blockNumber);
|
||||
this.totalTransactions.set(1234);
|
||||
this.pendingTransactions.set(3);
|
||||
this.networkStatus.set('HEALTHY');
|
||||
@@ -761,11 +770,12 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
truncateHash(hash: string): string {
|
||||
if (!hash || hash.length <= 18) return hash;
|
||||
if (!hash || hash.length <= 18) return hash || '';
|
||||
return `${hash.substring(0, 10)}...${hash.substring(hash.length - 6)}`;
|
||||
}
|
||||
|
||||
getRelativeTime(timestamp: string): string {
|
||||
if (!timestamp) return '';
|
||||
const now = new Date();
|
||||
const time = new Date(timestamp);
|
||||
const diffSeconds = Math.floor((now.getTime() - time.getTime()) / 1000);
|
||||
@@ -785,6 +795,7 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
getTxTypeIcon(type: string): string {
|
||||
if (!type) return 'receipt_long';
|
||||
const icons: Record<string, string> = {
|
||||
LICENSE_MINT: 'verified',
|
||||
DOCUMENT_HASH: 'fingerprint',
|
||||
@@ -796,6 +807,7 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
formatTxType(type: string): string {
|
||||
if (!type) return 'Unknown';
|
||||
return type.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
||||
}
|
||||
|
||||
@@ -805,12 +817,20 @@ export class BlockchainExplorerMiniComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
viewBlock(block: BlockDto): void {
|
||||
// Could open a dialog or navigate
|
||||
console.log('View block:', block);
|
||||
this.dialog.open(BlockDetailDialogComponent, {
|
||||
data: block,
|
||||
width: '600px',
|
||||
maxHeight: '90vh',
|
||||
panelClass: 'blockchain-detail-dialog',
|
||||
});
|
||||
}
|
||||
|
||||
viewTransaction(tx: BlockchainTransactionDto): void {
|
||||
// Could open a dialog or navigate
|
||||
console.log('View transaction:', tx);
|
||||
this.dialog.open(TransactionDetailDialogComponent, {
|
||||
data: tx,
|
||||
width: '600px',
|
||||
maxHeight: '90vh',
|
||||
panelClass: 'blockchain-detail-dialog',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user