diff --git a/main.js b/main.js index 8915834..39d7562 100644 --- a/main.js +++ b/main.js @@ -30,8 +30,8 @@ if (process.platform === 'win32') { // Functions -async function handleFileOpen() { - const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {properties: ['openDirectory']}) + async function handleFileOpen() { + const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {properties: ['openDirectory']}) if (canceled) { return } else { diff --git a/package.json b/package.json index 6d7f786..f3e0ec7 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "start": "cross-env NODE_ENV=development webpack serve --hot --host 0.0.0.0 --config=./webpack.dev.config.js --mode development", "build": "cross-env NODE_ENV=production webpack --config webpack.build.config.js --mode production", "package": "npm run build", - "postpackage": "electron-packager ./ --out=./builds" + "postpackage": "electron-packager ./ --out=./builds --overwrite" }, "dependencies": { "@emotion/react": "^11.10.0", @@ -49,7 +49,9 @@ "react": "^18.2.0", "react-async-devtools": "^10.0.1", "react-dom": "^18.2.0", + "react-loader-spinner": "^5.3.3", "react-router-dom": "^6.3.0", + "replace-special-characters": "^1.2.6", "write-json-file": "^5.0.0", "xml-parse-from-string": "^1.0.1", "xml2js": "^0.4.23" @@ -58,6 +60,7 @@ "@babel/core": "^7.18.10", "@babel/preset-env": "^7.18.10", "@babel/preset-react": "^7.18.6", + "@electron-forge/plugin-webpack": "^6.0.0-beta.65", "babel-loader": "^8.2.5", "cross-env": "^7.0.3", "css-loader": "^6.7.1", diff --git a/resources/config/config.json b/resources/config/config.json new file mode 100644 index 0000000..b0b532a --- /dev/null +++ b/resources/config/config.json @@ -0,0 +1,3 @@ +{ + "rootDir": "/home/rbrt/Schreibtisch/marvin" +} \ No newline at end of file diff --git a/src/assets/templates/lbb-template.docx b/resources/templates/lbb-template.docx similarity index 100% rename from src/assets/templates/lbb-template.docx rename to resources/templates/lbb-template.docx diff --git a/src/assets/templates/rp-template.docx b/resources/templates/rp-template.docx similarity index 100% rename from src/assets/templates/rp-template.docx rename to resources/templates/rp-template.docx diff --git a/src/DocxInserter.js b/src/DocxInserter.js index a86b0dd..3b2a505 100644 --- a/src/DocxInserter.js +++ b/src/DocxInserter.js @@ -1,11 +1,12 @@ // Config -import config from './config/config.json' +import config from '/resources/config/config.json' // Modules import createReport from 'docx-templates'; import fs from 'fs'; import { mkdir } from 'node:fs/promises'; import path from 'path'; +import replaceSpecialCharacters from "replace-special-characters"; // Components import ObjektkatalogApi from './ObjektkatalogApi'; @@ -20,7 +21,7 @@ export async function fillTemplate(log, objectData) { if (objectData.httpStatus === 200) { try { // Read template. - const template = fs.readFileSync('src/assets/templates/rp-template.docx'); + const template = fs.readFileSync('resources/templates/rp-template.docx'); // Create report. buffer = await createReport({ template, @@ -57,8 +58,10 @@ export async function fillTemplate(log, objectData) { // Write document to disk. if (buffer) { - console.log(objectData) - fs.writeFileSync(path.join(folderPath, objectData.titel), buffer) + const normCharacterTitle = replaceSpecialCharacters(objectData.titel) + const normSpacingTitle = normCharacterTitle.replace(/[^A-Z0-9]+/ig, "_"); + const filename = objectData.datum + '_' + normSpacingTitle + '.docx' + fs.writeFileSync(path.join(folderPath, filename), buffer) log = { ...log, status: 'green', diff --git a/src/ObjektkatalogApi.js b/src/ObjektkatalogApi.js index 98beea7..041f348 100644 --- a/src/ObjektkatalogApi.js +++ b/src/ObjektkatalogApi.js @@ -5,8 +5,21 @@ const {parseString} = require("xml2js"); */ class ObjektkatalogApi { json; - short; raw; + receivedData = { + datum: '', + hersteller: '', + herstellungsdatum: '', + herstellungsort: '', + httpStatus: 500, + inventarnummer: '', + masse: '', + materialTechnik: '', + titel: '', + }; + + visibility; + async getData(objectId) { if (objectId) { const response = await fetch('https://objektkatalog.gnm.de/rest_export/' + objectId); @@ -41,15 +54,38 @@ class ObjektkatalogApi { zustandsbeschreibung: responseJson[0]['f54b6da6006ea444c33a348b8c4370a8'] } } else { - return {httpStatus: 404}; + // No JSON + this.receivedData = { + ...this.receivedData, + httpStatus: 404, + } + this.visibility = false + return [this.receivedData, this.visibility] } } else { - return {httpStatus: response.status}; + // No response + this.receivedData= { + ...this.receivedData, + httpStatus: 404, + } + this.visibility = false + return [this.receivedData, this.visibility] } } else { - return {httpStatus: 400} + // No ObjectId + this.receivedData = { + ...this.receivedData, + httpStatus: 500, + } + this.visibility = false + return [this.receivedData, this.visibility] } + // Datum + // Default date of today + let today = new Date() + let todayFormat = today.getFullYear() + '-' + (String(today.getMonth() + 1).padStart(2, '0')) + '-' + String(today.getDate()).padStart(2, '0'); ; + // Inventarnummer let inventarnummer; if (this.raw.inventarnummer.length !== 0) { @@ -113,19 +149,22 @@ class ObjektkatalogApi { }) } - return this.short = { - inventarnummer: inventarnummer, - titel: titel, - hersteller: hersteller, - herstellungsort: herstellungsort, - herstellungsdatum: herstellungsdatum, - materialTechnik: materialTechnik, - masse: masse, - httpStatus: 200, - } - - + this.receivedData ={ + ...this.receivedData, + datum: todayFormat, + titel: titel, + hersteller: hersteller, + herstellungsort: herstellungsort, + herstellungsdatum: herstellungsdatum, + inventarnummer: inventarnummer, + materialTechnik: materialTechnik, + masse: masse, + httpStatus: 200, + } + this.visibility = true + return [this.receivedData, this.visibility] } } + module.exports = ObjektkatalogApi; diff --git a/src/actions/getData.js b/src/actions/getData.js deleted file mode 100644 index 5d04706..0000000 --- a/src/actions/getData.js +++ /dev/null @@ -1,7 +0,0 @@ -import GnmObject from "../ObjektkatalogApi"; - -/* - * - */ - - diff --git a/src/assets/css/styles.css b/src/assets/css/styles.css index 820c2de..b1a380a 100644 --- a/src/assets/css/styles.css +++ b/src/assets/css/styles.css @@ -1,4 +1,4 @@ -@import url('https://fonts.googleapis.com/css?family=Roboto'); +@import url('https://fonts.googleapis.com/css?family=Open+Sans'); a { color: #d5d5d5; @@ -9,10 +9,10 @@ a { } body { - background-color: #00152E ; + background-color: white ; color: #d5d5d5; - font-family: 'Roboto', regular, serif; - font-size: larger; + font-family: 'Open Sans', sans-serif; + font-size: 1rem; } @@ -26,7 +26,8 @@ body { } .closed{ - height: 0; + height: 0px; + overflow: hidden; position: relative; } @@ -47,13 +48,13 @@ div { } .edit-button { - background-color: #d5d5d5; - font-family: roboto, sans-serif; + background-color: #e30f27; + color: #d5d5d5; + font-family: "Open Sans", sans-serif; font-size: 1rem; - border-radius: 0 0.4rem 0.4rem 0; border: unset; padding: 0 4px; - height: 1.1rem; + height: 1.5rem; } .edit-input { @@ -64,6 +65,12 @@ div { display: flex; } +.flex-center { + display: flex; + justify-content: center; + align-items: center; +} + .flex-full { flex: 1 } @@ -88,21 +95,23 @@ input { border-top: none; border-right: none; border-left: none; - border-bottom: 2px solid darkred; + border-bottom: 2px solid #e30f27; border-radius: 3px; } input[type="date"]::-webkit-calendar-picker-indicator { - color: #d5d5d5; + color: black; width: 20px; height: 20px; border-width: thin; - filter: invert(1); } + + input:focus { background-color: #d5d5d5; color: #00152E; + font-size: .9rem; } input.edit-input:not(:disabled) { @@ -112,9 +121,9 @@ input.edit-input:not(:disabled) { .input-field { - background-color: #00152E ; - color: #d5d5d5; - font-size: medium; + background-color: #f2f2f2 ; + color: black; + font-size: .9rem; font-family: roboto, sans-serif; } @@ -130,19 +139,34 @@ input.edit-input:not(:disabled) { label { padding-right: 5px; + color: #464646; +} +.loading-icon-canvas { + position: absolute; + height: 98%; + width: 98%; + left: 0%; + top: 0%; + background-color: #555555; + opacity: .5; } +.loading-icon .log { font-size: 0.75em; height: 5em; } + +.nav-icon svg { + fill: black; +} .no-overflow { overflow: hidden; height: inherit; } .open { - height: 700px; + height: 500px; } p { margin: 0; @@ -157,7 +181,8 @@ p { } .red { - background-color: crimson; + background-color: #e30f27; + color: white; } .round-box { @@ -172,12 +197,12 @@ select { } .send-button { - font-family: roboto, sans-serif; + background-color: #e30f27; + color: white; + font-family: "Open Sans", sans-serif; font-size: 1rem; - border-radius: 0.4rem; border: unset; - background-color: darkred; - color: #d5d5d5; + } .scroll-y { @@ -191,6 +216,18 @@ select { font-size: .85rem; } +.settings-div { + background-color: #d5d5d5; + color: black; + width: 300px; + border-top: none; + border-right: none; + border-left: none; + border-bottom: 0.125rem solid #e30019; + border-radius: 0.1875rem; + overflow-x: scroll; + white-space:nowrap; +} .sticky-edit { display: flex; @@ -198,7 +235,7 @@ select { } textarea, textarea:focus, input:focus{ outline: none; - font-size: medium; + font-size: .9rem; ; } @@ -215,6 +252,32 @@ textarea.select-folder-field, textarea.select-folder-field:focus, input.select-f margin-bottom: 0.25em; } +/* width */ +::-webkit-scrollbar { + width: 10px; + height: 5px; +} + +/* Track */ +::-webkit-scrollbar-track { + background: #f1f1f1; + +} + +/* Handle */ +::-webkit-scrollbar-thumb { + background: #888; + width: 10px; +} + +/* Handle on hover */ +::-webkit-scrollbar-thumb:hover { + background: #555; +} + + .yellow{ background-color: goldenrod; } + + diff --git a/src/components/App.js b/src/components/App.js index 5b0b2b5..7597b7c 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -3,13 +3,16 @@ import {DataForm} from "./DataForm"; import {Log} from "./Log" import SettingsIcon from '@mui/icons-material/Settings'; +// Modules +import ObjektkatalogApi from "../ObjektkatalogApi"; + // React -import { Link } from "react-router-dom"; +import {NavLink} from "react-router-dom"; import React, {useState} from 'react' // CSS import '../assets/css/styles.css' -import ObjektkatalogApi from "../ObjektkatalogApi"; +import {LoadingIcon} from "./LoadingIcon"; //////////////////// @@ -18,34 +21,34 @@ import ObjektkatalogApi from "../ObjektkatalogApi"; export const App = () => { + // Variables + const objektkatalogApi = new ObjektkatalogApi(); // States - /* Initialize state of the log. - * log: variables fpr logging message. - * logClass: for visibility of log
. - */ - const [logState, setLogState] = useState({ - log: { - status: '', - message: '', - code: '', - tip:'', - }, // with message, code, tip - logClass: 'inactive' - }); + const [isLoading, setIsLoading] = useState(false); + + const [logState, setLogState] = useState({ + log: { + status: '', + message: '', + code: '', + tip: '', + }, // with message, code, tip + logClass: 'inactive' + }); const [objectData, setObjectData] = useState( { datum: '', - inventarnummer: '', - titel: '', hersteller: '', - herstellungsort: '', herstellungsdatum: '', - materialTechnik: '', - masse: '', + herstellungsort: '', httpStatus: 500, + inventarnummer: '', + masse: '', + materialTechnik: '', + titel: '', } ); @@ -53,91 +56,115 @@ export const App = () => { false ); - // Handler - async function getDataAndAskForCheckUpClickHandler(e) { - // Prevent page reload. - e.preventDefault(); + // Handler + const getDataAndAskForCheckUpClickHandler = async (e) => { + setIsLoading(true) + // Prevent page reload. + e.preventDefault() + let objectId = '' + objectId = e.target.form.objectId.value + // Get objectId from user input. - // Get objectId from user input. - let objectId = document.getElementById('object-id').value - - if (objectId) { - - // Get object data from objektkatalog.gnm.de - let objektkatalogApi = new ObjektkatalogApi(); - let receivedObjectData = await objektkatalogApi.getData(objectId); - if (receivedObjectData.httpStatus === 404) { - setLogState({ - log: { - status: 'red', - message: 'Kein Objekt mit dieser Inventarnummer gefunden!', - code: '', - tip:'', - }, // with message, code, tip - logClass: 'active' - }) - } else { - console.log(receivedObjectData) - // Fill and open check up form - setObjectData(receivedObjectData) - setCheckUpVisibility(true) + await setObjectData((prevState) => { + return { + ...prevState, + inventarnummer: objectId, } - } else { - setLogState({ + } + ) + + if (objectId) { + // Get object data from objektkatalog.gnm.de + const [receivedObjectData, receivedVisibility] = await objektkatalogApi.getData(objectId); + if (receivedObjectData.httpStatus !== 200) { + await setLogState({ log: { status: 'red', - message: 'Bitte Inventarnummer eingeben!', + message: 'Kein Objekt mit dieser Inventarnummer gefunden!', code: '', - tip:'', + tip: '', }, // with message, code, tip logClass: 'active' }) + await setCheckUpVisibility(false) + } else { + // Fill and open check up form + await setObjectData(receivedObjectData) + await setCheckUpVisibility(true) + await setLogState({ + log: { + status: '', + message: '', + code: '', + tip: '', + }, // with message, code, tip + logClass: 'inactive' + }) } + } else { + await setLogState({ + log: { + status: 'red', + message: 'Bitte Inventarnummer eingeben!', + code: '', + tip: '', + }, // with message, code, tip + logClass: 'active' + }) + await setCheckUpVisibility(false) } + setIsLoading(false) + } - return ( -
-
- -
-
-
-
-
- - -
-
- - -
+ return ( +
+
+ +
+
+
+ +
+ + +
+
+ + +
- + - -
- -
-
- {/* We give state and state setter of the parent as params to the child components, so that child events can change parent states */} - -
-
- ) + +
+ + +
+
+ {/* We give state and state setter of the parent as params to the child components, so that child events can change parent states */} + +
+
+ ) } export default App diff --git a/src/components/DataForm.js b/src/components/DataForm.js index f786868..8bb1821 100644 --- a/src/components/DataForm.js +++ b/src/components/DataForm.js @@ -8,13 +8,11 @@ import React from 'react' // Main // //////////////////// -export const DataForm = ({className, objectData, setObjectData, logState, setLogState}) => { +export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState, setLogState}) => { let log; // Handlers - const datumChangeHandler = (e) => { - console.log(e.target.form.datum.value) setObjectData((prevState) => { return { ...prevState, @@ -88,7 +86,6 @@ export const DataForm = ({className, objectData, setObjectData, logState, setLog const fillTemplateClickHandler = async (e) => { e.preventDefault(); - console.log(objectData, 'parentobjectData') log = await fillTemplate(logState, objectData); // Set new state of log div with message and visibility class setLogState({ @@ -97,12 +94,8 @@ export const DataForm = ({className, objectData, setObjectData, logState, setLog }); } - // Default date of today - let today = new Date() - let todayFormat = today.getFullYear() + '-' + (String(today.getMonth() + 1).padStart(2, '0')) + '-' + String(today.getDate()).padStart(2, '0'); ; - - return (
-
+ return (
+
-
+
{ + return ( + {config.rootDir} ) +} diff --git a/src/components/LoadingIcon.js b/src/components/LoadingIcon.js new file mode 100644 index 0000000..3904250 --- /dev/null +++ b/src/components/LoadingIcon.js @@ -0,0 +1,22 @@ +import {ThreeDots} from "react-loader-spinner"; +import React from "react"; + +export const LoadingIcon = ({isLoading}) => { + const classes = "loading-icon-canvas flex-center " + (isLoading ? 'active' : 'inactive') + return ( +
+
+ +
+
+ ) +} diff --git a/src/components/Log.js b/src/components/Log.js index 94045d0..192704d 100644 --- a/src/components/Log.js +++ b/src/components/Log.js @@ -9,7 +9,7 @@ export const Log = ({logState, setLogState}) => { let code; let tip; - const closeLog = (e, logClass) => { + const closeLog = (e) => { e.preventDefault() setLogState({log: logState.log, logClass: 'inactive'}); @@ -30,7 +30,7 @@ export const Log = ({logState, setLogState}) => {
- +

{logState.log.message}

diff --git a/src/config/config.json b/src/config/config.json deleted file mode 100644 index f62e2f6..0000000 --- a/src/config/config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rootDir": "/home/rbrt/Dokumente/marvin" -} \ No newline at end of file diff --git a/src/routes/settings.js b/src/routes/settings.js index f101a63..578df00 100644 --- a/src/routes/settings.js +++ b/src/routes/settings.js @@ -1,15 +1,18 @@ -import config from '../config/config.json' +// Config +import configImport from '/resources/config/config.json' + +// Components +import {Log} from "../components/Log"; // Icons import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; -import EditIcon from '@mui/icons-material/Edit'; - -// Modules -const {writeFile} = require('fs'); // React import React, {useState} from "react"; -import {Link} from "react-router-dom"; +import {NavLink} from "react-router-dom"; +import {Input} from "../components/Input"; +import EditIcon from "@mui/icons-material/Edit"; +import {writeFile} from "fs"; //////////////////// @@ -17,54 +20,62 @@ import {Link} from "react-router-dom"; //////////////////// export default function Settings() { - let rootDir; - const [disableButton, setDisableButton] = useState(true) + // States + const [config, setConfig] = useState( + configImport + ) - async function selectFolderHandler() { - return rootDir = await window.openFile(); + const [restart, setRestart] = useState( + false + ) + //Functions + async function selectFolderHandler(e) { + e.preventDefault() + const rootDirFromWindow = await window.openFile(); + setConfig((prevState) => { + return { + ...prevState, + rootDir: rootDirFromWindow + } + }) + const saveResult = await saveInputClickHandler(rootDirFromWindow) } - - // Handler - function saveInputClickHandler(rootDir) { - if (rootDir) { - config.rootDir = rootDir - writeFile('src/config/config.json', JSON.stringify(config, null, 2), (error) => { - if (error) { - console.log('An error has occurred ', error); - return; + async function saveInputClickHandler(rootDirFromWindow) { + let config2Safe = { + rootDir: rootDirFromWindow + } + if (rootDirFromWindow) { + let message; + await writeFile('resources/config/config.json', JSON.stringify(config2Safe, null, 2), (err) => { + if (err) { + message = err; + } else { + message = 'saved file' } - console.log('Data written successfully to disk'); - }); + return message + }) + setRestart(true) } } + return (
- +
- +
Die App muss nach Veränderungen neu gestartet werden!
+
- -