|
|
@@ -14,6 +14,9 @@ import { ReactiveFormsModule, FormBuilder, Validators, FormGroup } from '@angula
|
|
|
import { HttpClient, HttpClientModule } from '@angular/common/http';
|
|
|
import { webConfig } from '../../config';
|
|
|
|
|
|
+import { Site, Phase, Block } from '../../interfaces/site.interface';
|
|
|
+import { SiteService } from '../../services/site-management.service';
|
|
|
+
|
|
|
@Component({
|
|
|
selector: 'app-bulk-create-ffb-dialog',
|
|
|
templateUrl: './bulk-create-ffb-dialog.component.html',
|
|
|
@@ -49,20 +52,19 @@ export class BulkCreateFfbDialogComponent {
|
|
|
logs: string[] = [];
|
|
|
currentStatus = 'Idle';
|
|
|
|
|
|
- allSites: string[];
|
|
|
- allPhases: string[];
|
|
|
- allBlocks: string[];
|
|
|
+ private siteService = inject(SiteService);
|
|
|
+
|
|
|
+ // Caches for hierarchical data
|
|
|
+ siteCache: Site[] = [];
|
|
|
+ phaseCache: Record<string, Phase[]> = {};
|
|
|
+ blockCache: Record<string, Block[]> = {};
|
|
|
|
|
|
constructor(private fb: FormBuilder, @Inject(MAT_DIALOG_DATA) public data: any) {
|
|
|
- this.allSites = data?.allSites || ['Site A', 'Site B', 'Site C', 'Site D'];
|
|
|
- this.allPhases = data?.allPhases || ['Phase 1', 'Phase 2', 'Phase 3'];
|
|
|
- this.allBlocks = data?.allBlocks || ['Block 1', 'Block 2', 'Block 3'];
|
|
|
+ // Pre-load sites on init
|
|
|
+ this.loadSites();
|
|
|
|
|
|
this.configForm = this.fb.group({
|
|
|
count: [10, [Validators.required, Validators.min(1)]],
|
|
|
- sites: [this.allSites.join(', '), Validators.required],
|
|
|
- phases: [this.allPhases.join(', '), Validators.required],
|
|
|
- blocks: [this.allBlocks.join(', '), Validators.required],
|
|
|
startDate: [new Date('2025-01-01'), Validators.required],
|
|
|
endDate: [new Date('2026-12-31'), Validators.required],
|
|
|
minWeight: [100, [Validators.required, Validators.min(1)]],
|
|
|
@@ -70,6 +72,13 @@ export class BulkCreateFfbDialogComponent {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ loadSites() {
|
|
|
+ this.siteService.getSites().subscribe({
|
|
|
+ next: (sites) => this.siteCache = sites,
|
|
|
+ error: (err) => console.error('Failed to load sites', err)
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
async proceed() {
|
|
|
if (this.configForm.invalid) return;
|
|
|
|
|
|
@@ -81,10 +90,6 @@ export class BulkCreateFfbDialogComponent {
|
|
|
const config = this.configForm.value;
|
|
|
this.totalCount = config.count;
|
|
|
|
|
|
- const sites = config.sites.split(',').map((s: string) => s.trim()).filter((s: string) => s);
|
|
|
- const phases = config.phases.split(',').map((s: string) => s.trim()).filter((s: string) => s);
|
|
|
- const blocks = config.blocks.split(',').map((s: string) => s.trim()).filter((s: string) => s);
|
|
|
-
|
|
|
const start = new Date(config.startDate).getTime();
|
|
|
const end = new Date(config.endDate).getTime();
|
|
|
|
|
|
@@ -98,22 +103,54 @@ export class BulkCreateFfbDialogComponent {
|
|
|
this.progress = ((i) / this.totalCount) * 100;
|
|
|
|
|
|
try {
|
|
|
- // 1. Randomize Data
|
|
|
- const site = this.getRandomItem(sites);
|
|
|
+ // 1. Randomize Data (Hierarchical)
|
|
|
+ if (this.siteCache.length === 0) {
|
|
|
+ throw new Error('No sites available.');
|
|
|
+ }
|
|
|
+
|
|
|
+ const site = this.getRandomItem(this.siteCache);
|
|
|
+
|
|
|
+ // Fetch/Cache Phases
|
|
|
+ let phases = this.phaseCache[site._id!];
|
|
|
+ if (!phases) {
|
|
|
+ phases = await this.siteService.getPhasesBySite(site._id!).toPromise() || [];
|
|
|
+ this.phaseCache[site._id!] = phases;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (phases.length === 0) {
|
|
|
+ this.log(`[${i + 1}] Skip: No phases for site ${site.name}`);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
const phase = this.getRandomItem(phases);
|
|
|
+
|
|
|
+ // Fetch/Cache Blocks
|
|
|
+ let blocks = this.blockCache[phase._id!];
|
|
|
+ if (!blocks) {
|
|
|
+ blocks = await this.siteService.getBlocksByPhase(phase._id!).toPromise() || [];
|
|
|
+ this.blockCache[phase._id!] = blocks;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (blocks.length === 0) {
|
|
|
+ this.log(`[${i + 1}] Skip: No blocks for phase ${phase.name}`);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
const block = this.getRandomItem(blocks);
|
|
|
+
|
|
|
const date = new Date(start + Math.random() * (end - start));
|
|
|
const weight = Math.floor(Math.random() * (config.maxWeight - config.minWeight + 1)) + config.minWeight;
|
|
|
const quantity = Math.floor(weight / 10); // 1 bundle = 10kg assumption
|
|
|
|
|
|
// 2. Generate Remark
|
|
|
- this.log(`[${i + 1}] Generating remark for ${site}...`);
|
|
|
+ this.log(`[${i + 1}] Generating remark for ${site.name}...`);
|
|
|
const context = {
|
|
|
- site, phase, block, date, quantity, quantityUom: 'Bundle', weight, weightUom: 'Kg'
|
|
|
+ siteId: site._id, phaseId: phase._id, blockId: block._id, // Send IDs for enrichment
|
|
|
+ site: site.name, phase: phase.name, block: block.name,
|
|
|
+ date, quantity, quantityUom: 'Bundle', weight, weightUom: 'Kg'
|
|
|
};
|
|
|
|
|
|
// We await these calls to ensure sequential processing as requested ("loop until finished")
|
|
|
- // Note: In a real app we might want to run some in parallel, but requirement implies sequential/controlled loop
|
|
|
const remarkRes = await this.http.post<{ remark: string }>(`${webConfig.exposedUrl}/api/ffb-production/generate-remark`, context).toPromise();
|
|
|
const remark = remarkRes?.remark || 'No remark generated';
|
|
|
|
|
|
@@ -121,7 +158,9 @@ export class BulkCreateFfbDialogComponent {
|
|
|
this.log(`[${i + 1}] Persisting record...`);
|
|
|
const payload = {
|
|
|
productionDate: date.toISOString(),
|
|
|
- site, phase, block,
|
|
|
+ site: { id: site._id, name: site.name },
|
|
|
+ phase: { id: phase._id, name: phase.name },
|
|
|
+ block: { id: block._id, name: block.name },
|
|
|
quantity, quantityUom: 'Bundle',
|
|
|
weight, weightUom: 'Kg',
|
|
|
remarks: remark
|
|
|
@@ -152,7 +191,7 @@ export class BulkCreateFfbDialogComponent {
|
|
|
this.dialogRef.close('refresh');
|
|
|
}
|
|
|
|
|
|
- private getRandomItem(arr: string[]): string {
|
|
|
+ private getRandomItem(arr: any[]): any {
|
|
|
return arr[Math.floor(Math.random() * arr.length)];
|
|
|
}
|
|
|
|