Skip to content

Commit

Permalink
Feature/optimize product templates (#472)
Browse files Browse the repository at this point in the history
* Transfer of modelVariant when requesting product templates to return the corresponding template file
Transfer of model version to show that information in template files
remove links from text because they are not routed correctly on product templates page
fix: show all popup info texts on topics in tree
show additional tree item "Generierte Vorlage" as parent for topics

* added different templates files for "xt" and "xt bund"
download product templates in zip format if external product template is selected
convert http urls into https format for download external product templates
fixed Apache POI: {placeholder} is treated as different runs so the placeholders were not replaced
  • Loading branch information
Bock4Soft authored Mar 7, 2024
1 parent 0151618 commit 2380eaa
Show file tree
Hide file tree
Showing 14 changed files with 425 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Table } from 'antd';
import { Table, TableProps } from 'antd';
import parse from 'html-react-parser';
import React from 'react';
// import { TableEntry } from '@dipa-projekt/projektassistent-openapi';
Expand All @@ -10,9 +10,9 @@ import { useDocumentation } from '../../../../context/DocumentationContext';
export function PageEntryContent() {
const { selectedPageEntry } = useDocumentation();

// const onChange: TableProps<any>['onChange'] = (pagination, filters, sorter, extra) => {
// console.log('params', pagination, filters, sorter, extra);
// };
const onChange: TableProps<any>['onChange'] = (pagination, filters, sorter, extra) => {
console.log('params', pagination, filters, sorter, extra);
};
// console.log('PageEntryContent subPageEntries', selectedPageEntry);

return (
Expand Down
100 changes: 91 additions & 9 deletions client/src/components/projekthandbuch/produktvorlagen/SubmitArea.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import { Button, Checkbox, Form } from 'antd';
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { useTemplate } from '../../../context/TemplateContext';
import { OperationOpts, SingleProduct } from '@dipa-projekt/projektassistent-openapi';
import { ModelVariant, OperationOpts, SingleProduct } from '@dipa-projekt/projektassistent-openapi';
import { MultiProducts } from '@dipa-projekt/projektassistent-openapi/dist/models/MultiProducts';
import { ProductOfProject } from '@dipa-projekt/projektassistent-openapi/dist/models';
import API from '../../../api';
import { AjaxResponse } from 'rxjs/ajax';
import { TemplateFormModal } from './TemplateFormModal';
import { useTranslation } from 'react-i18next';
import { BookTwoTone } from '@ant-design/icons';
import { weitApiUrl } from '../../app/App';
import { decodeXml, getJsonDataFromXml } from '../../../shares/utils';
import parse from 'html-react-parser';
import { useTailoring } from '../../../context/TailoringContext';

export function SubmitArea() {
const { t } = useTranslation();

const { tailoringParameter } = useTailoring();

const { checkedKeys, productsMap, insertTopicDescription, setInsertTopicDescription } = useTemplate();

const [modalVisible, setModalVisible] = useState(false);

const [selectedProducts, setSelectedProducts] = useState<ProductOfProject[]>([]);

const selectedProductsRef = React.useRef(selectedProducts);
useEffect(() => {
selectedProductsRef.current = selectedProducts;
}, [selectedProducts]);

const buttonItemLayout = {
wrapperCol: {
span: 16,
Expand All @@ -30,18 +41,28 @@ export function SubmitArea() {
projectName: string;
responsible: string;
participants: string[];
modelVariant: string;
version: string;
}): SingleProduct {
return Object.assign(selectedProducts[0], {
return Object.assign(selectedProductsRef.current[0], {
projectName: values.projectName,
responsible: values.responsible,
participants: values.participants,
modelVariant: values.modelVariant,
version: values.version,
}) as SingleProduct;
}

function collectDataForMultiProducts(values: { projectName: string }): MultiProducts {
function collectDataForMultiProducts(values: {
projectName: string;
modelVariant: string;
version: string;
}): MultiProducts {
return {
projectName: values.projectName,
products: selectedProducts,
products: selectedProductsRef.current,
modelVariant: values.modelVariant,
version: values.version,
} as MultiProducts;
}

Expand Down Expand Up @@ -75,17 +96,58 @@ export function SubmitArea() {

for (const checkedKey of checkedKeys.checked) {
const product = productsMap.get(checkedKey);

if (product) {
productOfProjectMap.set(product.product.id, {
productName: product.product.title,
disciplineName: product.discipline.title,
modelVariant: product.modelVariant,
version: product.version,
responsible: '',
participants: [],
chapters: getChaptersWithSamplesData(product.topics),
externalCopyTemplates: product.externalCopyTemplates.filter((externalCopyTemplate) =>
checkedKeys.checked.includes(externalCopyTemplate.id)
),
});
} else if (
Array.from(productsMap.values()).filter((x) => x.externalCopyTemplates.some((y) => y.id === checkedKey))
) {
const productWithExternalCopyTemplate = Array.from(productsMap.values()).filter((x) =>
x.externalCopyTemplates.find((y) => y.id === checkedKey)
);

if (productWithExternalCopyTemplate.length > 0) {
const foundProduct = productWithExternalCopyTemplate.at(0);
if (!foundProduct) {
continue;
}

const existingProduct = productOfProjectMap.get(foundProduct.product.id);
const externalCopyTemplates = foundProduct.externalCopyTemplates.filter((externalCopyTemplate) =>
checkedKeys.checked.includes(externalCopyTemplate.id)
);

if (existingProduct) {
productOfProjectMap.set(
foundProduct.product.id,
Object.assign(existingProduct, {
externalCopyTemplates: externalCopyTemplates,
})
);
} else {
productOfProjectMap.set(foundProduct.product.id, {
productName: foundProduct.product.title,
disciplineName: foundProduct.discipline.title,
modelVariant: '',
version: '',
responsible: '',
participants: [],
chapters: [],
externalCopyTemplates: externalCopyTemplates,
});
}
}
}
}

Expand All @@ -101,16 +163,36 @@ export function SubmitArea() {
setSelectedProducts(products);
}

const handleClose = (values: { projectName: string; responsible: string; participants: string[] }) => {
const handleClose = async (values: { projectName: string; responsible: string; participants: string[] }) => {
setModalVisible(false);

if (values) {
const opts: OperationOpts = { responseOpts: { response: 'raw' } };

if (selectedProducts.length > 1) {
const projectModelVariantUrl =
weitApiUrl + '/V-Modellmetamodell/mm_2021/V-Modellvariante/' + tailoringParameter.modelVariantId;

const jsonDataFromXml = await getJsonDataFromXml(projectModelVariantUrl);
const version = parse(decodeXml(jsonDataFromXml.getElementsByTagName('Version')[0]?.value));

const modelVariantsUrl = weitApiUrl + '/V-Modellmetamodell/mm_2021/V-Modellvariante';
const jsonDataFromXml2 = await getJsonDataFromXml(modelVariantsUrl);

const modelVariants: ModelVariant[] = jsonDataFromXml2
.getElementsByTagName('V-Modellvariante')
.filter((variante) => variante.attributes.id === tailoringParameter.modelVariantId);

const modelVariantName = modelVariants[0].attributes.name;

const valuesWithModelVariantAndVersion = Object.assign(values, {
modelVariant: modelVariantName,
version: version,
});

if (selectedProductsRef.current.length > 1 || selectedProductsRef.current[0].externalCopyTemplates.length > 0) {
API.ProductsApi.getZipForMultiProducts(
{
multiProducts: collectDataForMultiProducts(values),
multiProducts: collectDataForMultiProducts(valuesWithModelVariantAndVersion),
},
opts
).subscribe((response: AjaxResponse<Blob>) => {
Expand All @@ -119,7 +201,7 @@ export function SubmitArea() {
} else {
API.ProductsApi.getDocxForSingleProduct(
{
singleProduct: collectDataForSingleProduct(values),
singleProduct: collectDataForSingleProduct(valuesWithModelVariantAndVersion),
},
opts
).subscribe((response: AjaxResponse<Blob>) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';

import { Col, FloatButton, Form, Layout, Row, Spin } from 'antd';
import { TemplateProps, TemplatesContent } from './TemplatesContent';
import { decodeXml, getJsonDataFromXml, simpleDecodeXml } from '../../../shares/utils';
import { decodeXml, getJsonDataFromXml, removeLinksFromHtml, simpleDecodeXml } from '../../../shares/utils';
import { NavTypeEnum } from '../documentation/navigation/Navigation';
import { useTailoring } from '../../../context/TailoringContext';
import { SubmitArea } from './SubmitArea';
Expand Down Expand Up @@ -140,7 +140,7 @@ export function Templates() {
return {
key: topicValue.attributes.id,
label: topicValue.attributes.name,
infoText: decodeXml(infoText),
infoText: removeLinksFromHtml(decodeXml(infoText)),
dataType: NavTypeEnum.TOPIC,
disabled: false,
checked: false,
Expand Down Expand Up @@ -191,7 +191,30 @@ export function Templates() {

const jsonDataFromXml = await getJsonDataFromXml(topicUrl);

return jsonDataFromXml.getElementsByTagName('Beschreibung')[0]?.value;
const textBausteinRef = jsonDataFromXml.getElementsByTagName('TextbausteinRef');

let description;

if (textBausteinRef.length > 0) {
description = await getTextBlockContent(textBausteinRef[0].attributes.id);
} else {
description = jsonDataFromXml.getElementsByTagName('Beschreibung')[0]?.value;
}

return description;
}

async function getTextBlockContent(textbausteinId: string): Promise<string> {
const textbausteinUrl =
weitApiUrl +
'/V-Modellmetamodell/mm_2021/V-Modellvariante/' +
tailoringParameter.modelVariantId +
'/Textbaustein/' +
textbausteinId;

const jsonDataFromXml: XMLElement = await getJsonDataFromXml(textbausteinUrl);

return jsonDataFromXml.getElementsByTagName('Text')[0]?.value;
}

async function getSampleTexts(): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,15 @@ export function TemplatesContent(props: { entries: TemplateProps[] }) {
const externalCopyTemplatesForMap = [];

if (product.children) {
for (const topic of product.children) {
if (topic.infoText != null) {
const topicChildren = product.children.filter((child) => child.dataType === NavTypeEnum.TOPIC);
const externalCopyChildren = product.children.filter(
(child) => child.dataType === NavTypeEnum.EXTERNAL_TEMPLATE
);

if (topicChildren.length > 0) {
const generatedTemplateTreeItems: DataNode[] = [];

for (const topic of topicChildren) {
const topicHeader = (
<>
<span style={{ marginRight: '8px' }}>{topic.label}</span>
Expand Down Expand Up @@ -180,32 +187,69 @@ export function TemplatesContent(props: { entries: TemplateProps[] }) {
}
}

topicsTreeItems.push({
generatedTemplateTreeItems.push({
title: topicHeader,
key: topic.key,
selectable: true,
checkable: topic.dataType === NavTypeEnum.EXTERNAL_TEMPLATE,
checkable: false,
disabled: !product.checked,
disableCheckbox: false, //topic.dataType !== NavTypeEnum.EXTERNAL_TEMPLATE,
disableCheckbox: false,
icon: getIcon(topic.dataType),
children: samplesTreeItems,
className: 'canBeDisabled',
});

if (topic.dataType === NavTypeEnum.TOPIC) {
topicsForMap.push({
id: topic.key,
title: topic.label,
text: topic.infoText,
samples: samplesForMap,
});
} else if (topic.dataType === NavTypeEnum.EXTERNAL_TEMPLATE) {
externalCopyTemplatesForMap.push({
id: topic.key,
title: topic.label,
uri: topic.infoText,
});
}
topicsForMap.push({
id: topic.key,
title: topic.label,
text: topic.infoText,
samples: samplesForMap,
});
}

// Generierte Vorlage
topicsTreeItems.push({
title: 'Generierte Vorlage',
key: product.key,
selectable: true,
checkable: true,
disabled: false,
icon: null,
children: generatedTemplateTreeItems,
});
}

if (externalCopyChildren.length > 0) {
for (const externalCopy of externalCopyChildren) {
const externalCopyHeader = (
<>
<span style={{ marginRight: '8px' }}>{externalCopy.label}</span>
<Popover
destroyTooltipOnHide={true}
content={parse(externalCopy.infoText)}
title={externalCopy.label}
>
<InfoCircleTwoTone style={{ cursor: 'help' }} />
</Popover>
</>
);

topicsTreeItems.push({
title: externalCopyHeader,
key: externalCopy.key,
selectable: true,
checkable: true,
disabled: false,
disableCheckbox: false,
icon: getIcon(externalCopy.dataType),
children: [],
});

externalCopyTemplatesForMap.push({
id: externalCopy.key,
title: externalCopy.label,
uri: externalCopy.infoText,
});
}
}
}
Expand All @@ -220,7 +264,7 @@ export function TemplatesContent(props: { entries: TemplateProps[] }) {
if (topicsTreeItems.length > 0) {
productTreeItems.push({
title: productHeader,
key: product.key,
key: product.key + '_generatedTemplate',
selectable: true,
icon: getIcon(product.dataType),
children: topicsTreeItems,
Expand Down
4 changes: 4 additions & 0 deletions client/src/context/TemplateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type TemplateSession = {
{
product: { id: string; title: string };
discipline: { id: string; title: string };
modelVariant: { id: string; title: string };
version: { title: string };
topics: { id: string; title: string; text: string; samples: { id: string; title: string; text: string }[] }[];
externalCopyTemplates: { id: string; title: string; uri: string }[];
}
Expand Down Expand Up @@ -54,6 +56,8 @@ const TemplateSessionContextProvider = ({ children }: TemplateSessionProviderPro
{
product: { id: string; title: string };
discipline: { id: string; title: string };
modelVariant: { id: string; title: string };
version: { title: string };
topics: { id: string; title: string; text: string; samples: { id: string; title: string; text: string }[] }[];
externalCopyTemplates: { id: string; title: string; uri: string }[];
}
Expand Down
17 changes: 17 additions & 0 deletions client/src/shares/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,20 @@ export function replaceUrlInText(text: string, tailoringParameter: any, projectF
'"'
);
}

export function removeLinksFromHtml(htmlString: string) {
const elem = document.createElement('div');
elem.innerHTML = htmlString;

const aTag = elem.getElementsByTagName('a');

while (aTag.length) {
const parent = aTag[0].parentNode;
while (aTag[0].firstChild) {
parent?.insertBefore(aTag[0].firstChild, aTag[0]);
}
parent?.removeChild(aTag[0]);
}

return elem.innerHTML;
}
Loading

0 comments on commit 2380eaa

Please sign in to comment.