Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BOM import #273

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -539,3 +539,9 @@ $RECYCLE.BIN/
Binner/BinnerInstaller/BinnerSetup.exe
Binner/Binner.Web/ClientApp/.vscode/settings.json
*.bak

# Slickedit
*.vpj
*.vtg
*.vpw
*.vpwhist
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@
"boms": {
"header": {
"description": "Die Stückliste ermöglicht es Ihnen, den Lagerbestand pro Projekt zu verwalten. Sie können die Mengen für jede hergestellte Leiterplatte reduzieren, und überprüfen welche Teile Sie in höherer Stückzahl kaufen müssen um die Kosten zu analysieren.<br/><br/>Wählen Sie das Projekt aus, für das Sie die Stückliste (BOM) verwalten möchten, oder erstellen Sie ein neues Projekt.<br/>"
},
"acceptedFileTypes": "Accepted file types: \"*.xls, *.xlsx, *.csv\""
replaysMike marked this conversation as resolved.
Show resolved Hide resolved
"import": {
"success": "BOM Imported!",
"failure": "Failed to import BOM!"
}
},
"partTypes": {
Expand Down Expand Up @@ -636,6 +641,7 @@
},
"button": {
"addBomProject": "Stücklistenprojekt hinzufügen",
"importBomProject": "Import BOM Project",
"addPart": "Bauteil hinzufügen",
"save": "Speichern",
"download": "Download",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@
"boms": {
"header": {
"description": "Bill of Materials, or BOM allows you to manage inventory quantities per project. You can reduce quantities for each PCB you produce, check which parts you need to buy more of and analyze costs.<br/><br/>Choose or create the project to manage BOM for.<br/>"
},
"acceptedFileTypes": "Accepted file types: \"*.xls, *.xlsx, *.csv\""
"import": {
"success": "BOM Imported!",
"failure": "Failed to import BOM!"
}
},
"partTypes": {
Expand Down Expand Up @@ -638,6 +643,7 @@
},
"button": {
"addBomProject": "Add BOM Project",
"importBomProject": "Import BOM Project",
"addPart": "Add Part",
"save": "Save",
"download": "Download",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@
"boms": {
"header": {
"description": "Bill of Materials, or BOM allows you to manage inventory quantities per project. You can reduce quantities for each PCB you produce, check which parts you need to buy more of and analyze costs.<br/><br/>Choose or create the project to manage BOM for.<br/>"
},
"acceptedFileTypes": "Accepted file types: \"*.xls, *.xlsx, *.csv\""
"import": {
"success": "BOM Imported!",
"failure": "Failed to import BOM!"
}
},
"partTypes": {
Expand Down Expand Up @@ -457,6 +462,7 @@
},
"button": {
"addBomProject": "Add BOM Project",
"importBomProject": "Import BOM Project",
"addPart": "Add Part",
"save": "Save",
"download": "Download",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@
"boms": {
"header": {
"description": "La nomenclature, ou BOM, vous permet de gérer les quantités d'inventaire par projet. Vous pouvez réduire les quantités pour chaque PCB que vous produisez, vérifier quelles pièces vous devez acheter en plus et analyser les coûts.<br/><br/>Choisissez ou créez le projet pour lequel vous souhaitez gérer la nomenclature.<br/>"
},
"acceptedFileTypes": "Accepted file types: \"*.xls, *.xlsx, *.csv\""
"import": {
"success": "BOM Imported!",
"failure": "Failed to import BOM!"
}
},
"partTypes": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@
"boms": {
"header": {
"description": "La Distinta Base, (DBA), consente di gestire le quantità dei componenti per progetto. Si può ridurre le quantità di ogni PCB da produrre, verificare quali componenti occorre acquistare ancora e analizzare i costi.<br/><br/>Scegliere o creare il progetto di cui gestire la DBA.<br/>"
},
"acceptedFileTypes": "Accepted file types: \"*.xls, *.xlsx, *.csv\""
"import": {
"success": "BOM Imported!",
"failure": "Failed to import BOM!"
}
},
"partTypes": {
Expand Down Expand Up @@ -635,6 +640,7 @@
},
"button": {
"addBomProject": "Aggiungi un Progetto DBA",
"importBomProject": "Import BOM Project",
"addPart": "Aggiungi componente",
"save": "Salva",
"download": "Download",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
"boms": {
"header": {
"description": "你可以使用物料清单工具,按照项目来管理零部件。你可以直接按照PCB板的套数扣除对应零件数量,或者检查还需要购买哪些零件以及分析成本。<br/><br/>请选择或创建需要管理BOM的项目。<br/>"
},
"acceptedFileTypes": "Accepted file types: \"*.xls, *.xlsx, *.csv\""
"import": {
"success": "BOM Imported!",
"failure": "Failed to import BOM!"
}
},
"partTypes": {
Expand Down Expand Up @@ -468,6 +473,7 @@
},
"button": {
"addBomProject": "创建BOM工程文件",
"importBomProject": "Import BOM Project",
"addPart": "添加部件",
"save": "保存",
"download": "下载",
Expand Down
90 changes: 90 additions & 0 deletions Binner/Binner.Web/ClientApp/src/pages/Boms.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import _ from 'underscore';
import { toast } from "react-toastify";
import { fetchApi } from '../common/fetchApi';
import { ProjectColors } from "../common/Types";
import { useDropzone } from "react-dropzone";
import { humanFileSize } from "../common/files";
import axios from "axios";
import { getAuthToken } from "../common/authentication";

export function Boms (props) {
const { t } = useTranslation();
Expand All @@ -22,6 +26,7 @@ export function Boms (props) {
const [pageSize, setPageSize] = useState(parseInt(localStorage.getItem("bomsRecordsPerPage")) || 5);
const [loading, setLoading] = useState(true);
const [addVisible, setAddVisible] = useState(false);
const [importVisible, setImportVisible] = useState(false);
const [project, setProject] = useState(defaultProject);
const [projects, setProjects] = useState([]);
const [changeTracker, setChangeTracker] = useState([]);
Expand All @@ -32,6 +37,7 @@ export function Boms (props) {
const [confirmDeleteIsOpen, setConfirmDeleteIsOpen] = useState(false);
const [confirmPartDeleteContent, setConfirmProjectDeleteContent] = useState(null);
const [confirmDeleteSelectedProject, setConfirmDeleteSelectedProject] = useState(null);
const [acceptedFile, setAcceptedFile] = useState(null);

const [colors] = useState(_.map(ProjectColors, function (c) {
return {
Expand All @@ -50,6 +56,26 @@ export function Boms (props) {
{ key: 5, text: '100', value: 100 },
];

const { acceptedFiles, getRootProps, getInputProps } = useDropzone({
maxFiles: 1,
onDrop: (acceptedFiles, rejectedFiles, e) => {
// do accept manually
const acceptedMimeTypes = ["application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "text/csv"];
let errorMsg = "";
for (let i = 0; i < acceptedFiles.length; i++) {
if (!acceptedMimeTypes.includes(acceptedFiles[i].type)) {
errorMsg += `${t('page.exportData.fileNotSupported', "File '{{name}}' with mime type '{{type}}' is not an accepted image type!", { name: acceptedFiles[i].name, type: acceptedFiles[i].type})}\r\n`;
}
}
if (errorMsg.length > 0) {
setAcceptedFile(null);
toast.error(errorMsg);
} else {
setAcceptedFile(acceptedFiles[0]);
}
}
});

const loadProjects = async (page, pageSize, reset = false) => {
setLoading(true);
let endOfData = false;
Expand Down Expand Up @@ -115,6 +141,10 @@ export function Boms (props) {
setAddVisible(!addVisible);
};

const handleShowImport = () => {
setImportVisible(!importVisible);
};

const handleLoadBom = (e, p) => {
e.preventDefault();
e.stopPropagation();
Expand All @@ -139,10 +169,42 @@ export function Boms (props) {
// reset form
setProject(defaultProject);
setAddVisible(false);
setImportVisible(false);
loadProjects(page, pageSize, true);
}
};

const onImportProject = async () => {
if (acceptedFile != null) {
const formData = new FormData();
formData.set("name", project.name);
formData.set("description", project.description);
formData.set("file", acceptedFile, acceptedFile.name);

fetchApi("api/authentication/identity").then((_) => {
axios
.request({
method: "post",
url: `api/project/import`,
data: formData,
headers: { Authorization: `Bearer ${getAuthToken()}` }
})
.then((response) => {
toast.dismiss();
if (response.status === 200) {
toast.success(t("page.boms.import.success", "BOM Imported!"));
setProject(defaultProject);
setAddVisible(false);
setImportVisible(false);
loadProjects(page, pageSize, true);
} else {
toast.error(t("page.boms.import.failure", `Failed to import BOM!`), { autoClose: 10000 });
}
});
});
}
};

const handleSort = (clickedColumn) => () => {
if (column !== clickedColumn) {
setColumn(clickedColumn);
Expand Down Expand Up @@ -281,6 +343,7 @@ export function Boms (props) {

<div style={{ minHeight: '35px' }}>
<Button primary onClick={handleShowAdd} icon size='mini' floated='right'><Icon name='plus' /> <Trans i18nKey="button.addBomProject">Add BOM Project</Trans></Button>
<Button primary onClick={handleShowImport} icon size='mini' floated='right'><Icon name='plus' /> <Trans i18nKey="button.importBomProject">Import BOM Project</Trans></Button>
</div>
<div>
{addVisible &&
Expand All @@ -297,6 +360,33 @@ export function Boms (props) {
</Segment>
}
</div>
<div>
{importVisible &&
<Segment style={{marginBottom: '10px'}}>
<Form onSubmit={onImportProject}>
<Form.Input width={6} label={t('label.name', 'Name')} required placeholder='555 Timer Project' focus value={project.name} onChange={handleChange} name='name' />
<Form.Field width={10} control={TextArea} label={t('label.description', 'Description')} value={project.description} onChange={handleChange} name='description' style={{height: '60px'}} />
<div
style={{ border: "1px dashed #000", padding: "50px", marginBottom: "20px", backgroundColor: "#f5f5f5" }}
{...getRootProps({ className: "dropzone" })}
>
<span style={{ fontSize: "0.6em" }}>{t('page.exportData.uploadNote', "Drag a document to upload, or click to select files")}</span>
<input {...getInputProps()} />
<div style={{ fontSize: "0.6em" }}>{t('page.boms.acceptedFileTypes', "Accepted file types: \"*.xls, *.xlsx, *.csv\"")}</div>
</div>
<aside>
<ol>
{acceptedFile &&
<li key={acceptedFile.path}>
{acceptedFile.path} - {humanFileSize(acceptedFile.size)}
</li>}
</ol>
</aside>
<Button primary disabled={!acceptedFile}>{t('button.import', "Import")}</Button>
</Form>
</Segment>
}
</div>
<Segment style={{marginTop: '0'}}>
<div style={{float: 'right', verticalAlign: 'middle', fontSize: '0.9em'}}>
<Dropdown
Expand Down
14 changes: 14 additions & 0 deletions Binner/Binner.Web/Controllers/ProjectController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Binner.Model.Requests;
using Binner.Model.Responses;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -87,6 +88,19 @@ public async Task<IActionResult> CreateProjectAsync(CreateProjectRequest request
return Ok(project);
}

/// <summary>
/// Import a new project
/// </summary>
/// <param name="import"></param>
/// <returns></returns>
[HttpPost("import")]
[Consumes("multipart/form-data")]
public async Task<IActionResult> ImportProjectAsync([FromForm] ImportProjectRequest request)
{
var project = await _projectService.ImportProjectAsync(request);
return Ok(project);
}

/// <summary>
/// Update an existing project
/// </summary>
Expand Down
Loading