feat: add Orcaslicer and docker
This commit is contained in:
1
frontend/.nvmrc
Normal file
1
frontend/.nvmrc
Normal file
@@ -0,0 +1 @@
|
||||
22
|
||||
14
frontend/Dockerfile
Normal file
14
frontend/Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
# Stage 1: Build
|
||||
FROM node:20 as build
|
||||
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build --configuration=production
|
||||
|
||||
# Stage 2: Serve
|
||||
FROM nginx:alpine
|
||||
COPY --from=build /app/dist/frontend/browser /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
9
frontend/nginx.conf
Normal file
9
frontend/nginx.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
2137
frontend/package-lock.json
generated
2137
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,24 +9,27 @@
|
||||
"test": "ng test"
|
||||
},
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": "^22.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/cdk": "^19.2.16",
|
||||
"@angular/common": "^19.2.0",
|
||||
"@angular/compiler": "^19.2.0",
|
||||
"@angular/core": "^19.2.0",
|
||||
"@angular/forms": "^19.2.0",
|
||||
"@angular/material": "^19.2.16",
|
||||
"@angular/platform-browser": "^19.2.0",
|
||||
"@angular/platform-browser-dynamic": "^19.2.0",
|
||||
"@angular/router": "^19.2.0",
|
||||
"@angular/cdk": "^19.2.19",
|
||||
"@angular/common": "^19.2.18",
|
||||
"@angular/compiler": "^19.2.18",
|
||||
"@angular/core": "^19.2.18",
|
||||
"@angular/forms": "^19.2.18",
|
||||
"@angular/material": "^19.2.19",
|
||||
"@angular/platform-browser": "^19.2.18",
|
||||
"@angular/platform-browser-dynamic": "^19.2.18",
|
||||
"@angular/router": "^19.2.18",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^19.2.12",
|
||||
"@angular/cli": "^19.2.12",
|
||||
"@angular/compiler-cli": "^19.2.0",
|
||||
"@angular-devkit/build-angular": "^19.2.19",
|
||||
"@angular/cli": "^19.2.19",
|
||||
"@angular/compiler-cli": "^19.2.18",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"jasmine-core": "~5.6.0",
|
||||
"karma": "~6.4.0",
|
||||
@@ -35,5 +38,8 @@
|
||||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.7.2"
|
||||
},
|
||||
"overrides": {
|
||||
"tar": "7.5.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,69 @@
|
||||
<mat-card>
|
||||
<mat-card-title>3D Print Cost Calculator</mat-card-title>
|
||||
<div class="calculator-container">
|
||||
<mat-card>
|
||||
<mat-card-header>
|
||||
<mat-card-title>3D Print Quote Calculator</mat-card-title>
|
||||
<mat-card-subtitle>Bambu Lab A1 Estimation</mat-card-subtitle>
|
||||
</mat-card-header>
|
||||
|
||||
<mat-card-content>
|
||||
<div class="upload-section">
|
||||
<input type="file" (change)="onFileSelected($event)" accept=".stl" #fileInput style="display: none;">
|
||||
<button mat-raised-button color="primary" (click)="fileInput.click()">
|
||||
{{ file ? file.name : 'Select STL File' }}
|
||||
</button>
|
||||
|
||||
<button mat-raised-button color="accent"
|
||||
[disabled]="!file || loading"
|
||||
(click)="uploadAndCalculate()">
|
||||
Calculate Quote
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
accept=".stl"
|
||||
(change)="onFileSelected($event)"
|
||||
/>
|
||||
<div *ngIf="loading" class="spinner-container">
|
||||
<mat-progress-spinner mode="indeterminate"></mat-progress-spinner>
|
||||
<p>Slicing model... this may take a minute...</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
(click)="uploadAndCalculate()"
|
||||
[disabled]="!file || loading"
|
||||
>
|
||||
{{ loading ? 'Calculating...' : 'Calculate' }}
|
||||
</button>
|
||||
<div *ngIf="error" class="error-message">
|
||||
{{ error }}
|
||||
</div>
|
||||
|
||||
<mat-progress-spinner
|
||||
*ngIf="loading"
|
||||
diameter="30"
|
||||
mode="indeterminate"
|
||||
></mat-progress-spinner>
|
||||
<div *ngIf="results" class="results-section">
|
||||
<div class="total-price">
|
||||
<h3>Total Estimate: € {{ results.cost.total | number:'1.2-2' }}</h3>
|
||||
</div>
|
||||
|
||||
<p *ngIf="error" class="error">{{ error }}</p>
|
||||
<div class="details-grid">
|
||||
<div class="detail-item">
|
||||
<span class="label">Print Time:</span>
|
||||
<span class="value">{{ results.print_time_formatted }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">Material Used:</span>
|
||||
<span class="value">{{ results.material_grams | number:'1.1-1' }} g</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="results">
|
||||
<p>Volume STL: {{ results.stl_volume_mm3 }} mm³</p>
|
||||
<p>Superficie: {{ results.surface_area_mm2 }} mm²</p>
|
||||
<p>Volume pareti: {{ results.wall_volume_mm3 }} mm³</p>
|
||||
<p>Volume infill: {{ results.infill_volume_mm3 }} mm³</p>
|
||||
<p>Volume stampa: {{ results.print_volume_mm3 }} mm³</p>
|
||||
<p>Peso stimato: {{ results.weight_g }} g</p>
|
||||
<p>Costo stimato: CHF {{ results.cost_chf }}</p>
|
||||
<p>Tempo stimato: {{ results.time_min }} min</p>
|
||||
</div>
|
||||
</mat-card>
|
||||
<h4>Cost Breakdown</h4>
|
||||
<ul class="breakdown-list">
|
||||
<li>
|
||||
<span>Material</span>
|
||||
<span>€ {{ results.cost.material | number:'1.2-2' }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>Machine Time</span>
|
||||
<span>€ {{ results.cost.machine | number:'1.2-2' }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>Energy</span>
|
||||
<span>€ {{ results.cost.energy | number:'1.2-2' }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>Service/Markup</span>
|
||||
<span>€ {{ results.cost.markup | number:'1.2-2' }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
@@ -4,10 +4,22 @@ import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
|
||||
interface QuoteResponse {
|
||||
printer: string;
|
||||
print_time_formatted: string;
|
||||
material_grams: number;
|
||||
cost: {
|
||||
material: number;
|
||||
machine: number;
|
||||
energy: number;
|
||||
markup: number;
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-calculator',
|
||||
standalone: true,
|
||||
@@ -15,7 +27,6 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
MatCardModule,
|
||||
MatFormFieldModule,
|
||||
MatButtonModule,
|
||||
MatProgressSpinnerModule
|
||||
],
|
||||
@@ -24,7 +35,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
})
|
||||
export class CalculatorComponent {
|
||||
file: File | null = null;
|
||||
results: any = null;
|
||||
results: QuoteResponse | null = null;
|
||||
error = '';
|
||||
loading = false;
|
||||
|
||||
@@ -41,22 +52,26 @@ export class CalculatorComponent {
|
||||
|
||||
uploadAndCalculate(): void {
|
||||
if (!this.file) {
|
||||
this.error = 'Seleziona un file STL prima di procedere.';
|
||||
this.error = 'Please select a file first.';
|
||||
return;
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append('file', this.file);
|
||||
this.loading = true;
|
||||
this.http.post<any>('http://localhost:8000/calculate/stl', formData)
|
||||
this.error = '';
|
||||
this.results = null;
|
||||
|
||||
this.http.post<QuoteResponse>('http://localhost:8000/calculate/stl', formData)
|
||||
.subscribe({
|
||||
next: res => {
|
||||
this.results = res;
|
||||
this.loading = false;
|
||||
},
|
||||
error: err => {
|
||||
this.error = err.error?.detail || err.message;
|
||||
console.error(err);
|
||||
this.error = err.error?.detail || "An error occurred during calculation.";
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user