diff --git a/main.js b/main.js index 39d7562..6dca97c 100644 --- a/main.js +++ b/main.js @@ -44,6 +44,7 @@ function createWindow() { mainWindow = new BrowserWindow({ width: 400, height: 600, + icon: __dirname + '/marvin.ico', show: false, webPreferences: { preload: path.join(__dirname, 'preload.js'), diff --git a/marvin.ico b/marvin.ico new file mode 100644 index 0000000..dd00e72 Binary files /dev/null and b/marvin.ico differ diff --git a/resources/config/config.json b/resources/config/config.json index 023c6a4..b0b532a 100644 --- a/resources/config/config.json +++ b/resources/config/config.json @@ -1,3 +1,3 @@ { - "rootDir": "/home/rbrt/Schreibtisch" + "rootDir": "/home/rbrt/Schreibtisch/marvin" } \ No newline at end of file diff --git a/src/DocxInserter.js b/src/DocxInserter.js index d1dcf60..37dece9 100644 --- a/src/DocxInserter.js +++ b/src/DocxInserter.js @@ -1,5 +1,3 @@ - - // Modules import createReport from 'docx-templates'; import fs from 'fs'; @@ -7,23 +5,49 @@ import { mkdir } from 'node:fs/promises'; import path from 'path'; import replaceSpecialCharacters from "replace-special-characters"; -// Components -import ObjektkatalogApi from './ObjektkatalogApi'; //////////////////// // Main // //////////////////// +// Fills the docx template. export async function fillTemplate(log, objectData) { + // Variables let buffer; let configJson - // Create docx document. + + // Create docx document if there was a response. if (objectData.httpStatus === 200) { + let documentInfo; try { + // Load config file. let ConfigFile = fs.readFileSync('resources/config/config.json') configJson = JSON.parse(ConfigFile) + + // Choose the template for selected document type. + switch (objectData.dokumenttyp) { + case 'rp': + documentInfo = { + documentType: 'restaurierungsprotokolle', + templateFile: 'rp-template.docx', + } + break; + case 'lbb': + documentInfo = { + documentType: 'leihgabenbegleitblaetter', + templateFile: 'lbb-template.docx', + } + break; + case 'a': + documentInfo = { + documentType: 'analysen', + templateFile: 'a-template.docx', + } + break; + } // Read template. - const template = fs.readFileSync('resources/templates/rp-template.docx'); + const template = fs.readFileSync(path.join('resources/templates/', documentInfo.templateFile)); + // Create report. buffer = await createReport({ template, @@ -38,32 +62,40 @@ export async function fillTemplate(log, objectData) { } }); } catch (err) { + // Set error log. log = { ...log, status: 'red', message: 'Konnte Template nicht erstellen: ' + err, tip: 'Ist das Template vorhanden?', }; + return log } - const folderPath = path.join(configJson.rootDir, objectData.inventarnummer); - // Create Folder if necessary. + + // Create folder (if necessary). + const objectPath = path.join(configJson.rootDir, objectData.inventarnummer); + const documentPath = path.join(objectPath, documentInfo.documentType); + const temporaryWorkDirPath = path.join(objectPath, 'werkstatt'); try { - const createDir = await mkdir(folderPath, {recursive: true}); + const createDocumentPath = await mkdir(documentPath, {recursive: true}); + const createTemporaryWorkDirPath = await mkdir(temporaryWorkDirPath, {recursive: true}); } catch (err) { + // Set error log. log = { ...log, status: 'red', message: 'Konnte den Pfad nicht erstellen' + err, tip: 'Bestehen Schreibrechte auf dem Ordner?' } + return log } - // Write document to disk. + // Write document to disk. if (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) + fs.writeFileSync(path.join(documentPath, filename), buffer) log = { ...log, status: 'green', diff --git a/src/ObjektkatalogApi.js b/src/ObjektkatalogApi.js index 041f348..424f497 100644 --- a/src/ObjektkatalogApi.js +++ b/src/ObjektkatalogApi.js @@ -1,9 +1,11 @@ const {parseString} = require("xml2js"); /* - * Sends Object ID to Objektkatalog-API and receives JSON + * Properties and functions for communication with Objektkatalog-API. */ class ObjektkatalogApi { + + // Variables json; raw; receivedData = { @@ -17,62 +19,97 @@ class ObjektkatalogApi { materialTechnik: '', titel: '', }; - + response; visibility; + + // Get data from objektkatalog.gnm.de. async getData(objectId) { if (objectId) { - const response = await fetch('https://objektkatalog.gnm.de/rest_export/' + objectId); - if (response.status === 200) { - const responseJson = await response.json(); //extract JSON from the http response - if (responseJson.length !== 0) { - this.raw = { - eid: responseJson[0]['eid'], - allgemeineBezeichnung: responseJson[0]['fc6392714594e73ddb2fa363815a8fdf'], - beschreibung: responseJson[0]['f81f557caccf45074edfb65ff077011f'], - darstellung: responseJson[0]['f217e1053b1e29411d4b00f3b1e1d52b'], - erwerbsmethode: responseJson[0]['fa4aa035d411275cd36a2fbb5c159a2c'], - fruehstes: responseJson[0]['ff4a178095c895a12fce6320c04ba0b0'], - fundort: responseJson[0]['f1c2bf9f6f3d78302bbfa9cc8b3e439c'], - gehoertZuAggregation: responseJson[0]['fca2cff9e713e02b93be1f9f19dff88e'], - herstellerString: responseJson[0]['f9f9408fdacc1230497c591c89655777'], - herstellungsdatum: responseJson[0]['fd9f0912229c78d94dd6f807e683d06e'], - herstellungsort: responseJson[0]['fbbccf2979c1143b0fa25325a849b121'], - individuelleEinordnung: responseJson[0]['f8e7a568a6a0fb8907b41f5ba8a9cabe'], - inventarnummer: responseJson[0]['f9830c2c7747a792ebbe1ca2587d1f26'], - klassifikation: responseJson[0]['fdcd0101d5c77884d185560aa7521645'], - masse: responseJson[0]['ffdac65ffbb12438e2fef5b26533c909'], - materialTechnik: responseJson[0]['f23b67bd18913642fc3936c1f2af5682'], - muster: responseJson[0]['f88917aeadae224c8d2c1fec35720dc8'], - provisio: responseJson[0]['f930a6ca774cc5640694c5aa081c1e66'], - sammlung: responseJson[0]['ffd22ba4399f8d62e61c4e99463dddaa'], - spaetestens: responseJson[0]['f1c2bf9f6f3d78302bbfa9cc8b3e439c'], - standort: responseJson[0]['f95e9be48ed110cf4f92298712d364bd'], - titel: responseJson[0]['f2049b2456b20f8fd80f714e154f1d47'], - unterbringung: responseJson[0]['f15265e39237868a568ee63453492b17'], - vitrinentext: responseJson[0]['fe5e3ff18aedf1d25c639dc79fb24ad1'], - zustandsbeschreibung: responseJson[0]['f54b6da6006ea444c33a348b8c4370a8'] + // RESTful GET. + this.response = await fetch('https://objektkatalog.gnm.de/rest_export/' + objectId).then((response) => { + if (response.status >= 400 && response.status < 600) { + throw new Error("Bad response from server: " + response.status); + } + return response; + }).then((returnedResponse) => { + // Everything went well. + return returnedResponse + }).catch((error) => { + // Error while fetch. + return {status: 503}; + }); + + switch (this.response.status) { + case 200: + // Parse to json. + const responseJson = await this.response.json(); //extract JSON from the http response + if (responseJson.length !== 0) { + // Json with raw data (LIDO inside!) + this.raw = { + eid: responseJson[0]['eid'], + allgemeineBezeichnung: responseJson[0]['fc6392714594e73ddb2fa363815a8fdf'], + beschreibung: responseJson[0]['f81f557caccf45074edfb65ff077011f'], + darstellung: responseJson[0]['f217e1053b1e29411d4b00f3b1e1d52b'], + erwerbsmethode: responseJson[0]['fa4aa035d411275cd36a2fbb5c159a2c'], + fruehstes: responseJson[0]['ff4a178095c895a12fce6320c04ba0b0'], + fundort: responseJson[0]['f1c2bf9f6f3d78302bbfa9cc8b3e439c'], + gehoertZuAggregation: responseJson[0]['fca2cff9e713e02b93be1f9f19dff88e'], + herstellerString: responseJson[0]['f9f9408fdacc1230497c591c89655777'], + herstellungsdatum: responseJson[0]['fd9f0912229c78d94dd6f807e683d06e'], + herstellungsort: responseJson[0]['fbbccf2979c1143b0fa25325a849b121'], + individuelleEinordnung: responseJson[0]['f8e7a568a6a0fb8907b41f5ba8a9cabe'], + inventarnummer: responseJson[0]['f9830c2c7747a792ebbe1ca2587d1f26'], + klassifikation: responseJson[0]['fdcd0101d5c77884d185560aa7521645'], + masse: responseJson[0]['ffdac65ffbb12438e2fef5b26533c909'], + materialTechnik: responseJson[0]['f23b67bd18913642fc3936c1f2af5682'], + muster: responseJson[0]['f88917aeadae224c8d2c1fec35720dc8'], + provisio: responseJson[0]['f930a6ca774cc5640694c5aa081c1e66'], + sammlung: responseJson[0]['ffd22ba4399f8d62e61c4e99463dddaa'], + spaetestens: responseJson[0]['f1c2bf9f6f3d78302bbfa9cc8b3e439c'], + standort: responseJson[0]['f95e9be48ed110cf4f92298712d364bd'], + titel: responseJson[0]['f2049b2456b20f8fd80f714e154f1d47'], + unterbringung: responseJson[0]['f15265e39237868a568ee63453492b17'], + vitrinentext: responseJson[0]['fe5e3ff18aedf1d25c639dc79fb24ad1'], + zustandsbeschreibung: responseJson[0]['f54b6da6006ea444c33a348b8c4370a8'] + } + break; + } else { + // No json, no data form. + this.receivedData = { + ...this.receivedData, + httpStatus: 404, + } + this.visibility = false + return [this.receivedData, this.visibility] } - } else { - // No JSON + case 408: + // No response, no data form. this.receivedData = { ...this.receivedData, - httpStatus: 404, + httpStatus: 408, + } + this.visibility = false + return [this.receivedData, this.visibility] + case 503: + // Objektkatalog not reachable. + this.receivedData = { + ...this.receivedData, + httpStatus: 503, + } + this.visibility = false + return [this.receivedData, this.visibility] + default: + // Any other error + this.receivedData = { + ...this.receivedData, + httpStatus: 500, } this.visibility = false return [this.receivedData, this.visibility] - } - } else { - // No response - this.receivedData= { - ...this.receivedData, - httpStatus: 404, - } - this.visibility = false - return [this.receivedData, this.visibility] } } else { - // No ObjectId + // No ObjectId, no data form. this.receivedData = { ...this.receivedData, httpStatus: 500, @@ -84,7 +121,8 @@ class ObjektkatalogApi { // 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'); ; + let todayFormat = today.getFullYear() + '-' + (String(today.getMonth() + 1).padStart(2, '0')) + '-' + String(today.getDate()).padStart(2, '0'); + ; // Inventarnummer let inventarnummer; @@ -102,7 +140,7 @@ class ObjektkatalogApi { titel = 'unbekannt'; } - // Hersteller + // Hersteller (LIDO format!) let herstellerArray = []; let hersteller; @@ -141,7 +179,7 @@ class ObjektkatalogApi { materialTechnik = 'unbekannt'; } - // Maße + // Maße (LIDO format!) let masse; if (this.raw.masse.length !== 0) { parseString(this.raw.masse[0].value, (err, result) => { @@ -149,18 +187,19 @@ class ObjektkatalogApi { }) } - this.receivedData ={ - ...this.receivedData, - datum: todayFormat, - titel: titel, - hersteller: hersteller, - herstellungsort: herstellungsort, - herstellungsdatum: herstellungsdatum, - inventarnummer: inventarnummer, - materialTechnik: materialTechnik, - masse: masse, - httpStatus: 200, - } + // Json with important data. + 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] } diff --git a/src/assets/css/styles.css b/src/assets/css/styles.css index 807a69f..fa3892a 100644 --- a/src/assets/css/styles.css +++ b/src/assets/css/styles.css @@ -167,6 +167,7 @@ label { .open { height: 550px; + overflow: hidden; } p { margin: 0; diff --git a/src/components/App.js b/src/components/App.js index 7597b7c..b643a31 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -41,6 +41,7 @@ export const App = () => { const [objectData, setObjectData] = useState( { datum: '', + dokumenttyp: '', hersteller: '', herstellungsdatum: '', herstellungsort: '', @@ -57,64 +58,110 @@ export const App = () => { ); // Handler + + // Get userinput from main form, ask Objektkatalog API for Data and fill check up form. const getDataAndAskForCheckUpClickHandler = async (e) => { + // Show loading widget. setIsLoading(true) + // Prevent page reload. e.preventDefault() - let objectId = '' - objectId = e.target.form.objectId.value - // Get objectId from user input. + // Get objectId and document type from user input. + let objectId = e.target.form.objectId.value + let documentType = e.target.form.documentType.value + + // Set object state. await setObjectData((prevState) => { return { ...prevState, inventarnummer: objectId, + dokumenttyp: documentType, } } ) if (objectId) { - // Get object data from objektkatalog.gnm.de + // Get object data from objektkatalog.gnm.de. const [receivedObjectData, receivedVisibility] = await objektkatalogApi.getData(objectId); - if (receivedObjectData.httpStatus !== 200) { - await setLogState({ - log: { - status: 'red', - message: 'Kein Objekt mit dieser Inventarnummer gefunden!', - code: '', - 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' - }) + // If no response from Objektkatalog. + switch (receivedObjectData.httpStatus) { + // Server not reachable. + case 503: + await setLogState({ + log: { + status: 'red', + message: 'Objektkatalog nicht erreichbar!', + code: '503', + tip: 'Sind sie mit dem Internet verbunden und ist der Objektkatalog online?', + }, // with message, code, tip + logClass: 'active' + }) + // Hide Loading + setIsLoading(false) + return; + case 200: + // Fill and open check up form. + await setObjectData(prevState => { + return { + ...prevState, + ...receivedObjectData + } + }) + // Show check up form. + await setCheckUpVisibility(true) + + // Set success log but hide. + await setLogState({ + log: { + status: '', + message: '', + code: '', + tip: '', + }, + logClass: 'inactive' + }) + break; + default: + // Set error log state. + await setLogState({ + log: { + status: 'red', + message: 'Kein Objekt mit dieser Inventarnummer gefunden!', + code: '', + tip: 'Groß-/Kleinschreibung beachten und keine Leerzeichen verwenden!', + }, // with message, code, tip + logClass: 'active' + }) + // Hide Loading + setIsLoading(false) + // Hide check up form. + await setCheckUpVisibility(false) + } } else { + // Set error log. await setLogState({ log: { status: 'red', message: 'Bitte Inventarnummer eingeben!', code: '', tip: '', - }, // with message, code, tip + }, logClass: 'active' }) + // Hide check up form. await setCheckUpVisibility(false) } + // Hide loading widget. setIsLoading(false) } + // Component with + // * Navigation (Nav) * + // * Main form (this)* + // * Loading widget (LoadingIcon)* + // * Checkup form (DataForm)* + // * Log (Log)* return (
@@ -128,10 +175,10 @@ export const App = () => {
- + + +
diff --git a/src/components/DataForm.js b/src/components/DataForm.js index 8bb1821..05f381a 100644 --- a/src/components/DataForm.js +++ b/src/components/DataForm.js @@ -8,10 +8,17 @@ import React from 'react' // Main // //////////////////// + +// Renders check up form and gives the possibility to change received values. export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState, setLogState}) => { + + // Variables let log; // Handlers + // Change handler response to every keystroke! + + // Change date. const datumChangeHandler = (e) => { setObjectData((prevState) => { return { @@ -20,7 +27,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState } }) } - + // Change identifier. const inventarnummerChangeHandler = (e) => { setObjectData((prevState) => { return { @@ -29,7 +36,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState } }) } - + // Change title. const titelChangeHandler = (e) => { setObjectData((prevState) => { return { @@ -39,6 +46,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState }) } + // Change creator. const herstellerChangeHandler = (e) => { setObjectData((prevState) => { return { @@ -47,7 +55,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState } }) } - + // Change creation place. const herstellungsortChangeHandler = (e) => { setObjectData((prevState) => { return { @@ -57,6 +65,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState }) } + // Change creation date. const herstellungsdatumChangeHandler = (e) => { setObjectData((prevState) => { return { @@ -66,6 +75,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState }) } + // Change material and technic. const materialTechnikChangeHandler = (e) => { setObjectData((prevState) => { return { @@ -75,6 +85,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState }) } + // Change sizes. const masseChangeHandler = (e) => { setObjectData((prevState) => { return { @@ -84,6 +95,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState }) } + // Fill the template and save it. const fillTemplateClickHandler = async (e) => { e.preventDefault(); log = await fillTemplate(logState, objectData); @@ -94,6 +106,7 @@ export const DataForm = ({checkUpVisibility, objectData, setObjectData, logState }); } + // Renders data form return (
diff --git a/webpack.dev.config.js b/webpack.dev.config.js index 6420d03..2b5243e 100644 --- a/webpack.dev.config.js +++ b/webpack.dev.config.js @@ -42,11 +42,14 @@ module.exports = { ], devtool: 'cheap-source-map', devServer: { - static: path.resolve(__dirname, 'dist'), + // Tell the server where to serve the content from. + static: { + directory: path.resolve(__dirname, 'dist'), + }, onBeforeSetupMiddleware() { spawn( 'electron', - ['.'], + ['--trace-warnings .'], { shell: true, env: process.env, stdio: 'inherit' } ) .on('close', code => process.exit(0))