diff --git a/backend/Backend/.gitignore b/backend/Backend/.gitignore index 64b97b7e..f445bf37 100644 --- a/backend/Backend/.gitignore +++ b/backend/Backend/.gitignore @@ -4,6 +4,9 @@ ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore secrets.json +Templates/template-docx +Templates/template-modified.docx +Templates/jpegsample.jpg # User-specific files *.rsuser diff --git a/backend/Backend/Backend.csproj b/backend/Backend/Backend.csproj index 34180f81..020b39f5 100644 --- a/backend/Backend/Backend.csproj +++ b/backend/Backend/Backend.csproj @@ -7,8 +7,10 @@ + + @@ -17,5 +19,9 @@ + + + + diff --git a/backend/Backend/Controllers/DocumentController.cs b/backend/Backend/Controllers/DocumentController.cs new file mode 100644 index 00000000..01fa51a3 --- /dev/null +++ b/backend/Backend/Controllers/DocumentController.cs @@ -0,0 +1,61 @@ +using Backend.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.StaticFiles; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using System.Web; +namespace Backend.Controllers +{ + [Route("[controller]")] + [ApiController] + public class DocumentController : ControllerBase + { + private readonly DocumentService _documentService; + + public DocumentController(DocumentService documentService) + { + _documentService = documentService; + } + + //[HttpPost("test")] + //public IActionResult test() + //{ + // var files = DocumentService.GetFileModelsFromRequest(this.Request); + + // return new OkResult(); + //} + + [HttpPost("CreateReport")] + public FileContentResult CreateReport() + { + string filepath = _documentService.createReport(this.Request); + + string filename = "template-modified.docx"; + byte[] filedata = System.IO.File.ReadAllBytes(filepath); + string contentType; + new FileExtensionContentTypeProvider().TryGetContentType(filepath, out contentType); + + var cd = new System.Net.Mime.ContentDisposition { + FileName = filename, + Inline = false + }; + + System.IO.File.Delete(filepath); + Response.Headers.Add("Content-Disposition", cd.ToString()); + Response.Headers.Add("Access-Control-Expose-Headers", "*"); + //Response.Headers.Add("Content-Transfer-Encoding", "Binary"); + //Response.Headers.Add("Content-Encoding", "gzip"); + return File(filedata, contentType); + } + + [HttpOptions("CreateReport")] + public IActionResult PreflightRoute() + { + return NoContent(); + } + + } +} diff --git a/backend/Backend/Controllers/SimaProController.cs b/backend/Backend/Controllers/SimaProController.cs index 28e58873..8f940a9a 100644 --- a/backend/Backend/Controllers/SimaProController.cs +++ b/backend/Backend/Controllers/SimaProController.cs @@ -36,5 +36,11 @@ public Task ProxyCatchAll(string rest) return this.HttpProxyAsync($"{rest}{queryString}", httpProxyOptions); } + [HttpOptions] + public IActionResult PreflightRoute() + { + return NoContent(); + } + } } diff --git a/backend/Backend/Helper/XmlToDocxHelper.cs b/backend/Backend/Helper/XmlToDocxHelper.cs new file mode 100644 index 00000000..4ec123d4 --- /dev/null +++ b/backend/Backend/Helper/XmlToDocxHelper.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Xml.Linq; + +namespace Backend.Helper +{ + public static class XmlToDocxHelper + { + + public static void ZipDOCX(this string unzipPath, string fileName) + { + + if (File.Exists(fileName)) + { + File.Delete(fileName); + } + + ZipFile.CreateFromDirectory(unzipPath, fileName); + + } + + public static string UnzipDOCX(this string zipPath) + { + string[] subs = zipPath.Split('.', StringSplitOptions.RemoveEmptyEntries); + + using (ZipArchive archive = ZipFile.OpenRead(zipPath)) + { + if (Directory.Exists(subs[0])) + { + Directory.Delete(subs[0], true); + } + + archive.ExtractToDirectory(subs[0]); + + } + + return subs[0]; + } + + //public static string XmlToString(this string xmlDirectory, string xmlPath) + //{ + // XDocument document = XDocument.Load(xmlDirectory + xmlPath); + // string docString = document.ToString(); + + // return docString; + //} + + //public static void StringToXml(this string xmlDirectory, string xmlPath, string documentString) + //{ + // XDocument xDocument = XDocument.Parse(documentString, LoadOptions.PreserveWhitespace); + // xDocument.Save(xmlDirectory + xmlPath); + //} + + } +} diff --git a/backend/Backend/Middleware/RequestInterceptorDelegatingHandler.cs b/backend/Backend/Middleware/RequestInterceptorDelegatingHandler.cs index 4bcff7d8..d791862a 100644 --- a/backend/Backend/Middleware/RequestInterceptorDelegatingHandler.cs +++ b/backend/Backend/Middleware/RequestInterceptorDelegatingHandler.cs @@ -28,6 +28,10 @@ protected override async Task SendAsync(HttpRequestMessage if (request.RequestUri.ToString().Contains("api/calculation")) { var requestResponse = await base.SendAsync(request, cancellationToken); + if (!requestResponse.IsSuccessStatusCode) + { + return requestResponse; + } var contentString = await requestResponse.Content.ReadAsStringAsync(); //Retrieve the CalculationId from the post request var responseContent = JObject.Parse(contentString); @@ -43,7 +47,10 @@ protected override async Task SendAsync(HttpRequestMessage while (true) { calculationStateResponse = await base.SendAsync(calculationStateRequest, cancellationToken); - + if (!calculationStateResponse.IsSuccessStatusCode) + { + return calculationStateResponse; + } var calculationResponseContent = JObject.Parse(await calculationStateResponse.Content.ReadAsStringAsync()); var calculationResultStatus = calculationResponseContent["Result"].ToString(); diff --git a/backend/Backend/Services/DocumentService.cs b/backend/Backend/Services/DocumentService.cs new file mode 100644 index 00000000..c4fce386 --- /dev/null +++ b/backend/Backend/Services/DocumentService.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Xml.Linq; +using System.Drawing; +using System.IO; +using System.ComponentModel; +using Backend.Helper; +using Microsoft.AspNetCore.Http; + +namespace Backend.Services +{ + public class DocumentService + { + public string createReport(HttpRequest request) + { + var imageNumbers = new List() { 7, 13, 11 }; + + GetFileModelsFromRequest(request, imageNumbers); + + var unzipPath = @"Templates\template-docx.docx".UnzipDOCX(); + var docxImagePath = unzipPath + @"\word\media\"; + + + foreach (var number in imageNumbers) + { + File.Delete(docxImagePath + "image" + number.ToString() + ".png"); + var image = Image.FromFile(@"Templates\image" + number.ToString() + ".png"); + image.Save(docxImagePath + "image" + number.ToString() + ".png", System.Drawing.Imaging.ImageFormat.Png); + image.Dispose(); + File.Delete(@"Templates\image" + number.ToString() + ".png"); + } + + unzipPath.ZipDOCX(@"Templates\template-modified.docx"); + Directory.Delete(@"Templates\template-docx",true); + return @"Templates\template-modified.docx"; + + //List images = new List() { + // Image.FromFile(@"Templates\image7.png"), + // Image.FromFile(@"Templates\image11.png"), + // Image.FromFile(@"Templates\image14.png") + //}; + + //File.Delete(imagePath + "image7.png"); + //File.Delete(imagePath + "image11.png"); + //File.Delete(imagePath + "image14.png"); + + //images[0].Save(imagePath + "image" + "7.png", System.Drawing.Imaging.ImageFormat.Png); + //images[1].Save(imagePath + "image" + "11.png", System.Drawing.Imaging.ImageFormat.Png); + //images[2].Save(imagePath + "image" + "14.png", System.Drawing.Imaging.ImageFormat.Png); + + + //File.Delete(@"Templates\image7.png"); + //File.Delete(@"Templates\image11.png"); + //File.Delete(@"Templates\image14.png"); + + + + + } + + public static void GetFileModelsFromRequest(HttpRequest request, List imageNumbers) + { + foreach (var formField in request.Form) + { + // Form data + var stringValues = formField.Value.Select(x => x.ToString()).Select(y => y.Replace("data:image/jpeg;base64,","")); + var stringValueTuples = stringValues.Zip(imageNumbers); + + foreach (var tuple in stringValueTuples) + { + //... process and add to the FileModel list + byte[] bytes = Convert.FromBase64String(tuple.First); + + Image image; + using (MemoryStream ms = new MemoryStream(bytes)) + { + image = Image.FromStream(ms); + image.Save(@"Templates\" + "image" + tuple.Second.ToString() +".png", System.Drawing.Imaging.ImageFormat.Png); + image.Dispose(); + ms.Dispose(); + } + } + } + } + + //XDocument template = XDocument.Load(@"Templates\template-xml.xml"); + //string document = template.ToString(); + + //var documentModified = ReplaceXmlInlineImageDate(document, @"Templates\jpegsample.jpg", "7"); + //documentModified = ReplaceXmlInlineImageDate(documentModified, @"Templates\jpegsample.jpg", "11"); + //documentModified = ReplaceXmlInlineImageDate(documentModified, @"Templates\jpegsample.jpg", "14"); + + //XDocument xDocument = XDocument.Parse(documentModified, LoadOptions.PreserveWhitespace); + //xDocument.Save(@"Templates\template-xml-edited.docx"); + + //private static string ReplaceXmlInlineImageDate(string document, string imagePath, string imageToBeReplacedNumber) + //{ + // //load image from filesystem + // Image image = Image.FromFile(imagePath); + // Bitmap bitmap = new Bitmap(image); + // TypeConverter converter = TypeDescriptor.GetConverter(typeof(Bitmap)); + // string imgContent = Convert.ToBase64String((byte[])converter.ConvertTo(bitmap, typeof(byte[]))); + + // //build document string + // string delimiterOpeningTag = "\r\n "; + // string delimiterClosingTag = "\r\n \r\n"; + // var documentArray = document.Split(delimiterOpeningTag); + // var documentArray2 = documentArray[1].Split(delimiterClosingTag); + // var restString = String.Join(delimiterClosingTag, documentArray2, 1, documentArray2.Length - 1); + // var documentModified = documentArray[0] + delimiterOpeningTag + imgContent + delimiterClosingTag + restString; + // return documentModified; + //} + } +} diff --git a/backend/Backend/Startup.cs b/backend/Backend/Startup.cs index 05ec1281..29941435 100644 --- a/backend/Backend/Startup.cs +++ b/backend/Backend/Startup.cs @@ -1,6 +1,7 @@ using AspNetCore.Proxy; using Backend.Security; using Backend.Middleware; +using Backend.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -49,6 +50,9 @@ public void ConfigureServices(IServiceCollection services) }); services.AddProxies(); + + services.AddTransient(); + //Used to get the authenticate/process the Http requests. services.AddTransient(); diff --git a/backend/Backend/Templates/jpegsample.jpg b/backend/Backend/Templates/jpegsample.jpg new file mode 100644 index 00000000..794ab71b Binary files /dev/null and b/backend/Backend/Templates/jpegsample.jpg differ diff --git a/backend/Backend/Templates/template-docx.docx b/backend/Backend/Templates/template-docx.docx new file mode 100644 index 00000000..110b2c09 Binary files /dev/null and b/backend/Backend/Templates/template-docx.docx differ diff --git a/frontend/src/assets/dummyData/dummyData.js b/frontend/src/assets/dummyData/dummyData.js new file mode 100644 index 00000000..5543fbaf --- /dev/null +++ b/frontend/src/assets/dummyData/dummyData.js @@ -0,0 +1,153 @@ +import logo from 'assets/logo/LogoCarbonteam.png'; +import logo_1 from 'assets/dummyImages/Image_1.PNG'; +import logo_2 from 'assets/dummyImages/Logo2.png'; + +export function getDummyProductData() { + // WTH are we looking for here? do we need to iterate over projects (api_demo_project, ...) or over final processes? + const products = [ + { + Id: '09f64eeb-13b0-4e09-9fb4-50398483ecfd', //(project_id?) final_process_id? (final_product_id?) + Name: 'Electric Motors#ID:09f64eeb-13b0-4e09-9fb4-50398483ecfd#Scenario:scenario_baseline', + Description: + 'Categorie:Generation/Product#Model:09f64eeb-13b0-4e09-9fb4-50398483ecfd#Location:Germany', + productImage: logo + }, + { + Id: 'aufwlc93-kldp-4fer-15s7-51245631fega', + Name: 'Electric Motors#ID:aufwlc93-kldp-4fer-15s7-51245631fega#Scenario:scenario_scenario1', + Description: + 'Categorie:Generation/Product#Model:09f64eeb-13b0-4e09-9fb4-503984f3ecfd#Location:Germany', + productImage: logo_1 + }, + { + Id: 'aufwlc93-kldp-4fer-15s7-5124563regen', + Name: 'Electric Motors#ID:aufwlc93-kldp-4fer-15s7-5124563regen#Scenario:scenario_scenario3', + Description: + 'Categorie:Generation/Product#Model:09f64eeb-13b0-4e09-9fb4-503984f3ecfd#Location:Germany', + productImage: logo_1 + }, + { + Id: 'aufwlc93-kldp-4fer-15s7-51245631mega', + Name: 'Electric Motors#ID:aufwlc93-kldp-4fer-15s7-51245631mega#Scenario:scenario_scenario2', + Description: + 'Categorie:Generation/Product#Model:09f64eeb-13b0-4e09-9fb4-503984f3ecfd#Location:Germany', + productImage: logo_1 + }, + { + Id: '7ghnaoeb-kfue-qp04-slfg-12059492gggg', + Name: 'Wireless Power Transmittor#ID:7ghnaoeb-kfue-qp04-slfg-12059492begp#Scenario:scenario_secondOption', + Description: + 'Categorie:Transmission/Product#Model:09f64eeb-12f0-4e09-9fb4-50398483ecfd#Location:Germany', + productImage: logo_2 + }, + { + Id: '7ghnaoeb-kfue-qp04-slfg-12059492begp', //(project_id?) final_process_id? (final_product_id?) + Name: 'Wireless Power Transmittor#ID:7ghnaoeb-kfue-qp04-slfg-12059492begp#Scenario:scenario_baseline', + Description: + 'Categorie:Transmission/Product#Model:09f64eeb-12f0-4e09-9fb4-50398483ecfd#Location:Germany', + productImage: logo_2 + }, + { + Id: 'whatis00-this-id00-just-d01n9352rnow', //(project_id?) final_process_id? (final_product_id?) + Name: 'Gas Turbine#ID:whatis00-this-id00-just-d01n9352rnow#Scenario:scenario_baseline', + Description: + 'Categorie:Generation/Product#Model:09f64eeb-12f0-4e09-9fb4-50395583ecfd#Location:Germany', + productImage: logo + }, + { + Id: 'aufglc25-kldd-4ger-16s2-51002631fell', //(project_id?) final_process_id? (final_product_id?) + Name: 'Alround Product#ID:aufglc25-kldd-4ger-16s2-51002631fell#Scenario:scenario_baseline', + Description: + 'Categorie:Generation/Product#Model:09f64eeb-1234-4e09-9fb4-50398483ecfd#Location:France', + productImage: logo_1 + }, + { + Id: 'aufglc25-kldd-4ger-16s2-5100Julian00', //(project_id?) final_process_id? (final_product_id?) + Name: 'Super Product#ID:aufglc25-kldd-4ger-16s2-5100Julian00#Scenario:scenario_baseline', + Description: + 'Categorie:Generation/Product#Model:09f64eeb-1234-4321-9fb4-50398483ecfd#Location:Germany', + productImage: logo_1 + }, + { + Id: 'aufglc25-kldd-4ger-16s2-5100100sai00', //(project_id?) final_process_id? (final_product_id?) + Name: 'Super Product#ID:aufglc25-kldd-4ger-16s2-5100100sai00#Scenario:scenario_1', + Description: + 'Categorie:Generation/Product#Model:09f64eeb-1234-4321-9fb4-50398483ecfd#Location:India', + productImage: logo_1 + }, + { + Id: 'aufglc25-kldd-4ger-16s2-5100MartinWl', //(project_id?) final_process_id? (final_product_id?) + Name: 'Super Service#ID:aufglc25-kldd-4ger-16s2-5100MartinWl#Scenario:scenario_baseline', + Description: + 'Categorie:Generation/Service#Model:09f64eeb-1234-4e09-9fb4-50398483ecfd#Location:Germany', + productImage: logo + }, + { + Id: 'aufglc25-kldd-4ger-16s2-510MartinD1l', //(project_id?) final_process_id? (final_product_id?) + Name: 'Super Service#ID:aufglc25-kldd-4ger-16s2-510MartinD1l#Scenario:scenario_1', + Description: + 'Categorie:Generation/Service#Model:09f64eeb-1234-4e09-9fb4-50398483ecfd#Location:Germany', + productImage: logo + }, + { + Id: 'aufglc25-kldd-4ger-16s2-510026Irem01', //(project_id?) final_process_id? (final_product_id?) + Name: 'Super Solution#ID:aufglc25-kldd-4ger-16s2-510026Irem01#Scenario:scenario_baseline', + Description: + 'Categorie:Generation/Solution#Model:09f64eeb-1234-4e09-9fb4-50398483ecfd#Location:Germany', + productImage: logo_1 + }, + { + Id: 'hello0th-ere0-4ger-16s2-51002631Mani', //(project_id?) final_process_id? (final_product_id?) + Name: 'Super Solution#ID:hello0th-ere0-4ger-16s2-51002631Mani#Scenario:scenario_1', + Description: + 'Categorie:Transmission/Solution#Model:09f64eeb-1234-4e09-9fb4-50398483ecfd#Location:India', + productImage: logo_1 + }, + { + Id: 'hello0th-ere0-4ger-16s2-51002Parham1', + Name: 'THE Solution#ID:hello0th-ere0-4ger-16s2-51002Parham1#Scenario:scenario_baseline', + Description: + 'Categorie:Transmission/Solution#Model:09f64eeb-1234-4e09-9fb4-50355483ecfd#Location:Iran', + productImage: logo_1 + } + ]; + return products; +} + +/** + * should get all the Models for one Product + * @param productID the id of the Project to get the models for + * @returns + */ +//export async function getModels(productID) { +export function getModels(productName, productID) { + switch (productID) { + case '09f64eeb-13b0-4e09-9fb4-50398483ecfd': + return [{ modelID: 1, productID: productID, modelName: 'Electric Motor Type 25b' }]; + case 'aufwlc93-kldp-4fer-15s7-51245631fega': + return []; + case '7ghnaoeb-kfue-qp04-slfg-12059492begp': + return [ + { modelID: 6, productID: productID, modelName: 'Transmitter DIN42a' }, + { modelID: 7, productID: productID, modelName: 'Transmitter DIN42b' }, + { modelID: 8, productID: productID, modelName: 'Transmitter DIN42c' }, + { modelID: 9, productID: productID, modelName: 'Transmitter DIN42d' } + ]; + case 'whatis00-this-id00-just-d01n9352rnow': + return [ + { modelID: 10, productID: productID, modelName: 'Gas Turbine Type 1' }, + { modelID: 11, productID: productID, modelName: 'Gas Turbine Type 2' }, + { modelID: 12, productID: productID, modelName: 'Gas Turbine Type 3' }, + { modelID: 13, productID: productID, modelName: 'Gas Turbine Type 4' } + ]; + case 'aufglc25-kldd-4ger-16s2-51002631fell': + return [ + { modelID: 14, productID: productID, modelName: 'Allround Product 1' }, + { modelID: 15, productID: productID, modelName: 'Allround Product 2' }, + { modelID: 16, productID: productID, modelName: 'Allround Product 3' }, + { modelID: 17, productID: productID, modelName: 'Allround Product 4' } + ]; + default: + return [{ modelID: 42, productID: productID, modelName: productName }]; + } +} diff --git a/frontend/src/assets/logo/DashboardImage1.PNG b/frontend/src/assets/logo/DashboardImage1.PNG new file mode 100644 index 00000000..8fffd907 Binary files /dev/null and b/frontend/src/assets/logo/DashboardImage1.PNG differ diff --git a/frontend/src/assets/logo/DashboardImage2.jpg b/frontend/src/assets/logo/DashboardImage2.jpg new file mode 100644 index 00000000..56fa257a Binary files /dev/null and b/frontend/src/assets/logo/DashboardImage2.jpg differ diff --git a/frontend/src/components/cards/miniCard.css b/frontend/src/components/cards/miniCard.css index 62a4a593..d8eadb5b 100644 --- a/frontend/src/components/cards/miniCard.css +++ b/frontend/src/components/cards/miniCard.css @@ -9,7 +9,7 @@ } .MiniCardImageContainer:hover { - border-color: var(--global--active--highlighting-color); + border-color: #ae56ff; } .MiniCardImageContainer img { diff --git a/frontend/src/components/dashboard/DashboardComponent.js b/frontend/src/components/dashboard/DashboardComponent.js deleted file mode 100644 index 02085751..00000000 --- a/frontend/src/components/dashboard/DashboardComponent.js +++ /dev/null @@ -1,110 +0,0 @@ -import React from 'react'; -import { Column, Row } from 'simple-flexbox'; -import { createUseStyles } from 'react-jss'; -import MiniCardComponent from 'components/cards/MiniCardComponent'; -import TodayTrendsComponent from './TodayTrendsComponent'; -import UnresolvedTicketsComponent from './UnresolvedTicketsComponent'; - -const useStyles = createUseStyles({ - cardsContainer: { - marginRight: -30, - marginTop: -30 - }, - cardRow: { - marginTop: 30, - '@media (max-width: 768px)': { - marginTop: 0 - } - }, - miniCardContainer: { - flexGrow: 1, - marginRight: 30, - '@media (max-width: 768px)': { - marginTop: 30, - maxWidth: 'none' - } - }, - todayTrends: { - marginTop: 30 - }, - lastRow: { - marginTop: 30 - }, - unresolvedTickets: { - marginRight: 30, - '@media (max-width: 1024px)': { - marginRight: 0 - } - }, - tasks: { - marginTop: 0, - '@media (max-width: 1024px)': { - marginTop: 30 - } - } -}); - -function DashboardComponent() { - const classes = useStyles(); - return ( - - - - - - - - - - - -
- -
- - - {/* */} - -
- ); -} - -export default DashboardComponent; diff --git a/frontend/src/components/dashboard/TodayTrendsComponent.js b/frontend/src/components/dashboard/TodayTrendsComponent.js deleted file mode 100644 index 6fb6dd8c..00000000 --- a/frontend/src/components/dashboard/TodayTrendsComponent.js +++ /dev/null @@ -1,150 +0,0 @@ -import React from 'react'; -import { Column, Row } from 'simple-flexbox'; -import { createUseStyles, useTheme } from 'react-jss'; -import LineChart from 'react-svg-line-chart'; - -const data = []; - -for (let x = 1; x <= 24; x++) { - data.push({ x: x, y: Math.floor(Math.random() * 100) }); -} - -const useStyles = createUseStyles((theme) => ({ - container: { - backgroundColor: '#FFFFFF', - border: `1px solid ${theme.color.lightGrayishBlue2}`, - borderRadius: 4, - cursor: 'pointer' - }, - graphContainer: { - marginTop: 24, - marginLeft: 0, - marginRight: 0, - width: '100%' - }, - graphSection: { - padding: 24 - }, - graphSubtitle: { - ...theme.typography.smallSubtitle, - color: theme.color.grayishBlue2, - marginTop: 4, - marginRight: 8 - }, - graphTitle: { - ...theme.typography.cardTitle, - color: theme.color.veryDarkGrayishBlue - }, - legendTitle: { - ...theme.typography.smallSubtitle, - fontWeight: '600', - color: theme.color.grayishBlue2, - marginLeft: 8 - }, - separator: { - backgroundColor: theme.color.lightGrayishBlue2, - width: 1, - minWidth: 1 - }, - statContainer: { - borderBottom: `1px solid ${theme.color.lightGrayishBlue2}`, - padding: '24px 32px 24px 32px', - height: 'calc(114px - 48px)', - '&:last-child': { - border: 'none' - } - }, - stats: { - borderTop: `1px solid ${theme.color.lightGrayishBlue2}`, - width: '100%' - }, - statTitle: { - fontWeight: '600', - fontSize: 16, - lineHeight: '22px', - letterSpacing: '0.3px', - textAlign: 'center', - color: theme.color.grayishBlue2, - whiteSpace: 'nowrap', - marginBottom: 6 - }, - statValue: { - ...theme.typography.title, - textAlign: 'center', - color: theme.color.veryDarkGrayishBlue - } -})); - -function TodayTrendsComponent() { - const theme = useTheme(); - const classes = useStyles({ theme }); - - function renderLegend(color, title) { - return ( - -
- {title} -
- ); - } - - function renderStat(title, value) { - return ( - - {title} - {value} - - ); - } - - return ( - - - - - Today’s trends - as of 25 May 2019, 09:41 PM - - {renderLegend(theme.color.lightBlue, 'Today')} - -
- -
-
- -
- - - {renderStat('Resolved', '449')} - {renderStat('Received', '426')} - {renderStat('Average first response time', '33m')} - {renderStat('Average response time', '3h 8m')} - {renderStat('Resolution within SLA', '94%')} - - - ); -} - -export default TodayTrendsComponent; diff --git a/frontend/src/components/dashboard/UnresolvedTicketsComponent.js b/frontend/src/components/dashboard/UnresolvedTicketsComponent.js deleted file mode 100644 index 952cbda6..00000000 --- a/frontend/src/components/dashboard/UnresolvedTicketsComponent.js +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; -import { Row } from 'simple-flexbox'; -import { createUseStyles, useTheme } from 'react-jss'; -import CardComponent from 'components/cards/CardComponent'; - -const useStyles = createUseStyles((theme) => ({ - itemTitle: { - ...theme.typography.itemTitle, - color: theme.color.veryDarkGrayishBlue - }, - itemValue: { - color: theme.color.grayishBlue2 - } -})); - -function UnresolvedTicketsComponent({ containerStyles }) { - const theme = useTheme(); - const classes = useStyles({ theme }); - - function renderStat(title, value) { - return ( - - {title} - {value} - - ); - } - - return ( - - ); -} - -export default UnresolvedTicketsComponent; diff --git a/frontend/src/components/dashboard/index.js b/frontend/src/components/dashboard/index.js deleted file mode 100644 index bc1836f6..00000000 --- a/frontend/src/components/dashboard/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import DashboardComponent from './DashboardComponent'; - -export default DashboardComponent; diff --git a/frontend/src/components/details/ColumnChartComponent.jsx b/frontend/src/components/details/ColumnChartComponent.jsx index ae9a93df..468ed7cd 100644 --- a/frontend/src/components/details/ColumnChartComponent.jsx +++ b/frontend/src/components/details/ColumnChartComponent.jsx @@ -24,7 +24,7 @@ const ColumnChartComponent = () => { plotOptions: { bar: { - columnWidth: '70%', + columnWidth: '75%', BorderRadius: 10, dataLabels: { position: 'top' // top, center, bottom @@ -42,7 +42,7 @@ const ColumnChartComponent = () => { }, offsetY: -20, style: { - fontSize: '12px', + fontSize: '10px', colors: ['#21cc82'] } }, @@ -63,7 +63,7 @@ const ColumnChartComponent = () => { labels: { rotate: -90, style: { - fontSize: 10 + fontSize: 6.8, } }, responsive: [ @@ -71,7 +71,7 @@ const ColumnChartComponent = () => { breakpoint: 6400, options: { chart: { - width: 500 + width: 600 }, legend: { position: 'bottom' diff --git a/frontend/src/components/details/DetailsComponent.js b/frontend/src/components/details/DetailsComponent.js index b2f06451..3b4c4724 100644 --- a/frontend/src/components/details/DetailsComponent.js +++ b/frontend/src/components/details/DetailsComponent.js @@ -1,12 +1,12 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import ScenarioComponent from './ScenarioComponent'; -import { jsPDF } from 'jspdf'; -import html2canvas from 'html2canvas'; import { Col, Container, Row } from 'react-grid-system'; import './navbar.css'; import { postCalculationRequest } from 'interface/BackendConnect'; import LoadingComponent from 'components/loading'; +import { exportPdf } from '../../interface/PdfExporter.js'; + /** * the main component for detail page which includes * canvas page and variable drop down list @@ -15,18 +15,34 @@ import LoadingComponent from 'components/loading'; */ class DetailsComponent extends Component { - /* State consists of three variable one for each of the possible state + /* + * selectedScenarioId: for storing the id of the selected scenario + * to send the calculation request to server + * slectedScenarioType: for sending it to SelectScenarioComponent * baselineScenario: only display the baseline scenario * modifiedScenario: only display the modified scenario * state at the beginng: only baseline scenario */ state = { + selectedScenarioId: '', + selectedScenarioType: '', baselineScenario: true, modifiedScenario: false, loadComparePage: false, - stillLoading: true + stillLoading: true, + onExportClicked: false }; + /** + * initializing selectedScenarioType and selectedScenarioId + * to request the baseline scenario for the first calculation request + */ + constructor(props) { + super(props); + this.state.selectedScenarioType = props.selectedProduct.scenarioType; + this.state.selectedScenarioId = props.selectedProduct.productID; + } + render() { /* * compare buttons exist only when a single scenario is displayed @@ -62,27 +78,34 @@ class DetailsComponent extends Component { this.setState({ baselineScenario, modifiedScenario, loadComparePage }); }; + /** + * this function will be envoced from SelectScenarioComponent, + * once the user changes the scenario in drop down menue + * + * then selectedScenarioId and selectedScenarioType will be updated + * + * @param item: selected scenario + */ + let handleNewScenarioSelection = (item) => { + this.setState({ selectedScenarioId: item.id }, () => { + this.setState({ stillLoading: true }); + this.setState({ selectedScenarioType: item.name }); + }); + }; + let handleExportPdfButton = () => { - // getting the element that should be exported - var div = document.getElementById('capture'); + this.setState({ onExportClicked: true }); - // converting html to an image and then exporting it by pdf - html2canvas(div).then((canvas) => { - var imgData = canvas.toDataURL('image/jpeg', 1); - // pdf configuration - var pdf = new jsPDF('p', 'mm', 'a4'); - var pageWidth = pdf.internal.pageSize.getWidth(); - var pageHeight = pdf.internal.pageSize.getHeight(); - var imageWidth = canvas.width; - var imageHeight = canvas.height; + // geting the element that should be exported + var div1 = document.getElementById('capturePieChart'); + var div2 = document.getElementById('captureColumnDiagram'); + var div3 = document.getElementById('captureTable'); - var ratio = - imageWidth / imageHeight >= pageWidth / pageHeight - ? pageWidth / imageWidth - : pageHeight / imageHeight; - pdf.addImage(imgData, 'JPEG', 0, 0, imageWidth * ratio, imageHeight * ratio); - pdf.save('invoice.pdf'); - }); + exportPdf(div1, div2, div3, pdfExportDoneCallback); + }; + + let pdfExportDoneCallback = () => { + this.setState({ onExportClicked: false }); }; /* * Important function that is given as the callback parameter to the postCalculationRequest in order to be called @@ -100,7 +123,7 @@ class DetailsComponent extends Component { const { selectedProduct } = this.props; if (this.state.stillLoading) { - postCalculationRequest(selectedProduct.productID, handleFinishedDataRequest); + postCalculationRequest(this.state.selectedScenarioId, handleFinishedDataRequest); return ; } @@ -114,9 +137,12 @@ class DetailsComponent extends Component { @@ -131,9 +157,12 @@ class DetailsComponent extends Component { @@ -151,7 +180,9 @@ class DetailsComponent extends Component { onExportClick={handleExportPdfButton} onCloseClick={handleCloseBaselineButton} scenarioName={scenarioNames.baseline} + selectedScenarioType={this.state.selectedScenarioType} selectedProduct={selectedProduct} + newScenarioHandler={handleNewScenarioSelection} /> @@ -163,7 +194,9 @@ class DetailsComponent extends Component { onExportClick={handleExportPdfButton} onCloseClick={handleCloseModifiedButton} scenarioName={scenarioNames.modified} + selectedScenarioType={this.state.selectedScenarioType} selectedProduct={selectedProduct} + newScenarioHandler={handleNewScenarioSelection} /> @@ -178,9 +211,10 @@ DetailsComponent.propTypes = { categories: PropTypes.array, // [(categories.generation, categories.transmission)], modelID: PropTypes.string, // 'none', modelName: PropTypes.string, // 'please select a Product', - productID: PropTypes.string, // 'dummydum-13b0-4e09-9fb4-50398483ecfd' + productID: PropTypes.string.isRequired, // 'dummydum-13b0-4e09-9fb4-50398483ecfd' productImage: PropTypes.string, //ImagePath? - productName: PropTypes.string //'please select a Product' + productName: PropTypes.string, //'please select a Product' + scenarioType: PropTypes.string }) }; diff --git a/frontend/src/components/details/MobileTableComponent.jsx b/frontend/src/components/details/MobileTableComponent.jsx index 984f24e9..c33e478b 100644 --- a/frontend/src/components/details/MobileTableComponent.jsx +++ b/frontend/src/components/details/MobileTableComponent.jsx @@ -19,16 +19,6 @@ class MobileTableComponent extends Component { const idKey = this.props.tableKey; return ( - {/* dynamic display of product and model */} -
{this.props.productName}
-
- {/* Don't display model name if model name == product name or undefined */} - {this.props.modelName === this.props.productName || - this.props.modelName === undefined - ? '' - : this.props.modelName} -
- {/* style needs to be defined in js */} diff --git a/frontend/src/components/details/NavbarComponent.js b/frontend/src/components/details/NavbarComponent.js index b576c52e..4fca2c4d 100644 --- a/frontend/src/components/details/NavbarComponent.js +++ b/frontend/src/components/details/NavbarComponent.js @@ -3,6 +3,7 @@ import React from 'react'; import slugs from 'resources/slugs'; import { Link } from 'react-router-dom'; import { useHistory } from 'react-router-dom'; +import { CircularProgress } from '@material-ui/core'; /** * a divider Pannel for seperating search compoents and result components @@ -17,17 +18,24 @@ const NavbarComponent = (props) => { {/* used the history.goback() function to go one step backward where it stores the previous steps including the prevs stage of clicking items on the sidebar */}
- history.goBack()}> + history.goBack()}>