mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-31 17:38:48 +00:00
@@ -0,0 +1,208 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="566px" height="165px" viewBox="0 0 566 165" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>empty_doc_lib</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<rect id="path-1" x="5.68434189e-14" y="-1.01962883e-12" width="78.1679389" height="78.1679389" rx="2"></rect>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-2">
|
||||
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-3" x="-4.54747351e-13" y="5.68434189e-13" width="78.1679389" height="78.1679389" rx="2"></rect>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-4">
|
||||
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-5" x="-1.08002496e-12" y="7.81597009e-14" width="78.1679389" height="78.1679389" rx="2"></rect>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-6">
|
||||
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-7" x="1.29318778e-12" y="9.23705556e-14" width="78.1679389" height="78.1679389" rx="2"></rect>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-8">
|
||||
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-9" x="-2.96651592e-13" y="-7.60280727e-13" width="78.1679389" height="78.1679389" rx="2"></rect>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-10">
|
||||
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-11" x="3.48165941e-13" y="2.27373675e-13" width="78.1679389" height="78.1679389" rx="2"></rect>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-12">
|
||||
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-13" x="0" y="-5.40012479e-13" width="78.1679389" height="78.1679389" rx="2"></rect>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-14">
|
||||
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-15" x="0" y="0" width="78.1679389" height="78.1679389" rx="2"></rect>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox" id="filter-16">
|
||||
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter2" result="shadowBlurOuter2"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0" type="matrix" in="shadowBlurOuter2" result="shadowMatrixOuter2"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="shadowMatrixOuter2"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="empty-folder-state-desktop" transform="translate(-37.000000, -168.000000)">
|
||||
<g id="empty_doc_lib" transform="translate(38.000000, 169.000000)">
|
||||
<g id="Group-5" transform="translate(241.569490, 92.634375) rotate(-355.000000) translate(-241.569490, -92.634375) translate(202.069490, 53.134375)">
|
||||
<g id="Rectangle-1196-Copy-2">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
|
||||
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-1"></use>
|
||||
</g>
|
||||
<g id="filetype_video" transform="translate(9.770992, 9.770992)">
|
||||
<polygon id="Fill-1" points="0 58.6259542 58.6259542 58.6259542 58.6259542 0 0 0"></polygon>
|
||||
<path d="M39.0839695,21.9847328 L43.9694656,21.9847328 L43.9694656,17.0992366 L39.0839695,17.0992366 L39.0839695,21.9847328 Z M39.0839695,31.7557252 L43.9694656,31.7557252 L43.9694656,26.870229 L39.0839695,26.870229 L39.0839695,31.7557252 Z M39.0839695,41.5267176 L43.9694656,41.5267176 L43.9694656,36.6412214 L39.0839695,36.6412214 L39.0839695,41.5267176 Z M14.6564885,21.9847328 L19.5419847,21.9847328 L19.5419847,17.0992366 L14.6564885,17.0992366 L14.6564885,21.9847328 Z M14.6564885,31.7557252 L19.5419847,31.7557252 L19.5419847,26.870229 L14.6564885,26.870229 L14.6564885,31.7557252 Z M14.6564885,41.5267176 L19.5419847,41.5267176 L19.5419847,36.6412214 L14.6564885,36.6412214 L14.6564885,41.5267176 Z M43.9694656,7.32824427 L43.9694656,12.2137405 L39.0839695,12.2137405 L39.0839695,7.32824427 L19.5419847,7.32824427 L19.5419847,12.2137405 L14.6564885,12.2137405 L14.6564885,7.32824427 L9.77099237,7.32824427 L9.77099237,51.2977099 L14.6564885,51.2977099 L14.6564885,46.4122137 L19.5419847,46.4122137 L19.5419847,51.2977099 L39.0839695,51.2977099 L39.0839695,46.4122137 L43.9694656,46.4122137 L43.9694656,51.2977099 L48.8549618,51.2977099 L48.8549618,7.32824427 L43.9694656,7.32824427 Z" id="Fill-2" fill="#FFC107"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group-7" transform="translate(515.948329, 81.354522) rotate(-345.000000) translate(-515.948329, -81.354522) translate(476.448329, 41.854522)">
|
||||
<g id="Rectangle-1196-Copy-3">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-3"></use>
|
||||
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-3"></use>
|
||||
</g>
|
||||
<g id="filetype_image" transform="translate(9.770992, 9.770992)">
|
||||
<polygon id="Fill-1" points="0 58.6259542 58.6259542 58.6259542 58.6259542 0 0 0"></polygon>
|
||||
<path d="M20.7636031,32.9773435 L26.8704733,40.3178015 L35.4200916,29.3132214 L46.412458,43.9697099 L12.2139847,43.9697099 L20.7636031,32.9773435 Z M51.2979542,46.412458 L51.2979542,12.2139847 C51.2979542,9.51474809 49.1116947,7.32848855 46.412458,7.32848855 L12.2139847,7.32848855 C9.51474809,7.32848855 7.32848855,9.51474809 7.32848855,12.2139847 L7.32848855,46.412458 C7.32848855,49.1116947 9.51474809,51.2979542 12.2139847,51.2979542 L46.412458,51.2979542 C49.1116947,51.2979542 51.2979542,49.1116947 51.2979542,46.412458 L51.2979542,46.412458 Z" id="Fill-2" fill="#22BE73"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group-8" transform="translate(309.051884, 62.261808) rotate(-5.000000) translate(-309.051884, -62.261808) translate(269.551884, 22.761808)">
|
||||
<g id="Rectangle-1196-Copy-4">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-6)" xlink:href="#path-5"></use>
|
||||
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-5"></use>
|
||||
</g>
|
||||
<g id="filetype_googledocs" transform="translate(9.770992, 9.770992)">
|
||||
<polygon id="Fill-1" points="6.82121026e-13 58.6259542 58.6259542 58.6259542 58.6259542 -2.98427949e-13 6.82121026e-13 -2.98427949e-13"></polygon>
|
||||
<g id="Group-6" transform="translate(9.770992, 4.885496)">
|
||||
<path d="M7.32824427,21.9847328 L31.7557252,21.9847328 L31.7557252,19.5419847 L7.32824427,19.5419847 L7.32824427,21.9847328 Z M7.32824427,26.870229 L31.7557252,26.870229 L31.7557252,24.4274809 L7.32824427,24.4274809 L7.32824427,26.870229 Z M7.32824427,31.7557252 L31.7557252,31.7557252 L31.7557252,29.3129771 L7.32824427,29.3129771 L7.32824427,31.7557252 Z M7.32824427,36.6412214 L21.9847328,36.6412214 L21.9847328,34.1984733 L7.32824427,34.1984733 L7.32824427,36.6412214 Z M29.3129771,0 L4.88549618,0 C2.18625954,0 0.0244274809,2.18625954 0.0244274809,4.88549618 L0,43.9694656 C0,46.6687023 2.16183206,48.8549618 4.8610687,48.8549618 L34.1984733,48.8549618 C36.8977099,48.8549618 39.0839695,46.6687023 39.0839695,43.9694656 L39.0839695,9.77099237 L29.3129771,0 Z" id="Fill-2" fill="#2979FF"></path>
|
||||
<polygon id="Fill-4" fill-opacity="0.5" fill="#FFFFFF" points="29.3129771 9.77099237 29.3129771 -2.84217094e-14 39.0839695 9.77099237"></polygon>
|
||||
<polygon id="Fill-5" fill-opacity="0.2" fill="#000000" points="39.0839695 9.77099237 39.0839695 19.5419847 29.3129771 9.77099237"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group-9" transform="translate(155.408682, 49.364493) rotate(-345.000000) translate(-155.408682, -49.364493) translate(115.908682, 9.864493)">
|
||||
<g id="Rectangle-1196-Copy-5">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-8)" xlink:href="#path-7"></use>
|
||||
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-7"></use>
|
||||
</g>
|
||||
<g id="filetype_pdf" transform="translate(9.770992, 9.770992)">
|
||||
<polygon id="Fill-1" points="0 58.6259542 58.6259542 58.6259542 58.6259542 0 0 0"></polygon>
|
||||
<path d="M45.1888855,25.5877863 L45.1888855,21.9187786 L37.853313,21.9187786 L37.853313,36.5923664 L41.5198779,36.5923664 L41.5198779,31.7777099 L45.1888855,31.7777099 L45.1888855,28.1087023 L41.5198779,28.1087023 L41.5198779,25.5877863 L45.1888855,25.5877863 Z M29.3696489,32.9233588 L31.7757557,32.9233588 L31.7757557,25.5877863 L29.3696489,25.5877863 L29.3696489,32.9233588 Z M35.4447634,32.9233588 L35.4447634,25.5877863 C35.4447634,24.5960305 35.1027786,23.7361832 34.4139237,23.0082443 C33.7275115,22.2827481 32.8481221,21.9187786 31.7781985,21.9187786 L25.703084,21.9187786 L25.703084,36.5923664 L31.7781985,36.5923664 C32.8481221,36.5923664 33.7275115,36.2308397 34.4139237,35.5029008 C35.1027786,34.7774046 35.4447634,33.9175573 35.4447634,32.9233588 L35.4447634,32.9233588 Z M17.1070534,28.1087023 L19.5131603,28.1087023 L19.5131603,25.5877863 L17.1070534,25.5877863 L17.1070534,28.1087023 Z M23.1821679,28.1087023 L23.1821679,25.5877863 C23.1821679,24.5960305 22.8181985,23.7361832 22.0927023,23.0082443 C21.3672061,22.2827481 20.5073588,21.9187786 19.5131603,21.9187786 L13.4380458,21.9187786 L13.4380458,36.5923664 L17.1070534,36.5923664 L17.1070534,31.7777099 L19.5131603,31.7777099 C20.5073588,31.7777099 21.3672061,31.4161832 22.0927023,30.6882443 C22.8181985,29.9627481 23.1821679,29.1029008 23.1821679,28.1087023 L23.1821679,28.1087023 Z M46.483542,7.32824427 C47.783084,7.32824427 48.9091908,7.8070229 49.8643053,8.7621374 C50.8218626,9.71725191 51.2981985,10.8433588 51.2981985,12.1429008 L51.2981985,46.3682443 C51.2981985,47.670229 50.8218626,48.8158779 49.8643053,49.8076336 C48.9091908,50.8018321 47.783084,51.2977099 46.483542,51.2977099 L12.2581985,51.2977099 C10.9586565,51.2977099 9.81300763,50.8018321 8.81880916,49.8076336 C7.82461069,48.8158779 7.32873282,47.670229 7.32873282,46.3682443 L7.32873282,12.1429008 C7.32873282,10.8433588 7.82461069,9.71725191 8.81880916,8.7621374 C9.81300763,7.8070229 10.9586565,7.32824427 12.2581985,7.32824427 L46.483542,7.32824427 Z" id="Fill-2" fill="#E91E63"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group-12" transform="translate(49.364493, 62.584254) rotate(-15.000000) translate(-49.364493, -62.584254) translate(9.864493, 23.084254)">
|
||||
<g id="Rectangle-1196-Copy-7">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-10)" xlink:href="#path-9"></use>
|
||||
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-9"></use>
|
||||
</g>
|
||||
<g id="filetype_forms" transform="translate(9.770992, 9.770992)">
|
||||
<polygon id="Fill-1" points="0 58.6259542 58.6259542 58.6259542 58.6259542 0 0 0"></polygon>
|
||||
<path d="M24.4274809,24.4250382 L41.5267176,24.4250382 L41.5267176,19.539542 L24.4274809,19.539542 L24.4274809,24.4250382 Z M24.4274809,31.7532824 L41.5267176,31.7532824 L41.5267176,26.8677863 L24.4274809,26.8677863 L24.4274809,31.7532824 Z M24.4274809,39.0839695 L41.5267176,39.0839695 L41.5267176,34.1984733 L24.4274809,34.1984733 L24.4274809,39.0839695 Z M17.0992366,24.4250382 L21.9847328,24.4250382 L21.9847328,19.539542 L17.0992366,19.539542 L17.0992366,24.4250382 Z M17.0992366,31.7532824 L21.9847328,31.7532824 L21.9847328,26.8677863 L17.0992366,26.8677863 L17.0992366,31.7532824 Z M17.0992366,39.0839695 L21.9847328,39.0839695 L21.9847328,34.1984733 L17.0992366,34.1984733 L17.0992366,39.0839695 Z M43.9694656,9.77099237 L14.6564885,9.77099237 C11.9694656,9.77099237 9.77099237,11.9694656 9.77099237,14.6564885 L9.77099237,43.9694656 C9.77099237,46.6564885 11.9694656,48.8549618 14.6564885,48.8549618 L43.9694656,48.8549618 C46.6564885,48.8549618 48.8549618,46.6564885 48.8549618,43.9694656 L48.8549618,14.6564885 C48.8549618,11.9694656 46.6564885,9.77099237 43.9694656,9.77099237 L43.9694656,9.77099237 Z" id="Fill-2" fill="#651FFF"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group-11" transform="translate(107.814782, 114.998541) rotate(-10.000000) translate(-107.814782, -114.998541) translate(68.314782, 75.498541)">
|
||||
<g id="Rectangle-1196-Copy-6">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-12)" xlink:href="#path-11"></use>
|
||||
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-11"></use>
|
||||
</g>
|
||||
<g id="filetype_excel" transform="translate(9.770992, 9.770992)">
|
||||
<polygon id="Fill-1" points="-5.68434189e-14 58.6259542 58.6259542 58.6259542 58.6259542 -1.56319402e-13 -5.68434189e-14 -1.56319402e-13"></polygon>
|
||||
<g id="Group-10" transform="translate(4.885496, 4.885496)" fill="#22BE73">
|
||||
<path d="M47.1621374,41.632 L28.848855,41.632 L28.848855,38.3025344 L33.2873282,38.3025344 L33.2873282,34.4161221 L28.848855,34.4161221 L28.848855,32.1981069 L33.2873282,32.1981069 L33.2873282,28.3116947 L28.848855,28.3116947 L28.848855,26.0936794 L33.2873282,26.0936794 L33.2873282,22.2072672 L28.848855,22.2072672 L28.848855,19.9868092 L33.2873282,19.9868092 L33.2873282,16.1028397 L28.848855,16.1028397 L28.848855,13.8823817 L33.2873282,13.8823817 L33.2873282,9.99841221 L28.848855,9.99841221 L28.848855,6.66894656 L47.1621374,6.66894656 L47.1621374,41.632 L47.1621374,41.632 Z M16.3444275,33.1141374 C15.4015267,30.7984122 14.2509924,28.5632977 13.5743511,26.1425344 C12.819542,28.3947481 11.7422901,30.5223817 10.8775573,32.730626 C9.6610687,32.7135267 8.4470229,32.6646718 7.23053435,32.613374 C8.65709924,29.821313 10.0348092,27.0072672 11.5053435,24.2323053 C10.2546565,21.3742901 8.88427481,18.572458 7.59694656,15.731542 C8.81832061,15.6582595 10.0396947,15.5874198 11.2610687,15.5190229 C12.0867176,17.690626 12.9929771,19.832916 13.6745038,22.0582595 C14.4073282,19.6985649 15.5016794,17.4781069 16.4372519,15.1990229 C17.6928244,15.1086412 18.9532824,15.032916 20.2112977,14.9718473 C18.7309924,18.0057405 17.2433588,21.0420763 15.7337405,24.0661985 C17.260458,27.1758168 18.8189313,30.2610076 20.3505344,33.3681832 C19.0143511,33.2900153 17.6806107,33.2069618 16.3444275,33.1141374 L16.3444275,33.1141374 Z M48.8329771,37.2203969 C48.8280916,27.591084 48.8158779,17.961771 48.8427481,8.32757252 C48.8036641,7.38467176 48.8720611,6.34161832 48.300458,5.51841221 C47.4845802,4.9590229 46.4512977,5.0249771 45.5132824,4.98589313 C39.9584733,5.01520611 34.4036641,5.00299237 28.848855,5.00299237 L28.848855,0.564519084 L25.551145,0.564519084 C17.0381679,2.06680916 8.5178626,3.52757252 4.68958206e-13,5.00787786 L4.68958206e-13,43.852458 C8.46900763,45.3327634 16.9429008,46.7544427 25.4021374,48.2909313 L28.848855,48.2909313 L28.848855,43.2979542 C34.6039695,43.2857405 40.359084,43.3126107 46.1068702,43.2979542 C47.0351145,43.2588702 48.4225954,43.2295573 48.6448855,42.0765802 C48.9819847,40.4839084 48.8036641,38.8350534 48.8329771,37.2203969 L48.8329771,37.2203969 Z" id="Fill-2"></path>
|
||||
<path d="M35.5077863,13.8821374 L43.2781679,13.8821374 L43.2781679,9.99816794 L35.5077863,9.99816794 L35.5077863,13.8821374 Z M35.5077863,19.9865649 L43.2781679,19.9865649 L43.2781679,16.1025954 L35.5077863,16.1025954 L35.5077863,19.9865649 Z M35.5077863,26.0909924 L43.2781679,26.0909924 L43.2781679,22.2070229 L35.5077863,22.2070229 L35.5077863,26.0909924 Z M35.5077863,32.1954198 L43.2781679,32.1954198 L43.2781679,28.3114504 L35.5077863,28.3114504 L35.5077863,32.1954198 Z M35.5077863,38.3022901 L43.2781679,38.3022901 L43.2781679,34.4183206 L35.5077863,34.4183206 L35.5077863,38.3022901 Z" id="Combined-Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group-4" transform="translate(388.820630, 86.064353) rotate(-350.000000) translate(-388.820630, -86.064353) translate(349.320630, 46.564353)">
|
||||
<g id="Rectangle-1196-Copy">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-14)" xlink:href="#path-13"></use>
|
||||
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-13"></use>
|
||||
</g>
|
||||
<g id="filetype_audio" transform="translate(9.770992, 9.770992)">
|
||||
<polygon id="Fill-1" points="-1.13686838e-13 58.6259542 58.6259542 58.6259542 58.6259542 -6.39488462e-14 -1.13686838e-13 -6.39488462e-14"></polygon>
|
||||
<path d="M29.3129771,7.32824427 L43.9694656,7.32824427 L43.9694656,17.0601527 L34.2375573,17.0601527 L34.2375573,41.5658015 C34.2375573,44.2381679 33.2629008,46.5270229 31.3160305,48.4348092 C29.3691603,50.3450382 27.0607634,51.2977099 24.3883969,51.2977099 C21.7160305,51.2977099 19.4271756,50.3450382 17.5193893,48.4348092 C15.6091603,46.5270229 14.6564885,44.2381679 14.6564885,41.5658015 C14.6564885,38.8934351 15.6091603,36.5850382 17.5193893,34.6381679 C19.4271756,32.6912977 21.7160305,31.7166412 24.3883969,31.7166412 C25.9932824,31.7166412 27.6323664,32.1758779 29.3129771,33.0919084 L29.3129771,7.32824427 Z" id="Fill-2" fill="#E91E63"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group-2" transform="translate(411.603053, 1.221374)">
|
||||
<g id="Group-3">
|
||||
<g id="Rectangle-1196">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-16)" xlink:href="#path-15"></use>
|
||||
<use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-15"></use>
|
||||
</g>
|
||||
<polygon id="Fill-1" points="9.77099237 68.3969466 68.3969466 68.3969466 68.3969466 9.77099237 9.77099237 9.77099237"></polygon>
|
||||
</g>
|
||||
<g id="filetype_book" transform="translate(9.770992, 9.770992)">
|
||||
<polygon id="Fill-1" points="0 58.6259542 58.6259542 58.6259542 58.6259542 0 0 0"></polygon>
|
||||
<path d="M14.6564885,9.77099237 L26.870229,9.77099237 L26.870229,29.3129771 L20.7633588,25.648855 L14.6564885,29.3129771 L14.6564885,9.77099237 Z M43.9694656,4.88549618 L14.6564885,4.88549618 C11.9572519,4.88549618 9.77099237,7.07175573 9.77099237,9.77099237 L9.77099237,48.8549618 C9.77099237,51.5541985 11.9572519,53.740458 14.6564885,53.740458 L43.9694656,53.740458 C46.6687023,53.740458 48.8549618,51.5541985 48.8549618,48.8549618 L48.8549618,9.77099237 C48.8549618,7.07175573 46.6687023,4.88549618 43.9694656,4.88549618 L43.9694656,4.88549618 Z" id="Fill-2" fill="#FF6D40"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,170 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { ProcessService } from './../services/process.service';
|
||||
import { ProcessAuditDirective } from './process-audit.directive';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
describe('ProcessAuditDirective', () => {
|
||||
|
||||
let fixture: ComponentFixture<BasicButtonComponent>;
|
||||
let component: BasicButtonComponent;
|
||||
let service: ProcessService;
|
||||
|
||||
function createFakePdfBlob(): Blob {
|
||||
let pdfData = atob(
|
||||
'JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwog' +
|
||||
'IC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAv' +
|
||||
'TWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0K' +
|
||||
'Pj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAg' +
|
||||
'L1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+' +
|
||||
'PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9u' +
|
||||
'dAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2Jq' +
|
||||
'Cgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJU' +
|
||||
'CjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVu' +
|
||||
'ZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4g' +
|
||||
'CjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAw' +
|
||||
'MDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9v' +
|
||||
'dCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G');
|
||||
return new Blob([pdfData], {type: 'application/pdf'});
|
||||
}
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [BasicButtonComponent, ProcessAuditDirective],
|
||||
providers: [ProcessService]
|
||||
});
|
||||
|
||||
TestBed.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(BasicButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
service = TestBed.get(ProcessService);
|
||||
|
||||
jasmine.Ajax.install();
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
it('should fetch the pdf Blob when the format is pdf', fakeAsync(() => {
|
||||
component.fileName = 'FakeAuditName';
|
||||
component.format = 'pdf';
|
||||
let blob = createFakePdfBlob();
|
||||
spyOn(service, 'fetchProcessAuditPdfById').and.returnValue(Observable.of(blob));
|
||||
spyOn(component, 'onAuditClick').and.callThrough();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
let button = fixture.nativeElement.querySelector('#auditButton');
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(component.onAuditClick).toHaveBeenCalledWith({ format: 'pdf', value: blob, fileName: 'FakeAuditName' });
|
||||
});
|
||||
|
||||
button.click();
|
||||
|
||||
}));
|
||||
|
||||
it('should fetch the json info when the format is json', fakeAsync(() => {
|
||||
component.fileName = 'FakeAuditName';
|
||||
component.format = 'json';
|
||||
component.download = true;
|
||||
const auditJson = {
|
||||
processInstanceId: 42516, processInstanceName: 'Fake Process - August 3rd 2017',
|
||||
processDefinitionName: 'Claim Approval Process', processDefinitionVersion: 1, processInstanceStartTime: 'Thu Aug 03 15:32:47 UTC 2017', processInstanceEndTime: null,
|
||||
processInstanceDurationInMillis: null,
|
||||
processInstanceInitiator: 'MyName MyLastname',
|
||||
entries: [{
|
||||
index: 1, type: 'startForm',
|
||||
selectedOutcome: null, formData: [{
|
||||
fieldName: 'User Name',
|
||||
fieldId: 'username', value: 'dsassd'
|
||||
},
|
||||
{ fieldName: 'Claim Amount', fieldId: 'claimamount', value: '22' }], taskName: null, taskAssignee: null, activityId: null,
|
||||
activityName: null, activityType: null, startTime: null, endTime: null, durationInMillis: null
|
||||
}
|
||||
], decisionInfo: { calculatedValues: [], appliedRules: [] }
|
||||
};
|
||||
spyOn(service, 'fetchProcessAuditJsonById').and.returnValue(Observable.of(auditJson));
|
||||
spyOn(component, 'onAuditClick').and.callThrough();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
let button = fixture.nativeElement.querySelector('#auditButton');
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(component.onAuditClick).toHaveBeenCalledWith({ format: 'json', value: auditJson, fileName: 'FakeAuditName' });
|
||||
});
|
||||
|
||||
button.click();
|
||||
|
||||
}));
|
||||
|
||||
it('should fetch the pdf Blob as default when the format is UNKNOW', fakeAsync(() => {
|
||||
component.fileName = 'FakeAuditName';
|
||||
component.format = 'fakeFormat';
|
||||
let blob = createFakePdfBlob();
|
||||
spyOn(service, 'fetchProcessAuditPdfById').and.returnValue(Observable.of(blob));
|
||||
spyOn(component, 'onAuditClick').and.callThrough();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
let button = fixture.nativeElement.querySelector('#auditButton');
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(component.onAuditClick).toHaveBeenCalledWith({ format: 'pdf', value: blob, fileName: 'FakeAuditName' });
|
||||
});
|
||||
|
||||
button.click();
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
@Component({
|
||||
selector: 'adf-basic-button',
|
||||
template: `
|
||||
<button id="auditButton"
|
||||
adf-process-audit
|
||||
[process-id]="currentProcessId"
|
||||
[download]="download"
|
||||
[fileName]="fileName"
|
||||
[format]="format"
|
||||
(clicked)="onAuditClick($event)">My button
|
||||
</button>`
|
||||
})
|
||||
class BasicButtonComponent {
|
||||
|
||||
download: boolean = false;
|
||||
fileName: string;
|
||||
format: string;
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
onAuditClick(event: any) {
|
||||
}
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ContentService } from '@alfresco/core';
|
||||
import { Directive, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||
import { ProcessService } from './../services/process.service';
|
||||
|
||||
const JSON_FORMAT: string = 'json';
|
||||
const PDF_FORMAT: string = 'pdf';
|
||||
|
||||
@Directive({
|
||||
selector: 'button[adf-process-audit]',
|
||||
host: {
|
||||
'role': 'button',
|
||||
'(click)': 'onClickAudit()'
|
||||
}
|
||||
})
|
||||
export class ProcessAuditDirective implements OnChanges {
|
||||
|
||||
@Input('process-id')
|
||||
processId: string;
|
||||
|
||||
@Input()
|
||||
fileName: string = 'Audit';
|
||||
|
||||
@Input()
|
||||
format: string = 'pdf';
|
||||
|
||||
@Input()
|
||||
download: boolean = true;
|
||||
|
||||
@Output()
|
||||
clicked: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
public audit: any;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param translateService
|
||||
* @param processListService
|
||||
*/
|
||||
constructor(private contentService: ContentService,
|
||||
private processListService: ProcessService) {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (!this.isValidType()) {
|
||||
this.setDefaultFormatType();
|
||||
}
|
||||
}
|
||||
|
||||
isValidType() {
|
||||
if (this.format && (this.isJsonFormat() || this.isPdfFormat())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
setDefaultFormatType(): void {
|
||||
this.format = PDF_FORMAT;
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch the audit information in the requested format
|
||||
*/
|
||||
fetchAuditInfo(): void {
|
||||
if (this.isPdfFormat()) {
|
||||
this.processListService.fetchProcessAuditPdfById(this.processId).subscribe(
|
||||
(blob: Blob) => {
|
||||
this.audit = blob;
|
||||
if (this.download) {
|
||||
this.contentService.downloadBlob(this.audit, this.fileName + '.pdf');
|
||||
}
|
||||
this.clicked.emit({ format: this.format, value: this.audit, fileName: this.fileName });
|
||||
},
|
||||
(err) => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
} else {
|
||||
this.processListService.fetchProcessAuditJsonById(this.processId).subscribe(
|
||||
(res) => {
|
||||
this.audit = res;
|
||||
this.clicked.emit({ format: this.format, value: this.audit, fileName: this.fileName });
|
||||
},
|
||||
(err) => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onClickAudit() {
|
||||
this.fetchAuditInfo();
|
||||
}
|
||||
|
||||
isJsonFormat() {
|
||||
return this.format === JSON_FORMAT;
|
||||
}
|
||||
|
||||
isPdfFormat() {
|
||||
return this.format === PDF_FORMAT;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
<div class="menu-container">
|
||||
<mat-list>
|
||||
<mat-list-item (click)="selectFilter(filter)" *ngFor="let filter of filters"
|
||||
class="adf-filters__entry" [class.active]="currentFilter === filter">
|
||||
<mat-icon *ngIf="hasIcon" matListIcon class="adf-filters__entry-icon">assignment</mat-icon>
|
||||
<span matLine [attr.data-automation-id]="filter.name + '_filter'">{{filter.name}}</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
@@ -0,0 +1,29 @@
|
||||
@mixin adf-process-filters-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
|
||||
.adf {
|
||||
|
||||
&-filters__entry {
|
||||
cursor: pointer;
|
||||
font-size: 14px!important;
|
||||
font-weight: bold;
|
||||
opacity: .54;
|
||||
padding-left: 30px;
|
||||
|
||||
.mat-list-item-content {
|
||||
height: 34px;
|
||||
}
|
||||
&.active, &:hover {
|
||||
color: mat-color($primary);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&-filters__entry-icon {
|
||||
padding-right: 12px !important;
|
||||
padding-left: 0px !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,210 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SimpleChange } from '@angular/core';
|
||||
import { AppsProcessService } from '@alfresco/core';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { FilterProcessRepresentationModel } from '../models/filter-process.model';
|
||||
import { ProcessFilterService } from '../services/process-filter.service';
|
||||
import { ProcessFiltersComponent } from './process-filters.component';
|
||||
|
||||
describe('ActivitiFilters', () => {
|
||||
|
||||
let filterList: ProcessFiltersComponent;
|
||||
let processFilterService: ProcessFilterService;
|
||||
let appsProcessService: AppsProcessService;
|
||||
|
||||
let fakeGlobalFilter = [];
|
||||
fakeGlobalFilter.push(new FilterProcessRepresentationModel({
|
||||
name: 'FakeInvolvedTasks',
|
||||
filter: { state: 'open', assignment: 'fake-involved' }
|
||||
}));
|
||||
fakeGlobalFilter.push(new FilterProcessRepresentationModel({
|
||||
name: 'FakeMyTasks',
|
||||
filter: { state: 'open', assignment: 'fake-assignee' }
|
||||
}));
|
||||
|
||||
fakeGlobalFilter.push(new FilterProcessRepresentationModel({
|
||||
name: 'Running',
|
||||
filter: { state: 'open', assignment: 'fake-running' }
|
||||
}));
|
||||
|
||||
let fakeGlobalFilterPromise = new Promise(function (resolve, reject) {
|
||||
resolve(fakeGlobalFilter);
|
||||
});
|
||||
|
||||
let fakeErrorFilterList = {
|
||||
error: 'wrong request'
|
||||
};
|
||||
|
||||
let fakeErrorFilterPromise = new Promise(function (resolve, reject) {
|
||||
reject(fakeErrorFilterList);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
processFilterService = new ProcessFilterService(null, null);
|
||||
appsProcessService = new AppsProcessService(null, null);
|
||||
filterList = new ProcessFiltersComponent(processFilterService, appsProcessService);
|
||||
});
|
||||
|
||||
it('should return the filter task list', (done) => {
|
||||
spyOn(processFilterService, 'getProcessFilters').and.returnValue(Observable.fromPromise(fakeGlobalFilterPromise));
|
||||
const appId = '1';
|
||||
let change = new SimpleChange(null, appId, true);
|
||||
filterList.ngOnChanges({ 'appId': change });
|
||||
|
||||
filterList.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(filterList.filters).toBeDefined();
|
||||
expect(filterList.filters.length).toEqual(3);
|
||||
expect(filterList.filters[0].name).toEqual('FakeInvolvedTasks');
|
||||
expect(filterList.filters[1].name).toEqual('FakeMyTasks');
|
||||
expect(filterList.filters[2].name).toEqual('Running');
|
||||
done();
|
||||
});
|
||||
|
||||
filterList.ngOnInit();
|
||||
});
|
||||
|
||||
it('should select the Running process filter', (done) => {
|
||||
spyOn(processFilterService, 'getProcessFilters').and.returnValue(Observable.fromPromise(fakeGlobalFilterPromise));
|
||||
const appId = '1';
|
||||
let change = new SimpleChange(null, appId, true);
|
||||
filterList.ngOnChanges({ 'appId': change });
|
||||
|
||||
expect(filterList.currentFilter).toBeUndefined();
|
||||
|
||||
filterList.success.subscribe((res) => {
|
||||
filterList.selectRunningFilter();
|
||||
expect(filterList.currentFilter.name).toEqual('Running');
|
||||
done();
|
||||
});
|
||||
|
||||
filterList.ngOnInit();
|
||||
});
|
||||
|
||||
it('should return the filter task list, filtered By Name', (done) => {
|
||||
|
||||
let fakeDeployedApplicationsPromise = new Promise(function (resolve, reject) {
|
||||
resolve({ id: 1 });
|
||||
});
|
||||
|
||||
spyOn(appsProcessService, 'getDeployedApplicationsByName').and.returnValue(Observable.fromPromise(fakeDeployedApplicationsPromise));
|
||||
spyOn(processFilterService, 'getProcessFilters').and.returnValue(Observable.fromPromise(fakeGlobalFilterPromise));
|
||||
|
||||
let change = new SimpleChange(null, 'test', true);
|
||||
filterList.ngOnChanges({ 'appName': change });
|
||||
|
||||
filterList.success.subscribe((res) => {
|
||||
let deployApp: any = appsProcessService.getDeployedApplicationsByName;
|
||||
expect(deployApp.calls.count()).toEqual(1);
|
||||
expect(res).toBeDefined();
|
||||
done();
|
||||
});
|
||||
|
||||
filterList.ngOnInit();
|
||||
});
|
||||
|
||||
it('should emit an error with a bad response', (done) => {
|
||||
spyOn(processFilterService, 'getProcessFilters').and.returnValue(Observable.fromPromise(fakeErrorFilterPromise));
|
||||
|
||||
const appId = '1';
|
||||
let change = new SimpleChange(null, appId, true);
|
||||
filterList.ngOnChanges({ 'appId': change });
|
||||
|
||||
filterList.error.subscribe((err) => {
|
||||
expect(err).toBeDefined();
|
||||
done();
|
||||
});
|
||||
|
||||
filterList.ngOnInit();
|
||||
});
|
||||
|
||||
it('should emit an error with a bad response', (done) => {
|
||||
spyOn(appsProcessService, 'getDeployedApplicationsByName').and.returnValue(Observable.fromPromise(fakeErrorFilterPromise));
|
||||
|
||||
const appId = 'fake-app';
|
||||
let change = new SimpleChange(null, appId, true);
|
||||
filterList.ngOnChanges({ 'appName': change });
|
||||
|
||||
filterList.error.subscribe((err) => {
|
||||
expect(err).toBeDefined();
|
||||
done();
|
||||
});
|
||||
|
||||
filterList.ngOnInit();
|
||||
});
|
||||
|
||||
it('should emit an event when a filter is selected', (done) => {
|
||||
let currentFilter = new FilterProcessRepresentationModel({
|
||||
filter: {
|
||||
state: 'open',
|
||||
assignment: 'fake-involved'
|
||||
}
|
||||
});
|
||||
|
||||
filterList.filterClick.subscribe((filter: FilterProcessRepresentationModel) => {
|
||||
expect(filter).toBeDefined();
|
||||
expect(filter).toEqual(currentFilter);
|
||||
expect(filterList.currentFilter).toEqual(currentFilter);
|
||||
done();
|
||||
});
|
||||
|
||||
filterList.selectFilter(currentFilter);
|
||||
});
|
||||
|
||||
it('should reload filters by appId on binding changes', () => {
|
||||
spyOn(filterList, 'getFiltersByAppId').and.stub();
|
||||
const appId = '1';
|
||||
|
||||
let change = new SimpleChange(null, appId, true);
|
||||
filterList.ngOnChanges({ 'appId': change });
|
||||
|
||||
expect(filterList.getFiltersByAppId).toHaveBeenCalledWith(appId);
|
||||
});
|
||||
|
||||
it('should reload filters by appId null on binding changes', () => {
|
||||
spyOn(filterList, 'getFiltersByAppId').and.stub();
|
||||
const appId = null;
|
||||
|
||||
let change = new SimpleChange(null, appId, true);
|
||||
filterList.ngOnChanges({ 'appId': change });
|
||||
|
||||
expect(filterList.getFiltersByAppId).toHaveBeenCalledWith(appId);
|
||||
});
|
||||
|
||||
it('should reload filters by app name on binding changes', () => {
|
||||
spyOn(filterList, 'getFiltersByAppName').and.stub();
|
||||
const appName = 'fake-app-name';
|
||||
|
||||
let change = new SimpleChange(null, appName, true);
|
||||
filterList.ngOnChanges({ 'appName': change });
|
||||
|
||||
expect(filterList.getFiltersByAppName).toHaveBeenCalledWith(appName);
|
||||
});
|
||||
|
||||
it('should return the current filter after one is selected', () => {
|
||||
let filter = new FilterProcessRepresentationModel({
|
||||
name: 'FakeMyTasks',
|
||||
filter: { state: 'open', assignment: 'fake-assignee' }
|
||||
});
|
||||
expect(filterList.currentFilter).toBeUndefined();
|
||||
filterList.selectFilter(filter);
|
||||
expect(filterList.getCurrentFilter()).toBe(filter);
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,204 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AppsProcessService } from '@alfresco/core';
|
||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
|
||||
import { ProcessInstanceFilterRepresentation } from 'alfresco-js-api';
|
||||
import { Observable, Observer } from 'rxjs/Rx';
|
||||
import { FilterProcessRepresentationModel } from '../models/filter-process.model';
|
||||
import { ProcessFilterService } from './../services/process-filter.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-process-instance-filters',
|
||||
templateUrl: './process-filters.component.html',
|
||||
styleUrls: ['process-filters.component.scss']
|
||||
})
|
||||
export class ProcessFiltersComponent implements OnInit, OnChanges {
|
||||
|
||||
@Input()
|
||||
filterParam: FilterProcessRepresentationModel;
|
||||
|
||||
@Output()
|
||||
filterClick: EventEmitter<ProcessInstanceFilterRepresentation> = new EventEmitter<ProcessInstanceFilterRepresentation>();
|
||||
|
||||
@Output()
|
||||
success: EventEmitter<ProcessInstanceFilterRepresentation[]> = new EventEmitter<ProcessInstanceFilterRepresentation[]>();
|
||||
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
@Input()
|
||||
appId: number;
|
||||
|
||||
@Input()
|
||||
appName: string;
|
||||
|
||||
@Input()
|
||||
showIcon: boolean = true;
|
||||
|
||||
private filterObserver: Observer<ProcessInstanceFilterRepresentation>;
|
||||
filter$: Observable<ProcessInstanceFilterRepresentation>;
|
||||
|
||||
currentFilter: ProcessInstanceFilterRepresentation;
|
||||
|
||||
filters: ProcessInstanceFilterRepresentation [] = [];
|
||||
|
||||
constructor(private processFilterService: ProcessFilterService, private appsProcessService: AppsProcessService) {
|
||||
this.filter$ = new Observable<ProcessInstanceFilterRepresentation>(observer => this.filterObserver = observer).share();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.filter$.subscribe((filter: ProcessInstanceFilterRepresentation) => {
|
||||
this.filters.push(filter);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
let appId = changes['appId'];
|
||||
if (appId && (appId.currentValue || appId.currentValue === null)) {
|
||||
this.getFiltersByAppId(appId.currentValue);
|
||||
return;
|
||||
}
|
||||
let appName = changes['appName'];
|
||||
if (appName && appName.currentValue) {
|
||||
this.getFiltersByAppName(appName.currentValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filter list filtered by appId
|
||||
* @param appId - optional
|
||||
*/
|
||||
getFiltersByAppId(appId?: number) {
|
||||
this.processFilterService.getProcessFilters(appId).subscribe(
|
||||
(res: ProcessInstanceFilterRepresentation[]) => {
|
||||
if (res.length === 0 && this.isFilterListEmpty()) {
|
||||
this.processFilterService.createDefaultFilters(appId).subscribe(
|
||||
(resDefault: ProcessInstanceFilterRepresentation[]) => {
|
||||
this.resetFilter();
|
||||
resDefault.forEach((filter) => {
|
||||
this.filterObserver.next(filter);
|
||||
});
|
||||
|
||||
this.selectProcessFilter(this.filterParam);
|
||||
this.success.emit(resDefault);
|
||||
},
|
||||
(errDefault: any) => {
|
||||
this.error.emit(errDefault);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.resetFilter();
|
||||
res.forEach((filter) => {
|
||||
this.filterObserver.next(filter);
|
||||
});
|
||||
|
||||
this.selectProcessFilter(this.filterParam);
|
||||
this.success.emit(res);
|
||||
}
|
||||
},
|
||||
(err: any) => {
|
||||
this.error.emit(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filter list filtered by appName
|
||||
* @param appName
|
||||
*/
|
||||
getFiltersByAppName(appName: string) {
|
||||
this.appsProcessService.getDeployedApplicationsByName(appName).subscribe(
|
||||
application => {
|
||||
this.getFiltersByAppId(application.id);
|
||||
this.selectProcessFilter(this.filterParam);
|
||||
},
|
||||
(err) => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the selected filter as next
|
||||
* @param filter
|
||||
*/
|
||||
public selectFilter(filter: ProcessInstanceFilterRepresentation) {
|
||||
this.currentFilter = filter;
|
||||
this.filterClick.emit(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the first filter of a list if present
|
||||
*/
|
||||
public selectProcessFilter(filterParam: FilterProcessRepresentationModel) {
|
||||
if (filterParam) {
|
||||
this.filters.filter((processFilter: ProcessInstanceFilterRepresentation, index) => {
|
||||
if (filterParam.name && filterParam.name.toLowerCase() === processFilter.name.toLowerCase() || filterParam.index === index) {
|
||||
this.currentFilter = processFilter;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.isCurrentFilterEmpty()) {
|
||||
this.selectDefaultTaskFilter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the Running filter
|
||||
*/
|
||||
public selectRunningFilter() {
|
||||
this.selectProcessFilter(this.processFilterService.getRunningFilterInstance(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Select as default task filter the first in the list
|
||||
*/
|
||||
public selectDefaultTaskFilter() {
|
||||
if (!this.isFilterListEmpty()) {
|
||||
this.currentFilter = this.filters[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current task
|
||||
* @returns {ProcessInstanceFilterRepresentation}
|
||||
*/
|
||||
getCurrentFilter(): ProcessInstanceFilterRepresentation {
|
||||
return this.currentFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the filter list is empty
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isFilterListEmpty(): boolean {
|
||||
return this.filters === undefined || (this.filters && this.filters.length === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the filters properties
|
||||
*/
|
||||
private resetFilter() {
|
||||
this.filters = [];
|
||||
this.currentFilter = undefined;
|
||||
}
|
||||
|
||||
private isCurrentFilterEmpty(): boolean {
|
||||
return this.currentFilter === undefined || null ? true : false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.activiti-process-container {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
overflow: visible;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.adf-comments-dialog {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.adf-in-medias-res-button {
|
||||
margin: 16px 0;
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
<div *ngIf="!processInstanceDetails">{{ 'ADF_PROCESS_LIST.DETAILS.MESSAGES.NONE'|translate }}</div>
|
||||
<mat-card *ngIf="processInstanceDetails">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{ getProcessNameOrDescription('medium') }}</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<adf-process-instance-header
|
||||
[processInstance]="processInstanceDetails"
|
||||
(showProcessDiagram)="onShowProcessDiagram($event)">
|
||||
</adf-process-instance-header>
|
||||
|
||||
<button class="adf-in-medias-res-button" mat-button id="show-diagram-button" type="button" mat-button mat-raised-button [disabled]="isDiagramDisabled()" (click)="onShowProcessDiagram(processInstanceId)">{{ 'ADF_PROCESS_LIST.DETAILS.BUTTON.SHOW_DIAGRAM' | translate }}</button>
|
||||
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<adf-process-instance-tasks
|
||||
[processInstanceDetails]="processInstanceDetails"
|
||||
(taskClick)="onTaskClicked($event)">
|
||||
</adf-process-instance-tasks>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<div data-automation-id="header-status" *ngIf="isRunning()" class="adf-in-medias-res-button">
|
||||
<button mat-button type="button" (click)="cancelProcess()">{{ 'ADF_PROCESS_LIST.DETAILS.BUTTON.CANCEL' | translate }}</button>
|
||||
</div>
|
||||
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<adf-process-instance-comments #activiticomments
|
||||
[readOnly]="false"
|
||||
[processInstanceId]="processInstanceDetails.id">
|
||||
</adf-process-instance-comments>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
</mat-card-content>
|
||||
</mat-card>
|
@@ -0,0 +1,145 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DebugElement, NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MaterialModule } from '../../material.module';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
import { FormModule, FormService } from '@alfresco/core';
|
||||
import { TaskListModule } from '../../task-list';
|
||||
|
||||
import { ProcessInstance } from '../models/process-instance.model';
|
||||
import { exampleProcess, exampleProcessNoName } from './../../mock';
|
||||
import { ProcessService } from './../services/process.service';
|
||||
import { ProcessInstanceDetailsComponent } from './process-instance-details.component';
|
||||
|
||||
describe('ProcessInstanceDetailsComponent', () => {
|
||||
|
||||
let service: ProcessService;
|
||||
let formService: FormService;
|
||||
let component: ProcessInstanceDetailsComponent;
|
||||
let fixture: ComponentFixture<ProcessInstanceDetailsComponent>;
|
||||
let getProcessSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
MaterialModule,
|
||||
FormModule,
|
||||
TaskListModule
|
||||
],
|
||||
declarations: [
|
||||
ProcessInstanceDetailsComponent
|
||||
],
|
||||
providers: [
|
||||
ProcessService
|
||||
],
|
||||
schemas: [ NO_ERRORS_SCHEMA ]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
fixture = TestBed.createComponent(ProcessInstanceDetailsComponent);
|
||||
component = fixture.componentInstance;
|
||||
service = fixture.debugElement.injector.get(ProcessService);
|
||||
formService = fixture.debugElement.injector.get(FormService);
|
||||
|
||||
getProcessSpy = spyOn(service, 'getProcess').and.returnValue(Observable.of(exampleProcess));
|
||||
});
|
||||
|
||||
it('should not load task details when no processInstanceId is specified', () => {
|
||||
fixture.detectChanges();
|
||||
expect(getProcessSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set a placeholder message when processInstanceId not initialised', () => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.innerText).toBe('ADF_PROCESS_LIST.DETAILS.MESSAGES.NONE');
|
||||
});
|
||||
|
||||
it('should display a header when the processInstanceId is provided', async(() => {
|
||||
fixture.detectChanges();
|
||||
component.ngOnChanges({ 'processInstanceId': new SimpleChange(null, '123', true) });
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
let headerEl: DebugElement = fixture.debugElement.query(By.css('.mat-card-title '));
|
||||
expect(headerEl).not.toBeNull();
|
||||
expect(headerEl.nativeElement.innerText).toBe('Process 123');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display default details when the process instance has no name', async(() => {
|
||||
getProcessSpy = getProcessSpy.and.returnValue(Observable.of(exampleProcessNoName));
|
||||
fixture.detectChanges();
|
||||
component.ngOnChanges({ 'processInstanceId': new SimpleChange(null, '123', true) });
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
let headerEl: DebugElement = fixture.debugElement.query(By.css('.mat-card-title '));
|
||||
expect(headerEl).not.toBeNull();
|
||||
expect(headerEl.nativeElement.innerText).toBe('My Process - Nov 10, 2016, 3:37:30 AM');
|
||||
});
|
||||
}));
|
||||
|
||||
describe('change detection', () => {
|
||||
|
||||
let change = new SimpleChange('123', '456', true);
|
||||
let nullChange = new SimpleChange('123', null, true);
|
||||
|
||||
beforeEach(async(() => {
|
||||
component.processInstanceId = '123';
|
||||
fixture.detectChanges();
|
||||
component.tasksList = jasmine.createSpyObj('tasksList', ['load']);
|
||||
fixture.whenStable().then(() => {
|
||||
getProcessSpy.calls.reset();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should fetch new process details when processInstanceId changed', () => {
|
||||
component.ngOnChanges({ 'processInstanceId': change });
|
||||
expect(getProcessSpy).toHaveBeenCalledWith('456');
|
||||
});
|
||||
|
||||
it('should NOT fetch new process details when empty changeset made', () => {
|
||||
component.ngOnChanges({});
|
||||
expect(getProcessSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should NOT fetch new process details when processInstanceId changed to null', () => {
|
||||
component.ngOnChanges({ 'processInstanceId': nullChange });
|
||||
expect(getProcessSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set a placeholder message when processInstanceId changed to null', () => {
|
||||
component.ngOnChanges({ 'processInstanceId': nullChange });
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.innerText).toBe('ADF_PROCESS_LIST.DETAILS.MESSAGES.NONE');
|
||||
});
|
||||
|
||||
it('should display cancel button if process is running', () => {
|
||||
component.processInstanceDetails = new ProcessInstance({
|
||||
ended : null
|
||||
});
|
||||
fixture.detectChanges();
|
||||
let buttonEl = fixture.debugElement.query(By.css('[data-automation-id="header-status"] button'));
|
||||
expect(buttonEl).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,146 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { LogService } from '@alfresco/core';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { TaskDetailsEvent } from '../../task-list';
|
||||
|
||||
import { ProcessInstance } from '../models/process-instance.model';
|
||||
import { ProcessService } from './../services/process.service';
|
||||
import { ProcessInstanceHeaderComponent } from './process-instance-header.component';
|
||||
import { ProcessInstanceTasksComponent } from './process-instance-tasks.component';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-process-instance-details',
|
||||
templateUrl: './process-instance-details.component.html',
|
||||
styleUrls: ['./process-instance-details.component.css']
|
||||
})
|
||||
export class ProcessInstanceDetailsComponent implements OnChanges {
|
||||
|
||||
@Input()
|
||||
processInstanceId: string;
|
||||
|
||||
@ViewChild(ProcessInstanceHeaderComponent)
|
||||
processInstanceHeader: ProcessInstanceHeaderComponent;
|
||||
|
||||
@ViewChild(ProcessInstanceTasksComponent)
|
||||
tasksList: ProcessInstanceTasksComponent;
|
||||
|
||||
@Input()
|
||||
showTitle: boolean = true;
|
||||
|
||||
@Input()
|
||||
showRefreshButton: boolean = true;
|
||||
|
||||
@Output()
|
||||
processCancelled: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
@Output()
|
||||
taskClick: EventEmitter<TaskDetailsEvent> = new EventEmitter<TaskDetailsEvent>();
|
||||
|
||||
processInstanceDetails: ProcessInstance;
|
||||
|
||||
@Output()
|
||||
showProcessDiagram: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param translate Translation service
|
||||
* @param activitiProcess Process service
|
||||
*/
|
||||
constructor(private activitiProcess: ProcessService,
|
||||
private logService: LogService) {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
let processInstanceId = changes['processInstanceId'];
|
||||
if (processInstanceId && !processInstanceId.currentValue) {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
if (processInstanceId && processInstanceId.currentValue) {
|
||||
this.load(processInstanceId.currentValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task detail to undefined
|
||||
*/
|
||||
reset() {
|
||||
this.processInstanceDetails = null;
|
||||
}
|
||||
|
||||
load(processId: string) {
|
||||
if (processId) {
|
||||
this.activitiProcess.getProcess(processId).subscribe(
|
||||
(res: ProcessInstance) => {
|
||||
this.processInstanceDetails = res;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
isRunning(): boolean {
|
||||
return this.processInstanceDetails && !this.processInstanceDetails.ended;
|
||||
}
|
||||
|
||||
isDiagramDisabled(): boolean {
|
||||
return !this.isRunning() ? true : undefined;
|
||||
}
|
||||
|
||||
cancelProcess() {
|
||||
this.activitiProcess.cancelProcess(this.processInstanceId).subscribe(
|
||||
(data) => {
|
||||
this.processCancelled.emit(data);
|
||||
}, (err) => {
|
||||
this.error.emit(err);
|
||||
});
|
||||
}
|
||||
|
||||
// bubbles (taskClick) event
|
||||
onTaskClicked(event: TaskDetailsEvent) {
|
||||
this.taskClick.emit(event);
|
||||
}
|
||||
|
||||
getProcessNameOrDescription(dateFormat): string {
|
||||
let name = '';
|
||||
if (this.processInstanceDetails) {
|
||||
name = this.processInstanceDetails.name ||
|
||||
this.processInstanceDetails.processDefinitionName + ' - ' + this.getFormatDate(this.processInstanceDetails.started, dateFormat);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
getFormatDate(value, format: string) {
|
||||
let datePipe = new DatePipe('en-US');
|
||||
try {
|
||||
return datePipe.transform(value, format);
|
||||
} catch (err) {
|
||||
this.logService.error(`ProcessListInstanceHeader: error parsing date ${value} to format ${format}`);
|
||||
}
|
||||
}
|
||||
|
||||
onShowProcessDiagram(processInstanceId: any) {
|
||||
this.showProcessDiagram.emit({value: this.processInstanceId});
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.adf-card-container {
|
||||
font-family: inherit;
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
<mat-card *ngIf="processInstance" class="adf-card-container">
|
||||
<mat-card-content>
|
||||
<adf-card-view [properties]="properties"></adf-card-view>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
@@ -0,0 +1,166 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CardViewUpdateService } from '@alfresco/core';
|
||||
import { MaterialModule } from '../../material.module';
|
||||
|
||||
import { ProcessInstance } from '../models/process-instance.model';
|
||||
import { exampleProcess } from '../../mock';
|
||||
import { ProcessService } from './../services/process.service';
|
||||
import { ProcessInstanceHeaderComponent } from './process-instance-header.component';
|
||||
|
||||
describe('ProcessInstanceHeaderComponent', () => {
|
||||
|
||||
let service: ProcessService;
|
||||
let component: ProcessInstanceHeaderComponent;
|
||||
let fixture: ComponentFixture<ProcessInstanceHeaderComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
MaterialModule
|
||||
],
|
||||
declarations: [
|
||||
ProcessInstanceHeaderComponent
|
||||
],
|
||||
providers: [
|
||||
ProcessService,
|
||||
CardViewUpdateService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
fixture = TestBed.createComponent(ProcessInstanceHeaderComponent);
|
||||
component = fixture.componentInstance;
|
||||
service = TestBed.get(ProcessService);
|
||||
|
||||
component.processInstance = new ProcessInstance(exampleProcess);
|
||||
});
|
||||
|
||||
it('should render empty component if no process details provided', () => {
|
||||
component.processInstance = undefined;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.children.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should display status as running when process is not complete', () => {
|
||||
component.processInstance.ended = null;
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-status"]');
|
||||
expect(valueEl.innerText).toBe('Running');
|
||||
});
|
||||
|
||||
it('should display status as completed when process is complete', () => {
|
||||
component.processInstance.ended = new Date('2016-11-03');
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-status"]');
|
||||
expect(valueEl.innerText).toBe('Completed');
|
||||
});
|
||||
|
||||
it('should display due date', () => {
|
||||
component.processInstance.ended = new Date('2016-11-03');
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-dateitem-dueDate"]');
|
||||
expect(valueEl.innerText).toBe('Nov 03 2016');
|
||||
});
|
||||
|
||||
it('should display placeholder if no due date', () => {
|
||||
component.processInstance.ended = null;
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-dateitem-dueDate"]');
|
||||
expect(valueEl.innerText).toBe('ADF_PROCESS_LIST.PROPERTIES.DUE_DATE_DEFAULT');
|
||||
});
|
||||
|
||||
it('should display process category', () => {
|
||||
component.processInstance.processDefinitionCategory = 'Accounts';
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-category"]');
|
||||
expect(valueEl.innerText).toBe('Accounts');
|
||||
});
|
||||
|
||||
it('should display placeholder if no process category', () => {
|
||||
component.processInstance.processDefinitionCategory = null;
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-category"]');
|
||||
expect(valueEl.innerText).toBe('ADF_PROCESS_LIST.PROPERTIES.CATEGORY_DEFAULT');
|
||||
});
|
||||
|
||||
it('should display created date', () => {
|
||||
component.processInstance.started = new Date('2016-11-03');
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-dateitem-created"]');
|
||||
expect(valueEl.innerText).toBe('Nov 03 2016');
|
||||
});
|
||||
|
||||
it('should display started by', () => {
|
||||
component.processInstance.startedBy = {firstName: 'Admin', lastName: 'User'};
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-assignee"]');
|
||||
expect(valueEl.innerText).toBe('Admin User');
|
||||
});
|
||||
|
||||
it('should display process instance id', () => {
|
||||
component.processInstance.id = '123';
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-id"]');
|
||||
expect(valueEl.innerText).toBe('123');
|
||||
});
|
||||
|
||||
it('should display description', () => {
|
||||
component.processInstance.processDefinitionDescription = 'Test process';
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-description"]');
|
||||
expect(valueEl.innerText).toBe('Test process');
|
||||
});
|
||||
|
||||
it('should display placeholder if no description', () => {
|
||||
component.processInstance.processDefinitionDescription = null;
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-description"]');
|
||||
expect(valueEl.innerText).toBe('ADF_PROCESS_LIST.PROPERTIES.DESCRIPTION_DEFAULT');
|
||||
});
|
||||
|
||||
it('should display businessKey value', () => {
|
||||
component.processInstance.businessKey = 'fakeBusinessKey';
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-businessKey"]');
|
||||
expect(valueEl.innerText).toBe('fakeBusinessKey');
|
||||
});
|
||||
|
||||
it('should display default key if no businessKey', () => {
|
||||
component.processInstance.businessKey = null;
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
let valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-businessKey"]');
|
||||
expect(valueEl.innerText).toBe('ADF_PROCESS_LIST.PROPERTIES.BUSINESS_KEY_DEFAULT');
|
||||
});
|
||||
});
|
@@ -0,0 +1,117 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CardViewDateItemModel, CardViewItem, CardViewTextItemModel } from '@alfresco/core';
|
||||
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { ProcessInstance } from '../models/process-instance.model';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-process-instance-header',
|
||||
templateUrl: './process-instance-header.component.html',
|
||||
styleUrls: ['./process-instance-header.component.css']
|
||||
})
|
||||
export class ProcessInstanceHeaderComponent implements OnChanges {
|
||||
|
||||
@Input()
|
||||
processInstance: ProcessInstance;
|
||||
|
||||
properties: CardViewItem [];
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
refreshData() {
|
||||
if (this.processInstance) {
|
||||
this.properties = [
|
||||
new CardViewTextItemModel(
|
||||
{
|
||||
label: 'ADF_PROCESS_LIST.PROPERTIES.STATUS',
|
||||
value: this.getProcessStatus(),
|
||||
key: 'status'
|
||||
}),
|
||||
new CardViewDateItemModel(
|
||||
{
|
||||
label: 'ADF_PROCESS_LIST.PROPERTIES.DUE_DATE',
|
||||
value: this.processInstance.ended,
|
||||
format: 'MMM DD YYYY',
|
||||
key: 'dueDate',
|
||||
default: 'ADF_PROCESS_LIST.PROPERTIES.DUE_DATE_DEFAULT'
|
||||
}),
|
||||
new CardViewTextItemModel(
|
||||
{
|
||||
label: 'ADF_PROCESS_LIST.PROPERTIES.CATEGORY',
|
||||
value: this.processInstance.processDefinitionCategory,
|
||||
key: 'category',
|
||||
default: 'ADF_PROCESS_LIST.PROPERTIES.CATEGORY_DEFAULT'
|
||||
}),
|
||||
new CardViewTextItemModel(
|
||||
{
|
||||
label: 'ADF_PROCESS_LIST.PROPERTIES.BUSINESS_KEY',
|
||||
value: this.processInstance.businessKey,
|
||||
key: 'businessKey',
|
||||
default: 'ADF_PROCESS_LIST.PROPERTIES.BUSINESS_KEY_DEFAULT'
|
||||
}),
|
||||
new CardViewTextItemModel(
|
||||
{
|
||||
label: 'ADF_PROCESS_LIST.PROPERTIES.CREATED_BY',
|
||||
value: this.getStartedByFullName(),
|
||||
key: 'assignee',
|
||||
default: 'ADF_PROCESS_LIST.PROPERTIES.CREATED_BY_DEFAULT'
|
||||
}),
|
||||
new CardViewDateItemModel(
|
||||
{
|
||||
label: 'ADF_PROCESS_LIST.PROPERTIES.CREATED',
|
||||
value: this.processInstance.started,
|
||||
format: 'MMM DD YYYY',
|
||||
key: 'created'
|
||||
}),
|
||||
new CardViewTextItemModel(
|
||||
{label: 'ADF_PROCESS_LIST.PROPERTIES.ID',
|
||||
value: this.processInstance.id,
|
||||
key: 'id'
|
||||
}),
|
||||
new CardViewTextItemModel(
|
||||
{label: 'ADF_PROCESS_LIST.PROPERTIES.DESCRIPTION',
|
||||
value: this.processInstance.processDefinitionDescription,
|
||||
key: 'description',
|
||||
default: 'ADF_PROCESS_LIST.PROPERTIES.DESCRIPTION_DEFAULT'
|
||||
})
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
getProcessStatus(): string {
|
||||
if (this.processInstance) {
|
||||
return this.isRunning() ? 'Running' : 'Completed';
|
||||
}
|
||||
}
|
||||
|
||||
getStartedByFullName(): string {
|
||||
let fullName = '';
|
||||
if (this.processInstance && this.processInstance.startedBy) {
|
||||
fullName += this.processInstance.startedBy.firstName || '';
|
||||
fullName += fullName ? ' ' : '';
|
||||
fullName += this.processInstance.startedBy.lastName || '';
|
||||
}
|
||||
return fullName;
|
||||
}
|
||||
|
||||
isRunning(): boolean {
|
||||
return this.processInstance && !this.processInstance.ended;
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.activiti-label {
|
||||
font-weight: bolder;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.adf-process-badge {
|
||||
pointer-events: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.adf-chip-label {
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin-right: 8px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.activiti-label + .icon {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
.task-details-dialog {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.process-tasks-refresh {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.adf-start-process-dialog {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.adf-start-process-dialog-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.adf-start-process-dialog-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
margin-left: 9px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
line-height: 18px;
|
||||
color: rgba(0, 0, 0, .54);
|
||||
display: block;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.process-tasks__task-item {
|
||||
cursor: pointer;
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
<div *ngIf="showRefreshButton" class="process-tasks-refresh" >
|
||||
<button mat-icon-button (click)="onRefreshClicked()">
|
||||
<mat-icon class="md-24" aria-label="Refresh">refresh</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ACTIVE FORM -->
|
||||
|
||||
<mat-chip-list>
|
||||
<span class="adf-chip-label">{{ 'ADF_PROCESS_LIST.DETAILS.LABELS.TASKS_ACTIVE'|translate }}</span>
|
||||
<mat-chip class="adf-process-badge" color="accent" selected="true">{{activeTasks?.length}}</mat-chip>
|
||||
</mat-chip-list>
|
||||
|
||||
<div class="menu-container" *ngIf="activeTasks?.length > 0" data-automation-id="active-tasks">
|
||||
<mat-list>
|
||||
<mat-list-item class="process-tasks__task-item" *ngFor="let task of activeTasks" (click)="clickTask($event, task)">
|
||||
<mat-icon mat-list-icon>assignment</mat-icon>
|
||||
<h3 matLine>{{task.name || 'Nameless task'}}</h3>
|
||||
<span matLine>
|
||||
{{ 'ADF_PROCESS_LIST.DETAILS.LABELS.TASK_SUBTITLE' | translate:{user: getUserFullName(task.assignee), created: getFormatDate(task.created, 'mediumDate') } }}
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
|
||||
<!-- START FORM -->
|
||||
|
||||
<div *ngIf="activeTasks?.length === 0" data-automation-id="active-tasks-none" class="no-results">
|
||||
{{ 'ADF_PROCESS_LIST.DETAILS.TASKS.NO_ACTIVE' | translate }}
|
||||
</div>
|
||||
|
||||
<div *ngIf="hasStartFormDefined()">
|
||||
<span class="activiti-label">{{ 'ADF_PROCESS_LIST.DETAILS.LABELS.START_FORM'|translate }}</span>
|
||||
|
||||
<!--IF START TASK COMPLETED -->
|
||||
<div class="menu-container">
|
||||
<mat-list>
|
||||
<mat-list-item class="process-tasks__task-item" (click)="clickStartTask($event)">
|
||||
<mat-icon mat-list-icon>assignment</mat-icon>
|
||||
<h3 matLine>{{ 'ADF_PROCESS_LIST.DETAILS.LABELS.START_FORM'|translate }}</h3>
|
||||
<span matLine>
|
||||
{{ 'ADF_PROCESS_LIST.DETAILS.LABELS.TASK_SUBTITLE' | translate:{user:getUserFullName(processInstanceDetails.startedBy), created: getFormatDate(processInstanceDetails.started, 'mediumDate') } }}
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- COMPLETED FORM -->
|
||||
<mat-chip-list>
|
||||
<span class="adf-chip-label">{{ 'ADF_PROCESS_LIST.DETAILS.LABELS.TASKS_COMPLETED'|translate }}</span>
|
||||
<mat-chip class="adf-process-badge" color="accent" selected="true">{{completedTasks?.length}}</mat-chip>
|
||||
</mat-chip-list>
|
||||
|
||||
<div class="menu-container" *ngIf="completedTasks?.length > 0" data-automation-id="completed-tasks">
|
||||
<mat-list>
|
||||
<mat-list-item class="process-tasks__task-item" *ngFor="let task of completedTasks" (click)="clickTask($event, task)">
|
||||
<mat-icon mat-list-icon>assignment</mat-icon>
|
||||
<h3 matLine>{{task.name || 'Nameless task'}}</h3>
|
||||
<span matLine>
|
||||
{{ 'ADF_PROCESS_LIST.DETAILS.LABELS.TASK_SUBTITLE' | translate:{user:getUserFullName(task.assignee), created: getFormatDate(task.created, 'mediumDate') } }}
|
||||
</span>
|
||||
</mat-list-item>
|
||||
</mat-list>
|
||||
</div>
|
||||
|
||||
<div *ngIf="completedTasks?.length === 0" data-automation-id="completed-tasks-none" class="no-results">
|
||||
{{ 'ADF_PROCESS_LIST.DETAILS.TASKS.NO_COMPLETED' | translate }}
|
||||
</div>
|
||||
|
||||
<ng-template *ngIf="hasStartFormDefined()" #startDialog>
|
||||
<div id="adf-start-process-dialog" class="adf-start-process-dialog">
|
||||
<h4 matDialogTitle>{{ 'ADF_PROCESS_LIST.DETAILS.LABELS.START_FORM'|translate }}</h4>
|
||||
<div mat-dialog-content class="adf-start-process-dialog-content">
|
||||
<adf-start-form [processId]="processId"
|
||||
[showRefreshButton]="false" [readOnlyForm]="true"
|
||||
(formContentClicked)='onFormContentClick($event)'>
|
||||
</adf-start-form>
|
||||
</div>
|
||||
<div mat-dialog-actions class="adf-start-process-dialog-actions">
|
||||
<button mat-button type="button" (click)="closeSartDialog()">{{ 'ADF_PROCESS_LIST.DETAILS.TASKS.TASK_CLOSE' | translate }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
@@ -0,0 +1,150 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DebugElement, NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
import { TaskDetailsModel } from '../../task-list';
|
||||
|
||||
import { taskDetailsMock } from '../../mock';
|
||||
import { ProcessInstance } from './../models/process-instance.model';
|
||||
import { ProcessService } from './../services/process.service';
|
||||
import { ProcessInstanceTasksComponent } from './process-instance-tasks.component';
|
||||
|
||||
describe('ProcessInstanceTasksComponent', () => {
|
||||
|
||||
let component: ProcessInstanceTasksComponent;
|
||||
let fixture: ComponentFixture<ProcessInstanceTasksComponent>;
|
||||
let debugElement: DebugElement;
|
||||
let service: ProcessService;
|
||||
let getProcessTasksSpy: jasmine.Spy;
|
||||
|
||||
let exampleProcessInstance = new ProcessInstance({ id: '123' });
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [],
|
||||
declarations: [
|
||||
ProcessInstanceTasksComponent
|
||||
],
|
||||
providers: [
|
||||
ProcessService
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
fixture = TestBed.createComponent(ProcessInstanceTasksComponent);
|
||||
component = fixture.componentInstance;
|
||||
debugElement = fixture.debugElement;
|
||||
service = fixture.debugElement.injector.get(ProcessService);
|
||||
getProcessTasksSpy = spyOn(service, 'getProcessTasks').and.returnValue(Observable.of([new TaskDetailsModel(taskDetailsMock)]));
|
||||
|
||||
});
|
||||
|
||||
it('should initially render message about no active tasks if no process instance ID provided', async(() => {
|
||||
component.processInstanceDetails = undefined;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let msgEl = fixture.debugElement.query(By.css('[data-automation-id="active-tasks-none"]'));
|
||||
expect(msgEl).not.toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should initially render message about no completed tasks if no process instance ID provided', async(() => {
|
||||
component.processInstanceDetails = undefined;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let msgEl = fixture.debugElement.query(By.css('[data-automation-id="completed-tasks-none"]'));
|
||||
expect(msgEl).not.toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not render active tasks list if no process instance ID provided', () => {
|
||||
component.processInstanceDetails = undefined;
|
||||
fixture.detectChanges();
|
||||
let listEl = fixture.debugElement.query(By.css('[data-automation-id="active-tasks"]'));
|
||||
expect(listEl).toBeNull();
|
||||
});
|
||||
|
||||
it('should not render completed tasks list if no process instance ID provided', () => {
|
||||
component.processInstanceDetails = undefined;
|
||||
fixture.detectChanges();
|
||||
let listEl = fixture.debugElement.query(By.css('[data-automation-id="completed-tasks"]'));
|
||||
expect(listEl).toBeNull();
|
||||
});
|
||||
|
||||
it('should display active tasks', () => {
|
||||
let change = new SimpleChange(null, exampleProcessInstance, true);
|
||||
fixture.detectChanges();
|
||||
component.ngOnChanges({ 'processInstanceDetails': change });
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
component.ngOnChanges({ 'processInstanceDetails': change });
|
||||
let listEl = fixture.debugElement.query(By.css('[data-automation-id="active-tasks"]'));
|
||||
expect(listEl).not.toBeNull();
|
||||
expect(listEl.queryAll(By.css('mat-list-item')).length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should display completed tasks', () => {
|
||||
let change = new SimpleChange(null, exampleProcessInstance, true);
|
||||
fixture.detectChanges();
|
||||
component.ngOnChanges({ 'processInstanceDetails': change });
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
let listEl = fixture.debugElement.query(By.css('[data-automation-id="completed-tasks"]'));
|
||||
expect(listEl).not.toBeNull();
|
||||
expect(listEl.queryAll(By.css('mat-list-item')).length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('task reloading', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
component.processInstanceDetails = exampleProcessInstance;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable();
|
||||
}));
|
||||
|
||||
it('should render a refresh button by default', () => {
|
||||
expect(fixture.debugElement.query(By.css('.process-tasks-refresh'))).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should render a refresh button if configured to', () => {
|
||||
component.showRefreshButton = true;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('.process-tasks-refresh'))).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT render a refresh button if configured not to', () => {
|
||||
component.showRefreshButton = false;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('.process-tasks-refresh'))).toBeNull();
|
||||
});
|
||||
|
||||
it('should call service to get tasks when reload button clicked', () => {
|
||||
getProcessTasksSpy.calls.reset();
|
||||
component.onRefreshClicked();
|
||||
expect(getProcessTasksSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,178 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { LogService } from '@alfresco/core';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { Observable, Observer } from 'rxjs/Rx';
|
||||
import { TaskDetailsEvent, TaskDetailsModel } from '../../task-list';
|
||||
import { ProcessInstance } from '../models/process-instance.model';
|
||||
import { ProcessService } from './../services/process.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-process-instance-tasks',
|
||||
templateUrl: './process-instance-tasks.component.html',
|
||||
styleUrls: ['./process-instance-tasks.component.css']
|
||||
})
|
||||
export class ProcessInstanceTasksComponent implements OnInit, OnChanges {
|
||||
|
||||
@Input()
|
||||
processInstanceDetails: ProcessInstance;
|
||||
|
||||
@Input()
|
||||
showRefreshButton: boolean = true;
|
||||
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
activeTasks: TaskDetailsModel[] = [];
|
||||
completedTasks: TaskDetailsModel[] = [];
|
||||
|
||||
private taskObserver: Observer<TaskDetailsModel>;
|
||||
private completedTaskObserver: Observer<TaskDetailsModel>;
|
||||
|
||||
task$: Observable<TaskDetailsModel>;
|
||||
completedTask$: Observable<TaskDetailsModel>;
|
||||
|
||||
message: string;
|
||||
processId: string;
|
||||
|
||||
// @ViewChild('dialog')
|
||||
// dialog: any;
|
||||
|
||||
@ViewChild('startDialog')
|
||||
startDialog: any;
|
||||
|
||||
@ViewChild('taskdetails')
|
||||
taskdetails: any;
|
||||
|
||||
@Output()
|
||||
taskClick: EventEmitter<TaskDetailsEvent> = new EventEmitter<TaskDetailsEvent>();
|
||||
|
||||
constructor(private activitiProcess: ProcessService,
|
||||
private logService: LogService,
|
||||
private dialog: MatDialog) {
|
||||
this.task$ = new Observable<TaskDetailsModel>(observer => this.taskObserver = observer).share();
|
||||
this.completedTask$ = new Observable<TaskDetailsModel>(observer => this.completedTaskObserver = observer).share();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.task$.subscribe((task: TaskDetailsModel) => {
|
||||
this.activeTasks.push(task);
|
||||
});
|
||||
this.completedTask$.subscribe((task: TaskDetailsModel) => {
|
||||
this.completedTasks.push(task);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
let processInstanceDetails = changes['processInstanceDetails'];
|
||||
if (processInstanceDetails && processInstanceDetails.currentValue) {
|
||||
this.load(processInstanceDetails.currentValue.id);
|
||||
}
|
||||
}
|
||||
|
||||
load(processInstanceId: string) {
|
||||
this.loadActive(processInstanceId);
|
||||
this.loadCompleted(processInstanceId);
|
||||
}
|
||||
|
||||
loadActive(processInstanceId: string) {
|
||||
this.activeTasks = [];
|
||||
if (processInstanceId) {
|
||||
this.activitiProcess.getProcessTasks(processInstanceId, null).subscribe(
|
||||
(res: TaskDetailsModel[]) => {
|
||||
res.forEach((task) => {
|
||||
this.taskObserver.next(task);
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
this.error.emit(err);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.activeTasks = [];
|
||||
}
|
||||
}
|
||||
|
||||
loadCompleted(processInstanceId: string) {
|
||||
this.completedTasks = [];
|
||||
if (processInstanceId) {
|
||||
this.activitiProcess.getProcessTasks(processInstanceId, 'completed').subscribe(
|
||||
(res: TaskDetailsModel[]) => {
|
||||
res.forEach((task) => {
|
||||
this.completedTaskObserver.next(task);
|
||||
});
|
||||
},
|
||||
(err) => {
|
||||
this.error.emit(err);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.completedTasks = [];
|
||||
}
|
||||
}
|
||||
|
||||
hasStartFormDefined(): boolean {
|
||||
return this.processInstanceDetails && this.processInstanceDetails.startFormDefined === true;
|
||||
}
|
||||
|
||||
getUserFullName(user: any) {
|
||||
if (user) {
|
||||
return (user.firstName && user.firstName !== 'null'
|
||||
? user.firstName + ' ' : '') +
|
||||
user.lastName;
|
||||
}
|
||||
return 'Nobody';
|
||||
}
|
||||
|
||||
getFormatDate(value, format: string) {
|
||||
let datePipe = new DatePipe('en-US');
|
||||
try {
|
||||
return datePipe.transform(value, format);
|
||||
} catch (err) {
|
||||
this.logService.error(`ProcessListInstanceTask: error parsing date ${value} to format ${format}`);
|
||||
}
|
||||
}
|
||||
|
||||
clickTask($event: any, task: TaskDetailsModel) {
|
||||
let args = new TaskDetailsEvent(task);
|
||||
this.taskClick.emit(args);
|
||||
}
|
||||
|
||||
clickStartTask() {
|
||||
this.processId = this.processInstanceDetails.id;
|
||||
this.showStartDialog();
|
||||
}
|
||||
|
||||
showStartDialog() {
|
||||
this.dialog.open(this.startDialog, { height: '500px', width: '700px' });
|
||||
}
|
||||
|
||||
closeSartDialog() {
|
||||
this.dialog.closeAll();
|
||||
}
|
||||
|
||||
onRefreshClicked() {
|
||||
this.load(this.processInstanceDetails.id);
|
||||
}
|
||||
|
||||
onFormContentClick() {
|
||||
this.closeSartDialog();
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
adf-datatable >>> .column-header {
|
||||
color: #232323;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
adf-datatable >>> .data-cell {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
adf-datatable >>> .cell-value{
|
||||
width: 250px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis
|
||||
}
|
||||
|
||||
.adf-process-list-loading-margin {
|
||||
margin-left: calc((100% - 100px) / 2);
|
||||
margin-right: calc((100% - 100px) / 2);
|
||||
}
|
||||
|
||||
.no-content-message {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
opacity: 0.54;
|
||||
color: #000;
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<div *ngIf="!requestNode">{{ 'ADF_PROCESS_LIST.FILTERS.MESSAGES.NONE' | translate }}</div>
|
||||
<div *ngIf="requestNode">
|
||||
<adf-datatable
|
||||
[data]="data"
|
||||
[loading]="isLoading"
|
||||
(rowClick)="onRowClick($event)"
|
||||
(row-keyup)="onRowKeyUp($event)">
|
||||
<loading-content-template>
|
||||
<ng-template>
|
||||
<!--Add your custom loading template here-->
|
||||
<mat-progress-spinner
|
||||
class="adf-process-list-loading-margin"
|
||||
[color]="'primary'"
|
||||
[mode]="'indeterminate'">
|
||||
</mat-progress-spinner>
|
||||
</ng-template>
|
||||
</loading-content-template>
|
||||
<no-content-template>
|
||||
<!--Add your custom empty template here-->
|
||||
<ng-template>
|
||||
<div class="no-content-message">
|
||||
{{ 'ADF_PROCESS_LIST.LIST.NONE' | translate }}
|
||||
</div>
|
||||
</ng-template>
|
||||
</no-content-template>
|
||||
</adf-datatable>
|
||||
</div>
|
@@ -0,0 +1,485 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { MatProgressSpinnerModule } from '@angular/material';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { ProcessInstanceListComponent } from './process-list.component';
|
||||
|
||||
import { AppConfigService } from '@alfresco/core';
|
||||
import { DataRowEvent, DataSorting, ObjectDataRow, ObjectDataTableAdapter } from '@alfresco/core';
|
||||
|
||||
import { fakeProcessInstance, fakeProcessInstancesWithNoName } from '../../mock';
|
||||
import { ProcessService } from '../services/process.service';
|
||||
|
||||
describe('ProcessInstanceListComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<ProcessInstanceListComponent>;
|
||||
let component: ProcessInstanceListComponent;
|
||||
let service: ProcessService;
|
||||
let getProcessInstancesSpy: jasmine.Spy;
|
||||
let appConfig: AppConfigService;
|
||||
|
||||
let fakeCutomSchema = [
|
||||
{
|
||||
'key': 'fakeName',
|
||||
'type': 'text',
|
||||
'title': 'ADF_PROCESS_LIST.PROPERTIES.FAKE',
|
||||
'sortable': true
|
||||
},
|
||||
{
|
||||
'key': 'fakeProcessName',
|
||||
'type': 'text',
|
||||
'title': 'ADF_PROCESS_LIST.PROPERTIES.PROCESS_FAKE',
|
||||
'sortable': true
|
||||
}
|
||||
];
|
||||
|
||||
let fakeColumnSchema = {
|
||||
'default': [
|
||||
{
|
||||
'key': 'name',
|
||||
'type': 'text',
|
||||
'title': 'ADF_PROCESS_LIST.PROPERTIES.NAME',
|
||||
'sortable': true
|
||||
},
|
||||
{
|
||||
'key': 'created',
|
||||
'type': 'text',
|
||||
'title': 'ADF_PROCESS_LIST.PROPERTIES.CREATED',
|
||||
'cssClass': 'hidden',
|
||||
'sortable': true
|
||||
}
|
||||
]
|
||||
, fakeCutomSchema };
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
MatProgressSpinnerModule
|
||||
],
|
||||
declarations: [ ProcessInstanceListComponent ],
|
||||
providers: [
|
||||
ProcessService
|
||||
]
|
||||
}).compileComponents().then(() => {
|
||||
fixture = TestBed.createComponent(ProcessInstanceListComponent);
|
||||
component = fixture.componentInstance;
|
||||
appConfig = TestBed.get(AppConfigService);
|
||||
service = fixture.debugElement.injector.get(ProcessService);
|
||||
|
||||
getProcessInstancesSpy = spyOn(service, 'getProcessInstances').and.returnValue(Observable.of(fakeProcessInstance));
|
||||
appConfig.config = Object.assign(appConfig.config, {
|
||||
'adf-process-list': {
|
||||
'presets': {
|
||||
'fakeCutomSchema': [
|
||||
{
|
||||
'key': 'fakeName',
|
||||
'type': 'text',
|
||||
'title': 'ADF_PROCESS_LIST.PROPERTIES.FAKE',
|
||||
'sortable': true
|
||||
},
|
||||
{
|
||||
'key': 'fakeProcessName',
|
||||
'type': 'text',
|
||||
'title': 'ADF_PROCESS_LIST.PROPERTIES.PROCESS_FAKE',
|
||||
'sortable': true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
}));
|
||||
|
||||
it('should use the default schemaColumn as default', () => {
|
||||
component.ngAfterContentInit();
|
||||
expect(component.data.getColumns()).toBeDefined();
|
||||
expect(component.data.getColumns().length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should use the schemaColumn passed in input', () => {
|
||||
component.data = new ObjectDataTableAdapter(
|
||||
[],
|
||||
[
|
||||
{type: 'text', key: 'fake-id', title: 'Name'}
|
||||
]
|
||||
);
|
||||
|
||||
component.ngAfterContentInit();
|
||||
expect(component.data.getColumns()).toBeDefined();
|
||||
expect(component.data.getColumns().length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should fetch the custom schemaColumn from app.config.json', () => {
|
||||
component.ngAfterContentInit();
|
||||
fixture.detectChanges();
|
||||
expect(component.layoutPresets).toEqual(fakeColumnSchema);
|
||||
});
|
||||
|
||||
it('should fetch custom schemaColumn when the input presetColumn is defined', () => {
|
||||
component.presetColumn = 'fakeCutomColumns';
|
||||
component.ngAfterContentInit();
|
||||
fixture.detectChanges();
|
||||
expect(component.data.getColumns()).toBeDefined();
|
||||
expect(component.data.getColumns().length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should return an empty process list when no input parameters are passed', () => {
|
||||
component.ngAfterContentInit();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should emit onSuccess event when process instances loaded', fakeAsync(() => {
|
||||
let emitSpy = spyOn(component.success, 'emit');
|
||||
component.appId = 1;
|
||||
component.state = 'open';
|
||||
component.processDefinitionKey = null;
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
expect(emitSpy).toHaveBeenCalledWith(fakeProcessInstance);
|
||||
}));
|
||||
|
||||
it('should return the process instances list in original order when datalist passed non-existent columns', async(() => {
|
||||
component.data = new ObjectDataTableAdapter(
|
||||
[],
|
||||
[
|
||||
{type: 'text', key: 'fake-id', title: 'Name'}
|
||||
]
|
||||
);
|
||||
component.appId = 1;
|
||||
component.state = 'open';
|
||||
component.processDefinitionKey = null;
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 773443333');
|
||||
expect(component.data.getRows()[1].getValue('name')).toEqual('Process 382927392');
|
||||
});
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should order the process instances by name column when no sort passed', async(() => {
|
||||
component.appId = 1;
|
||||
component.state = 'open';
|
||||
component.processDefinitionKey = null;
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 382927392');
|
||||
expect(component.data.getRows()[1].getValue('name')).toEqual('Process 773443333');
|
||||
});
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should order the process instances by descending column when specified', async(() => {
|
||||
component.appId = 1;
|
||||
component.state = 'open';
|
||||
component.processDefinitionKey = null;
|
||||
component.sort = 'name-desc';
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 773443333');
|
||||
expect(component.data.getRows()[1].getValue('name')).toEqual('Process 382927392');
|
||||
});
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should order the process instances by ascending column when specified', async(() => {
|
||||
component.appId = 1;
|
||||
component.state = 'open';
|
||||
component.processDefinitionKey = null;
|
||||
component.sort = 'started-asc';
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 773443333');
|
||||
expect(component.data.getRows()[1].getValue('name')).toEqual('Process 382927392');
|
||||
});
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should order the process instances by descending start date when specified', async(() => {
|
||||
component.appId = 1;
|
||||
component.state = 'open';
|
||||
component.processDefinitionKey = null;
|
||||
component.sort = 'started-desc';
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 382927392');
|
||||
expect(component.data.getRows()[1].getValue('name')).toEqual('Process 773443333');
|
||||
});
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should return a default name if no name is specified on the process', async(() => {
|
||||
getProcessInstancesSpy = getProcessInstancesSpy.and.returnValue(Observable.of(fakeProcessInstancesWithNoName));
|
||||
component.appId = 1;
|
||||
component.state = 'open';
|
||||
component.processDefinitionKey = 'fakeprocess';
|
||||
component.success.subscribe( (res) => {
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Fake Process Name - Nov 9, 2017, 12:36:14 PM');
|
||||
expect(component.data.getRows()[1].getValue('name')).toEqual('Fake Process Name - Nov 9, 2017, 12:37:25 PM');
|
||||
});
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should return a currentId null when the processList is empty', () => {
|
||||
component.selectFirst();
|
||||
expect(component.getCurrentId()).toBeNull();
|
||||
});
|
||||
|
||||
it('should return selected true for the selected process', () => {
|
||||
component.data = new ObjectDataTableAdapter(
|
||||
[
|
||||
{ id: '999', name: 'Fake-name' },
|
||||
{ id: '888', name: 'Fake-name-888' }
|
||||
],
|
||||
[
|
||||
{ type: 'text', key: 'id', title: 'Id' },
|
||||
{ type: 'text', key: 'name', title: 'Name' }
|
||||
]
|
||||
);
|
||||
component.selectFirst();
|
||||
const dataRow = component.data.getRows();
|
||||
expect(dataRow).toBeDefined();
|
||||
expect(dataRow[0].getValue('id')).toEqual('999');
|
||||
expect(dataRow[0].isSelected).toEqual(true);
|
||||
expect(dataRow[1].getValue('id')).toEqual('888');
|
||||
expect(dataRow[1].isSelected).toEqual(false);
|
||||
});
|
||||
|
||||
it('should throw an exception when the response is wrong', fakeAsync(() => {
|
||||
let emitSpy: jasmine.Spy = spyOn(component.error, 'emit');
|
||||
let fakeError = 'Fake server error';
|
||||
getProcessInstancesSpy.and.returnValue(Observable.throw(fakeError));
|
||||
component.appId = 1;
|
||||
component.state = 'open';
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
expect(emitSpy).toHaveBeenCalledWith(fakeError);
|
||||
}));
|
||||
|
||||
it('should emit onSuccess event when reload() called', fakeAsync(() => {
|
||||
component.appId = 1;
|
||||
component.state = 'open';
|
||||
component.processDefinitionKey = null;
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
let emitSpy = spyOn(component.success, 'emit');
|
||||
component.reload();
|
||||
tick();
|
||||
expect(emitSpy).toHaveBeenCalledWith(fakeProcessInstance);
|
||||
}));
|
||||
|
||||
it('should reload processes when reload() is called', (done) => {
|
||||
component.data = new ObjectDataTableAdapter(
|
||||
[],
|
||||
[
|
||||
{type: 'text', key: 'fake-id', title: 'Name'}
|
||||
]
|
||||
);
|
||||
component.state = 'open';
|
||||
component.success.subscribe( (res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 773443333');
|
||||
done();
|
||||
});
|
||||
component.reload();
|
||||
});
|
||||
|
||||
it('should emit row click event', (done) => {
|
||||
let row = new ObjectDataRow({
|
||||
id: '999'
|
||||
});
|
||||
let rowEvent = new DataRowEvent(row, null);
|
||||
|
||||
component.rowClick.subscribe(taskId => {
|
||||
expect(taskId).toEqual('999');
|
||||
expect(component.getCurrentId()).toEqual('999');
|
||||
done();
|
||||
});
|
||||
|
||||
component.onRowClick(rowEvent);
|
||||
});
|
||||
|
||||
it('should emit row click event on Enter', (done) => {
|
||||
let prevented = false;
|
||||
let keyEvent = new CustomEvent('Keyboard event', { detail: {
|
||||
keyboardEvent: { key: 'Enter' },
|
||||
row: new ObjectDataRow({ id: '999' })
|
||||
}});
|
||||
|
||||
spyOn(keyEvent, 'preventDefault').and.callFake(() => prevented = true);
|
||||
|
||||
component.rowClick.subscribe((taskId: string) => {
|
||||
expect(taskId).toEqual('999');
|
||||
expect(component.getCurrentId()).toEqual('999');
|
||||
expect(prevented).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
|
||||
component.onRowKeyUp(keyEvent);
|
||||
});
|
||||
|
||||
it('should NOT emit row click event on every other key', async(() => {
|
||||
let triggered = false;
|
||||
let keyEvent = new CustomEvent('Keyboard event', { detail: {
|
||||
keyboardEvent: { key: 'Space' },
|
||||
row: new ObjectDataRow({ id: 999 })
|
||||
}});
|
||||
|
||||
component.rowClick.subscribe(() => triggered = true);
|
||||
component.onRowKeyUp(keyEvent);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(triggered).toBeFalsy();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('component changes', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.data = new ObjectDataTableAdapter(
|
||||
[],
|
||||
[
|
||||
{type: 'text', key: 'fake-id', title: 'Name'}
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
it('should NOT reload the process list when no parameters changed', () => {
|
||||
expect(component.isListEmpty()).toBeTruthy();
|
||||
component.ngOnChanges({});
|
||||
expect(component.isListEmpty()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should reload the list when the appId parameter changes', (done) => {
|
||||
const appId = '1';
|
||||
let change = new SimpleChange(null, appId, true);
|
||||
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 773443333');
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnChanges({'appId': change});
|
||||
});
|
||||
|
||||
it('should reload the list when the processDefinitionKey parameter changes', (done) => {
|
||||
const processDefinitionKey = 'fakeprocess';
|
||||
let change = new SimpleChange(null, processDefinitionKey, true);
|
||||
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 773443333');
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnChanges({'processDefinitionKey': change});
|
||||
});
|
||||
|
||||
it('should reload the list when the state parameter changes', (done) => {
|
||||
const state = 'open';
|
||||
let change = new SimpleChange(null, state, true);
|
||||
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 773443333');
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnChanges({'state': change});
|
||||
});
|
||||
|
||||
it('should reload the list when the sort parameter changes', (done) => {
|
||||
const sort = 'created-desc';
|
||||
let change = new SimpleChange(null, sort, true);
|
||||
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 773443333');
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnChanges({'sort': change});
|
||||
});
|
||||
|
||||
it('should sort the list when the sort parameter changes', (done) => {
|
||||
const sort = 'created-asc';
|
||||
let change = new SimpleChange(null, sort, true);
|
||||
let sortSpy = spyOn(component.data, 'setSorting');
|
||||
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(sortSpy).toHaveBeenCalledWith(new DataSorting('started', 'asc'));
|
||||
done();
|
||||
});
|
||||
|
||||
component.sort = sort;
|
||||
component.ngOnChanges({'sort': change});
|
||||
});
|
||||
|
||||
it('should reload the process list when the name parameter changes', (done) => {
|
||||
const name = 'FakeTaskName';
|
||||
let change = new SimpleChange(null, name, true);
|
||||
|
||||
component.success.subscribe((res) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(component.data).toBeDefined();
|
||||
expect(component.isListEmpty()).not.toBeTruthy();
|
||||
expect(component.data.getRows().length).toEqual(2);
|
||||
expect(component.data.getRows()[0].getValue('name')).toEqual('Process 773443333');
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnChanges({'name': change});
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,314 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DataColumn, DataRowEvent, DataSorting, DataTableAdapter, ObjectDataColumn, ObjectDataRow, ObjectDataTableAdapter } from '@alfresco/core';
|
||||
import { AppConfigService, DataColumnListComponent } from '@alfresco/core';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { AfterContentInit, Component, ContentChild, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||
import { ProcessFilterParamRepresentationModel } from '../models/filter-process.model';
|
||||
import { ProcessInstance } from '../models/process-instance.model';
|
||||
import { processPresetsDefaultModel } from '../models/process-preset.model';
|
||||
import { ProcessService } from '../services/process.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-process-instance-list',
|
||||
styleUrls: ['./process-list.component.css'],
|
||||
templateUrl: './process-list.component.html'
|
||||
})
|
||||
export class ProcessInstanceListComponent implements OnChanges, AfterContentInit {
|
||||
|
||||
@ContentChild(DataColumnListComponent) columnList: DataColumnListComponent;
|
||||
|
||||
@Input()
|
||||
appId: number;
|
||||
|
||||
@Input()
|
||||
processDefinitionKey: string;
|
||||
|
||||
@Input()
|
||||
state: string;
|
||||
|
||||
@Input()
|
||||
sort: string;
|
||||
|
||||
@Input()
|
||||
name: string;
|
||||
|
||||
@Input()
|
||||
presetColumn: string;
|
||||
|
||||
requestNode: ProcessFilterParamRepresentationModel;
|
||||
|
||||
@Input()
|
||||
data: DataTableAdapter;
|
||||
|
||||
@Output()
|
||||
rowClick: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
@Output()
|
||||
success: EventEmitter<ProcessInstance[]> = new EventEmitter<ProcessInstance[]>();
|
||||
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
currentInstanceId: string;
|
||||
isLoading: boolean = true;
|
||||
layoutPresets = {};
|
||||
|
||||
constructor(private processService: ProcessService,
|
||||
private appConfig: AppConfigService) {
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
this.loadLayoutPresets();
|
||||
this.setupSchema();
|
||||
|
||||
if (this.appId) {
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup html-based (html definitions) or code behind (data adapter) schema.
|
||||
* If component is assigned with an empty data adater the default schema settings applied.
|
||||
*/
|
||||
setupSchema() {
|
||||
let schema: DataColumn[] = [];
|
||||
|
||||
if (this.columnList && this.columnList.columns && this.columnList.columns.length > 0) {
|
||||
schema = this.columnList.columns.map(c => <DataColumn> c);
|
||||
}
|
||||
|
||||
if (!this.data) {
|
||||
this.data = new ObjectDataTableAdapter([], schema.length > 0 ? schema : this.getLayoutPreset(this.presetColumn));
|
||||
} else {
|
||||
if (schema && schema.length > 0) {
|
||||
this.data.setColumns(schema);
|
||||
} else if (this.data.getColumns().length === 0) {
|
||||
this.presetColumn ? this.setupDefaultColumns(this.presetColumn) : this.setupDefaultColumns();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (this.isPropertyChanged(changes)) {
|
||||
this.reload();
|
||||
}
|
||||
}
|
||||
|
||||
private isPropertyChanged(changes: SimpleChanges): boolean {
|
||||
let changed: boolean = false;
|
||||
|
||||
let appId = changes['appId'];
|
||||
let processDefinitionKey = changes['processDefinitionKey'];
|
||||
let state = changes['state'];
|
||||
let sort = changes['sort'];
|
||||
let name = changes['name'];
|
||||
|
||||
if (appId && appId.currentValue) {
|
||||
changed = true;
|
||||
} else if (processDefinitionKey && processDefinitionKey.currentValue) {
|
||||
changed = true;
|
||||
} else if (state && state.currentValue) {
|
||||
changed = true;
|
||||
} else if (sort && sort.currentValue) {
|
||||
changed = true;
|
||||
} else if (name && name.currentValue) {
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
public reload() {
|
||||
this.requestNode = this.createRequestNode();
|
||||
this.load(this.requestNode);
|
||||
}
|
||||
|
||||
private load(requestNode: ProcessFilterParamRepresentationModel) {
|
||||
this.isLoading = true;
|
||||
this.processService.getProcessInstances(requestNode, this.processDefinitionKey)
|
||||
.subscribe(
|
||||
(response) => {
|
||||
let instancesRow = this.createDataRow(response);
|
||||
this.renderInstances(instancesRow);
|
||||
this.selectFirst();
|
||||
this.success.emit(response);
|
||||
this.isLoading = false;
|
||||
},
|
||||
error => {
|
||||
this.error.emit(error);
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array of ObjectDataRow
|
||||
* @param instances
|
||||
* @returns {ObjectDataRow[]}
|
||||
*/
|
||||
private createDataRow(instances: any[]): ObjectDataRow[] {
|
||||
let instancesRows: ObjectDataRow[] = [];
|
||||
instances.forEach((row) => {
|
||||
instancesRows.push(new ObjectDataRow(row));
|
||||
});
|
||||
return instancesRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the instances list
|
||||
*
|
||||
* @param instances
|
||||
*/
|
||||
private renderInstances(instances: any[]) {
|
||||
instances = this.optimizeNames(instances);
|
||||
this.setDatatableSorting();
|
||||
this.data.setRows(instances);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the datatable rows based on current value of 'sort' property
|
||||
*/
|
||||
private setDatatableSorting() {
|
||||
if (!this.sort) {
|
||||
return;
|
||||
}
|
||||
let sortingParams: string[] = this.sort.split('-');
|
||||
if (sortingParams.length === 2) {
|
||||
let sortColumn = sortingParams[0] === 'created' ? 'started' : sortingParams[0];
|
||||
let sortOrder = sortingParams[1];
|
||||
this.data.setSorting(new DataSorting(sortColumn, sortOrder));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the first instance of a list if present
|
||||
*/
|
||||
selectFirst() {
|
||||
if (!this.isListEmpty()) {
|
||||
let row = this.data.getRows()[0];
|
||||
row.isSelected = true;
|
||||
this.data.selectedRow = row;
|
||||
this.currentInstanceId = row.getValue('id');
|
||||
} else {
|
||||
if (this.data) {
|
||||
this.data.selectedRow = null;
|
||||
}
|
||||
this.currentInstanceId = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current id
|
||||
* @returns {string}
|
||||
*/
|
||||
getCurrentId(): string {
|
||||
return this.currentInstanceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the list is empty
|
||||
* @returns {ObjectDataTableAdapter|boolean}
|
||||
*/
|
||||
isListEmpty(): boolean {
|
||||
return this.data === undefined ||
|
||||
(this.data && this.data.getRows() && this.data.getRows().length === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the event rowClick passing the current task id when the row is clicked
|
||||
* @param event
|
||||
*/
|
||||
onRowClick(event: DataRowEvent) {
|
||||
let item = event;
|
||||
this.currentInstanceId = item.value.getValue('id');
|
||||
this.rowClick.emit(this.currentInstanceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the event rowClick passing the current task id when pressed the Enter key on the selected row
|
||||
* @param event
|
||||
*/
|
||||
onRowKeyUp(event: CustomEvent) {
|
||||
if (event.detail.keyboardEvent.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
this.currentInstanceId = event.detail.row.getValue('id');
|
||||
this.rowClick.emit(this.currentInstanceId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize name field
|
||||
* @param instances
|
||||
* @returns {any[]}
|
||||
*/
|
||||
private optimizeNames(instances: any[]) {
|
||||
instances = instances.map(t => {
|
||||
t.obj.name = this.getProcessNameOrDescription(t.obj, 'medium');
|
||||
return t;
|
||||
});
|
||||
return instances;
|
||||
}
|
||||
|
||||
getProcessNameOrDescription(processInstance, dateFormat): string {
|
||||
let name = '';
|
||||
if (processInstance) {
|
||||
name = processInstance.name ||
|
||||
processInstance.processDefinitionName + ' - ' + this.getFormatDate(processInstance.started, dateFormat);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
getFormatDate(value, format: string) {
|
||||
let datePipe = new DatePipe('en-US');
|
||||
try {
|
||||
return datePipe.transform(value, format);
|
||||
} catch (err) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
private createRequestNode() {
|
||||
let requestNode = {
|
||||
appDefinitionId: this.appId,
|
||||
state: this.state,
|
||||
sort: this.sort
|
||||
};
|
||||
return new ProcessFilterParamRepresentationModel(requestNode);
|
||||
}
|
||||
|
||||
setupDefaultColumns(preset: string = 'default'): void {
|
||||
if (this.data) {
|
||||
const columns = this.getLayoutPreset(preset);
|
||||
this.data.setColumns(columns);
|
||||
}
|
||||
}
|
||||
|
||||
private loadLayoutPresets(): void {
|
||||
const externalSettings = this.appConfig.get('adf-process-list.presets', null);
|
||||
|
||||
if (externalSettings) {
|
||||
this.layoutPresets = Object.assign({}, processPresetsDefaultModel, externalSettings);
|
||||
} else {
|
||||
this.layoutPresets = processPresetsDefaultModel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private getLayoutPreset(name: string = 'default'): DataColumn[] {
|
||||
return (this.layoutPresets[name] || this.layoutPresets['default']).map(col => new ObjectDataColumn(col));
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
<mat-card class="adf-start-process">
|
||||
<mat-card-title>{{'ADF_PROCESS_LIST.START_PROCESS.FORM.TITLE' | translate}}
|
||||
</mat-card-title>
|
||||
<mat-card-content *ngIf="isProcessDefinitionEmpty()">
|
||||
<mat-card-subtitle id="error-message" *ngIf="errorMessageId">
|
||||
{{errorMessageId|translate}}
|
||||
</mat-card-subtitle>
|
||||
<mat-form-field class="adf-process-input-container">
|
||||
<input matInput placeholder="{{'ADF_PROCESS_LIST.START_PROCESS.FORM.LABEL.NAME'|translate}}" [(ngModel)]="name" id="processName" required />
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-select placeholder="{{'ADF_PROCESS_LIST.START_PROCESS.FORM.LABEL.TYPE'|translate}}" [(ngModel)]="currentProcessDef.id" (ngModelChange)="onProcessDefChange($event)" required>
|
||||
<mat-option>{{'ADF_PROCESS_LIST.START_PROCESS.FORM.TYPE_PLACEHOLDER' | translate}}</mat-option>
|
||||
<mat-option *ngFor="let processDef of processDefinitions" [value]="processDef.id">
|
||||
{{ processDef.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<adf-start-form *ngIf="hasStartForm()"
|
||||
[disableStartProcessButton]="!hasProcessName()"
|
||||
[processDefinitionId]="currentProcessDef.id"
|
||||
(outcomeClick)="onOutcomeClick($event)"
|
||||
[showRefreshButton]="false">
|
||||
<button form-custom-button mat-button (click)="cancelStartProcess()" id="cancle_process" class=""> {{'ADF_PROCESS_LIST.START_PROCESS.FORM.ACTION.CANCEL'| translate}} </button>
|
||||
</adf-start-form>
|
||||
</mat-card-content>
|
||||
<mat-card-content *ngIf="hasErrorMessage()">
|
||||
<mat-card-subtitle class="error-message" id="no-process-message">
|
||||
{{'ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEFINITIONS' | translate}}
|
||||
</mat-card-subtitle>
|
||||
</mat-card-content>
|
||||
<mat-card-actions *ngIf="!hasStartForm()">
|
||||
<button mat-button *ngIf="!hasStartForm()" (click)="cancelStartProcess()" id="cancle_process" class=""> {{'ADF_PROCESS_LIST.START_PROCESS.FORM.ACTION.CANCEL'| translate}} </button>
|
||||
<button mat-button *ngIf="!hasStartForm()" [disabled]="!validateForm()" (click)="startProcess()" data-automation-id="btn-start" id="button-start" class="btn-start"> {{'ADF_PROCESS_LIST.START_PROCESS.FORM.ACTION.START' | translate}} </button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
@@ -0,0 +1,35 @@
|
||||
.adf {
|
||||
&-start-process {
|
||||
width: calc(66.6666% - 48px);
|
||||
margin-left: calc(33.3333333333% / 2);
|
||||
margin-right: calc(33.3333333333% / 2);
|
||||
margin-top: 10px;
|
||||
.mat-select-trigger {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
mat-select {
|
||||
width: 100%;
|
||||
padding: 16px 0px 0px 0px;
|
||||
}
|
||||
mat-card-actions {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
&-process-input-container {
|
||||
mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
&-start-form-container {
|
||||
.mat-card {
|
||||
box-shadow: none !important;
|
||||
padding: 0px !important;
|
||||
}
|
||||
}
|
||||
&-start-form-actions {
|
||||
text-align: right !important;
|
||||
}
|
||||
}
|
@@ -0,0 +1,396 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DebugElement, SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatInputModule,
|
||||
MatSelectModule
|
||||
} from '@angular/material';
|
||||
import { FormModule, FormService } from '@alfresco/core';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
import { ProcessInstanceVariable } from '../models/process-instance-variable.model';
|
||||
import { ProcessService } from '../services/process.service';
|
||||
import { newProcess, taskFormMock, testProcessDefRepr, testProcessDefs, testProcessDefWithForm } from '../../mock';
|
||||
import { StartProcessInstanceComponent } from './start-process.component';
|
||||
|
||||
describe('StartProcessInstanceComponent', () => {
|
||||
|
||||
let component: StartProcessInstanceComponent;
|
||||
let fixture: ComponentFixture<StartProcessInstanceComponent>;
|
||||
let processService: ProcessService;
|
||||
let formService: FormService;
|
||||
let getDefinitionsSpy: jasmine.Spy;
|
||||
let getStartFormDefinitionSpy: jasmine.Spy;
|
||||
let startProcessSpy: jasmine.Spy;
|
||||
let debugElement: DebugElement;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
FormModule,
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatInputModule,
|
||||
MatSelectModule
|
||||
],
|
||||
declarations: [
|
||||
StartProcessInstanceComponent
|
||||
],
|
||||
providers: [
|
||||
ProcessService,
|
||||
FormService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
fixture = TestBed.createComponent(StartProcessInstanceComponent);
|
||||
component = fixture.componentInstance;
|
||||
debugElement = fixture.debugElement;
|
||||
processService = fixture.debugElement.injector.get(ProcessService);
|
||||
formService = fixture.debugElement.injector.get(FormService);
|
||||
|
||||
getDefinitionsSpy = spyOn(processService, 'getProcessDefinitions').and.returnValue(Observable.of(testProcessDefs));
|
||||
startProcessSpy = spyOn(processService, 'startProcess').and.returnValue(Observable.of(newProcess));
|
||||
getStartFormDefinitionSpy = spyOn(formService, 'getStartFormDefinition').and.returnValue(Observable.of(taskFormMock));
|
||||
|
||||
});
|
||||
|
||||
it('should create instance of StartProcessInstanceComponent', () => {
|
||||
expect(fixture.componentInstance instanceof StartProcessInstanceComponent).toBe(true, 'should create StartProcessInstanceComponent');
|
||||
});
|
||||
|
||||
describe('process definitions list', () => {
|
||||
|
||||
it('should call service to fetch process definitions with appId', () => {
|
||||
let change = new SimpleChange(null, '123', true);
|
||||
component.ngOnChanges({'appId': change});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getDefinitionsSpy).toHaveBeenCalledWith('123');
|
||||
});
|
||||
|
||||
it('should call service to fetch process definitions without appId', () => {
|
||||
let change = new SimpleChange(null, null, true);
|
||||
component.ngOnChanges({'appId': change});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getDefinitionsSpy).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should call service to fetch process definitions with appId when provided', () => {
|
||||
let change = new SimpleChange(null, '123', true);
|
||||
component.ngOnChanges({'appId': change});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(getDefinitionsSpy).toHaveBeenCalledWith('123');
|
||||
});
|
||||
|
||||
it('should display the correct number of processes in the select list', () => {
|
||||
let change = new SimpleChange(null, '123', true);
|
||||
component.ngOnChanges({'appId': change});
|
||||
fixture.detectChanges();
|
||||
|
||||
let selectElement = fixture.nativeElement.querySelector('mat-select');
|
||||
expect(selectElement.children.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should display the option def details', () => {
|
||||
let change = new SimpleChange(null, '123', true);
|
||||
component.ngOnChanges({'appId': change});
|
||||
component.processDefinitions = testProcessDefs;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger');
|
||||
let optionElement = fixture.nativeElement.querySelectorAll('mat-option');
|
||||
selectElement.click();
|
||||
expect(selectElement).not.toBeNull();
|
||||
expect(selectElement).toBeDefined();
|
||||
expect(optionElement).not.toBeNull();
|
||||
expect(optionElement).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should indicate an error to the user if process defs cannot be loaded', async(() => {
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.throw({}));
|
||||
let change = new SimpleChange(null, '123', true);
|
||||
component.ngOnChanges({'appId': change});
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
let errorEl = fixture.nativeElement.querySelector('#error-message');
|
||||
expect(errorEl).not.toBeNull('Expected error message to be present');
|
||||
expect(errorEl.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.ERROR.LOAD_PROCESS_DEFS');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should show no process available message when no process definition is loaded', async(() => {
|
||||
getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of([]));
|
||||
let change = new SimpleChange(null, '123', true);
|
||||
component.ngOnChanges({'appId': change});
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
let noprocessElement = fixture.nativeElement.querySelector('#no-process-message');
|
||||
expect(noprocessElement).not.toBeNull('Expected no available process message to be present');
|
||||
expect(noprocessElement.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEFINITIONS');
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('input changes', () => {
|
||||
|
||||
let change = new SimpleChange(123, 456, true);
|
||||
let nullChange = new SimpleChange(123, null, true);
|
||||
|
||||
beforeEach(async(() => {
|
||||
component.appId = 123;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
getDefinitionsSpy.calls.reset();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reload processes when appId input changed', () => {
|
||||
component.ngOnChanges({appId: change});
|
||||
expect(getDefinitionsSpy).toHaveBeenCalledWith(456);
|
||||
});
|
||||
|
||||
it('should reload processes when appId input changed to null', () => {
|
||||
component.ngOnChanges({appId: nullChange});
|
||||
expect(getDefinitionsSpy).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should get current processDeff', () => {
|
||||
component.ngOnChanges({appId: change});
|
||||
component.onProcessDefChange('my:Process');
|
||||
fixture.detectChanges();
|
||||
expect(getDefinitionsSpy).toHaveBeenCalled();
|
||||
expect(component.processDefinitions).toBe(testProcessDefs);
|
||||
});
|
||||
});
|
||||
|
||||
describe('start process', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.name = 'My new process';
|
||||
let change = new SimpleChange(null, 123, true);
|
||||
component.ngOnChanges({'appId': change});
|
||||
});
|
||||
|
||||
it('should call service to start process if required fields provided', async(() => {
|
||||
component.onProcessDefChange('my:process1');
|
||||
component.startProcess();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(startProcessSpy).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should avoid calling service to start process if required fields NOT provided', async(() => {
|
||||
component.name = '';
|
||||
component.startProcess();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(startProcessSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call service to start process with the correct parameters', async(() => {
|
||||
component.onProcessDefChange('my:process1');
|
||||
component.startProcess();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(startProcessSpy).toHaveBeenCalledWith('my:process1', 'My new process', undefined, undefined, undefined);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call service to start process with the variables setted', async(() => {
|
||||
let inputProcessVariable: ProcessInstanceVariable[] = [];
|
||||
|
||||
let variable: ProcessInstanceVariable = {};
|
||||
variable.name = 'nodeId';
|
||||
variable.value = 'id';
|
||||
|
||||
inputProcessVariable.push(variable);
|
||||
|
||||
component.variables = inputProcessVariable;
|
||||
component.onProcessDefChange('my:process1');
|
||||
component.startProcess();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(startProcessSpy).toHaveBeenCalledWith('my:process1', 'My new process', undefined, undefined, inputProcessVariable);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should output start event when process started successfully', async(() => {
|
||||
let emitSpy = spyOn(component.start, 'emit');
|
||||
component.onProcessDefChange('my:process1');
|
||||
component.startProcess();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(emitSpy).toHaveBeenCalledWith(newProcess);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw error event when process cannot be started', async(() => {
|
||||
let errorSpy = spyOn(component.error, 'error');
|
||||
let error = {message: 'My error'};
|
||||
startProcessSpy = startProcessSpy.and.returnValue(Observable.throw(error));
|
||||
component.onProcessDefChange('my:process1');
|
||||
component.startProcess();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(errorSpy).toHaveBeenCalledWith(error);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should indicate an error to the user if process cannot be started', async(() => {
|
||||
startProcessSpy = startProcessSpy.and.returnValue(Observable.throw({}));
|
||||
component.onProcessDefChange('my:process1');
|
||||
component.startProcess();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
let errorEl = fixture.nativeElement.querySelector('#error-message');
|
||||
expect(errorEl).not.toBeNull();
|
||||
expect(errorEl.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.ERROR.START');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should emit start event when start the process with currentProcessDef and name', () => {
|
||||
let startSpy: jasmine.Spy = spyOn(component.start, 'emit');
|
||||
component.currentProcessDef.id = '1001';
|
||||
component.name = 'my:Process';
|
||||
component.startProcess();
|
||||
fixture.detectChanges();
|
||||
expect(startSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not emit start event when start the process without currentProcessDef and name', () => {
|
||||
let startSpy: jasmine.Spy = spyOn(component.start, 'emit');
|
||||
component.startProcess();
|
||||
fixture.detectChanges();
|
||||
expect(startSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should able to start the process when the required fields are filled up', async(() => {
|
||||
let startSpy: jasmine.Spy = spyOn(component.start, 'emit');
|
||||
component.name = 'my:process1';
|
||||
component.onProcessDefChange('my:process1');
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let startButton = fixture.nativeElement.querySelector('#button-start');
|
||||
startButton.click();
|
||||
expect(startSpy).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return true if startFrom defined', async(() => {
|
||||
component.currentProcessDef = testProcessDefRepr;
|
||||
component.name = 'my:process1';
|
||||
component.currentProcessDef.hasStartForm = true;
|
||||
component.hasStartForm();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.hasStartForm()).toBe(true);
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('start forms', () => {
|
||||
|
||||
let startBtn;
|
||||
|
||||
describe('without start form', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
component.name = 'My new process';
|
||||
let change = new SimpleChange(null, '123', true);
|
||||
component.ngOnChanges({'appId': change});
|
||||
fixture.detectChanges();
|
||||
component.onProcessDefChange('my:process1');
|
||||
fixture.whenStable();
|
||||
startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
}));
|
||||
|
||||
it('should have start button disabled when name not filled out', async(() => {
|
||||
component.name = '';
|
||||
fixture.detectChanges();
|
||||
expect(startBtn.disabled).toBe(true);
|
||||
}));
|
||||
|
||||
it('should have start button disabled when no process is selected', async(() => {
|
||||
component.onProcessDefChange('');
|
||||
fixture.detectChanges();
|
||||
expect(startBtn.disabled).toBe(true);
|
||||
}));
|
||||
|
||||
it('should enable start button when name and process filled out', async(() => {
|
||||
fixture.detectChanges();
|
||||
let startButton = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(startButton.disabled).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should disable the start process button when process name is empty', async(() => {
|
||||
component.name = '';
|
||||
fixture.detectChanges();
|
||||
let startButton = fixture.nativeElement.querySelector('#button-start');
|
||||
expect(startButton.disabled).toBeTruthy();
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('with start form', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
getDefinitionsSpy.and.returnValue(Observable.of(testProcessDefWithForm));
|
||||
let change = new SimpleChange(null, '123', true);
|
||||
component.ngOnChanges({'appId': change});
|
||||
component.onProcessDefChange('my:process1');
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable();
|
||||
startBtn = fixture.nativeElement.querySelector('#button-start');
|
||||
});
|
||||
|
||||
it('should initialize start form', () => {
|
||||
expect(component.startForm).toBeDefined();
|
||||
expect(component.startForm).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should load start form from service', () => {
|
||||
expect(getStartFormDefinitionSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not show the start process button', async(() => {
|
||||
component.name = 'My new process';
|
||||
fixture.detectChanges();
|
||||
expect(startBtn).toBeNull();
|
||||
}));
|
||||
|
||||
it('should emit cancel event on cancel Button', () => {
|
||||
let cancelButton = fixture.nativeElement.querySelector('#cancle_process');
|
||||
let cancelSpy: jasmine.Spy = spyOn(component.cancel, 'emit');
|
||||
cancelButton.click();
|
||||
fixture.detectChanges();
|
||||
expect(cancelSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,161 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { StartFormComponent } from '@alfresco/core';
|
||||
import { ProcessInstanceVariable } from '../models/process-instance-variable.model';
|
||||
import { ProcessDefinitionRepresentation } from './../models/process-definition.model';
|
||||
import { ProcessInstance } from './../models/process-instance.model';
|
||||
import { ProcessService } from './../services/process.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-start-process',
|
||||
templateUrl: './start-process.component.html',
|
||||
styleUrls: ['./start-process.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class StartProcessInstanceComponent implements OnChanges {
|
||||
|
||||
@Input()
|
||||
appId: number;
|
||||
|
||||
@Input()
|
||||
variables: ProcessInstanceVariable[];
|
||||
|
||||
@Output()
|
||||
start: EventEmitter<ProcessInstance> = new EventEmitter<ProcessInstance>();
|
||||
|
||||
@Output()
|
||||
cancel: EventEmitter<ProcessInstance> = new EventEmitter<ProcessInstance>();
|
||||
|
||||
@Output()
|
||||
error: EventEmitter<ProcessInstance> = new EventEmitter<ProcessInstance>();
|
||||
|
||||
@ViewChild(StartFormComponent)
|
||||
startForm: StartFormComponent;
|
||||
|
||||
processDefinitions: ProcessDefinitionRepresentation[] = [];
|
||||
|
||||
name: string;
|
||||
|
||||
currentProcessDef: ProcessDefinitionRepresentation = new ProcessDefinitionRepresentation();
|
||||
|
||||
errorMessageId: string = '';
|
||||
|
||||
constructor(private activitiProcess: ProcessService) {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
let appIdChange = changes['appId'];
|
||||
let appId = appIdChange ? appIdChange.currentValue : null;
|
||||
this.load(appId);
|
||||
}
|
||||
|
||||
public load(appId?: number) {
|
||||
this.resetSelectedProcessDefinition();
|
||||
this.resetErrorMessage();
|
||||
this.activitiProcess.getProcessDefinitions(appId).subscribe(
|
||||
(res) => {
|
||||
this.processDefinitions = res;
|
||||
},
|
||||
() => {
|
||||
this.errorMessageId = 'ADF_PROCESS_LIST.START_PROCESS.ERROR.LOAD_PROCESS_DEFS';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public startProcess(outcome?: string) {
|
||||
if (this.currentProcessDef.id && this.name) {
|
||||
this.resetErrorMessage();
|
||||
let formValues = this.startForm ? this.startForm.form.values : undefined;
|
||||
this.activitiProcess.startProcess(this.currentProcessDef.id, this.name, outcome, formValues, this.variables).subscribe(
|
||||
(res) => {
|
||||
this.name = '';
|
||||
this.start.emit(res);
|
||||
},
|
||||
(err) => {
|
||||
this.errorMessageId = 'ADF_PROCESS_LIST.START_PROCESS.ERROR.START';
|
||||
this.error.error(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onProcessDefChange(processDefinitionId) {
|
||||
let processDef = this.processDefinitions.find((processDefinition) => {
|
||||
return processDefinition.id === processDefinitionId;
|
||||
});
|
||||
if (processDef) {
|
||||
this.currentProcessDef = JSON.parse(JSON.stringify(processDef));
|
||||
} else {
|
||||
this.resetSelectedProcessDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
public cancelStartProcess() {
|
||||
this.cancel.emit();
|
||||
}
|
||||
|
||||
hasStartForm() {
|
||||
return this.currentProcessDef && this.currentProcessDef.hasStartForm;
|
||||
}
|
||||
|
||||
isProcessDefinitionEmpty() {
|
||||
return this.processDefinitions ? (this.processDefinitions.length > 0 || this.errorMessageId) : this.errorMessageId;
|
||||
}
|
||||
|
||||
isStartFormMissingOrValid() {
|
||||
if (this.startForm) {
|
||||
return this.startForm.form && this.startForm.form.isValid;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
validateForm() {
|
||||
return this.currentProcessDef.id && this.name && this.isStartFormMissingOrValid();
|
||||
}
|
||||
|
||||
private resetSelectedProcessDefinition() {
|
||||
this.currentProcessDef = new ProcessDefinitionRepresentation();
|
||||
}
|
||||
|
||||
private resetErrorMessage(): void {
|
||||
this.errorMessageId = '';
|
||||
}
|
||||
|
||||
hasErrorMessage() {
|
||||
return this.processDefinitions.length === 0 && !this.errorMessageId;
|
||||
}
|
||||
|
||||
public onOutcomeClick(outcome: string) {
|
||||
this.startProcess(outcome);
|
||||
}
|
||||
|
||||
public reset() {
|
||||
this.resetSelectedProcessDefinition();
|
||||
this.name = '';
|
||||
if (this.startForm) {
|
||||
this.startForm.data = {};
|
||||
}
|
||||
this.resetErrorMessage();
|
||||
}
|
||||
|
||||
hasProcessName(): boolean {
|
||||
return this.name ? true : false;
|
||||
}
|
||||
}
|
18
lib/process-services/process-list/index.ts
Normal file
18
lib/process-services/process-list/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './public-api';
|
@@ -0,0 +1,70 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ProcessFilterRequestRepresentation, ProcessInstanceFilterRepresentation, UserProcessInstanceFilterRepresentation } from 'alfresco-js-api';
|
||||
|
||||
export class FilterProcessRepresentationModel implements UserProcessInstanceFilterRepresentation {
|
||||
appId: number;
|
||||
filter: ProcessInstanceFilterRepresentation;
|
||||
icon: number;
|
||||
id: number;
|
||||
index: number;
|
||||
name: string;
|
||||
recent: boolean;
|
||||
|
||||
constructor(obj: any) {
|
||||
if (obj) {
|
||||
this.id = obj.id || null;
|
||||
this.appId = obj.appId || null;
|
||||
this.name = obj.name || null;
|
||||
this.recent = !!obj.recent;
|
||||
this.icon = obj.icon || null;
|
||||
this.filter = obj.filter || null;
|
||||
this.index = obj.index;
|
||||
}
|
||||
}
|
||||
|
||||
hasFilter() {
|
||||
return !!this.filter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This object represent the parameters of a process filter.
|
||||
*
|
||||
*
|
||||
* @returns {ProcessFilterParamRepresentationModel} .
|
||||
*/
|
||||
export class ProcessFilterParamRepresentationModel implements ProcessFilterRequestRepresentation {
|
||||
|
||||
processDefinitionId?: number;
|
||||
appDefinitionId?: number;
|
||||
state?: string;
|
||||
sort?: string;
|
||||
page?: number;
|
||||
size?: number;
|
||||
|
||||
constructor(obj?: any) {
|
||||
this.processDefinitionId = obj.processDefinitionId || null;
|
||||
this.appDefinitionId = obj.appDefinitionId || null;
|
||||
this.state = obj.state || null;
|
||||
this.sort = obj.sort || null;
|
||||
this.page = obj.page || null;
|
||||
this.size = obj.size || null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export class ProcessDefinitionRepresentation {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
key: string;
|
||||
category: string;
|
||||
version: number;
|
||||
deploymentId: string;
|
||||
tenantId: string;
|
||||
metaDataValues: any[];
|
||||
hasStartForm: boolean;
|
||||
|
||||
constructor(obj?: any) {
|
||||
this.id = obj && obj.id || null;
|
||||
this.name = obj && obj.name || null;
|
||||
this.description = obj && obj.description || null;
|
||||
this.key = obj && obj.key || null;
|
||||
this.category = obj && obj.category || null;
|
||||
this.version = obj && obj.version || 0;
|
||||
this.deploymentId = obj && obj.deploymentId || null;
|
||||
this.tenantId = obj && obj.tenantId || null;
|
||||
this.metaDataValues = obj && obj.metaDataValues || [];
|
||||
this.hasStartForm = obj && obj.hasStartForm === true ? true : false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export class ProcessFilterRequestRepresentation {
|
||||
processDefinitionId: string;
|
||||
appDefinitionId: string;
|
||||
state: string;
|
||||
sort: string;
|
||||
page: number;
|
||||
size: number;
|
||||
|
||||
constructor(obj?: any) {
|
||||
this.processDefinitionId = obj && obj.processDefinitionId || null;
|
||||
this.appDefinitionId = obj && obj.appDefinitionId || null;
|
||||
this.state = obj && obj.state || null;
|
||||
this.sort = obj && obj.sort || null;
|
||||
this.page = obj && obj.page || 0;
|
||||
this.size = obj && obj.size || 25;
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { RestVariable } from 'alfresco-js-api';
|
||||
|
||||
export class ProcessInstanceVariable implements RestVariable {
|
||||
|
||||
name?: string;
|
||||
scope?: string;
|
||||
type?: string;
|
||||
value?: string;
|
||||
valueUrl?: string;
|
||||
|
||||
constructor(obj?: any) {
|
||||
this.name = obj && obj.name !== undefined ? obj.name : null;
|
||||
this.scope = obj && obj.scope !== undefined ? obj.scope : null;
|
||||
this.value = obj && obj.value !== undefined ? obj.value : null;
|
||||
this.valueUrl = obj && obj.valueUrl !== undefined ? obj.valueUrl : null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { LightUserRepresentation, ProcessInstanceRepresentation, RestVariable } from 'alfresco-js-api';
|
||||
|
||||
export class ProcessInstance implements ProcessInstanceRepresentation {
|
||||
|
||||
businessKey?: string;
|
||||
ended?: Date;
|
||||
graphicalNotationDefined?: boolean;
|
||||
id?: string;
|
||||
name?: string;
|
||||
processDefinitionCategory?: string;
|
||||
processDefinitionDeploymentId?: string;
|
||||
processDefinitionDescription?: string;
|
||||
processDefinitionId?: string;
|
||||
processDefinitionKey?: string;
|
||||
processDefinitionName?: string;
|
||||
processDefinitionVersion?: number;
|
||||
startFormDefined?: boolean;
|
||||
started?: Date;
|
||||
startedBy?: LightUserRepresentation;
|
||||
tenantId?: string;
|
||||
variables?: RestVariable[];
|
||||
|
||||
constructor(data?: any) {
|
||||
this.businessKey = data && data.businessKey !== undefined ? data.businessKey : null;
|
||||
this.ended = data && data.ended !== undefined ? data.ended : null;
|
||||
this.graphicalNotationDefined = data && data.graphicalNotationDefined !== undefined ? data.graphicalNotationDefined : null;
|
||||
this.id = data && data.id !== undefined ? data.id : null;
|
||||
this.name = data && data.name !== undefined ? data.name : null;
|
||||
this.processDefinitionCategory = data && data.processDefinitionCategory !== undefined ? data.processDefinitionCategory : null;
|
||||
this.processDefinitionDeploymentId = data && data.processDefinitionDeploymentId !== undefined ? data.processDefinitionDeploymentId : null;
|
||||
this.processDefinitionDescription = data && data.processDefinitionDescription !== undefined ? data.processDefinitionDescription : null;
|
||||
this.processDefinitionId = data && data.processDefinitionId !== undefined ? data.processDefinitionId : null;
|
||||
this.processDefinitionKey = data && data.processDefinitionKey !== undefined ? data.processDefinitionKey : null;
|
||||
this.processDefinitionName = data && data.processDefinitionName !== undefined ? data.processDefinitionName : null;
|
||||
this.processDefinitionVersion = data && data.processDefinitionVersion !== undefined ? data.processDefinitionVersion : null;
|
||||
this.startFormDefined = data && data.startFormDefined !== undefined ? data.startFormDefined : null;
|
||||
this.started = data && data.started !== undefined ? data.started : null;
|
||||
this.startedBy = data && data.startedBy !== undefined ? data.startedBy : null;
|
||||
this.tenantId = data && data.tenantId !== undefined ? data.tenantId : null;
|
||||
this.variables = data && data.variables !== undefined ? data.variables : null;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export let processPresetsDefaultModel = {
|
||||
'default': [
|
||||
{
|
||||
'key': 'name',
|
||||
'type': 'text',
|
||||
'title': 'ADF_PROCESS_LIST.PROPERTIES.NAME',
|
||||
'sortable': true
|
||||
},
|
||||
{
|
||||
'key': 'created',
|
||||
'type': 'text',
|
||||
'title': 'ADF_PROCESS_LIST.PROPERTIES.CREATED',
|
||||
'cssClass': 'hidden',
|
||||
'sortable': true
|
||||
}
|
||||
]
|
||||
};
|
84
lib/process-services/process-list/process-list.module.ts
Normal file
84
lib/process-services/process-list/process-list.module.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { FormModule } from '@alfresco/core';
|
||||
import { MaterialModule } from '../material.module';
|
||||
|
||||
import { CardViewModule, CardViewUpdateService, DataColumnModule, DataTableModule, DirectiveModule, PipeModule } from '@alfresco/core';
|
||||
import { TaskListModule } from '../task-list';
|
||||
import { PeopleModule } from '../people';
|
||||
import { CommentsModule } from '../comments';
|
||||
|
||||
import { ProcessAuditDirective } from './components/process-audit.directive';
|
||||
import { ProcessFiltersComponent } from './components/process-filters.component';
|
||||
import { ProcessInstanceDetailsComponent } from './components/process-instance-details.component';
|
||||
import { ProcessInstanceHeaderComponent } from './components/process-instance-header.component';
|
||||
import { ProcessInstanceTasksComponent } from './components/process-instance-tasks.component';
|
||||
import { ProcessInstanceListComponent } from './components/process-list.component';
|
||||
import { StartProcessInstanceComponent } from './components/start-process.component';
|
||||
|
||||
import { ProcessService } from './services/process.service';
|
||||
import { ProcessFilterService } from './services/process-filter.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
DataTableModule,
|
||||
FormModule,
|
||||
TaskListModule,
|
||||
MaterialModule,
|
||||
FlexLayoutModule,
|
||||
TranslateModule,
|
||||
CardViewModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
PipeModule,
|
||||
DataColumnModule,
|
||||
DirectiveModule,
|
||||
PeopleModule,
|
||||
CommentsModule
|
||||
],
|
||||
declarations: [
|
||||
ProcessInstanceListComponent,
|
||||
ProcessFiltersComponent,
|
||||
ProcessInstanceDetailsComponent,
|
||||
ProcessAuditDirective,
|
||||
ProcessInstanceHeaderComponent,
|
||||
ProcessInstanceTasksComponent,
|
||||
StartProcessInstanceComponent
|
||||
],
|
||||
providers: [
|
||||
ProcessService,
|
||||
ProcessFilterService,
|
||||
CardViewUpdateService
|
||||
],
|
||||
exports: [
|
||||
ProcessInstanceListComponent,
|
||||
ProcessFiltersComponent,
|
||||
ProcessInstanceDetailsComponent,
|
||||
ProcessAuditDirective,
|
||||
ProcessInstanceHeaderComponent,
|
||||
ProcessInstanceTasksComponent,
|
||||
StartProcessInstanceComponent
|
||||
]
|
||||
})
|
||||
export class ProcessListModule {}
|
38
lib/process-services/process-list/public-api.ts
Normal file
38
lib/process-services/process-list/public-api.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// component
|
||||
export * from './components/process-filters.component';
|
||||
export * from './components/process-instance-details.component';
|
||||
export * from './components/process-audit.directive';
|
||||
export * from './components/process-instance-header.component';
|
||||
export * from './components/process-instance-tasks.component';
|
||||
export * from './components/process-list.component';
|
||||
export * from './components/start-process.component';
|
||||
|
||||
// services
|
||||
export * from './services/process.service';
|
||||
export * from './services/process-filter.service';
|
||||
|
||||
// models
|
||||
export * from './models/filter-process.model';
|
||||
export * from './models/process-definition.model';
|
||||
export * from './models/process-instance.model';
|
||||
export * from './models/process-instance-filter.model';
|
||||
export * from './models/process-instance-variable.model';
|
||||
|
||||
export * from './process-list.module';
|
@@ -0,0 +1,169 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { async } from '@angular/core/testing';
|
||||
import { AlfrescoApi } from 'alfresco-js-api';
|
||||
import { AlfrescoApiService } from '@alfresco/core';
|
||||
import { fakeError, fakeProcessFilters } from '../../mock';
|
||||
import { FilterProcessRepresentationModel } from '../models/filter-process.model';
|
||||
import { ProcessFilterService } from './process-filter.service';
|
||||
|
||||
describe('Process filter', () => {
|
||||
|
||||
let service: ProcessFilterService;
|
||||
let apiService: AlfrescoApiService;
|
||||
let alfrescoApi: AlfrescoApi;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
|
||||
providers: [
|
||||
ProcessFilterService
|
||||
]
|
||||
});
|
||||
service = TestBed.get(ProcessFilterService);
|
||||
apiService = TestBed.get(AlfrescoApiService);
|
||||
alfrescoApi = apiService.getInstance();
|
||||
});
|
||||
|
||||
describe('filters', () => {
|
||||
|
||||
let getFilters: jasmine.Spy;
|
||||
let createFilter: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
getFilters = spyOn(alfrescoApi.activiti.userFiltersApi, 'getUserProcessInstanceFilters')
|
||||
.and
|
||||
.returnValue(Promise.resolve(fakeProcessFilters));
|
||||
|
||||
createFilter = spyOn(alfrescoApi.activiti.userFiltersApi, 'createUserProcessInstanceFilter')
|
||||
.and
|
||||
.callFake((filter: FilterProcessRepresentationModel) => Promise.resolve(filter));
|
||||
});
|
||||
|
||||
describe('get filters', () => {
|
||||
|
||||
it('should call the API without an appId defined by default', () => {
|
||||
service.getProcessFilters(null);
|
||||
expect(getFilters).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call the API with the correct appId when specified', () => {
|
||||
service.getProcessFilters(226);
|
||||
expect(getFilters).toHaveBeenCalledWith({appId: 226});
|
||||
});
|
||||
|
||||
it('should return the task filter by id', (done) => {
|
||||
service.getProcessFilterById(333).subscribe(
|
||||
(processFilter: FilterProcessRepresentationModel) => {
|
||||
expect(processFilter).toBeDefined();
|
||||
expect(processFilter.id).toEqual(333);
|
||||
expect(processFilter.name).toEqual('Running');
|
||||
expect(processFilter.filter.sort).toEqual('created-desc');
|
||||
expect(processFilter.filter.state).toEqual('running');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the task filter by name', (done) => {
|
||||
service.getProcessFilterByName('Running').subscribe(
|
||||
(res: FilterProcessRepresentationModel) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(res.id).toEqual(333);
|
||||
expect(res.name).toEqual('Running');
|
||||
expect(res.filter.sort).toEqual('created-desc');
|
||||
expect(res.filter.state).toEqual('running');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the non-empty filter list that is returned by the API', async(() => {
|
||||
service.getProcessFilters(null).subscribe(
|
||||
(res) => {
|
||||
expect(res.length).toBe(1);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return the default filters', (done) => {
|
||||
service.createDefaultFilters(1234).subscribe(
|
||||
(res: FilterProcessRepresentationModel []) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(res.length).toEqual(3);
|
||||
expect(res[0].name).toEqual('Running');
|
||||
expect(res[1].name).toEqual('Completed');
|
||||
expect(res[2].name).toEqual('All');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
getFilters = getFilters.and.returnValue(Promise.reject(fakeError));
|
||||
|
||||
service.getProcessFilters(null).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('add filter', () => {
|
||||
|
||||
let filter = fakeProcessFilters.data[0];
|
||||
|
||||
it('should call the API to create the filter', () => {
|
||||
service.addProcessFilter(filter);
|
||||
expect(createFilter).toHaveBeenCalledWith(filter);
|
||||
});
|
||||
|
||||
it('should return the created filter', async(() => {
|
||||
service.addProcessFilter(filter).subscribe((createdFilter: FilterProcessRepresentationModel) => {
|
||||
expect(createdFilter).toBe(filter);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
createFilter = createFilter.and.returnValue(Promise.reject(fakeError));
|
||||
|
||||
service.addProcessFilter(filter).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return a default error if no data is returned by the API', async(() => {
|
||||
createFilter = createFilter.and.returnValue(Promise.reject(null));
|
||||
service.addProcessFilter(filter).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe('Server error');
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,174 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AlfrescoApiService, LogService } from '@alfresco/core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { FilterProcessRepresentationModel } from '../models/filter-process.model';
|
||||
|
||||
@Injectable()
|
||||
export class ProcessFilterService {
|
||||
|
||||
constructor(private alfrescoApiService: AlfrescoApiService,
|
||||
private logService: LogService) {
|
||||
}
|
||||
|
||||
getProcessFilters(appId: number): Observable<FilterProcessRepresentationModel[]> {
|
||||
return Observable.fromPromise(this.callApiProcessFilters(appId))
|
||||
.map((response: any) => {
|
||||
let filters: FilterProcessRepresentationModel[] = [];
|
||||
response.data.forEach((filter: FilterProcessRepresentationModel) => {
|
||||
let filterModel = new FilterProcessRepresentationModel(filter);
|
||||
filters.push(filterModel);
|
||||
});
|
||||
return filters;
|
||||
})
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the process filter by id
|
||||
* @param filterId - number - The id of the filter
|
||||
* @param appId - string - optional - The id of app
|
||||
* @returns {Observable<FilterProcessRepresentationModel>}
|
||||
*/
|
||||
getProcessFilterById(filterId: number, appId?: number): Observable<FilterProcessRepresentationModel> {
|
||||
return Observable.fromPromise(this.callApiProcessFilters(appId))
|
||||
.map((response: any) => {
|
||||
return response.data.find(filter => filter.id === filterId);
|
||||
}).catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the process filter by name
|
||||
* @param filterName - string - The name of the filter
|
||||
* @param appId - string - optional - The id of app
|
||||
* @returns {Observable<FilterProcessRepresentationModel>}
|
||||
*/
|
||||
getProcessFilterByName(filterName: string, appId?: number): Observable<FilterProcessRepresentationModel> {
|
||||
return Observable.fromPromise(this.callApiProcessFilters(appId))
|
||||
.map((response: any) => {
|
||||
return response.data.find(filter => filter.name === filterName);
|
||||
}).catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the default filters
|
||||
* @param appId
|
||||
* @returns {FilterProcessRepresentationModel[]}
|
||||
*/
|
||||
public createDefaultFilters(appId: number): Observable<any[]> {
|
||||
let runningFilter = this.getRunningFilterInstance(appId);
|
||||
let runningObservable = this.addProcessFilter(runningFilter);
|
||||
|
||||
let completedFilter = this.getCompletedFilterInstance(appId);
|
||||
let completedObservable = this.addProcessFilter(completedFilter);
|
||||
|
||||
let allFilter = this.getAllFilterInstance(appId);
|
||||
let allObservable = this.addProcessFilter(allFilter);
|
||||
|
||||
return Observable.create(observer => {
|
||||
Observable.forkJoin(
|
||||
runningObservable,
|
||||
completedObservable,
|
||||
allObservable
|
||||
).subscribe(
|
||||
(res) => {
|
||||
let filters: FilterProcessRepresentationModel[] = [];
|
||||
res.forEach((filter) => {
|
||||
if (filter.name === runningFilter.name) {
|
||||
filters.push(runningFilter);
|
||||
} else if (filter.name === completedFilter.name) {
|
||||
filters.push(completedFilter);
|
||||
} else if (filter.name === allFilter.name) {
|
||||
filters.push(allFilter);
|
||||
}
|
||||
});
|
||||
observer.next(filters);
|
||||
observer.complete();
|
||||
},
|
||||
(err: any) => {
|
||||
this.logService.error(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getRunningFilterInstance(appId: number): FilterProcessRepresentationModel {
|
||||
return new FilterProcessRepresentationModel({
|
||||
'name': 'Running',
|
||||
'appId': appId,
|
||||
'recent': true,
|
||||
'icon': 'glyphicon-random',
|
||||
'filter': { 'sort': 'created-desc', 'name': '', 'state': 'running' }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a static Completed filter instance
|
||||
* @param appId
|
||||
* @returns {FilterProcessRepresentationModel}
|
||||
*/
|
||||
private getCompletedFilterInstance(appId: number): FilterProcessRepresentationModel {
|
||||
return new FilterProcessRepresentationModel({
|
||||
'name': 'Completed',
|
||||
'appId': appId,
|
||||
'recent': false,
|
||||
'icon': 'glyphicon-ok-sign',
|
||||
'filter': { 'sort': 'created-desc', 'name': '', 'state': 'completed' }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a static All filter instance
|
||||
* @param appId
|
||||
* @returns {FilterProcessRepresentationModel}
|
||||
*/
|
||||
private getAllFilterInstance(appId: number): FilterProcessRepresentationModel {
|
||||
return new FilterProcessRepresentationModel({
|
||||
'name': 'All',
|
||||
'appId': appId,
|
||||
'recent': true,
|
||||
'icon': 'glyphicon-th',
|
||||
'filter': { 'sort': 'created-desc', 'name': '', 'state': 'all' }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a filter
|
||||
* @param filter - FilterProcessRepresentationModel
|
||||
* @returns {FilterProcessRepresentationModel}
|
||||
*/
|
||||
addProcessFilter(filter: FilterProcessRepresentationModel): Observable<FilterProcessRepresentationModel> {
|
||||
return Observable.fromPromise(this.alfrescoApiService.getInstance().activiti.userFiltersApi.createUserProcessInstanceFilter(filter))
|
||||
.map(res => res)
|
||||
.map((response: FilterProcessRepresentationModel) => {
|
||||
return response;
|
||||
}).catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
callApiProcessFilters(appId?: number) {
|
||||
if (appId) {
|
||||
return this.alfrescoApiService.getInstance().activiti.userFiltersApi.getUserProcessInstanceFilters({ appId: appId });
|
||||
} else {
|
||||
return this.alfrescoApiService.getInstance().activiti.userFiltersApi.getUserProcessInstanceFilters();
|
||||
}
|
||||
}
|
||||
|
||||
private handleProcessError(error: any) {
|
||||
return Observable.throw(error || 'Server error');
|
||||
}
|
||||
}
|
@@ -0,0 +1,538 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { async } from '@angular/core/testing';
|
||||
import { AlfrescoApi } from 'alfresco-js-api';
|
||||
import { AlfrescoApiService } from '@alfresco/core';
|
||||
import { exampleProcess, fakeProcessInstances } from '../../mock';
|
||||
import { fakeError, fakeProcessDef, fakeTasksList } from '../../mock';
|
||||
import { ProcessFilterParamRepresentationModel } from '../models/filter-process.model';
|
||||
import { ProcessInstanceVariable } from '../models/process-instance-variable.model';
|
||||
import { ProcessService } from './process.service';
|
||||
|
||||
declare let moment: any;
|
||||
|
||||
describe('ProcessService', () => {
|
||||
|
||||
let service: ProcessService;
|
||||
let apiService: AlfrescoApiService;
|
||||
let alfrescoApi: AlfrescoApi;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
|
||||
providers: [
|
||||
ProcessService
|
||||
]
|
||||
});
|
||||
service = TestBed.get(ProcessService);
|
||||
apiService = TestBed.get(AlfrescoApiService);
|
||||
alfrescoApi = apiService.getInstance();
|
||||
});
|
||||
|
||||
describe('process instances', () => {
|
||||
|
||||
let getProcessInstances: jasmine.Spy;
|
||||
|
||||
let filter: ProcessFilterParamRepresentationModel = new ProcessFilterParamRepresentationModel({
|
||||
processDefinitionId: '1',
|
||||
appDefinitionId: '1',
|
||||
page: 1,
|
||||
sort: 'created-asc',
|
||||
state: 'completed'
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
getProcessInstances = spyOn(alfrescoApi.activiti.processApi, 'getProcessInstances')
|
||||
.and
|
||||
.returnValue(Promise.resolve({ data: [ exampleProcess ] }));
|
||||
});
|
||||
|
||||
it('should return the correct number of instances', async(() => {
|
||||
service.getProcessInstances(filter).subscribe((instances) => {
|
||||
expect(instances.length).toBe(1);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return the correct instance data', async(() => {
|
||||
service.getProcessInstances(filter).subscribe((instances) => {
|
||||
let instance = instances[0];
|
||||
expect(instance.id).toBe(exampleProcess.id);
|
||||
expect(instance.name).toBe(exampleProcess.name);
|
||||
expect(instance.started).toBe(exampleProcess.started);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should filter by processDefinitionKey', async(() => {
|
||||
getProcessInstances = getProcessInstances.and.returnValue(Promise.resolve(fakeProcessInstances));
|
||||
|
||||
service.getProcessInstances(filter, 'fakeProcessDefinitionKey1').subscribe((instances) => {
|
||||
expect(instances.length).toBe(1);
|
||||
let instance = instances[0];
|
||||
expect(instance.id).toBe('340124');
|
||||
expect(instance.name).toBe('James Franklin EMEA Onboarding');
|
||||
expect(instance.started).toEqual(new Date('2017-10-09T12:19:44.560+0000'));
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call service to fetch process instances', () => {
|
||||
service.getProcessInstances(filter);
|
||||
expect(getProcessInstances).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call service with supplied parameters', () => {
|
||||
service.getProcessInstances(filter);
|
||||
expect(getProcessInstances).toHaveBeenCalledWith(filter);
|
||||
});
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
getProcessInstances = getProcessInstances.and.returnValue(Promise.reject(fakeError));
|
||||
service.getProcessInstances(null).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return a default error if no data is returned by the API', async(() => {
|
||||
getProcessInstances = getProcessInstances.and.returnValue(Promise.reject(null));
|
||||
service.getProcessInstances(null).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe('Server error');
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('process instance', () => {
|
||||
|
||||
const processId = 'test';
|
||||
let getProcessInstance: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
getProcessInstance = spyOn(alfrescoApi.activiti.processApi, 'getProcessInstance')
|
||||
.and
|
||||
.returnValue(Promise.resolve(exampleProcess));
|
||||
});
|
||||
|
||||
it('should return the correct instance data', async(() => {
|
||||
service.getProcess(processId).subscribe((instance) => {
|
||||
expect(instance.id).toBe(exampleProcess.id);
|
||||
expect(instance.name).toBe(exampleProcess.name);
|
||||
expect(instance.started).toBe(exampleProcess.started);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call service to fetch process instances', () => {
|
||||
service.getProcess(processId);
|
||||
expect(getProcessInstance).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call service with supplied process ID', () => {
|
||||
service.getProcess(processId);
|
||||
expect(getProcessInstance).toHaveBeenCalledWith(processId);
|
||||
});
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
getProcessInstance = getProcessInstance.and.returnValue(Promise.reject(fakeError));
|
||||
service.getProcess(null).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return a default error if no data is returned by the API', async(() => {
|
||||
getProcessInstance = getProcessInstance.and.returnValue(Promise.reject(null));
|
||||
service.getProcess(null).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe('Server error');
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('start process instance', () => {
|
||||
|
||||
const processDefId = '1234', processName = 'My process instance';
|
||||
let startNewProcessInstance: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
startNewProcessInstance = spyOn(alfrescoApi.activiti.processApi, 'startNewProcessInstance')
|
||||
.and
|
||||
.returnValue(Promise.resolve(exampleProcess));
|
||||
});
|
||||
|
||||
it('should call the API to create the process instance', () => {
|
||||
service.startProcess(processDefId, processName);
|
||||
expect(startNewProcessInstance).toHaveBeenCalledWith({
|
||||
name: processName,
|
||||
processDefinitionId: processDefId
|
||||
});
|
||||
});
|
||||
|
||||
it('should call the API to create the process instance with form parameters', () => {
|
||||
let formParams = {
|
||||
type: 'ford',
|
||||
color: 'red'
|
||||
};
|
||||
service.startProcess(processDefId, processName, null, formParams);
|
||||
expect(startNewProcessInstance).toHaveBeenCalledWith({
|
||||
name: processName,
|
||||
processDefinitionId: processDefId,
|
||||
values: formParams
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the created process instance', async(() => {
|
||||
service.startProcess(processDefId, processName).subscribe((createdProcess) => {
|
||||
expect(createdProcess.id).toBe(exampleProcess.id);
|
||||
expect(createdProcess.name).toBe(exampleProcess.name);
|
||||
expect(createdProcess.started).toBe(exampleProcess.started);
|
||||
expect(createdProcess.startedBy.id).toBe(exampleProcess.startedBy.id);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
startNewProcessInstance = startNewProcessInstance.and.returnValue(Promise.reject(fakeError));
|
||||
|
||||
service.startProcess(processDefId, processName).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return a default error if no data is returned by the API', async(() => {
|
||||
startNewProcessInstance = startNewProcessInstance.and.returnValue(Promise.reject(null));
|
||||
service.startProcess(processDefId, processName).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe('Server error');
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('cancel process instance', () => {
|
||||
|
||||
const processInstanceId = '1234';
|
||||
let deleteProcessInstance: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
deleteProcessInstance = spyOn(alfrescoApi.activiti.processApi, 'deleteProcessInstance')
|
||||
.and
|
||||
.returnValue(Promise.resolve());
|
||||
});
|
||||
|
||||
it('should call service to delete process instances', () => {
|
||||
service.cancelProcess(processInstanceId);
|
||||
expect(deleteProcessInstance).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call service with supplied process ID', () => {
|
||||
service.cancelProcess(processInstanceId);
|
||||
expect(deleteProcessInstance).toHaveBeenCalledWith(processInstanceId);
|
||||
});
|
||||
|
||||
it('should run the success callback', (done) => {
|
||||
service.cancelProcess(processInstanceId).subscribe(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
deleteProcessInstance = deleteProcessInstance.and.returnValue(Promise.reject(fakeError));
|
||||
service.cancelProcess(null).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return a default error if no data is returned by the API', async(() => {
|
||||
deleteProcessInstance = deleteProcessInstance.and.returnValue(Promise.reject(null));
|
||||
service.cancelProcess(null).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe('Server error');
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('process definitions', () => {
|
||||
|
||||
let getProcessDefinitions: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
getProcessDefinitions = spyOn(alfrescoApi.activiti.processApi, 'getProcessDefinitions')
|
||||
.and
|
||||
.returnValue(Promise.resolve({ data: [ fakeProcessDef, fakeProcessDef ] }));
|
||||
});
|
||||
|
||||
it('should return the correct number of process defs', async(() => {
|
||||
service.getProcessDefinitions().subscribe((defs) => {
|
||||
expect(defs.length).toBe(2);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return the correct process def data', async(() => {
|
||||
service.getProcessDefinitions().subscribe((defs) => {
|
||||
expect(defs[0].id).toBe(fakeProcessDef.id);
|
||||
expect(defs[0].key).toBe(fakeProcessDef.key);
|
||||
expect(defs[0].name).toBe(fakeProcessDef.name);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call API with correct parameters when no appId provided', () => {
|
||||
service.getProcessDefinitions();
|
||||
expect(getProcessDefinitions).toHaveBeenCalledWith({
|
||||
latest: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should call API with correct parameters when appId provided', () => {
|
||||
const appId = 1;
|
||||
service.getProcessDefinitions(appId);
|
||||
expect(getProcessDefinitions).toHaveBeenCalledWith({
|
||||
latest: true,
|
||||
appDefinitionId: appId
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
getProcessDefinitions = getProcessDefinitions.and.returnValue(Promise.reject(fakeError));
|
||||
service.getProcessDefinitions().subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return a default error if no data is returned by the API', async(() => {
|
||||
getProcessDefinitions = getProcessDefinitions.and.returnValue(Promise.reject(null));
|
||||
service.getProcessDefinitions().subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe('Server error');
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('process instance tasks', () => {
|
||||
|
||||
const processId = '1001';
|
||||
let listTasks: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
listTasks = spyOn(alfrescoApi.activiti.taskApi, 'listTasks')
|
||||
.and
|
||||
.returnValue(Promise.resolve(fakeTasksList));
|
||||
});
|
||||
|
||||
it('should return the correct number of tasks', async(() => {
|
||||
service.getProcessTasks(processId).subscribe((tasks) => {
|
||||
expect(tasks.length).toBe(2);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return the correct task data', async(() => {
|
||||
let fakeTasks = fakeTasksList.data;
|
||||
service.getProcessTasks(processId).subscribe((tasks) => {
|
||||
let task = tasks[0];
|
||||
expect(task.id).toBe(fakeTasks[0].id);
|
||||
expect(task.name).toBe(fakeTasks[0].name);
|
||||
expect(task.created).toEqual(moment(new Date('2016-11-10T00:00:00+00:00'), 'YYYY-MM-DD').format());
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call service to fetch process instance tasks', () => {
|
||||
service.getProcessTasks(processId);
|
||||
expect(listTasks).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call service with processInstanceId parameter', () => {
|
||||
service.getProcessTasks(processId);
|
||||
expect(listTasks).toHaveBeenCalledWith({
|
||||
processInstanceId: processId
|
||||
});
|
||||
});
|
||||
|
||||
it('should call service with processInstanceId and state parameters', () => {
|
||||
service.getProcessTasks(processId, 'completed');
|
||||
expect(listTasks).toHaveBeenCalledWith({
|
||||
processInstanceId: processId,
|
||||
state: 'completed'
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
listTasks = listTasks.and.returnValue(Promise.reject(fakeError));
|
||||
service.getProcessTasks(processId).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return a default error if no data is returned by the API', async(() => {
|
||||
listTasks = listTasks.and.returnValue(Promise.reject(null));
|
||||
service.getProcessTasks(processId).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe('Server error');
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('process variables', () => {
|
||||
|
||||
let getVariablesSpy: jasmine.Spy;
|
||||
let createOrUpdateProcessInstanceVariablesSpy: jasmine.Spy;
|
||||
let deleteProcessInstanceVariableSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
getVariablesSpy = spyOn(alfrescoApi.activiti.processInstanceVariablesApi, 'getProcessInstanceVariables').and.returnValue(Promise.resolve([{
|
||||
name: 'var1',
|
||||
value: 'Test1'
|
||||
}, {
|
||||
name: 'var3',
|
||||
value: 'Test3'
|
||||
}]));
|
||||
|
||||
createOrUpdateProcessInstanceVariablesSpy = spyOn(alfrescoApi.activiti.processInstanceVariablesApi,
|
||||
'createOrUpdateProcessInstanceVariables').and.returnValue(Promise.resolve({}));
|
||||
|
||||
deleteProcessInstanceVariableSpy = spyOn(alfrescoApi.activiti.processInstanceVariablesApi,
|
||||
'deleteProcessInstanceVariable').and.returnValue(Promise.resolve());
|
||||
});
|
||||
|
||||
describe('get variables', () => {
|
||||
|
||||
it('should call service to fetch variables', () => {
|
||||
service.getProcessInstanceVariables(null);
|
||||
expect(getVariablesSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
getVariablesSpy = getVariablesSpy.and.returnValue(Promise.reject(fakeError));
|
||||
service.getProcessInstanceVariables(null).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return a default error if no data is returned by the API', async(() => {
|
||||
getVariablesSpy = getVariablesSpy.and.returnValue(Promise.reject(null));
|
||||
service.getProcessInstanceVariables(null).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe('Server error');
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('create or update variables', () => {
|
||||
|
||||
let updatedVariables = [new ProcessInstanceVariable({
|
||||
name: 'var1',
|
||||
value: 'Test1'
|
||||
}), new ProcessInstanceVariable({
|
||||
name: 'var3',
|
||||
value: 'Test3'
|
||||
})];
|
||||
|
||||
it('should call service to create or update variables', () => {
|
||||
service.createOrUpdateProcessInstanceVariables('123', updatedVariables);
|
||||
expect(createOrUpdateProcessInstanceVariablesSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
createOrUpdateProcessInstanceVariablesSpy = createOrUpdateProcessInstanceVariablesSpy.and.returnValue(Promise.reject(fakeError));
|
||||
service.createOrUpdateProcessInstanceVariables('123', updatedVariables).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return a default error if no data is returned by the API', async(() => {
|
||||
createOrUpdateProcessInstanceVariablesSpy = createOrUpdateProcessInstanceVariablesSpy.and.returnValue(Promise.reject(null));
|
||||
service.createOrUpdateProcessInstanceVariables('123', updatedVariables).subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe('Server error');
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('delete variables', () => {
|
||||
|
||||
it('should call service to delete variables', () => {
|
||||
service.deleteProcessInstanceVariable('123', 'myVar');
|
||||
expect(deleteProcessInstanceVariableSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should pass on any error that is returned by the API', async(() => {
|
||||
deleteProcessInstanceVariableSpy = deleteProcessInstanceVariableSpy.and.returnValue(Promise.reject(fakeError));
|
||||
service.deleteProcessInstanceVariable('123', 'myVar').subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe(fakeError);
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should return a default error if no data is returned by the API', async(() => {
|
||||
deleteProcessInstanceVariableSpy = deleteProcessInstanceVariableSpy.and.returnValue(Promise.reject(null));
|
||||
service.deleteProcessInstanceVariable('123', 'myVar').subscribe(
|
||||
() => {},
|
||||
(res) => {
|
||||
expect(res).toBe('Server error');
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
157
lib/process-services/process-list/services/process.service.ts
Normal file
157
lib/process-services/process-list/services/process.service.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2016 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AlfrescoApiService } from '@alfresco/core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { TaskDetailsModel } from '../../task-list';
|
||||
import { ProcessFilterParamRepresentationModel } from '../models/filter-process.model';
|
||||
import { ProcessDefinitionRepresentation } from '../models/process-definition.model';
|
||||
import { ProcessInstanceVariable } from '../models/process-instance-variable.model';
|
||||
import { ProcessInstance } from '../models/process-instance.model';
|
||||
|
||||
declare let moment: any;
|
||||
|
||||
@Injectable()
|
||||
export class ProcessService {
|
||||
|
||||
constructor(private alfrescoApiService: AlfrescoApiService) {
|
||||
}
|
||||
|
||||
getProcessInstances(requestNode: ProcessFilterParamRepresentationModel, processDefinitionKey?: string): Observable<ProcessInstance[]> {
|
||||
return Observable.fromPromise(this.alfrescoApiService.getInstance().activiti.processApi.getProcessInstances(requestNode))
|
||||
.map((res: any) => {
|
||||
if (processDefinitionKey) {
|
||||
return res.data.filter(process => process.processDefinitionKey === processDefinitionKey);
|
||||
} else {
|
||||
return res.data;
|
||||
}
|
||||
}).catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch the Process Audit information as a pdf
|
||||
* @param processId - the process id
|
||||
*/
|
||||
fetchProcessAuditPdfById(processId: string): Observable<Blob> {
|
||||
return Observable.fromPromise(this.alfrescoApiService.getInstance().activiti.processApi.getProcessAuditPdf(processId))
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch the Process Audit information in a json format
|
||||
* @param processId - the process id
|
||||
*/
|
||||
fetchProcessAuditJsonById(processId: string): Observable<any> {
|
||||
return Observable.fromPromise(this.alfrescoApiService.getInstance().activiti.processApi.getProcessAuditJson(processId))
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
getProcess(processInstanceId: string): Observable<ProcessInstance> {
|
||||
return Observable.fromPromise(this.alfrescoApiService.getInstance().activiti.processApi.getProcessInstance(processInstanceId))
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
getProcessTasks(processInstanceId: string, state?: string): Observable<TaskDetailsModel[]> {
|
||||
let taskOpts = state ? {
|
||||
processInstanceId: processInstanceId,
|
||||
state: state
|
||||
} : {
|
||||
processInstanceId: processInstanceId
|
||||
};
|
||||
return Observable.fromPromise(this.alfrescoApiService.getInstance().activiti.taskApi.listTasks(taskOpts))
|
||||
.map(this.extractData)
|
||||
.map(tasks => tasks.map((task: any) => {
|
||||
task.created = moment(task.created, 'YYYY-MM-DD').format();
|
||||
return task;
|
||||
}))
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
getProcessDefinitions(appId?: number): Observable<ProcessDefinitionRepresentation[]> {
|
||||
let opts = appId ? {
|
||||
latest: true,
|
||||
appDefinitionId: appId
|
||||
} : {
|
||||
latest: true
|
||||
};
|
||||
return Observable.fromPromise(
|
||||
this.alfrescoApiService.getInstance().activiti.processApi.getProcessDefinitions(opts)
|
||||
)
|
||||
.map(this.extractData)
|
||||
.map(processDefs => processDefs.map((pd) => new ProcessDefinitionRepresentation(pd)))
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
startProcess(processDefinitionId: string, name: string, outcome?: string, startFormValues?: any, variables?: ProcessInstanceVariable[]): Observable<ProcessInstance> {
|
||||
let startRequest: any = {
|
||||
name: name,
|
||||
processDefinitionId: processDefinitionId
|
||||
};
|
||||
if (outcome) {
|
||||
startRequest.outcome = outcome;
|
||||
}
|
||||
if (startFormValues) {
|
||||
startRequest.values = startFormValues;
|
||||
}
|
||||
if (variables) {
|
||||
startRequest.variables = variables;
|
||||
}
|
||||
return Observable.fromPromise(
|
||||
this.alfrescoApiService.getInstance().activiti.processApi.startNewProcessInstance(startRequest)
|
||||
)
|
||||
.map((pd) => new ProcessInstance(pd))
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
cancelProcess(processInstanceId: string): Observable<void> {
|
||||
return Observable.fromPromise(
|
||||
this.alfrescoApiService.getInstance().activiti.processApi.deleteProcessInstance(processInstanceId)
|
||||
)
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
getProcessInstanceVariables(processDefinitionId: string): Observable<ProcessInstanceVariable[]> {
|
||||
return Observable.fromPromise(
|
||||
this.alfrescoApiService.getInstance().activiti.processInstanceVariablesApi.getProcessInstanceVariables(processDefinitionId)
|
||||
)
|
||||
.map((processVars: any[]) => processVars.map((pd) => new ProcessInstanceVariable(pd)))
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
createOrUpdateProcessInstanceVariables(processDefinitionId: string, variables: ProcessInstanceVariable[]): Observable<ProcessInstanceVariable[]> {
|
||||
return Observable.fromPromise(
|
||||
this.alfrescoApiService.getInstance().activiti.processInstanceVariablesApi.createOrUpdateProcessInstanceVariables(processDefinitionId, variables)
|
||||
)
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
deleteProcessInstanceVariable(processDefinitionId: string, variableName: string): Observable<void> {
|
||||
return Observable.fromPromise(
|
||||
this.alfrescoApiService.getInstance().activiti.processInstanceVariablesApi.deleteProcessInstanceVariable(processDefinitionId, variableName)
|
||||
)
|
||||
.catch(err => this.handleProcessError(err));
|
||||
}
|
||||
|
||||
private extractData(res: any) {
|
||||
return res.data || {};
|
||||
}
|
||||
|
||||
private handleProcessError(error: any) {
|
||||
return Observable.throw(error || 'Server error');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user