Compare commits
No commits in common. "899444e815c59995a2cc731813784504ac88fe45" and "edce32888094b2002b62a3f36d3551de1c10895d" have entirely different histories.
899444e815
...
edce328880
@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 72 70" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(1.093797,0,0,1.093797,0.640357,-0.002979)">
|
|
||||||
<path d="M39.703,63.992C33.273,63.965 32.923,63.847 31.428,63.333C28.308,62.274 25.677,60.059 24.093,57.166L23.605,56.273L22.819,56.189C21.521,56.058 19.247,55.63 17.735,55.249C11.032,53.558 6.21,50.355 4.507,46.45C4.352,46.116 4.138,45.39 4.031,44.83C3.459,41.997 4.555,39.068 7.079,36.734C7.626,36.222 7.876,35.877 8.031,35.424C8.138,35.091 8.341,34.638 8.46,34.436C8.686,34.067 8.686,34.055 8.341,33.305C7.4,31.269 7.388,29.197 8.329,27.244L8.686,26.506L8.4,25.947C7.698,24.577 7.436,22.732 7.757,21.386L7.9,20.767L7.103,20.041C4.519,17.636 3.4,14.6 4.066,11.718C4.781,8.539 7.198,5.896 11.389,3.681C15.104,1.728 19.473,0.585 25.236,0.073C26.51,-0.046 31.999,0.109 33.488,0.3C40.929,1.252 46.93,3.824 50.157,7.467C53.669,11.432 53.36,16.338 49.371,20.041L48.573,20.767L48.716,21.386C49.038,22.744 48.776,24.565 48.061,25.97C47.775,26.554 47.775,26.566 48.002,26.935C48.537,27.792 48.799,28.876 48.799,30.245C48.799,30.465 48.795,30.668 48.786,30.858C47.381,30.973 46.02,31.246 44.722,31.661C45.016,31.051 45.191,29.981 45.025,29.697C44.93,29.518 44.858,29.53 44.43,29.828C41.763,31.638 38.048,32.948 33.547,33.65C32.059,33.876 31.285,33.924 28.237,33.924C25.189,33.924 24.415,33.876 22.926,33.65C18.545,32.959 14.878,31.709 12.294,29.995C11.484,29.459 11.496,29.459 11.413,29.804C11.222,30.554 11.639,31.709 12.425,32.686C13.687,34.257 16.783,35.865 20.152,36.734C25.553,38.129 31.889,38.015 36.956,36.571C35.707,37.901 34.654,39.418 33.845,41.075C32.625,41.258 31.365,41.384 30.082,41.449L28.499,41.532L29.237,42.33C29.654,42.771 30.308,43.58 30.689,44.128C31.344,45.069 31.404,45.128 31.785,45.069C31.904,45.049 32.16,45.014 32.465,44.973C32.161,46.294 32,47.67 32,49.083C32,51.584 32.504,53.969 33.415,56.141C33.042,56.019 32.813,55.848 32.404,55.606C31.63,55.141 30.725,54.118 30.344,53.26C30.213,52.963 29.951,52.058 29.749,51.26C28.82,47.45 26.891,44.664 23.688,42.509C22.272,41.556 21.39,41.163 18.414,40.187C15.759,39.306 13.496,38.317 12.294,37.496C11.484,36.948 11.508,36.948 11.413,37.424C11.175,38.71 12.294,40.437 14.223,41.747C15.342,42.497 17.259,43.366 19.176,43.997C21.188,44.652 22.2,45.176 23.224,46.069C24.927,47.545 25.546,48.76 26.189,51.808C26.76,54.522 27.57,56.082 29.213,57.642C30.356,58.725 31.416,59.38 32.797,59.845L33.797,60.19L35.768,60.196C36.883,61.649 38.21,62.93 39.703,63.992ZM10.186,43.271C9.71,42.794 9.115,42.08 8.841,41.675L8.365,40.949L8.019,41.687C7.329,43.175 7.662,44.997 8.889,46.462C10.913,48.867 15.223,50.962 20.152,51.927C22.724,52.427 22.498,52.415 22.414,52.022C22.105,50.605 21.605,49.617 20.926,49.01C20.283,48.45 19.438,48.057 17.366,47.355C16.366,47.009 15.068,46.509 14.497,46.235C13.008,45.545 11.198,44.295 10.186,43.271ZM12.818,23.601C12.187,23.327 11.603,23.065 11.532,23.041C11.425,22.994 11.413,23.089 11.472,23.387C12.079,26.066 16.437,28.649 22.081,29.673C24.224,30.066 25.534,30.173 28.237,30.173C30.94,30.173 32.249,30.066 34.392,29.673C40.036,28.649 44.394,26.066 45.001,23.387C45.061,23.089 45.049,22.994 44.942,23.041C44.87,23.065 44.287,23.327 43.656,23.601C40.751,24.875 37.274,25.744 32.987,26.268C31.261,26.482 25.212,26.482 23.486,26.268C19.2,25.744 15.723,24.875 12.818,23.601ZM31.19,3.872C23.391,3.241 15.259,5.098 10.663,8.563C9.448,9.48 8.71,10.337 8.138,11.48C7.722,12.302 7.674,12.516 7.674,13.361C7.674,14.635 8.138,15.623 9.305,16.85C13.115,20.886 22.414,23.327 31.309,22.613C38.429,22.041 44.287,19.922 47.168,16.85C48.335,15.623 48.799,14.635 48.799,13.361C48.799,12.516 48.752,12.302 48.335,11.48C47.764,10.337 47.025,9.48 45.811,8.563C42.37,5.979 37.345,4.36 31.19,3.872ZM25.796,7.932C29.999,7.265 34.869,8.527 36.595,10.718C37.357,11.694 37.548,12.218 37.548,13.361C37.548,14.516 37.357,15.04 36.595,16.016C34.488,18.683 28.249,19.791 23.677,18.302C21.355,17.54 20.057,16.576 19.223,14.992C18.795,14.183 18.795,12.552 19.223,11.742C20.259,9.765 22.533,8.444 25.796,7.932ZM30.118,11.67C27.32,11.206 23.915,11.873 22.807,13.099C22.581,13.361 22.581,13.373 22.807,13.635C23.605,14.504 26.082,15.242 28.237,15.242C30.392,15.242 32.868,14.504 33.666,13.635C33.892,13.373 33.892,13.361 33.666,13.099C33.083,12.468 31.797,11.944 30.118,11.67Z"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(1,0,0,1,35.1075,-13.675)">
|
|
||||||
<path d="M19.178,83.675L21.86,83.675C22.896,83.675 23.714,83.056 23.949,82.006L24.677,79.011C25.075,78.891 25.513,78.706 25.906,78.51L28.52,80.101C29.389,80.672 30.396,80.644 31.191,79.849L33.035,77.979C33.748,77.216 33.821,76.234 33.251,75.277L31.691,72.637C31.913,72.276 32.021,71.92 32.166,71.528L35.186,70.826C36.217,70.564 36.892,69.747 36.892,68.705L36.892,66.06C36.892,65.038 36.191,64.272 35.186,64.011L32.197,63.289C32.038,62.825 31.867,62.484 31.722,62.148L33.37,59.446C33.904,58.571 33.804,57.481 33.086,56.8L31.18,54.956C30.427,54.209 29.411,54.031 28.562,54.596L25.906,56.269C25.436,56.028 25.075,55.914 24.683,55.769L23.949,52.722C23.714,51.712 22.896,51.042 21.86,51.042L19.178,51.042C18.131,51.042 17.304,51.737 17.063,52.728L16.381,55.737C15.886,55.903 15.519,56.028 15.101,56.269L12.444,54.596C11.633,54.068 10.585,54.204 9.852,54.956L7.997,56.8C7.279,57.501 7.077,58.571 7.642,59.446L9.316,62.148C9.166,62.484 8.981,62.876 8.81,63.289L5.852,64.011C4.841,64.272 4.177,65.064 4.177,66.06L4.177,68.705C4.177,69.747 4.847,70.564 5.852,70.826L8.847,71.528C9.037,71.92 9.182,72.276 9.342,72.637L7.782,75.302C7.211,76.234 7.311,77.267 8.023,77.979L9.847,79.849C10.585,80.638 11.613,80.646 12.487,80.101L15.158,78.51C15.596,78.757 15.989,78.891 16.381,79.011L17.063,82.006C17.299,83.005 18.131,83.675 19.178,83.675ZM20.491,72.507C17.681,72.507 15.371,70.197 15.371,67.387C15.371,64.551 17.681,62.236 20.491,62.236C23.326,62.236 25.642,64.551 25.642,67.387C25.642,70.197 23.326,72.507 20.491,72.507Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 6.1 KiB |
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(0.824327,0,0,0.976141,2.488276,-2.712014)">
|
|
||||||
<path d="M56.78,22.995C64.017,23.632 69.667,28.805 69.667,35.084L69.667,42.696C69.667,49.405 63.217,54.851 55.273,54.851L52.514,54.851L39.525,64.685C37.406,66.289 34.196,66.289 32.077,64.685L19.088,54.851L16.329,54.851C8.385,54.851 1.935,49.405 1.935,42.696L1.935,35.084C1.935,28.805 7.585,23.632 14.822,22.995L14.822,8.007C14.822,6.476 16.294,5.233 18.107,5.233C19.921,5.233 21.393,6.476 21.393,8.007L21.393,22.929L50.209,22.929L50.209,8.007C50.209,6.476 51.681,5.233 53.495,5.233C55.308,5.233 56.78,6.476 56.78,8.007L56.78,22.995ZM28.932,54.851L35.801,60.052L42.67,54.851L28.932,54.851ZM55.273,49.302C59.591,49.302 63.096,46.342 63.096,42.696L63.096,35.084C63.096,31.438 59.591,28.478 55.273,28.478L16.329,28.478C12.011,28.478 8.506,31.438 8.506,35.084L8.506,42.696C8.506,46.342 12.011,49.302 16.329,49.302L55.273,49.302Z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB |
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(0.881796,0,0,0.881796,2.413866,6)">
|
|
||||||
<path d="M8.144,48.624L11.105,53.718L33.55,42.441L55.994,53.718L58.987,48.624L36.537,37.341L36.537,10.769L30.594,10.769L30.594,37.341L8.144,48.624ZM0.384,53.413C-0.592,55.654 0.33,57.211 2.725,57.489L15.69,58.924C18.959,59.318 20.849,57.171 19.138,54.231L11.408,40.828C9.765,37.956 6.931,38.488 5.654,41.467L0.384,53.413ZM66.721,53.413L61.477,41.467C60.174,38.488 57.366,37.956 55.717,40.828L47.962,54.231C46.256,57.171 48.172,59.318 51.415,58.924L64.38,57.489C66.77,57.211 67.697,55.654 66.721,53.413ZM31.172,1.489L23.477,11.964C21.527,14.591 22.46,17.332 25.814,17.332L41.248,17.332C44.665,17.332 45.558,14.639 43.592,11.97L35.87,1.484C34.395,-0.486 32.642,-0.506 31.172,1.489Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(0.783639,0,0,0.783639,3,9.136381)">
|
|
||||||
<path d="M10.966,55.715L61.631,55.715C67.901,55.715 71.026,52.48 70.761,46.429L52.249,29.059C50.874,27.784 49.224,27.139 47.553,27.139C45.84,27.139 44.303,27.727 42.877,29.003L28.763,41.616L22.999,36.414C21.686,35.233 20.243,34.639 18.78,34.639C17.334,34.639 16.041,35.201 14.754,36.383L2.961,46.953C3.114,52.711 5.792,55.715 10.966,55.715ZM10.251,58.341L63.763,58.341C70.533,58.341 74.014,54.886 74.014,48.218L74.014,10.16C74.014,3.486 70.533,0.011 63.763,0.011L10.251,0.011C3.506,0.011 0,3.486 0,10.16L0,48.218C0,54.886 3.506,58.341 10.251,58.341ZM10.606,52.219C7.701,52.219 6.122,50.714 6.122,47.678L6.122,10.694C6.122,7.658 7.701,6.134 10.606,6.134L63.407,6.134C66.287,6.134 67.891,7.658 67.891,10.694L67.891,47.678C67.891,50.714 66.287,52.219 63.407,52.219L10.606,52.219Z" style="fill-rule:nonzero;"/>
|
|
||||||
<path d="M24.209,29.743C28.302,29.743 31.677,26.369 31.677,22.239C31.677,18.146 28.302,14.74 24.209,14.74C20.079,14.74 16.705,18.146 16.705,22.239C16.705,26.369 20.079,29.743 24.209,29.743Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 72 70" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<path d="M38.645,64.222L17.549,64.222C11.671,64.198 8.506,61.129 8.506,55.228L8.506,13.858C8.506,7.958 11.939,4.864 17.619,4.864L54.008,4.864C59.907,4.864 63.096,7.958 63.096,13.858L63.096,35.121C61.386,34.433 59.559,33.974 57.653,33.783L57.653,22.31L54.347,22.31L54.347,23.05C54.347,27.911 52.199,30.851 48.168,31.749L45.342,34.575C44.524,35.393 43.197,35.393 42.379,34.575L39.549,31.745C35.516,30.843 33.374,27.904 33.374,23.05L33.374,22.31L13.973,22.31L13.973,55.44C13.973,57.721 15.098,58.965 17.297,59.072C17.269,58.754 17.255,58.42 17.255,58.07L17.255,54.215C17.255,49.957 19.434,48.069 23.402,48.069L36.442,48.069C35.975,49.668 35.702,51.349 35.651,53.085L24.624,53.085C23.237,53.085 22.516,53.749 22.516,55.165L22.516,57.102C22.516,58.518 23.237,59.21 24.624,59.21L36.417,59.21C36.93,60.995 37.685,62.679 38.645,64.222ZM57.653,17.418L57.653,13.652C57.653,11.264 56.396,10.001 54.013,10.001L17.613,10.001C15.206,10.001 13.973,11.264 13.973,13.652L13.973,17.418L33.807,17.418C34.894,13.675 37.985,11.731 42.487,11.731L45.258,11.731C49.934,11.731 52.908,13.675 53.94,17.418L57.653,17.418ZM42.481,16.868C40.074,16.868 38.841,18.13 38.841,20.518L38.841,23.262C38.841,25.65 40.074,26.902 42.481,26.902L45.264,26.902C47.647,26.902 48.904,25.65 48.904,23.262L48.904,20.518C48.904,18.149 47.666,16.888 45.32,16.868L42.481,16.868Z"/>
|
|
||||||
<g transform="matrix(1,0,0,1,35.1075,-13.675)">
|
|
||||||
<path d="M19.178,83.675L21.86,83.675C22.896,83.675 23.714,83.056 23.949,82.006L24.677,79.011C25.075,78.891 25.513,78.706 25.906,78.51L28.52,80.101C29.389,80.672 30.396,80.644 31.191,79.849L33.035,77.979C33.748,77.216 33.821,76.234 33.251,75.277L31.691,72.637C31.913,72.276 32.021,71.92 32.166,71.528L35.186,70.826C36.217,70.564 36.892,69.747 36.892,68.705L36.892,66.06C36.892,65.038 36.191,64.272 35.186,64.011L32.197,63.289C32.038,62.825 31.867,62.484 31.722,62.148L33.37,59.446C33.904,58.571 33.804,57.481 33.086,56.8L31.18,54.956C30.427,54.209 29.411,54.031 28.562,54.596L25.906,56.269C25.436,56.028 25.075,55.914 24.683,55.769L23.949,52.722C23.714,51.712 22.896,51.042 21.86,51.042L19.178,51.042C18.131,51.042 17.304,51.737 17.063,52.728L16.381,55.737C15.886,55.903 15.519,56.028 15.101,56.269L12.444,54.596C11.633,54.068 10.585,54.204 9.852,54.956L7.997,56.8C7.279,57.501 7.077,58.571 7.642,59.446L9.316,62.148C9.166,62.484 8.981,62.876 8.81,63.289L5.852,64.011C4.841,64.272 4.177,65.064 4.177,66.06L4.177,68.705C4.177,69.747 4.847,70.564 5.852,70.826L8.847,71.528C9.037,71.92 9.182,72.276 9.342,72.637L7.782,75.302C7.211,76.234 7.311,77.267 8.023,77.979L9.847,79.849C10.585,80.638 11.613,80.646 12.487,80.101L15.158,78.51C15.596,78.757 15.989,78.891 16.381,79.011L17.063,82.006C17.299,83.005 18.131,83.675 19.178,83.675ZM20.491,72.507C17.681,72.507 15.371,70.197 15.371,67.387C15.371,64.551 17.681,62.236 20.491,62.236C23.326,62.236 25.642,64.551 25.642,67.387C25.642,70.197 23.326,72.507 20.491,72.507Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.3 KiB |
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g transform="matrix(0.781552,0,0,0.781552,1,17.941979)">
|
|
||||||
<path d="M79.329,9.044C79.329,3.244 76.045,0.021 70.183,0.021L9.093,0.021C3.221,0.021 0,3.244 0,9.044L0,26.911C0,32.701 3.263,35.953 9.125,35.953L70.225,35.953C76.077,35.953 79.329,32.701 79.329,26.911L79.329,9.044ZM72.826,10.089L72.826,25.876C72.826,28.201 71.564,29.461 69.323,29.461L9.964,29.461C7.775,29.461 6.482,28.201 6.482,25.876L6.482,10.089C6.482,7.763 7.785,6.503 9.995,6.503L69.354,6.503C71.564,6.503 72.826,7.763 72.826,10.089ZM13.04,4.664L10.112,4.664L10.112,20.338C10.112,21.156 10.774,21.776 11.602,21.776C12.461,21.776 13.04,21.134 13.04,20.297L13.04,4.664ZM18.661,4.664L15.732,4.664L15.732,15.295C15.732,16.144 16.404,16.744 17.211,16.744C18.092,16.744 18.661,16.123 18.661,15.264L18.661,4.664ZM24.27,4.664L21.32,4.664L21.32,15.295C21.32,16.144 22.014,16.744 22.81,16.744C23.67,16.744 24.27,16.123 24.27,15.264L24.27,4.664ZM29.849,4.664L26.92,4.664L26.92,15.295C26.92,16.144 27.592,16.744 28.42,16.744C29.279,16.744 29.849,16.123 29.849,15.264L29.849,4.664ZM35.479,4.664L32.55,4.664L32.55,15.295C32.55,16.144 33.222,16.744 34.019,16.744C34.9,16.744 35.479,16.123 35.479,15.264L35.479,4.664ZM41.11,4.664L38.181,4.664L38.181,20.338C38.181,21.156 38.843,21.776 39.65,21.776C40.53,21.776 41.11,21.134 41.11,20.297L41.11,4.664ZM46.709,4.664L43.781,4.664L43.781,15.295C43.781,16.144 44.452,16.744 45.259,16.744C46.118,16.744 46.709,16.123 46.709,15.264L46.709,4.664ZM52.319,4.664L49.39,4.664L49.39,15.295C49.39,16.144 50.062,16.744 50.88,16.744C51.739,16.744 52.319,16.123 52.319,15.264L52.319,4.664ZM57.918,4.664L54.999,4.664L54.999,15.295C54.999,16.144 55.661,16.744 56.468,16.744C57.348,16.744 57.918,16.123 57.918,15.264L57.918,4.664ZM63.548,4.664L60.62,4.664L60.62,15.295C60.62,16.144 61.292,16.744 62.088,16.744C62.948,16.744 63.548,16.123 63.548,15.264L63.548,4.664ZM69.158,4.664L66.229,4.664L66.229,20.338C66.229,21.156 66.891,21.776 67.719,21.776C68.578,21.776 69.158,21.134 69.158,20.297L69.158,4.664Z" style="fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB |
@ -696,66 +696,3 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton {
|
|||||||
.ant-color-picker .ant-collapse .ant-collapse-item .ant-collapse-header {
|
.ant-color-picker .ant-collapse .ant-collapse-item .ant-collapse-header {
|
||||||
margin: 5.5px 0;
|
margin: 5.5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subSteps.ant-timeline .ant-timeline-item-last > .ant-timeline-item-content {
|
|
||||||
min-height: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subSteps.ant-timeline .ant-timeline-item-last {
|
|
||||||
padding-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subSteps.ant-timeline {
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-steps.ant-steps-vertical
|
|
||||||
> .ant-steps-item
|
|
||||||
> .ant-steps-item-container
|
|
||||||
> .ant-steps-item-tail::after {
|
|
||||||
width: 1.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subSteps.ant-timeline .ant-timeline-item-head {
|
|
||||||
background-color: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subSteps.ant-timeline .subSteps-dot {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: var(--color-primary);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subSteps.ant-timeline .subSteps-dot svg path {
|
|
||||||
stroke: var(--layout-modal-bg);
|
|
||||||
stroke-width: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subSteps.ant-timeline .subSteps-dot span {
|
|
||||||
width: 5px;
|
|
||||||
height: 5px;
|
|
||||||
margin-top: 0px;
|
|
||||||
margin-right: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subSteps.ant-timeline .subSteps-dot.subSteps-dot-disabled {
|
|
||||||
background-color: var(--color-primary-disabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
.subSteps.ant-timeline .subSteps-dot.subSteps-dot-disabled svg path {
|
|
||||||
stroke: var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-steps .ant-steps-item-finish .ant-steps-item-icon svg path {
|
|
||||||
stroke: var(--color-primary);
|
|
||||||
stroke-width: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-descriptions .ant-descriptions-item-label::after {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import {
|
|||||||
Navigate
|
Navigate
|
||||||
} from 'react-router-dom'
|
} from 'react-router-dom'
|
||||||
import { App, ConfigProvider } from 'antd'
|
import { App, ConfigProvider } from 'antd'
|
||||||
import { lazy } from 'react'
|
|
||||||
|
|
||||||
import Dashboard from './components/Dashboard/Dashboard.jsx'
|
import Dashboard from './components/Dashboard/Dashboard.jsx'
|
||||||
import PrivateRoute from './components/PrivateRoute'
|
import PrivateRoute from './components/PrivateRoute'
|
||||||
@ -34,10 +33,6 @@ import AuthCallback from './components/App/AuthCallback.jsx'
|
|||||||
import EmailNotificationTemplate from './components/Email/EmailNotificationTemplate.jsx'
|
import EmailNotificationTemplate from './components/Email/EmailNotificationTemplate.jsx'
|
||||||
import MarketplaceAuthCallback from './components/Dashboard/Sales/Marketplaces/MarketplaceAuthCallback.jsx'
|
import MarketplaceAuthCallback from './components/Dashboard/Sales/Marketplaces/MarketplaceAuthCallback.jsx'
|
||||||
import AuthLaunch from './components/App/AppLaunch.jsx'
|
import AuthLaunch from './components/App/AppLaunch.jsx'
|
||||||
const SlicerIntegration = lazy(
|
|
||||||
() =>
|
|
||||||
import('./components/Dashboard/Production/Printers/SlicerIntegration.jsx')
|
|
||||||
)
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProductionRoutes,
|
ProductionRoutes,
|
||||||
@ -126,10 +121,6 @@ const AppContent = () => {
|
|||||||
path='/email/notification'
|
path='/email/notification'
|
||||||
element={<EmailNotificationTemplate />}
|
element={<EmailNotificationTemplate />}
|
||||||
/>
|
/>
|
||||||
<Route
|
|
||||||
path='/slicer'
|
|
||||||
element={<SlicerIntegration />}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path='/dashboard'
|
path='/dashboard'
|
||||||
|
|||||||
@ -1,144 +0,0 @@
|
|||||||
import { useContext, useRef, useState } from 'react'
|
|
||||||
import { Button, Dropdown, Flex, Modal, Space } from 'antd'
|
|
||||||
import NewFilamentProfile, {
|
|
||||||
EditFilamentProfile
|
|
||||||
} from './FilamentProfiles/NewFilamentProfile'
|
|
||||||
import ColumnViewButton from '../common/ColumnViewButton'
|
|
||||||
import ExportListButton from '../common/ExportListButton'
|
|
||||||
import FilterSidebarButton from '../common/FilterSidebarButton'
|
|
||||||
import ObjectTable from '../common/ObjectTable'
|
|
||||||
import ObjectTableViewButton from '../common/ObjectTableViewButton'
|
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
|
||||||
import PlusIcon from '../../Icons/PlusIcon'
|
|
||||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
|
||||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
|
||||||
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
|
|
||||||
import useViewMode from '../hooks/useViewMode'
|
|
||||||
|
|
||||||
const FilamentProfiles = () => {
|
|
||||||
const { deleteObject } = useContext(ApiServerContext)
|
|
||||||
const [profileModal, setProfileModal] = useState({
|
|
||||||
open: false,
|
|
||||||
profileId: null
|
|
||||||
})
|
|
||||||
const tableRef = useRef()
|
|
||||||
const [viewMode, setViewMode] = useViewMode('FilamentProfiles')
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
|
||||||
useColumnVisibility('filamentProfile')
|
|
||||||
const [showFilterSidebar, setShowFilterSidebar] =
|
|
||||||
useFilterSidebarVisibility('FilamentProfiles')
|
|
||||||
|
|
||||||
const actionItems = {
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'New Filament Profile',
|
|
||||||
key: 'newFilamentProfile',
|
|
||||||
icon: <PlusIcon />
|
|
||||||
},
|
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
|
||||||
label: 'Reload List',
|
|
||||||
key: 'reloadList',
|
|
||||||
icon: <ReloadIcon />
|
|
||||||
}
|
|
||||||
],
|
|
||||||
onClick: ({ key }) => {
|
|
||||||
if (key === 'reloadList') {
|
|
||||||
tableRef.current?.reload()
|
|
||||||
} else if (key === 'newFilamentProfile') {
|
|
||||||
setProfileModal({ open: true, profileId: null })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleRowAction = (action, profile) => {
|
|
||||||
if (action.name === 'edit') {
|
|
||||||
setProfileModal({ open: true, profileId: profile._id })
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.name === 'delete') {
|
|
||||||
Modal.confirm({
|
|
||||||
title: 'Delete filament profile?',
|
|
||||||
content: `This will permanently delete ${profile.name || profile._reference}.`,
|
|
||||||
okText: 'Delete',
|
|
||||||
okType: 'danger',
|
|
||||||
onOk: async () => {
|
|
||||||
await deleteObject(profile._id, 'filamentProfile')
|
|
||||||
await tableRef.current?.reload()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Flex vertical={'true'} gap='large' className='h-100'>
|
|
||||||
<Flex justify={'space-between'}>
|
|
||||||
<Space>
|
|
||||||
<Dropdown menu={actionItems}>
|
|
||||||
<Button>Actions</Button>
|
|
||||||
</Dropdown>
|
|
||||||
<ColumnViewButton
|
|
||||||
type='filamentProfile'
|
|
||||||
visibleState={columnVisibility}
|
|
||||||
updateVisibleState={setColumnVisibility}
|
|
||||||
/>
|
|
||||||
<ExportListButton objectType='filamentProfile' />
|
|
||||||
</Space>
|
|
||||||
<Space>
|
|
||||||
<FilterSidebarButton
|
|
||||||
active={showFilterSidebar}
|
|
||||||
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
|
|
||||||
/>
|
|
||||||
<ObjectTableViewButton
|
|
||||||
viewMode={viewMode}
|
|
||||||
setViewMode={setViewMode}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<ObjectTable
|
|
||||||
ref={tableRef}
|
|
||||||
type='filamentProfile'
|
|
||||||
cards={viewMode === 'cards'}
|
|
||||||
visibleColumns={columnVisibility}
|
|
||||||
showFilterSidebar={showFilterSidebar}
|
|
||||||
expandHeight={true}
|
|
||||||
onRowAction={handleRowAction}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
open={profileModal.open}
|
|
||||||
footer={null}
|
|
||||||
width={800}
|
|
||||||
onCancel={() => setProfileModal({ open: false, profileId: null })}
|
|
||||||
destroyOnHidden
|
|
||||||
>
|
|
||||||
{profileModal.open &&
|
|
||||||
(profileModal.profileId ? (
|
|
||||||
<EditFilamentProfile
|
|
||||||
profileId={profileModal.profileId}
|
|
||||||
onOk={() => {
|
|
||||||
setProfileModal({ open: false, profileId: null })
|
|
||||||
tableRef.current?.reload()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<NewFilamentProfile
|
|
||||||
onOk={() => {
|
|
||||||
setProfileModal({ open: false, profileId: null })
|
|
||||||
tableRef.current?.reload()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FilamentProfiles
|
|
||||||
@ -1,529 +0,0 @@
|
|||||||
import { useRef, useState } from 'react'
|
|
||||||
import { useLocation } from 'react-router-dom'
|
|
||||||
import { Card, Flex, Space } from 'antd'
|
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
|
||||||
import ActionHandler from '../../common/ActionHandler'
|
|
||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
|
||||||
import DocumentPrintButton from '../../common/DocumentPrintButton'
|
|
||||||
import EditButtons from '../../common/EditButtons'
|
|
||||||
import FilamentIcon from '../../../Icons/FilamentIcon'
|
|
||||||
import GCodeFileIcon from '../../../Icons/GCodeFileIcon'
|
|
||||||
import HotEndIcon from '../../../Icons/HotEndIcon'
|
|
||||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon'
|
|
||||||
import InfoCollapse from '../../common/InfoCollapse'
|
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder'
|
|
||||||
import LockIndicator from '../../common/LockIndicator'
|
|
||||||
import MultiMotionIcon from '../../../Icons/MultiMotionIcon'
|
|
||||||
import NoteIcon from '../../../Icons/NoteIcon'
|
|
||||||
import NotesPanel from '../../common/NotesPanel'
|
|
||||||
import ObjectActions from '../../common/ObjectActions'
|
|
||||||
import ObjectForm from '../../common/ObjectForm'
|
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
|
||||||
import ObjectTable from '../../common/ObjectTable'
|
|
||||||
import RulerIcon from '../../../Icons/RulerIcon'
|
|
||||||
import ScrollBox from '../../common/ScrollBox'
|
|
||||||
import SettingsIcon from '../../../Icons/SettingsIcon'
|
|
||||||
import UserNotifierToggle from '../../common/UserNotifierToggle'
|
|
||||||
import ViewButton from '../../common/ViewButton'
|
|
||||||
import useCollapseState from '../../hooks/useCollapseState'
|
|
||||||
import {
|
|
||||||
FILAMENT_PROFILE_BED_TEMPERATURE_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_COMPATIBILITY_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_COOLING_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_EXHAUST_FAN_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_FLOW_PRESSURE_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_GCODE_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_LOADING_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_MATERIAL_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_RETRACTION_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_SHRINKAGE_DRYING_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_SPEED_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_TEMPERATURE_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_WIPE_TOWER_PROPERTIES
|
|
||||||
} from '../../../../database/models/FilamentProfile'
|
|
||||||
|
|
||||||
const toVisibleProperties = (propertyNames) =>
|
|
||||||
Object.fromEntries(propertyNames.map((name) => [name, true]))
|
|
||||||
|
|
||||||
const FilamentProfileInfo = () => {
|
|
||||||
const location = useLocation()
|
|
||||||
const objectFormRef = useRef(null)
|
|
||||||
const actionHandlerRef = useRef(null)
|
|
||||||
const filamentProfileId = new URLSearchParams(location.search).get(
|
|
||||||
'filamentProfileId'
|
|
||||||
)
|
|
||||||
const [collapseState, updateCollapseState] = useCollapseState(
|
|
||||||
'FilamentProfileInfo',
|
|
||||||
{
|
|
||||||
info: true,
|
|
||||||
material: true,
|
|
||||||
flowPressure: true,
|
|
||||||
temperature: true,
|
|
||||||
bedTemperature: true,
|
|
||||||
speed: true,
|
|
||||||
cooling: true,
|
|
||||||
exhaustFan: true,
|
|
||||||
retraction: true,
|
|
||||||
loading: true,
|
|
||||||
wipeTower: true,
|
|
||||||
gCodeSettings: true,
|
|
||||||
compatibility: true,
|
|
||||||
shrinkageDrying: true,
|
|
||||||
notes: true,
|
|
||||||
auditLogs: false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const [objectFormState, setObjectFormState] = useState({
|
|
||||||
isEditing: false,
|
|
||||||
editLoading: false,
|
|
||||||
formValid: false,
|
|
||||||
lock: null,
|
|
||||||
loading: false,
|
|
||||||
objectData: {}
|
|
||||||
})
|
|
||||||
|
|
||||||
const actions = {
|
|
||||||
edit: () => {
|
|
||||||
objectFormRef.current?.startEditing?.()
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
cancelEdit: () => {
|
|
||||||
objectFormRef.current?.cancelEditing?.()
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
finishEdit: () => {
|
|
||||||
objectFormRef.current?.handleUpdate?.()
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
delete: () => {
|
|
||||||
objectFormRef.current?.handleDelete?.()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
gap='large'
|
|
||||||
vertical='true'
|
|
||||||
style={{ maxHeight: '100%', minHeight: 0 }}
|
|
||||||
>
|
|
||||||
<Flex justify='space-between'>
|
|
||||||
<Space size='middle'>
|
|
||||||
<Space size='small'>
|
|
||||||
<ObjectActions
|
|
||||||
type='filamentProfile'
|
|
||||||
id={filamentProfileId}
|
|
||||||
disabled={objectFormState.loading}
|
|
||||||
objectData={objectFormState.objectData}
|
|
||||||
/>
|
|
||||||
<ViewButton
|
|
||||||
disabled={objectFormState.loading}
|
|
||||||
items={[
|
|
||||||
{ key: 'info', label: 'Filament Profile Information' },
|
|
||||||
{ key: 'material', label: 'Material' },
|
|
||||||
{ key: 'flowPressure', label: 'Flow & Pressure' },
|
|
||||||
{ key: 'temperature', label: 'Temperature' },
|
|
||||||
{ key: 'bedTemperature', label: 'Bed Temperatures' },
|
|
||||||
{ key: 'speed', label: 'Speed' },
|
|
||||||
{ key: 'cooling', label: 'Part Cooling Fan' },
|
|
||||||
{ key: 'exhaustFan', label: 'Exhaust Fan' },
|
|
||||||
{ key: 'retraction', label: 'Retraction Overrides' },
|
|
||||||
{ key: 'loading', label: 'Loading & Multi-tool' },
|
|
||||||
{ key: 'wipeTower', label: 'Wipe Tower & Flushing' },
|
|
||||||
{ key: 'gCodeSettings', label: 'G-Code Settings' },
|
|
||||||
{ key: 'compatibility', label: 'Compatibility' },
|
|
||||||
{ key: 'shrinkageDrying', label: 'Shrinkage & Drying' },
|
|
||||||
{ key: 'notes', label: 'Notes' },
|
|
||||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
|
||||||
]}
|
|
||||||
visibleState={collapseState}
|
|
||||||
updateVisibleState={updateCollapseState}
|
|
||||||
/>
|
|
||||||
<UserNotifierToggle
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectFormState.objectData}
|
|
||||||
disabled={objectFormState.loading}
|
|
||||||
/>
|
|
||||||
<DocumentPrintButton
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectFormState.objectData}
|
|
||||||
disabled={objectFormState.loading}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
<LockIndicator lock={objectFormState.lock} />
|
|
||||||
</Space>
|
|
||||||
<EditButtons
|
|
||||||
isEditing={objectFormState.isEditing}
|
|
||||||
handleUpdate={() => actionHandlerRef.current.callAction('finishEdit')}
|
|
||||||
cancelEditing={() =>
|
|
||||||
actionHandlerRef.current.callAction('cancelEdit')
|
|
||||||
}
|
|
||||||
startEditing={() => actionHandlerRef.current.callAction('edit')}
|
|
||||||
editLoading={objectFormState.editLoading}
|
|
||||||
formValid={objectFormState.formValid}
|
|
||||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
|
||||||
loading={objectFormState.editLoading}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<ScrollBox>
|
|
||||||
<Flex vertical gap='large'>
|
|
||||||
<ActionHandler
|
|
||||||
actions={actions}
|
|
||||||
loading={objectFormState.loading}
|
|
||||||
ref={actionHandlerRef}
|
|
||||||
>
|
|
||||||
<ObjectForm
|
|
||||||
id={filamentProfileId}
|
|
||||||
type='filamentProfile'
|
|
||||||
ref={objectFormRef}
|
|
||||||
onStateChange={(state) =>
|
|
||||||
setObjectFormState((current) => ({ ...current, ...state }))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{({ loading, isEditing, objectData }) => (
|
|
||||||
<Flex vertical gap='large'>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Filament Profile Information'
|
|
||||||
icon={<InfoCircleIcon />}
|
|
||||||
active={collapseState.info}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('info', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='info'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='140px'
|
|
||||||
visibleProperties={{
|
|
||||||
_id: true,
|
|
||||||
name: true,
|
|
||||||
_reference: true,
|
|
||||||
filamentType: true,
|
|
||||||
filament: true,
|
|
||||||
createdAt: true,
|
|
||||||
updatedAt: true
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Material'
|
|
||||||
icon={<FilamentIcon />}
|
|
||||||
active={collapseState.material}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('material', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='material'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='220px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_MATERIAL_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Flow & Pressure'
|
|
||||||
icon={<SettingsIcon />}
|
|
||||||
active={collapseState.flowPressure}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('flowPressure', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='flowPressure'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='325px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_FLOW_PRESSURE_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Temperature'
|
|
||||||
icon={<HotEndIcon />}
|
|
||||||
active={collapseState.temperature}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('temperature', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='temperature'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='280px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_TEMPERATURE_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Bed Temperatures'
|
|
||||||
icon={<RulerIcon />}
|
|
||||||
active={collapseState.bedTemperature}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('bedTemperature', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='bedTemperature'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='280px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_BED_TEMPERATURE_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Speed'
|
|
||||||
icon={<MultiMotionIcon />}
|
|
||||||
active={collapseState.speed}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('speed', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='speed'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='265px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_SPEED_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Part Cooling Fan'
|
|
||||||
icon={<SettingsIcon />}
|
|
||||||
active={collapseState.cooling}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('cooling', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='cooling'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='350px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_COOLING_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Exhaust Fan'
|
|
||||||
icon={<SettingsIcon />}
|
|
||||||
active={collapseState.exhaustFan}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('exhaustFan', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='exhaustFan'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='285px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_EXHAUST_FAN_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Retraction Overrides'
|
|
||||||
icon={<SettingsIcon />}
|
|
||||||
active={collapseState.retraction}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('retraction', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='retraction'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='250px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_RETRACTION_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Loading & Multi-tool'
|
|
||||||
icon={<FilamentIcon />}
|
|
||||||
active={collapseState.loading}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('loading', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='loading'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='220px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_LOADING_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Wipe Tower & Flushing'
|
|
||||||
icon={<SettingsIcon />}
|
|
||||||
active={collapseState.wipeTower}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('wipeTower', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='wipeTower'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='330px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_WIPE_TOWER_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='G-Code Settings'
|
|
||||||
icon={<GCodeFileIcon />}
|
|
||||||
active={collapseState.gCodeSettings}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('gCodeSettings', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='gCodeSettings'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='270px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_GCODE_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Compatibility'
|
|
||||||
icon={<SettingsIcon />}
|
|
||||||
active={collapseState.compatibility}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('compatibility', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='compatibility'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='270px'
|
|
||||||
visibleProperties={toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_COMPATIBILITY_PROPERTIES
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Shrinkage & Drying'
|
|
||||||
icon={<SettingsIcon />}
|
|
||||||
active={collapseState.shrinkageDrying}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('shrinkageDrying', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='shrinkageDrying'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='filamentProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='340px'
|
|
||||||
visibleProperties={{
|
|
||||||
...toVisibleProperties(
|
|
||||||
FILAMENT_PROFILE_SHRINKAGE_DRYING_PROPERTIES
|
|
||||||
),
|
|
||||||
filamentNotes: true
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</ObjectForm>
|
|
||||||
</ActionHandler>
|
|
||||||
|
|
||||||
<InfoCollapse
|
|
||||||
title='Notes'
|
|
||||||
icon={<NoteIcon />}
|
|
||||||
active={collapseState.notes}
|
|
||||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
|
||||||
collapseKey='notes'
|
|
||||||
>
|
|
||||||
<Card>
|
|
||||||
<NotesPanel _id={filamentProfileId} type='filamentProfile' />
|
|
||||||
</Card>
|
|
||||||
</InfoCollapse>
|
|
||||||
|
|
||||||
<InfoCollapse
|
|
||||||
title='Audit Logs'
|
|
||||||
icon={<AuditLogIcon />}
|
|
||||||
active={collapseState.auditLogs}
|
|
||||||
onToggle={(expanded) => updateCollapseState('auditLogs', expanded)}
|
|
||||||
collapseKey='auditLogs'
|
|
||||||
>
|
|
||||||
{objectFormState.loading ? (
|
|
||||||
<InfoCollapsePlaceholder />
|
|
||||||
) : (
|
|
||||||
<ObjectTable
|
|
||||||
type='auditLog'
|
|
||||||
masterFilter={{ 'parent._id': filamentProfileId }}
|
|
||||||
visibleColumns={{ _id: false, 'parent._id': false }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</InfoCollapse>
|
|
||||||
</Flex>
|
|
||||||
</ScrollBox>
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FilamentProfileInfo
|
|
||||||
@ -1,420 +0,0 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import NewObjectForm from '../../common/NewObjectForm'
|
|
||||||
import ObjectForm from '../../common/ObjectForm'
|
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
|
||||||
import WizardView from '../../common/WizardView'
|
|
||||||
import {
|
|
||||||
FILAMENT_PROFILE_BED_TEMPERATURE_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_COMPATIBILITY_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_COOLING_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_EXHAUST_FAN_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_FLOW_PRESSURE_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_MATERIAL_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_SPEED_PROPERTIES,
|
|
||||||
FILAMENT_PROFILE_TEMPERATURE_PROPERTIES
|
|
||||||
} from '../../../../database/models/FilamentProfile'
|
|
||||||
|
|
||||||
const PRUSA_GENERIC_PETG_DEFAULTS = {
|
|
||||||
filamentType: 'filament',
|
|
||||||
filamentIsSupport: false,
|
|
||||||
filamentSoluble: false,
|
|
||||||
filamentPrintable: 3,
|
|
||||||
filamentAdhesivenessCategory: 0,
|
|
||||||
temperatureVitrification: 80,
|
|
||||||
idleTemperature: 0,
|
|
||||||
pelletFlowCoefficient: 0.4157,
|
|
||||||
requiredNozzleHrc: 0,
|
|
||||||
filamentFlowRatio: 0.98,
|
|
||||||
enablePressureAdvance: false,
|
|
||||||
pressureAdvance: 0.02,
|
|
||||||
adaptivePressureAdvance: false,
|
|
||||||
adaptivePressureAdvanceBridges: false,
|
|
||||||
adaptivePressureAdvanceOverhangs: false,
|
|
||||||
adaptivePressureAdvanceModel: '0,0,0\n0,0,0',
|
|
||||||
activateChamberTempControl: false,
|
|
||||||
chamberTemperature: 0,
|
|
||||||
chamberMinimalTemperature: 0,
|
|
||||||
nozzleTemperatureInitialLayer: 230,
|
|
||||||
nozzleTemperature: 240,
|
|
||||||
nozzleTemperatureRangeLow: 220,
|
|
||||||
nozzleTemperatureRangeHigh: 260,
|
|
||||||
hotPlateTempInitialLayer: 85,
|
|
||||||
hotPlateTemp: 85,
|
|
||||||
coolPlateTempInitialLayer: 60,
|
|
||||||
coolPlateTemp: 60,
|
|
||||||
engPlateTempInitialLayer: 0,
|
|
||||||
engPlateTemp: 0,
|
|
||||||
texturedPlateTempInitialLayer: 45,
|
|
||||||
texturedPlateTemp: 45,
|
|
||||||
texturedCoolPlateTempInitialLayer: 40,
|
|
||||||
texturedCoolPlateTemp: 40,
|
|
||||||
supertackPlateTempInitialLayer: 35,
|
|
||||||
supertackPlateTemp: 35,
|
|
||||||
filamentAdaptiveVolumetricSpeed: false,
|
|
||||||
filamentMaxVolumetricSpeed: 8,
|
|
||||||
volumetricSpeedCoefficients: '',
|
|
||||||
closeFanTheFirstXLayers: 3,
|
|
||||||
fullFanSpeedLayer: 0,
|
|
||||||
fanMinSpeed: 40,
|
|
||||||
fanMaxSpeed: 90,
|
|
||||||
reduceFanStopStartFreq: true,
|
|
||||||
slowDownForLayerCooling: true,
|
|
||||||
dontSlowDownOuterWall: false,
|
|
||||||
slowDownMinSpeed: 10,
|
|
||||||
slowDownLayerTime: 8,
|
|
||||||
fanCoolingLayerTime: 30,
|
|
||||||
enableOverhangBridgeFan: true,
|
|
||||||
overhangFanThreshold: '25%',
|
|
||||||
overhangFanSpeed: 90,
|
|
||||||
internalBridgeFanSpeed: -1,
|
|
||||||
supportMaterialInterfaceFanSpeed: -1,
|
|
||||||
ironingFanSpeed: -1,
|
|
||||||
initialLayerFanSpeed: -1,
|
|
||||||
firstXLayerFanSpeed: 0,
|
|
||||||
additionalCoolingFanSpeed: 0,
|
|
||||||
additionalFanFullSpeedLayer: 0,
|
|
||||||
closeAdditionalFanFirstXLayers: true,
|
|
||||||
activateAirFiltration: false,
|
|
||||||
activateAirFiltrationDuringPrint: true,
|
|
||||||
activateAirFiltrationOnCompletion: true,
|
|
||||||
duringPrintExhaustFanSpeed: 60,
|
|
||||||
completePrintExhaustFanSpeed: 80,
|
|
||||||
filamentLoadingSpeed: 28,
|
|
||||||
filamentLoadingSpeedStart: 3,
|
|
||||||
filamentUnloadingSpeed: 90,
|
|
||||||
filamentUnloadingSpeedStart: 100,
|
|
||||||
filamentChangeLength: 10,
|
|
||||||
filamentChangeLengthNc: 10,
|
|
||||||
filamentToolchangeDelay: 0,
|
|
||||||
filamentExtruderCompatibility: 0,
|
|
||||||
filamentExtruderVariant: 'Direct Drive Standard',
|
|
||||||
filamentMultitoolRamming: false,
|
|
||||||
filamentMultitoolRammingFlow: 10,
|
|
||||||
filamentMultitoolRammingVolume: 10,
|
|
||||||
filamentRammingParameters:
|
|
||||||
'120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0| 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6',
|
|
||||||
filamentRammingTravelTime: 0,
|
|
||||||
filamentRammingTravelTimeNc: 0,
|
|
||||||
filamentRammingVolumetricSpeed: -1,
|
|
||||||
filamentRammingVolumetricSpeedNc: -1,
|
|
||||||
filamentMinimalPurgeOnWipeTower: 15,
|
|
||||||
filamentTowerInterfacePreExtrusionDist: 10,
|
|
||||||
filamentTowerInterfacePreExtrusionLength: 0,
|
|
||||||
filamentTowerInterfacePrintTemp: -1,
|
|
||||||
filamentTowerInterfacePurgeVolume: 20,
|
|
||||||
filamentTowerIroningArea: 4,
|
|
||||||
filamentCoolingBeforeTower: 10,
|
|
||||||
filamentCoolingInitialSpeed: 2.2,
|
|
||||||
filamentCoolingFinalSpeed: 3.4,
|
|
||||||
filamentCoolingMoves: 4,
|
|
||||||
filamentFlushTemp: 0,
|
|
||||||
filamentFlushTempFast: 0,
|
|
||||||
filamentFlushVolumetricSpeed: 0,
|
|
||||||
filamentPreCoolingTemperature: 0,
|
|
||||||
filamentPreCoolingTemperatureNc: 0,
|
|
||||||
filamentPreheatTemperatureDelta: 0,
|
|
||||||
filamentPrimeVolumeNc: 60,
|
|
||||||
filamentStampingDistance: 0,
|
|
||||||
filamentStampingLoadingSpeed: 0,
|
|
||||||
longRetractionsWhenEc: false,
|
|
||||||
retractionDistancesWhenEc: 10,
|
|
||||||
filamentStartGcode:
|
|
||||||
'; filament start gcode\nM900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5',
|
|
||||||
filamentEndGcode: '; filament end gcode \n',
|
|
||||||
filamentChangeExtrusionRoleGcode: '',
|
|
||||||
filamentShrink: '100%',
|
|
||||||
filamentShrinkageCompensationZ: '100%',
|
|
||||||
compatiblePrinters: [],
|
|
||||||
compatiblePrintersCondition: '',
|
|
||||||
compatiblePrints: [],
|
|
||||||
compatiblePrintsCondition: '',
|
|
||||||
filamentNotes: ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const REQUIRED_PROPERTIES = ['name', 'filamentType', 'filament']
|
|
||||||
|
|
||||||
const SUMMARY_PROPERTIES = [
|
|
||||||
...REQUIRED_PROPERTIES,
|
|
||||||
...FILAMENT_PROFILE_MATERIAL_PROPERTIES,
|
|
||||||
...FILAMENT_PROFILE_FLOW_PRESSURE_PROPERTIES,
|
|
||||||
...FILAMENT_PROFILE_TEMPERATURE_PROPERTIES,
|
|
||||||
...FILAMENT_PROFILE_BED_TEMPERATURE_PROPERTIES,
|
|
||||||
...FILAMENT_PROFILE_SPEED_PROPERTIES,
|
|
||||||
...FILAMENT_PROFILE_COOLING_PROPERTIES,
|
|
||||||
...FILAMENT_PROFILE_EXHAUST_FAN_PROPERTIES
|
|
||||||
]
|
|
||||||
|
|
||||||
const FilamentProfileWizard = ({
|
|
||||||
objectData,
|
|
||||||
formValid,
|
|
||||||
loading,
|
|
||||||
onSubmit,
|
|
||||||
editing,
|
|
||||||
filamentId = null
|
|
||||||
}) => {
|
|
||||||
const visibleProperties = useMemo(() => {
|
|
||||||
if (!filamentId) return undefined
|
|
||||||
return { filament: false, 'filament._id': false }
|
|
||||||
}, [filamentId])
|
|
||||||
|
|
||||||
const steps = useMemo(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
title: 'Required',
|
|
||||||
key: 'required',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='filamentProfile'
|
|
||||||
properties={REQUIRED_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='110px'
|
|
||||||
required={true}
|
|
||||||
objectData={objectData}
|
|
||||||
visibleProperties={visibleProperties}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Material',
|
|
||||||
key: 'material',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='filamentProfile'
|
|
||||||
properties={FILAMENT_PROFILE_MATERIAL_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='210px'
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Flow & Pressure',
|
|
||||||
key: 'flowPressure',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='filamentProfile'
|
|
||||||
properties={FILAMENT_PROFILE_FLOW_PRESSURE_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='290px'
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Temperature',
|
|
||||||
key: 'temperature',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='filamentProfile'
|
|
||||||
properties={[
|
|
||||||
...FILAMENT_PROFILE_TEMPERATURE_PROPERTIES,
|
|
||||||
...FILAMENT_PROFILE_BED_TEMPERATURE_PROPERTIES
|
|
||||||
]}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='250px'
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Speed',
|
|
||||||
key: 'speed',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='filamentProfile'
|
|
||||||
properties={FILAMENT_PROFILE_SPEED_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='220px'
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Cooling',
|
|
||||||
key: 'cooling',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='filamentProfile'
|
|
||||||
properties={FILAMENT_PROFILE_COOLING_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='320px'
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Exhaust Fan',
|
|
||||||
key: 'exhaustFan',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='filamentProfile'
|
|
||||||
properties={FILAMENT_PROFILE_EXHAUST_FAN_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='270px'
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Compatibility',
|
|
||||||
key: 'compatibility',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='filamentProfile'
|
|
||||||
properties={FILAMENT_PROFILE_COMPATIBILITY_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='270px'
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Summary',
|
|
||||||
key: 'summary',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='filamentProfile'
|
|
||||||
properties={SUMMARY_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing={false}
|
|
||||||
labelWidth='250px'
|
|
||||||
objectData={objectData}
|
|
||||||
visibleProperties={visibleProperties}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[objectData, visibleProperties]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<WizardView
|
|
||||||
sizeBarWidth='185px'
|
|
||||||
steps={steps}
|
|
||||||
loading={loading}
|
|
||||||
formValid={formValid}
|
|
||||||
title={editing ? 'Edit Filament Profile' : 'New Filament Profile'}
|
|
||||||
submitText={editing ? 'Save' : 'Create'}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
FilamentProfileWizard.propTypes = {
|
|
||||||
objectData: PropTypes.object,
|
|
||||||
formValid: PropTypes.bool.isRequired,
|
|
||||||
loading: PropTypes.bool,
|
|
||||||
onSubmit: PropTypes.func.isRequired,
|
|
||||||
editing: PropTypes.bool,
|
|
||||||
filamentId: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
const NewFilamentProfile = ({ onOk, filamentId = null }) => (
|
|
||||||
<NewObjectForm
|
|
||||||
type='filamentProfile'
|
|
||||||
defaultValues={{
|
|
||||||
...PRUSA_GENERIC_PETG_DEFAULTS,
|
|
||||||
...(filamentId ? { filamentType: 'filament', filament: filamentId } : {})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ handleSubmit, submitLoading, objectData, formValid }) => (
|
|
||||||
<FilamentProfileWizard
|
|
||||||
objectData={objectData}
|
|
||||||
formValid={formValid}
|
|
||||||
loading={submitLoading}
|
|
||||||
filamentId={filamentId}
|
|
||||||
onSubmit={async () => {
|
|
||||||
const result = await handleSubmit()
|
|
||||||
if (result?._id) onOk(result)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</NewObjectForm>
|
|
||||||
)
|
|
||||||
|
|
||||||
NewFilamentProfile.propTypes = {
|
|
||||||
onOk: PropTypes.func.isRequired,
|
|
||||||
filamentId: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EditFilamentProfile = ({ profileId, onOk, filamentId = null }) => {
|
|
||||||
const formRef = useRef(null)
|
|
||||||
const startedEditingRef = useRef(false)
|
|
||||||
const saveRequestedRef = useRef(false)
|
|
||||||
const stateRef = useRef({})
|
|
||||||
const [formState, setFormState] = useState({
|
|
||||||
loading: true,
|
|
||||||
editLoading: false,
|
|
||||||
formValid: false,
|
|
||||||
isEditing: false,
|
|
||||||
objectData: {}
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
!formState.loading &&
|
|
||||||
!formState.isEditing &&
|
|
||||||
!startedEditingRef.current
|
|
||||||
) {
|
|
||||||
startedEditingRef.current = true
|
|
||||||
formRef.current?.startEditing()
|
|
||||||
}
|
|
||||||
}, [formState.isEditing, formState.loading])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ObjectForm
|
|
||||||
id={profileId}
|
|
||||||
type='filamentProfile'
|
|
||||||
ref={formRef}
|
|
||||||
onStateChange={(nextState) => {
|
|
||||||
stateRef.current = { ...stateRef.current, ...nextState }
|
|
||||||
setFormState((current) => ({ ...current, ...nextState }))
|
|
||||||
|
|
||||||
if (saveRequestedRef.current && nextState.isEditing === false) {
|
|
||||||
saveRequestedRef.current = false
|
|
||||||
onOk(stateRef.current.objectData)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ objectData, formValid, editLoading, handleUpdate }) => (
|
|
||||||
<FilamentProfileWizard
|
|
||||||
editing
|
|
||||||
objectData={objectData}
|
|
||||||
formValid={formValid}
|
|
||||||
loading={formState.loading || editLoading}
|
|
||||||
filamentId={filamentId}
|
|
||||||
onSubmit={() => {
|
|
||||||
saveRequestedRef.current = true
|
|
||||||
handleUpdate()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ObjectForm>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
EditFilamentProfile.propTypes = {
|
|
||||||
profileId: PropTypes.string.isRequired,
|
|
||||||
onOk: PropTypes.func.isRequired,
|
|
||||||
filamentId: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NewFilamentProfile
|
|
||||||
@ -1,31 +1,19 @@
|
|||||||
import { useMemo } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
import NewObjectForm from '../../common/NewObjectForm'
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
import WizardView from '../../common/WizardView'
|
import WizardView from '../../common/WizardView'
|
||||||
|
|
||||||
const NewGCodeFile = ({
|
const NewGCodeFile = ({ onOk, defaultValues }) => {
|
||||||
onOk,
|
return (
|
||||||
defaultValues,
|
<NewObjectForm
|
||||||
slicerIntegration = false,
|
type={'gcodeFile'}
|
||||||
shouldPrint = false,
|
defaultValues={{
|
||||||
currentStep,
|
|
||||||
onStepChange
|
|
||||||
}) => {
|
|
||||||
const initialValues = useMemo(
|
|
||||||
() => ({
|
|
||||||
state: { type: 'draft' },
|
state: { type: 'draft' },
|
||||||
...defaultValues
|
...defaultValues
|
||||||
}),
|
}}
|
||||||
[defaultValues]
|
>
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NewObjectForm type={'gcodeFile'} defaultValues={initialValues}>
|
|
||||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
const steps = [
|
const steps = [
|
||||||
...(!slicerIntegration
|
|
||||||
? [
|
|
||||||
{
|
{
|
||||||
title: 'Upload',
|
title: 'Upload',
|
||||||
key: 'upload',
|
key: 'upload',
|
||||||
@ -41,9 +29,7 @@ const NewGCodeFile = ({
|
|||||||
showLabels={false}
|
showLabels={false}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
]
|
|
||||||
: []),
|
|
||||||
{
|
{
|
||||||
title: 'Required',
|
title: 'Required',
|
||||||
key: 'required',
|
key: 'required',
|
||||||
@ -87,13 +73,10 @@ const NewGCodeFile = ({
|
|||||||
loading={submitLoading}
|
loading={submitLoading}
|
||||||
formValid={formValid}
|
formValid={formValid}
|
||||||
title='New GCode File'
|
title='New GCode File'
|
||||||
showSteps={!slicerIntegration || !shouldPrint}
|
|
||||||
currentStep={currentStep}
|
|
||||||
onStepChange={onStepChange}
|
|
||||||
onSubmit={async () => {
|
onSubmit={async () => {
|
||||||
const result = await handleSubmit()
|
const result = await handleSubmit()
|
||||||
if (result) {
|
if (result) {
|
||||||
onOk(result)
|
onOk()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -106,11 +89,7 @@ const NewGCodeFile = ({
|
|||||||
NewGCodeFile.propTypes = {
|
NewGCodeFile.propTypes = {
|
||||||
onOk: PropTypes.func.isRequired,
|
onOk: PropTypes.func.isRequired,
|
||||||
reset: PropTypes.bool,
|
reset: PropTypes.bool,
|
||||||
defaultValues: PropTypes.object,
|
defaultValues: PropTypes.object
|
||||||
slicerIntegration: PropTypes.bool,
|
|
||||||
currentStep: PropTypes.number,
|
|
||||||
onStepChange: PropTypes.func,
|
|
||||||
shouldPrint: PropTypes.bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NewGCodeFile
|
export default NewGCodeFile
|
||||||
|
|||||||
@ -2,20 +2,17 @@ import { useState, useContext, useEffect, useCallback } from 'react'
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import WizardView from '../../common/WizardView'
|
import WizardView from '../../common/WizardView'
|
||||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||||
import { Descriptions, Flex, Typography } from 'antd'
|
import { Flex, Typography } from 'antd'
|
||||||
import ObjectTable from '../../common/ObjectTable'
|
import ObjectTable from '../../common/ObjectTable'
|
||||||
import ObjectProperty from '../../common/ObjectProperty'
|
import ObjectSelect from '../../common/ObjectSelect'
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
const DeployJob = ({
|
|
||||||
onOk,
|
const DeployJob = ({ onOk, objectData = undefined }) => {
|
||||||
objectData = undefined,
|
|
||||||
slicerIntegration = false
|
|
||||||
}) => {
|
|
||||||
const [deployLoading, setDeployLoading] = useState(false)
|
const [deployLoading, setDeployLoading] = useState(false)
|
||||||
const [job, setJob] = useState(objectData)
|
const [job, setJob] = useState(objectData)
|
||||||
const [deployedSubJobsCount, setDeployedSubJobsCount] = useState(0)
|
const [deployedSubJobsCount, setDeployedSubJobsCount] = useState(0)
|
||||||
const [subJobsCount, setSubJobsCount] = useState(999)
|
const [subJobsCount, setSubJobsCount] = useState(999)
|
||||||
const [allPrintersOnline, setAllPrintersOnline] = useState(false)
|
|
||||||
const { sendObjectAction, fetchObjects } = useContext(ApiServerContext)
|
const { sendObjectAction, fetchObjects } = useContext(ApiServerContext)
|
||||||
|
|
||||||
const handleDeploy = useCallback(async () => {
|
const handleDeploy = useCallback(async () => {
|
||||||
@ -54,95 +51,22 @@ const DeployJob = ({
|
|||||||
}
|
}
|
||||||
}, [deployedSubJobsCount, subJobsCount, deployLoading, onOk])
|
}, [deployedSubJobsCount, subJobsCount, deployLoading, onOk])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (objectData?.printers) {
|
|
||||||
if (objectData.printers.every((printer) => printer.online)) {
|
|
||||||
setAllPrintersOnline(true)
|
|
||||||
} else {
|
|
||||||
setAllPrintersOnline(false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setAllPrintersOnline(false)
|
|
||||||
}
|
|
||||||
}, [objectData])
|
|
||||||
|
|
||||||
console.log(objectData)
|
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
title: 'Confirm',
|
title: 'Confirm',
|
||||||
key: 'confirm',
|
key: 'confirm',
|
||||||
content: (
|
content: (
|
||||||
<Flex vertical gap={'middle'} style={{ width: '100%' }}>
|
<Flex vertical gap={'middle'} style={{ width: '100%' }}>
|
||||||
<Flex gap={'middle'} align='center' style={{ width: '100%' }}>
|
<Flex gap={'small'} align='center' style={{ width: '100%' }}>
|
||||||
<Descriptions
|
<Text type='secondary'>Job:</Text>
|
||||||
column={1}
|
<ObjectSelect
|
||||||
items={[
|
type={'job'}
|
||||||
{
|
style={{ flexGrow: 1 }}
|
||||||
label: (
|
value={objectData}
|
||||||
<Flex
|
|
||||||
align='center'
|
|
||||||
gap={'small'}
|
|
||||||
style={{ height: '100%' }}
|
|
||||||
>
|
|
||||||
<Text type='secondary'>Job</Text>
|
|
||||||
</Flex>
|
|
||||||
),
|
|
||||||
children: (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
flexGrow: 1,
|
|
||||||
marginLeft: '3px',
|
|
||||||
marginBottom: '1px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ObjectProperty
|
|
||||||
type={'object'}
|
|
||||||
name={'job'}
|
|
||||||
objectData={{ job: objectData }}
|
|
||||||
value={(object) => object.job}
|
|
||||||
readOnly={true}
|
|
||||||
isEditing={true}
|
|
||||||
objectType={'job'}
|
|
||||||
onChange={(newJob) => {
|
onChange={(newJob) => {
|
||||||
setJob(newJob)
|
setJob(newJob)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: (
|
|
||||||
<Flex
|
|
||||||
align='center'
|
|
||||||
gap={'small'}
|
|
||||||
style={{ height: '100%' }}
|
|
||||||
>
|
|
||||||
<Text type='secondary'>Printers</Text>
|
|
||||||
</Flex>
|
|
||||||
),
|
|
||||||
children: (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
flexGrow: 1,
|
|
||||||
marginLeft: '3px',
|
|
||||||
marginBottom: '1px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ObjectProperty
|
|
||||||
type={'objectList'}
|
|
||||||
name={'printers'}
|
|
||||||
objectData={{ printers: objectData.printers }}
|
|
||||||
value={(object) => object.printers}
|
|
||||||
readOnly={true}
|
|
||||||
isEditing={true}
|
|
||||||
objectType={'printer'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<ObjectTable
|
<ObjectTable
|
||||||
@ -150,11 +74,11 @@ const DeployJob = ({
|
|||||||
scrollHeight={'200px'}
|
scrollHeight={'200px'}
|
||||||
visibleColumns={{
|
visibleColumns={{
|
||||||
printer: false,
|
printer: false,
|
||||||
job: false
|
'job._id': false,
|
||||||
|
'printer._id': false
|
||||||
}}
|
}}
|
||||||
masterFilter={{ 'job._id': job?._id }}
|
masterFilter={{ 'job._id': job?._id }}
|
||||||
size={'small'}
|
size={'small'}
|
||||||
showActions={!slicerIntegration}
|
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
@ -165,13 +89,9 @@ const DeployJob = ({
|
|||||||
<WizardView
|
<WizardView
|
||||||
steps={steps}
|
steps={steps}
|
||||||
loading={deployLoading}
|
loading={deployLoading}
|
||||||
formValid={objectData != undefined && allPrintersOnline}
|
formValid={objectData != undefined}
|
||||||
title='Deploy Job'
|
title='Deploy Job'
|
||||||
showSteps={false}
|
showSteps={false}
|
||||||
showSkipButton={slicerIntegration}
|
|
||||||
onSkip={() => {
|
|
||||||
onOk()
|
|
||||||
}}
|
|
||||||
submitText='Deploy'
|
submitText='Deploy'
|
||||||
progress={(deployedSubJobsCount / subJobsCount) * 100}
|
progress={(deployedSubJobsCount / subJobsCount) * 100}
|
||||||
onSubmit={() => {
|
onSubmit={() => {
|
||||||
@ -184,8 +104,7 @@ const DeployJob = ({
|
|||||||
DeployJob.propTypes = {
|
DeployJob.propTypes = {
|
||||||
onOk: PropTypes.func.isRequired,
|
onOk: PropTypes.func.isRequired,
|
||||||
objectData: PropTypes.object,
|
objectData: PropTypes.object,
|
||||||
reset: PropTypes.bool,
|
reset: PropTypes.bool
|
||||||
slicerIntegration: PropTypes.bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DeployJob
|
export default DeployJob
|
||||||
|
|||||||
@ -1,26 +1,17 @@
|
|||||||
import { useMemo } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
import NewObjectForm from '../../common/NewObjectForm'
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
import WizardView from '../../common/WizardView'
|
import WizardView from '../../common/WizardView'
|
||||||
|
|
||||||
const NewJob = ({
|
const NewJob = ({ onOk, defaultValues }) => {
|
||||||
onOk,
|
return (
|
||||||
defaultValues,
|
<NewObjectForm
|
||||||
slicerIntegration = false,
|
type={'job'}
|
||||||
currentStep,
|
defaultValues={{
|
||||||
onStepChange
|
|
||||||
}) => {
|
|
||||||
const initialValues = useMemo(
|
|
||||||
() => ({
|
|
||||||
state: { type: 'draft' },
|
state: { type: 'draft' },
|
||||||
...defaultValues
|
...defaultValues
|
||||||
}),
|
}}
|
||||||
[defaultValues]
|
>
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NewObjectForm type={'job'} defaultValues={initialValues}>
|
|
||||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
@ -68,17 +59,10 @@ const NewJob = ({
|
|||||||
loading={submitLoading}
|
loading={submitLoading}
|
||||||
formValid={formValid}
|
formValid={formValid}
|
||||||
title='New Job'
|
title='New Job'
|
||||||
showSteps={!slicerIntegration}
|
|
||||||
currentStep={currentStep}
|
|
||||||
onStepChange={onStepChange}
|
|
||||||
showSkipButton={slicerIntegration}
|
|
||||||
onSkip={() => {
|
|
||||||
onOk(null)
|
|
||||||
}}
|
|
||||||
onSubmit={async () => {
|
onSubmit={async () => {
|
||||||
const result = await handleSubmit()
|
const result = await handleSubmit()
|
||||||
if (result) {
|
if (result) {
|
||||||
onOk(result)
|
onOk()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -91,10 +75,7 @@ const NewJob = ({
|
|||||||
NewJob.propTypes = {
|
NewJob.propTypes = {
|
||||||
onOk: PropTypes.func.isRequired,
|
onOk: PropTypes.func.isRequired,
|
||||||
reset: PropTypes.bool,
|
reset: PropTypes.bool,
|
||||||
defaultValues: PropTypes.object,
|
defaultValues: PropTypes.object
|
||||||
slicerIntegration: PropTypes.bool,
|
|
||||||
currentStep: PropTypes.number,
|
|
||||||
onStepChange: PropTypes.func
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NewJob
|
export default NewJob
|
||||||
|
|||||||
@ -1,143 +0,0 @@
|
|||||||
import { useContext, useRef, useState } from 'react'
|
|
||||||
import { Button, Dropdown, Flex, Modal, Space } from 'antd'
|
|
||||||
import NewPrinterProfile, {
|
|
||||||
EditPrinterProfile
|
|
||||||
} from './PrinterProfiles/NewPrinterProfile'
|
|
||||||
import ColumnViewButton from '../common/ColumnViewButton'
|
|
||||||
import ExportListButton from '../common/ExportListButton'
|
|
||||||
import FilterSidebarButton from '../common/FilterSidebarButton'
|
|
||||||
import ObjectTable from '../common/ObjectTable'
|
|
||||||
import ObjectTableViewButton from '../common/ObjectTableViewButton'
|
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
|
||||||
import PlusIcon from '../../Icons/PlusIcon'
|
|
||||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
|
||||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
|
||||||
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
|
|
||||||
import useViewMode from '../hooks/useViewMode'
|
|
||||||
const PrinterProfiles = () => {
|
|
||||||
const { deleteObject } = useContext(ApiServerContext)
|
|
||||||
const [profileModal, setProfileModal] = useState({
|
|
||||||
open: false,
|
|
||||||
profileId: null
|
|
||||||
})
|
|
||||||
const tableRef = useRef()
|
|
||||||
const [viewMode, setViewMode] = useViewMode('PrinterProfiles')
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
|
||||||
useColumnVisibility('printerProfile')
|
|
||||||
const [showFilterSidebar, setShowFilterSidebar] =
|
|
||||||
useFilterSidebarVisibility('PrinterProfiles')
|
|
||||||
|
|
||||||
const actionItems = {
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'New Printer Profile',
|
|
||||||
key: 'newPrinterProfile',
|
|
||||||
icon: <PlusIcon />
|
|
||||||
},
|
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
|
||||||
label: 'Reload List',
|
|
||||||
key: 'reloadList',
|
|
||||||
icon: <ReloadIcon />
|
|
||||||
}
|
|
||||||
],
|
|
||||||
onClick: ({ key }) => {
|
|
||||||
if (key === 'reloadList') {
|
|
||||||
tableRef.current?.reload()
|
|
||||||
} else if (key === 'newPrinterProfile') {
|
|
||||||
setProfileModal({ open: true, profileId: null })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleRowAction = (action, profile) => {
|
|
||||||
if (action.name === 'edit') {
|
|
||||||
setProfileModal({ open: true, profileId: profile._id })
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.name === 'delete') {
|
|
||||||
Modal.confirm({
|
|
||||||
title: 'Delete printer profile?',
|
|
||||||
content: `This will permanently delete ${profile.name || profile._reference}.`,
|
|
||||||
okText: 'Delete',
|
|
||||||
okType: 'danger',
|
|
||||||
onOk: async () => {
|
|
||||||
await deleteObject(profile._id, 'printerProfile')
|
|
||||||
await tableRef.current?.reload()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Flex vertical={'true'} gap='large' className='h-100'>
|
|
||||||
<Flex justify={'space-between'}>
|
|
||||||
<Space>
|
|
||||||
<Dropdown menu={actionItems}>
|
|
||||||
<Button>Actions</Button>
|
|
||||||
</Dropdown>
|
|
||||||
<ColumnViewButton
|
|
||||||
type='printerProfile'
|
|
||||||
visibleState={columnVisibility}
|
|
||||||
updateVisibleState={setColumnVisibility}
|
|
||||||
/>
|
|
||||||
<ExportListButton objectType='printerProfile' />
|
|
||||||
</Space>
|
|
||||||
<Space>
|
|
||||||
<FilterSidebarButton
|
|
||||||
active={showFilterSidebar}
|
|
||||||
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
|
|
||||||
/>
|
|
||||||
<ObjectTableViewButton
|
|
||||||
viewMode={viewMode}
|
|
||||||
setViewMode={setViewMode}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<ObjectTable
|
|
||||||
ref={tableRef}
|
|
||||||
type='printerProfile'
|
|
||||||
cards={viewMode === 'cards'}
|
|
||||||
visibleColumns={columnVisibility}
|
|
||||||
showFilterSidebar={showFilterSidebar}
|
|
||||||
expandHeight={true}
|
|
||||||
onRowAction={handleRowAction}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
open={profileModal.open}
|
|
||||||
footer={null}
|
|
||||||
width={800}
|
|
||||||
onCancel={() => setProfileModal({ open: false, profileId: null })}
|
|
||||||
destroyOnHidden
|
|
||||||
>
|
|
||||||
{profileModal.open &&
|
|
||||||
(profileModal.profileId ? (
|
|
||||||
<EditPrinterProfile
|
|
||||||
profileId={profileModal.profileId}
|
|
||||||
onOk={() => {
|
|
||||||
setProfileModal({ open: false, profileId: null })
|
|
||||||
tableRef.current?.reload()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<NewPrinterProfile
|
|
||||||
onOk={() => {
|
|
||||||
setProfileModal({ open: false, profileId: null })
|
|
||||||
tableRef.current?.reload()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PrinterProfiles
|
|
||||||
@ -1,372 +0,0 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import NewObjectForm from '../../common/NewObjectForm'
|
|
||||||
import ObjectForm from '../../common/ObjectForm'
|
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
|
||||||
import WizardView from '../../common/WizardView'
|
|
||||||
|
|
||||||
const DEFAULT_EXTRUDER = {
|
|
||||||
nozzleDiameter: 0.4,
|
|
||||||
nozzleVolume: 0,
|
|
||||||
nozzleType: 'hardened_steel',
|
|
||||||
minLayerHeight: 0.05,
|
|
||||||
maxLayerHeight: 0.3,
|
|
||||||
positionOffsetX: 0,
|
|
||||||
positionOffsetY: 0,
|
|
||||||
retractionLength: 0.8,
|
|
||||||
retractionExtraLengthOnRestart: 0,
|
|
||||||
retractionSpeed: 35,
|
|
||||||
deretractionSpeed: 0,
|
|
||||||
retractionTravelDistanceThreshold: 2,
|
|
||||||
retractOnLayerChange: false,
|
|
||||||
wipeWhileRetracting: false,
|
|
||||||
wipeDistance: 1,
|
|
||||||
retractAmountBeforeWipe: 70,
|
|
||||||
retractAmountAfterWipe: 0,
|
|
||||||
zHopOnSurfaces: 'All Surfaces',
|
|
||||||
zHopType: 'Auto',
|
|
||||||
zHopHeight: 0,
|
|
||||||
zHopTravelingAngle: 0,
|
|
||||||
zHopOnlyLiftZAbove: 0,
|
|
||||||
zHopOnlyLiftZBelow: 0,
|
|
||||||
materialSwitchRetractionLength: 10,
|
|
||||||
materialSwitchExtraLengthOnRestart: 0,
|
|
||||||
longRetractionWhenCut: false
|
|
||||||
}
|
|
||||||
|
|
||||||
const PRUSA_MK3S_PROFILE_DEFAULTS = {
|
|
||||||
extruders: [{ ...DEFAULT_EXTRUDER }],
|
|
||||||
printBedWidth: 250,
|
|
||||||
printBedHeight: 210,
|
|
||||||
originX: 0,
|
|
||||||
originY: 0,
|
|
||||||
bedExcludeArea: [{ x: 0, y: 0 }],
|
|
||||||
printableHeight: 210,
|
|
||||||
supportMultiBedTypes: false,
|
|
||||||
bestObjectPositionX: 125,
|
|
||||||
bestObjectPositionY: 105,
|
|
||||||
zOffset: 0,
|
|
||||||
preferredOrientation: 0,
|
|
||||||
machineMaxAccelerationRetracting: { min: 2500, max: 2500 },
|
|
||||||
machineMaxSpeedE: { min: 120, max: 120 },
|
|
||||||
machineMaxSpeedX: { min: 200, max: 200 },
|
|
||||||
machineMaxSpeedY: { min: 200, max: 200 },
|
|
||||||
machinePauseGcode: 'M601',
|
|
||||||
machineStartGcode: `M862.1 P[nozzle_diameter] ; nozzle diameter check
|
|
||||||
M115 U3.13.0 ; tell printer latest fw version
|
|
||||||
G90 ; use absolute coordinates
|
|
||||||
M83 ; extruder relative mode
|
|
||||||
M104 S[first_layer_temperature] ; set extruder temp
|
|
||||||
M140 S[first_layer_bed_temperature] ; set bed temp
|
|
||||||
M190 S[first_layer_bed_temperature] ; wait for bed temp
|
|
||||||
M109 S[first_layer_temperature] ; wait for extruder temp
|
|
||||||
G28 W ; home all without mesh bed level
|
|
||||||
G80 ; mesh bed leveling
|
|
||||||
G1 Z0.3 F720
|
|
||||||
G1 Y-3 F1000 ; go outside print area
|
|
||||||
G92 E0
|
|
||||||
G1 X60 E9 F1000 ; intro line
|
|
||||||
G1 X100 E9 F1000 ; intro line
|
|
||||||
{else}
|
|
||||||
G1 Z0.2 F720
|
|
||||||
G1 Y-3 F1000 ; go outside print area
|
|
||||||
G92 E0
|
|
||||||
G1 X60 E9 F1000 ; intro line
|
|
||||||
G1 X100 E12.5 F1000 ; intro line
|
|
||||||
{endif}
|
|
||||||
G92 E0
|
|
||||||
M221 S{if layer_height<0.075}100{else}95{endif}`,
|
|
||||||
machineEndGcode: `{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}
|
|
||||||
G1 X0 Y200 F3600 ; park
|
|
||||||
{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}
|
|
||||||
G4 ; wait
|
|
||||||
M221 S100 ; reset flow
|
|
||||||
M900 K0 ; reset LA
|
|
||||||
M104 S0 ; turn off temperature
|
|
||||||
M140 S0 ; turn off heatbed
|
|
||||||
M107 ; turn off fan
|
|
||||||
M84 ; disable motors
|
|
||||||
; max_layer_z = [max_layer_z]`,
|
|
||||||
layerChangeGcode: `;AFTER_LAYER_CHANGE
|
|
||||||
;[layer_z]`,
|
|
||||||
beforeLayerChangeGcode: `;BEFORE_LAYER_CHANGE
|
|
||||||
;[layer_z]
|
|
||||||
G92 E0
|
|
||||||
`,
|
|
||||||
printerNotes: `Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.
|
|
||||||
PRINTER_VENDOR_PRUSA3D
|
|
||||||
PRINTER_MODEL_MK3
|
|
||||||
`,
|
|
||||||
scanFirstLayer: false,
|
|
||||||
machineLoadFilamentTime: 17,
|
|
||||||
machineUnloadFilamentTime: 16,
|
|
||||||
thumbnails: [{ width: 160, height: 120 }],
|
|
||||||
auxiliaryFan: false,
|
|
||||||
machineMaxJunctionDeviation: { min: 0.02, max: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
const REQUIRED_PROPERTIES = ['name', 'printer']
|
|
||||||
|
|
||||||
const DIMENSION_PROPERTIES = [
|
|
||||||
'printBedWidth',
|
|
||||||
'printBedHeight',
|
|
||||||
'originX',
|
|
||||||
'originY',
|
|
||||||
'printableHeight',
|
|
||||||
'supportMultiBedTypes',
|
|
||||||
'bestObjectPositionX',
|
|
||||||
'bestObjectPositionY',
|
|
||||||
'zOffset',
|
|
||||||
'preferredOrientation'
|
|
||||||
]
|
|
||||||
|
|
||||||
const EXTRUDER_STEP_PROPERTIES = ['extruders']
|
|
||||||
|
|
||||||
const MOTION_PROPERTIES = [
|
|
||||||
'machineMaxAccelerationRetracting',
|
|
||||||
'machineMaxSpeedE',
|
|
||||||
'machineMaxSpeedX',
|
|
||||||
'machineMaxSpeedY',
|
|
||||||
'machineMaxJunctionDeviation'
|
|
||||||
]
|
|
||||||
|
|
||||||
const OPTIONAL_PROPERTIES = [
|
|
||||||
'scanFirstLayer',
|
|
||||||
'machineLoadFilamentTime',
|
|
||||||
'machineUnloadFilamentTime',
|
|
||||||
'auxiliaryFan'
|
|
||||||
]
|
|
||||||
|
|
||||||
const SUMMARY_PROPERTIES = [
|
|
||||||
...REQUIRED_PROPERTIES,
|
|
||||||
...DIMENSION_PROPERTIES,
|
|
||||||
...MOTION_PROPERTIES,
|
|
||||||
...OPTIONAL_PROPERTIES
|
|
||||||
]
|
|
||||||
|
|
||||||
const PrinterProfileWizard = ({
|
|
||||||
objectData,
|
|
||||||
formValid,
|
|
||||||
loading,
|
|
||||||
onSubmit,
|
|
||||||
editing,
|
|
||||||
printerId = null
|
|
||||||
}) => {
|
|
||||||
const visibleProperties = useMemo(() => {
|
|
||||||
if (!printerId) return undefined
|
|
||||||
return { printer: false, 'printer._id': false }
|
|
||||||
}, [printerId])
|
|
||||||
|
|
||||||
const steps = useMemo(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
title: 'Required',
|
|
||||||
key: 'required',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='printerProfile'
|
|
||||||
properties={REQUIRED_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='75px'
|
|
||||||
required={true}
|
|
||||||
objectData={objectData}
|
|
||||||
visibleProperties={visibleProperties}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Dimensions',
|
|
||||||
key: 'dimensions',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='printerProfile'
|
|
||||||
properties={DIMENSION_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='190px'
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Extruders',
|
|
||||||
key: 'extruders',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='printerProfile'
|
|
||||||
properties={EXTRUDER_STEP_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
showLabels={false}
|
|
||||||
labelWidth='90px'
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Motion',
|
|
||||||
key: 'motion',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='printerProfile'
|
|
||||||
properties={MOTION_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
labelWidth='185px'
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Optional',
|
|
||||||
key: 'optional',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='printerProfile'
|
|
||||||
properties={OPTIONAL_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
labelWidth='170px'
|
|
||||||
bordered={false}
|
|
||||||
isEditing
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Summary',
|
|
||||||
key: 'summary',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='printerProfile'
|
|
||||||
properties={SUMMARY_PROPERTIES}
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing={false}
|
|
||||||
labelWidth='210px'
|
|
||||||
objectData={objectData}
|
|
||||||
visibleProperties={visibleProperties}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[objectData, visibleProperties]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<WizardView
|
|
||||||
sizeBarWidth='160px'
|
|
||||||
steps={steps}
|
|
||||||
loading={loading}
|
|
||||||
formValid={formValid}
|
|
||||||
title={editing ? 'Edit Printer Profile' : 'New Printer Profile'}
|
|
||||||
submitText={editing ? 'Save' : 'Create'}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
PrinterProfileWizard.propTypes = {
|
|
||||||
objectData: PropTypes.object,
|
|
||||||
formValid: PropTypes.bool.isRequired,
|
|
||||||
loading: PropTypes.bool,
|
|
||||||
onSubmit: PropTypes.func.isRequired,
|
|
||||||
editing: PropTypes.bool,
|
|
||||||
printerId: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
const NewPrinterProfile = ({ onOk, printerId = null }) => (
|
|
||||||
<NewObjectForm
|
|
||||||
type='printerProfile'
|
|
||||||
defaultValues={{
|
|
||||||
...PRUSA_MK3S_PROFILE_DEFAULTS,
|
|
||||||
...(printerId ? { printer: printerId } : {})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ handleSubmit, submitLoading, objectData, formValid }) => (
|
|
||||||
<PrinterProfileWizard
|
|
||||||
objectData={objectData}
|
|
||||||
formValid={formValid}
|
|
||||||
loading={submitLoading}
|
|
||||||
printerId={printerId}
|
|
||||||
onSubmit={async () => {
|
|
||||||
const result = await handleSubmit()
|
|
||||||
if (result?._id) onOk(result)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</NewObjectForm>
|
|
||||||
)
|
|
||||||
|
|
||||||
NewPrinterProfile.propTypes = {
|
|
||||||
onOk: PropTypes.func.isRequired,
|
|
||||||
printerId: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EditPrinterProfile = ({ profileId, onOk, printerId = null }) => {
|
|
||||||
const formRef = useRef(null)
|
|
||||||
const startedEditingRef = useRef(false)
|
|
||||||
const saveRequestedRef = useRef(false)
|
|
||||||
const stateRef = useRef({})
|
|
||||||
const [formState, setFormState] = useState({
|
|
||||||
loading: true,
|
|
||||||
editLoading: false,
|
|
||||||
formValid: false,
|
|
||||||
isEditing: false,
|
|
||||||
objectData: {}
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
!formState.loading &&
|
|
||||||
!formState.isEditing &&
|
|
||||||
!startedEditingRef.current
|
|
||||||
) {
|
|
||||||
startedEditingRef.current = true
|
|
||||||
formRef.current?.startEditing()
|
|
||||||
}
|
|
||||||
}, [formState.isEditing, formState.loading])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ObjectForm
|
|
||||||
id={profileId}
|
|
||||||
type='printerProfile'
|
|
||||||
ref={formRef}
|
|
||||||
onStateChange={(nextState) => {
|
|
||||||
stateRef.current = { ...stateRef.current, ...nextState }
|
|
||||||
setFormState((current) => ({ ...current, ...nextState }))
|
|
||||||
|
|
||||||
if (saveRequestedRef.current && nextState.isEditing === false) {
|
|
||||||
saveRequestedRef.current = false
|
|
||||||
onOk(stateRef.current.objectData)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({ objectData, formValid, editLoading, handleUpdate }) => (
|
|
||||||
<PrinterProfileWizard
|
|
||||||
editing
|
|
||||||
objectData={objectData}
|
|
||||||
formValid={formValid}
|
|
||||||
loading={formState.loading || editLoading}
|
|
||||||
printerId={printerId}
|
|
||||||
onSubmit={() => {
|
|
||||||
saveRequestedRef.current = true
|
|
||||||
handleUpdate()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ObjectForm>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
EditPrinterProfile.propTypes = {
|
|
||||||
profileId: PropTypes.string.isRequired,
|
|
||||||
onOk: PropTypes.func.isRequired,
|
|
||||||
printerId: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NewPrinterProfile
|
|
||||||
@ -1,381 +0,0 @@
|
|||||||
import { useRef, useState } from 'react'
|
|
||||||
import { useLocation } from 'react-router-dom'
|
|
||||||
import { Card, Flex, Space } from 'antd'
|
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
|
||||||
import ActionHandler from '../../common/ActionHandler'
|
|
||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon'
|
|
||||||
import DocumentPrintButton from '../../common/DocumentPrintButton'
|
|
||||||
import EditButtons from '../../common/EditButtons'
|
|
||||||
import HotEndIcon from '../../../Icons/HotEndIcon'
|
|
||||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon'
|
|
||||||
import MultiMotionIcon from '../../../Icons/MultiMotionIcon'
|
|
||||||
import PictureIcon from '../../../Icons/PictureIcon'
|
|
||||||
import RulerIcon from '../../../Icons/RulerIcon'
|
|
||||||
import InfoCollapse from '../../common/InfoCollapse'
|
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder'
|
|
||||||
import LockIndicator from '../../common/LockIndicator'
|
|
||||||
import NoteIcon from '../../../Icons/NoteIcon'
|
|
||||||
import NotesPanel from '../../common/NotesPanel'
|
|
||||||
import ObjectActions from '../../common/ObjectActions'
|
|
||||||
import ObjectForm from '../../common/ObjectForm'
|
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
|
||||||
import ObjectTable from '../../common/ObjectTable'
|
|
||||||
import ScrollBox from '../../common/ScrollBox'
|
|
||||||
import UserNotifierToggle from '../../common/UserNotifierToggle'
|
|
||||||
import ViewButton from '../../common/ViewButton'
|
|
||||||
import useCollapseState from '../../hooks/useCollapseState'
|
|
||||||
import ObjectProperty from '../../common/ObjectProperty'
|
|
||||||
import { getModelProperty } from '../../../../database/ObjectModels'
|
|
||||||
import GCodeFileIcon from '../../../Icons/GCodeFileIcon'
|
|
||||||
import SettingsIcon from '../../../Icons/SettingsIcon'
|
|
||||||
import ExclamationOctagonIcon from '../../../Icons/ExclamationOctagonIcon'
|
|
||||||
|
|
||||||
const PrinterProfileInfo = () => {
|
|
||||||
const location = useLocation()
|
|
||||||
const objectFormRef = useRef(null)
|
|
||||||
const actionHandlerRef = useRef(null)
|
|
||||||
const printerProfileId = new URLSearchParams(location.search).get(
|
|
||||||
'printerProfileId'
|
|
||||||
)
|
|
||||||
const [collapseState, updateCollapseState] = useCollapseState(
|
|
||||||
'PrinterProfileInfo',
|
|
||||||
{
|
|
||||||
info: true,
|
|
||||||
dimensions: true,
|
|
||||||
bedExcludeArea: true,
|
|
||||||
extruders: true,
|
|
||||||
motion: true,
|
|
||||||
gCodeSettings: true,
|
|
||||||
optional: true,
|
|
||||||
thumbnails: true,
|
|
||||||
notes: true,
|
|
||||||
auditLogs: false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const [objectFormState, setObjectFormState] = useState({
|
|
||||||
isEditing: false,
|
|
||||||
editLoading: false,
|
|
||||||
formValid: false,
|
|
||||||
lock: null,
|
|
||||||
loading: false,
|
|
||||||
objectData: {}
|
|
||||||
})
|
|
||||||
|
|
||||||
const actions = {
|
|
||||||
edit: () => {
|
|
||||||
objectFormRef.current?.startEditing?.()
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
cancelEdit: () => {
|
|
||||||
objectFormRef.current?.cancelEditing?.()
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
finishEdit: () => {
|
|
||||||
objectFormRef.current?.handleUpdate?.()
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
delete: () => {
|
|
||||||
objectFormRef.current?.handleDelete?.()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
gap='large'
|
|
||||||
vertical='true'
|
|
||||||
style={{ maxHeight: '100%', minHeight: 0 }}
|
|
||||||
>
|
|
||||||
<Flex justify='space-between'>
|
|
||||||
<Space size='middle'>
|
|
||||||
<Space size='small'>
|
|
||||||
<ObjectActions
|
|
||||||
type='printerProfile'
|
|
||||||
id={printerProfileId}
|
|
||||||
disabled={objectFormState.loading}
|
|
||||||
objectData={objectFormState.objectData}
|
|
||||||
/>
|
|
||||||
<ViewButton
|
|
||||||
disabled={objectFormState.loading}
|
|
||||||
items={[
|
|
||||||
{ key: 'info', label: 'Printer Profile Information' },
|
|
||||||
{ key: 'dimensions', label: 'Dimensions' },
|
|
||||||
{ key: 'bedExcludeArea', label: 'Excluded Bed Area' },
|
|
||||||
{ key: 'extruders', label: 'Extruders' },
|
|
||||||
{ key: 'motion', label: 'Motion' },
|
|
||||||
{ key: 'gCodeSettings', label: 'G-Code Settings' },
|
|
||||||
{ key: 'miscellaneous', label: 'Miscellaneous' },
|
|
||||||
{ key: 'thumbnails', label: 'Thumbnail Sizes' },
|
|
||||||
{ key: 'notes', label: 'Notes' },
|
|
||||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
|
||||||
]}
|
|
||||||
visibleState={collapseState}
|
|
||||||
updateVisibleState={updateCollapseState}
|
|
||||||
/>
|
|
||||||
<UserNotifierToggle
|
|
||||||
type='printerProfile'
|
|
||||||
objectData={objectFormState.objectData}
|
|
||||||
disabled={objectFormState.loading}
|
|
||||||
/>
|
|
||||||
<DocumentPrintButton
|
|
||||||
type='printerProfile'
|
|
||||||
objectData={objectFormState.objectData}
|
|
||||||
disabled={objectFormState.loading}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
<LockIndicator lock={objectFormState.lock} />
|
|
||||||
</Space>
|
|
||||||
<EditButtons
|
|
||||||
isEditing={objectFormState.isEditing}
|
|
||||||
handleUpdate={() => actionHandlerRef.current.callAction('finishEdit')}
|
|
||||||
cancelEditing={() =>
|
|
||||||
actionHandlerRef.current.callAction('cancelEdit')
|
|
||||||
}
|
|
||||||
startEditing={() => actionHandlerRef.current.callAction('edit')}
|
|
||||||
editLoading={objectFormState.editLoading}
|
|
||||||
formValid={objectFormState.formValid}
|
|
||||||
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
|
||||||
loading={objectFormState.editLoading}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<ScrollBox>
|
|
||||||
<Flex vertical gap='large'>
|
|
||||||
<ActionHandler
|
|
||||||
actions={actions}
|
|
||||||
loading={objectFormState.loading}
|
|
||||||
ref={actionHandlerRef}
|
|
||||||
>
|
|
||||||
<ObjectForm
|
|
||||||
id={printerProfileId}
|
|
||||||
type='printerProfile'
|
|
||||||
ref={objectFormRef}
|
|
||||||
onStateChange={(state) =>
|
|
||||||
setObjectFormState((current) => ({ ...current, ...state }))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{({ loading, isEditing, objectData }) => (
|
|
||||||
<Flex vertical gap='large'>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Printer Profile Information'
|
|
||||||
icon={<InfoCircleIcon />}
|
|
||||||
active={collapseState.info}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('info', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='info'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='printerProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='140px'
|
|
||||||
visibleProperties={{
|
|
||||||
_id: true,
|
|
||||||
name: true,
|
|
||||||
_reference: true,
|
|
||||||
printer: true,
|
|
||||||
createdAt: true,
|
|
||||||
updatedAt: true
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Dimensions'
|
|
||||||
icon={<RulerIcon />}
|
|
||||||
active={collapseState.dimensions}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('dimensions', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='dimensions'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='printerProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='230px'
|
|
||||||
visibleProperties={{
|
|
||||||
printBedWidth: true,
|
|
||||||
printBedHeight: true,
|
|
||||||
originX: true,
|
|
||||||
originY: true,
|
|
||||||
printableHeight: true,
|
|
||||||
supportMultiBedTypes: true,
|
|
||||||
bestObjectPositionX: true,
|
|
||||||
bestObjectPositionY: true,
|
|
||||||
zOffset: true,
|
|
||||||
preferredOrientation: true
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Excluded Bed Area'
|
|
||||||
icon={<ExclamationOctagonIcon />}
|
|
||||||
active={collapseState.bedExcludeArea}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('bedExcludeArea', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='bedExcludeArea'
|
|
||||||
>
|
|
||||||
<ObjectProperty
|
|
||||||
{...getModelProperty('printerProfile', 'bedExcludeArea')}
|
|
||||||
isEditing={isEditing}
|
|
||||||
objectData={objectData}
|
|
||||||
loading={loading}
|
|
||||||
size='medium'
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Extruders'
|
|
||||||
icon={<HotEndIcon />}
|
|
||||||
active={collapseState.extruders}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('extruders', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='extruders'
|
|
||||||
>
|
|
||||||
<ObjectProperty
|
|
||||||
{...getModelProperty('printerProfile', 'extruders')}
|
|
||||||
isEditing={isEditing}
|
|
||||||
objectData={objectData}
|
|
||||||
loading={loading}
|
|
||||||
size='medium'
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Motion'
|
|
||||||
icon={<MultiMotionIcon />}
|
|
||||||
active={collapseState.motion}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('motion', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='motion'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='printerProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='225px'
|
|
||||||
visibleProperties={{
|
|
||||||
machineMaxAccelerationRetracting: true,
|
|
||||||
machineMaxSpeedE: true,
|
|
||||||
machineMaxSpeedX: true,
|
|
||||||
machineMaxSpeedY: true,
|
|
||||||
machineMaxJunctionDeviation: true
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='G-Code Settings'
|
|
||||||
icon={<GCodeFileIcon />}
|
|
||||||
active={collapseState.gCodeSettings}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('gCodeSettings', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='gCodeSettings'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='printerProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='240px'
|
|
||||||
visibleProperties={{
|
|
||||||
machinePauseGcode: true,
|
|
||||||
machineStartGcode: true,
|
|
||||||
machineEndGcode: true,
|
|
||||||
layerChangeGcode: true,
|
|
||||||
beforeLayerChangeGcode: true
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Miscellaneous'
|
|
||||||
icon={<SettingsIcon />}
|
|
||||||
active={collapseState.miscellaneous}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('miscellaneous', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='miscellaneous'
|
|
||||||
>
|
|
||||||
<ObjectInfo
|
|
||||||
loading={loading}
|
|
||||||
indicator={<LoadingOutlined />}
|
|
||||||
isEditing={isEditing}
|
|
||||||
type='printerProfile'
|
|
||||||
objectData={objectData}
|
|
||||||
labelWidth='210px'
|
|
||||||
visibleProperties={{
|
|
||||||
printerNotes: true,
|
|
||||||
scanFirstLayer: true,
|
|
||||||
machineLoadFilamentTime: true,
|
|
||||||
machineUnloadFilamentTime: true,
|
|
||||||
auxiliaryFan: true
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title='Thumbnail Sizes'
|
|
||||||
icon={<PictureIcon />}
|
|
||||||
active={collapseState.thumbnails}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('thumbnails', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='thumbnails'
|
|
||||||
>
|
|
||||||
<ObjectProperty
|
|
||||||
{...getModelProperty('printerProfile', 'thumbnails')}
|
|
||||||
isEditing={isEditing}
|
|
||||||
objectData={objectData}
|
|
||||||
loading={loading}
|
|
||||||
size='medium'
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</ObjectForm>
|
|
||||||
</ActionHandler>
|
|
||||||
|
|
||||||
<InfoCollapse
|
|
||||||
title='Notes'
|
|
||||||
icon={<NoteIcon />}
|
|
||||||
active={collapseState.notes}
|
|
||||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
|
||||||
collapseKey='notes'
|
|
||||||
>
|
|
||||||
<Card>
|
|
||||||
<NotesPanel _id={printerProfileId} type='printerProfile' />
|
|
||||||
</Card>
|
|
||||||
</InfoCollapse>
|
|
||||||
|
|
||||||
<InfoCollapse
|
|
||||||
title='Audit Logs'
|
|
||||||
icon={<AuditLogIcon />}
|
|
||||||
active={collapseState.auditLogs}
|
|
||||||
onToggle={(expanded) => updateCollapseState('auditLogs', expanded)}
|
|
||||||
collapseKey='auditLogs'
|
|
||||||
>
|
|
||||||
{objectFormState.loading ? (
|
|
||||||
<InfoCollapsePlaceholder />
|
|
||||||
) : (
|
|
||||||
<ObjectTable
|
|
||||||
type='auditLog'
|
|
||||||
masterFilter={{ 'parent._id': printerProfileId }}
|
|
||||||
visibleColumns={{ _id: false, 'parent._id': false }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</InfoCollapse>
|
|
||||||
</Flex>
|
|
||||||
</ScrollBox>
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PrinterProfileInfo
|
|
||||||
@ -9,9 +9,9 @@ import InfoCollapse from '../../common/InfoCollapse.jsx'
|
|||||||
import ViewButton from '../../common/ViewButton.jsx'
|
import ViewButton from '../../common/ViewButton.jsx'
|
||||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
import ObjectForm from '../../common/ObjectForm.jsx'
|
||||||
|
import EditButtons from '../../common/EditButtons.jsx'
|
||||||
import ActionHandler from '../../common/ActionHandler.jsx'
|
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||||
import PrinterIcon from '../../../Icons/PrinterIcon.jsx'
|
import PrinterIcon from '../../../Icons/PrinterIcon.jsx'
|
||||||
@ -35,7 +35,7 @@ import ScrollBox from '../../common/ScrollBox.jsx'
|
|||||||
const log = loglevel.getLogger('ControlPrinter')
|
const log = loglevel.getLogger('ControlPrinter')
|
||||||
log.setLevel(config.logLevel)
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
const ControlPrinter = ({ slicerIntegration = false }) => {
|
const ControlPrinter = () => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const objectFormRef = useRef(null)
|
const objectFormRef = useRef(null)
|
||||||
const actionHandlerRef = useRef(null)
|
const actionHandlerRef = useRef(null)
|
||||||
@ -226,11 +226,7 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
type='printer'
|
type='printer'
|
||||||
id={printerId}
|
id={printerId}
|
||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
visibleActions={{
|
visibleActions={{ edit: false }}
|
||||||
edit: false,
|
|
||||||
info: !slicerIntegration,
|
|
||||||
control: !slicerIntegration
|
|
||||||
}}
|
|
||||||
objectData={objectFormState.objectData}
|
objectData={objectFormState.objectData}
|
||||||
/>
|
/>
|
||||||
<ViewButton
|
<ViewButton
|
||||||
@ -279,6 +275,24 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</Space>
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<EditButtons
|
||||||
|
isEditing={objectFormState.isEditing}
|
||||||
|
handleUpdate={() => {
|
||||||
|
actionHandlerRef.current.callAction('finishEdit')
|
||||||
|
}}
|
||||||
|
cancelEditing={() => {
|
||||||
|
actionHandlerRef.current.callAction('cancelEdit')
|
||||||
|
}}
|
||||||
|
startEditing={() => {
|
||||||
|
actionHandlerRef.current.callAction('edit')
|
||||||
|
}}
|
||||||
|
editLoading={objectFormState.editLoading}
|
||||||
|
formValid={objectFormState.formValid}
|
||||||
|
disabled={objectFormState.lock?.locked || objectFormState.loading}
|
||||||
|
loading={objectFormState.editLoading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<ScrollBox>
|
<ScrollBox>
|
||||||
@ -339,16 +353,11 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
currentSubJob: false,
|
currentSubJob: false,
|
||||||
'currentSubJob._id': false,
|
'currentSubJob._id': false,
|
||||||
createdAt: false,
|
createdAt: false,
|
||||||
updatedAt: false,
|
updatedAt: false
|
||||||
state: !slicerIntegration,
|
|
||||||
name: !slicerIntegration
|
|
||||||
}}
|
|
||||||
objectPropertyProps={{
|
|
||||||
showHyperlink: !slicerIntegration
|
|
||||||
}}
|
}}
|
||||||
objectData={printerObjectData}
|
objectData={printerObjectData}
|
||||||
type='printer'
|
type='printer'
|
||||||
labelWidth='120px'
|
labelWidth='100px'
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@ -377,15 +386,12 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
loading={jobObjectLoading}
|
loading={jobObjectLoading}
|
||||||
column={sideBarVisible ? 1 : undefined}
|
column={sideBarVisible ? 1 : undefined}
|
||||||
showHyperlink={!slicerIntegration}
|
showHyperlink={true}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
printers: false,
|
printers: false,
|
||||||
createdAt: false,
|
createdAt: false,
|
||||||
finishedAt: false
|
finishedAt: false
|
||||||
}}
|
}}
|
||||||
objectPropertyProps={{
|
|
||||||
showHyperlink: !slicerIntegration
|
|
||||||
}}
|
|
||||||
objectData={jobObjectData}
|
objectData={jobObjectData}
|
||||||
type='job'
|
type='job'
|
||||||
/>
|
/>
|
||||||
@ -422,15 +428,12 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
loading={subJobObjectLoading}
|
loading={subJobObjectLoading}
|
||||||
column={sideBarVisible ? 1 : undefined}
|
column={sideBarVisible ? 1 : undefined}
|
||||||
showHyperlink={!slicerIntegration}
|
showHyperlink={true}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
printers: false,
|
printers: false,
|
||||||
createdAt: false,
|
createdAt: false,
|
||||||
finishedAt: false
|
finishedAt: false
|
||||||
}}
|
}}
|
||||||
objectPropertyProps={{
|
|
||||||
showHyperlink: !slicerIntegration
|
|
||||||
}}
|
|
||||||
objectData={subJobObjectData}
|
objectData={subJobObjectData}
|
||||||
type='subJob'
|
type='subJob'
|
||||||
/>
|
/>
|
||||||
@ -471,7 +474,7 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
loading={filamentStockObjectLoading}
|
loading={filamentStockObjectLoading}
|
||||||
column={sideBarVisible ? 1 : undefined}
|
column={sideBarVisible ? 1 : undefined}
|
||||||
showHyperlink={!slicerIntegration}
|
showHyperlink={true}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
updatedAt: false,
|
updatedAt: false,
|
||||||
createdAt: false
|
createdAt: false
|
||||||
@ -555,8 +558,4 @@ const ControlPrinter = ({ slicerIntegration = false }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlPrinter.propTypes = {
|
|
||||||
slicerIntegration: PropTypes.bool
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ControlPrinter
|
export default ControlPrinter
|
||||||
|
|||||||
@ -96,8 +96,8 @@ const NewPrinter = ({ onOk, defaultValues }) => {
|
|||||||
title='New Printer'
|
title='New Printer'
|
||||||
onSubmit={async () => {
|
onSubmit={async () => {
|
||||||
const result = await handleSubmit()
|
const result = await handleSubmit()
|
||||||
if (result?._id) {
|
if (result) {
|
||||||
onOk(result)
|
onOk()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,225 +0,0 @@
|
|||||||
import { useContext, useEffect, useState } from 'react'
|
|
||||||
import { Divider, Flex, Layout, Typography, Button, Badge, Modal } from 'antd'
|
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
|
||||||
import { useLocation } from 'react-router-dom'
|
|
||||||
import ControlPrinter from './ControlPrinter.jsx'
|
|
||||||
import SlicerUploadFlow from './SlicerUploadFlow.jsx'
|
|
||||||
import PrinterIcon from '../../../Icons/PrinterIcon.jsx'
|
|
||||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
|
||||||
import FileIcon from '../../../Icons/FileIcon.jsx'
|
|
||||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
|
||||||
import { getModelProperty } from '../../../../database/ObjectModels.js'
|
|
||||||
import { ApiServerContext } from '../../context/ApiServerContext.jsx'
|
|
||||||
import { useThemeContext } from '../../context/ThemeContext.jsx'
|
|
||||||
|
|
||||||
const { Content } = Layout
|
|
||||||
const { Title } = Typography
|
|
||||||
|
|
||||||
const SlicerIntegration = () => {
|
|
||||||
const location = useLocation()
|
|
||||||
const printerId = new URLSearchParams(location.search).get('printerId')
|
|
||||||
const { updateObject } = useContext(ApiServerContext)
|
|
||||||
|
|
||||||
const [pendingSlicerUploads, setPendingSlicerUploads] = useState([])
|
|
||||||
const [currentPendingSlicerUpload, setCurrentPendingSlicerUpload] =
|
|
||||||
useState(null)
|
|
||||||
|
|
||||||
const { setPrimaryColorOverride } = useThemeContext()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setPrimaryColorOverride('#00baa8')
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const [objectFormState, setEditFormState] = useState({
|
|
||||||
isEditing: false,
|
|
||||||
editLoading: false,
|
|
||||||
formValid: false,
|
|
||||||
locked: false,
|
|
||||||
loading: false,
|
|
||||||
objectData: {
|
|
||||||
pendingSlicerUploads: []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const [showSlicerUploadFlow, setShowSlicerUploadFlow] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setPendingSlicerUploads(
|
|
||||||
objectFormState.objectData?.pendingSlicerUploads || []
|
|
||||||
)
|
|
||||||
}, [objectFormState.objectData?.pendingSlicerUploads])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const nextUpload = pendingSlicerUploads.find(
|
|
||||||
(upload) => upload.new === true
|
|
||||||
)
|
|
||||||
|
|
||||||
if (nextUpload) {
|
|
||||||
setCurrentPendingSlicerUpload(nextUpload)
|
|
||||||
setShowSlicerUploadFlow(true)
|
|
||||||
} else {
|
|
||||||
setCurrentPendingSlicerUpload(null)
|
|
||||||
setShowSlicerUploadFlow(false)
|
|
||||||
}
|
|
||||||
}, [pendingSlicerUploads])
|
|
||||||
|
|
||||||
const showPendingSlicerUpload = () => {
|
|
||||||
const upload =
|
|
||||||
pendingSlicerUploads.find(
|
|
||||||
(pendingUpload) => pendingUpload.new === true
|
|
||||||
) || pendingSlicerUploads[0]
|
|
||||||
|
|
||||||
if (upload) {
|
|
||||||
setCurrentPendingSlicerUpload(upload)
|
|
||||||
setShowSlicerUploadFlow(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleGCodeFileCreated = async (gcodeFile) => {
|
|
||||||
const updatedUploads = pendingSlicerUploads.map((upload) =>
|
|
||||||
upload._id === currentPendingSlicerUpload?._id
|
|
||||||
? { ...upload, gcodeFile: gcodeFile }
|
|
||||||
: upload
|
|
||||||
)
|
|
||||||
|
|
||||||
setPendingSlicerUploads(updatedUploads)
|
|
||||||
setCurrentPendingSlicerUpload((previous) => ({
|
|
||||||
...previous,
|
|
||||||
gcodeFile
|
|
||||||
}))
|
|
||||||
await updateObject(printerId, 'printer', {
|
|
||||||
...objectFormState.objectData,
|
|
||||||
pendingSlicerUploads: updatedUploads
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleJobCreated = async (job) => {
|
|
||||||
const updatedUploads = pendingSlicerUploads.map((upload) =>
|
|
||||||
upload._id === currentPendingSlicerUpload?._id
|
|
||||||
? { ...upload, job }
|
|
||||||
: upload
|
|
||||||
)
|
|
||||||
|
|
||||||
setPendingSlicerUploads(updatedUploads)
|
|
||||||
setCurrentPendingSlicerUpload((previous) => ({
|
|
||||||
...previous,
|
|
||||||
job
|
|
||||||
}))
|
|
||||||
await updateObject(printerId, 'printer', {
|
|
||||||
...objectFormState.objectData,
|
|
||||||
pendingSlicerUploads: updatedUploads
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSlicerUploadComplete = async () => {
|
|
||||||
const updatedUploads = pendingSlicerUploads.filter(
|
|
||||||
(upload) => upload._id !== currentPendingSlicerUpload?._id
|
|
||||||
)
|
|
||||||
|
|
||||||
setPendingSlicerUploads(updatedUploads)
|
|
||||||
await updateObject(printerId, 'printer', {
|
|
||||||
...objectFormState.objectData,
|
|
||||||
pendingSlicerUploads: updatedUploads
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSlicerUploadFlowClosed = async () => {
|
|
||||||
const updatedUploads = pendingSlicerUploads.map((upload) =>
|
|
||||||
upload._id === currentPendingSlicerUpload?._id
|
|
||||||
? { ...upload, new: false }
|
|
||||||
: upload
|
|
||||||
)
|
|
||||||
|
|
||||||
setPendingSlicerUploads(updatedUploads)
|
|
||||||
await updateObject(printerId, 'printer', {
|
|
||||||
...objectFormState.objectData,
|
|
||||||
pendingSlicerUploads: updatedUploads
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const slicerUploadsButton = () => {
|
|
||||||
if (pendingSlicerUploads.length > 0) {
|
|
||||||
return (
|
|
||||||
<Badge
|
|
||||||
count={pendingSlicerUploads.length}
|
|
||||||
style={{ marginTop: '8px' }}
|
|
||||||
size='small'
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
type='text'
|
|
||||||
icon={<FileIcon />}
|
|
||||||
onClick={showPendingSlicerUpload}
|
|
||||||
/>
|
|
||||||
</Badge>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout style={{ height: 'var(--unit-100vh)' }}>
|
|
||||||
<Layout style={{ padding: '24px' }}>
|
|
||||||
<Content>
|
|
||||||
<Flex vertical style={{ height: '100%' }} gap='20px'>
|
|
||||||
<ObjectForm
|
|
||||||
id={printerId}
|
|
||||||
type='printer'
|
|
||||||
onStateChange={(state) =>
|
|
||||||
setEditFormState((previousState) => ({
|
|
||||||
...previousState,
|
|
||||||
...state
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{({ loading, objectData }) => (
|
|
||||||
<Flex justify='space-between' align='center'>
|
|
||||||
<Flex align='center' gap='middle'>
|
|
||||||
{loading ? (
|
|
||||||
<LoadingOutlined spin style={{ fontSize: '26px' }} />
|
|
||||||
) : (
|
|
||||||
<PrinterIcon style={{ fontSize: '26px' }} />
|
|
||||||
)}
|
|
||||||
<Title level={3} style={{ margin: 0 }}>
|
|
||||||
{loading
|
|
||||||
? 'Loading...'
|
|
||||||
: objectData?.name || 'Slicer Integration'}
|
|
||||||
</Title>
|
|
||||||
{!loading && (
|
|
||||||
<ObjectProperty
|
|
||||||
objectData={objectData}
|
|
||||||
{...getModelProperty('printer', 'state')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
{slicerUploadsButton()}
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</ObjectForm>
|
|
||||||
<Divider style={{ margin: '0' }} />
|
|
||||||
<ControlPrinter slicerIntegration={true} />
|
|
||||||
</Flex>
|
|
||||||
</Content>
|
|
||||||
<Modal
|
|
||||||
open={showSlicerUploadFlow}
|
|
||||||
onCancel={() => setShowSlicerUploadFlow(false)}
|
|
||||||
afterClose={handleSlicerUploadFlowClosed}
|
|
||||||
footer={null}
|
|
||||||
width={740}
|
|
||||||
destroyOnHidden={true}
|
|
||||||
>
|
|
||||||
{currentPendingSlicerUpload && (
|
|
||||||
<SlicerUploadFlow
|
|
||||||
key={currentPendingSlicerUpload._id}
|
|
||||||
currentPendingSlicerUpload={currentPendingSlicerUpload}
|
|
||||||
printer={objectFormState.objectData}
|
|
||||||
onGCodeFileCreated={handleGCodeFileCreated}
|
|
||||||
onJobCreated={handleJobCreated}
|
|
||||||
onComplete={handleSlicerUploadComplete}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Modal>
|
|
||||||
</Layout>
|
|
||||||
</Layout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SlicerIntegration
|
|
||||||
@ -1,152 +0,0 @@
|
|||||||
import { useMemo, useState } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import NewGCodeFile from '../GCodeFiles/NewGCodeFile.jsx'
|
|
||||||
import NewJob from '../Jobs/NewJob.jsx'
|
|
||||||
import DeployJob from '../Jobs/DeployJob.jsx'
|
|
||||||
import WizardView from '../../common/WizardView.jsx'
|
|
||||||
|
|
||||||
const SlicerUploadFlow = ({
|
|
||||||
currentPendingSlicerUpload,
|
|
||||||
printer,
|
|
||||||
onGCodeFileCreated,
|
|
||||||
onJobCreated,
|
|
||||||
onComplete
|
|
||||||
}) => {
|
|
||||||
const [createdGCodeFile, setCreatedGCodeFile] = useState(
|
|
||||||
() => currentPendingSlicerUpload.gcodeFile || null
|
|
||||||
)
|
|
||||||
const [createdJob, setCreatedJob] = useState(
|
|
||||||
() => currentPendingSlicerUpload.job || null
|
|
||||||
)
|
|
||||||
const [currentSubStep, setCurrentSubStep] = useState(0)
|
|
||||||
|
|
||||||
const gcodeFileDefaults = useMemo(
|
|
||||||
() => ({
|
|
||||||
...(currentPendingSlicerUpload?.properties || {}),
|
|
||||||
file: currentPendingSlicerUpload?.file
|
|
||||||
}),
|
|
||||||
[currentPendingSlicerUpload]
|
|
||||||
)
|
|
||||||
|
|
||||||
const jobDefaults = useMemo(
|
|
||||||
() => ({
|
|
||||||
quantity: 1,
|
|
||||||
gcodeFile: createdGCodeFile,
|
|
||||||
printers: [printer]
|
|
||||||
}),
|
|
||||||
[createdGCodeFile, printer]
|
|
||||||
)
|
|
||||||
|
|
||||||
const steps = [
|
|
||||||
{
|
|
||||||
title: 'GCode File',
|
|
||||||
key: 'gcodeFile',
|
|
||||||
subSteps: [
|
|
||||||
{
|
|
||||||
title: 'Required',
|
|
||||||
key: 'required'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Summary',
|
|
||||||
key: 'summary'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
content: (
|
|
||||||
<NewGCodeFile
|
|
||||||
defaultValues={gcodeFileDefaults}
|
|
||||||
slicerIntegration={true}
|
|
||||||
shouldPrint={currentPendingSlicerUpload.shouldPrint}
|
|
||||||
currentStep={currentSubStep}
|
|
||||||
onStepChange={setCurrentSubStep}
|
|
||||||
onOk={(gcodeFile) => {
|
|
||||||
if (currentPendingSlicerUpload.shouldPrint) {
|
|
||||||
setCurrentSubStep(0)
|
|
||||||
setCreatedGCodeFile(gcodeFile)
|
|
||||||
onGCodeFileCreated(gcodeFile)
|
|
||||||
} else {
|
|
||||||
onComplete({ gcodeFile })
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
if (currentPendingSlicerUpload.shouldPrint) {
|
|
||||||
steps.push({
|
|
||||||
title: 'Print Job',
|
|
||||||
key: 'job',
|
|
||||||
subSteps: [
|
|
||||||
{
|
|
||||||
title: 'Required',
|
|
||||||
key: 'required'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Summary',
|
|
||||||
key: 'summary'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
content: createdGCodeFile ? (
|
|
||||||
<NewJob
|
|
||||||
slicerIntegration={true}
|
|
||||||
defaultValues={jobDefaults}
|
|
||||||
currentStep={currentSubStep}
|
|
||||||
onStepChange={setCurrentSubStep}
|
|
||||||
onOk={(job) => {
|
|
||||||
if (job == null || job == undefined) {
|
|
||||||
onComplete({ gcodeFile: createdGCodeFile })
|
|
||||||
} else {
|
|
||||||
setCurrentSubStep(0)
|
|
||||||
setCreatedJob(job)
|
|
||||||
onJobCreated(job)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
})
|
|
||||||
|
|
||||||
steps.push({
|
|
||||||
title: 'Deploy',
|
|
||||||
key: 'deploy',
|
|
||||||
content: createdJob ? (
|
|
||||||
<DeployJob
|
|
||||||
objectData={createdJob}
|
|
||||||
slicerIntegration={true}
|
|
||||||
onOk={() =>
|
|
||||||
onComplete({ gcodeFile: createdGCodeFile, job: createdJob })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<WizardView
|
|
||||||
title='Slicer Upload'
|
|
||||||
steps={steps}
|
|
||||||
currentStep={createdJob ? 2 : createdGCodeFile ? 1 : 0}
|
|
||||||
currentSubStep={currentSubStep}
|
|
||||||
showButtons={false}
|
|
||||||
showTitle={false}
|
|
||||||
showSteps={currentPendingSlicerUpload.shouldPrint}
|
|
||||||
formValid={true}
|
|
||||||
onSubmit={() => {}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
SlicerUploadFlow.propTypes = {
|
|
||||||
currentPendingSlicerUpload: PropTypes.shape({
|
|
||||||
file: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
|
|
||||||
gcodeFile: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
|
||||||
job: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
|
||||||
shouldPrint: PropTypes.bool.isRequired,
|
|
||||||
properties: PropTypes.object
|
|
||||||
}).isRequired,
|
|
||||||
printer: PropTypes.object.isRequired,
|
|
||||||
onGCodeFileCreated: PropTypes.func.isRequired,
|
|
||||||
onJobCreated: PropTypes.func.isRequired,
|
|
||||||
onComplete: PropTypes.func.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SlicerUploadFlow
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import { Flex, Tag, Typography } from 'antd'
|
|
||||||
|
|
||||||
const { Text } = Typography
|
|
||||||
|
|
||||||
const ArrayDisplay = ({ value, prefix, suffix }) => {
|
|
||||||
if (!Array.isArray(value) || value.length === 0) {
|
|
||||||
return <Text type='secondary'>n/a</Text>
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex gap='small' wrap>
|
|
||||||
{value.map((item, index) => (
|
|
||||||
<Tag key={`${item}-${index}`}>
|
|
||||||
{prefix}
|
|
||||||
{item}
|
|
||||||
{suffix}
|
|
||||||
</Tag>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayDisplay.propTypes = {
|
|
||||||
value: PropTypes.array,
|
|
||||||
prefix: PropTypes.node,
|
|
||||||
suffix: PropTypes.node
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ArrayDisplay
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import { Button, Flex, Input, InputNumber } from 'antd'
|
|
||||||
|
|
||||||
const ArrayInput = ({
|
|
||||||
value = [],
|
|
||||||
onChange,
|
|
||||||
disabled = false,
|
|
||||||
numeric = false,
|
|
||||||
min,
|
|
||||||
max,
|
|
||||||
step,
|
|
||||||
placeholder
|
|
||||||
}) => {
|
|
||||||
const values = Array.isArray(value) ? value : []
|
|
||||||
|
|
||||||
const updateValue = (index, nextValue) => {
|
|
||||||
const nextValues = [...values]
|
|
||||||
nextValues[index] = nextValue
|
|
||||||
onChange?.(nextValues)
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeValue = (index) => {
|
|
||||||
onChange?.(values.filter((_, valueIndex) => valueIndex !== index))
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex vertical gap='small' style={{ width: '100%' }}>
|
|
||||||
{values.map((item, index) => (
|
|
||||||
<Flex key={index} gap='small' style={{ width: '100%' }}>
|
|
||||||
{numeric ? (
|
|
||||||
<InputNumber
|
|
||||||
value={item}
|
|
||||||
onChange={(nextValue) => updateValue(index, nextValue)}
|
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
step={step}
|
|
||||||
placeholder={placeholder}
|
|
||||||
disabled={disabled}
|
|
||||||
style={{ flex: 1 }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Input
|
|
||||||
value={item}
|
|
||||||
onChange={(event) => updateValue(index, event.target.value)}
|
|
||||||
placeholder={placeholder}
|
|
||||||
disabled={disabled}
|
|
||||||
style={{ flex: 1 }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
onClick={() => removeValue(index)}
|
|
||||||
disabled={disabled}
|
|
||||||
danger
|
|
||||||
aria-label={`Remove ${placeholder || 'item'}`}
|
|
||||||
>
|
|
||||||
Remove
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
))}
|
|
||||||
<Button
|
|
||||||
onClick={() => onChange?.([...values, numeric ? null : ''])}
|
|
||||||
disabled={disabled}
|
|
||||||
type='dashed'
|
|
||||||
block
|
|
||||||
>
|
|
||||||
Add {placeholder || 'item'}
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayInput.propTypes = {
|
|
||||||
value: PropTypes.array,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
numeric: PropTypes.bool,
|
|
||||||
min: PropTypes.number,
|
|
||||||
max: PropTypes.number,
|
|
||||||
step: PropTypes.number,
|
|
||||||
placeholder: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ArrayInput
|
|
||||||
@ -76,7 +76,7 @@ const InputNumberCal = ({
|
|||||||
const next = e.target.value
|
const next = e.target.value
|
||||||
setExprValue(next)
|
setExprValue(next)
|
||||||
const num = parseFloat(next)
|
const num = parseFloat(next)
|
||||||
if (next === '') {
|
if (!next) {
|
||||||
onChange?.(null)
|
onChange?.(null)
|
||||||
} else if (!next.match(/[+\-*/=]/) && !Number.isNaN(num)) {
|
} else if (!next.match(/[+\-*/=]/) && !Number.isNaN(num)) {
|
||||||
onChange?.(num)
|
onChange?.(num)
|
||||||
@ -142,11 +142,11 @@ const InputNumberCal = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InputNumber
|
<InputNumber
|
||||||
{...commonProps}
|
value={value}
|
||||||
value={value != null ? value : null}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
onKeyDown={handleNumberKeyDown}
|
onKeyDown={handleNumberKeyDown}
|
||||||
|
{...commonProps}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
import { Flex, Typography, Tag, Space } from 'antd'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
const { Text } = Typography
|
|
||||||
|
|
||||||
const getPrecision = (step) => {
|
|
||||||
if (step == null) return null
|
|
||||||
const stepStr = String(step)
|
|
||||||
const dotIndex = stepStr.indexOf('.')
|
|
||||||
return dotIndex === -1 ? 0 : stepStr.length - dotIndex - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatValue = (value, step) => {
|
|
||||||
if (value == null || value === '') return null
|
|
||||||
const parsed = parseFloat(value)
|
|
||||||
if (Number.isNaN(parsed)) return null
|
|
||||||
|
|
||||||
const precision = getPrecision(step)
|
|
||||||
return precision == null ? parsed.toString() : parsed.toFixed(precision)
|
|
||||||
}
|
|
||||||
|
|
||||||
const MinMaxDisplay = ({ value, prefix = '', suffix = '', step }) => {
|
|
||||||
const min = formatValue(value?.min, step)
|
|
||||||
const max = formatValue(value?.max, step)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex gap={'middle'} align='center' style={{ height: '100%' }}>
|
|
||||||
<Flex wrap>
|
|
||||||
<Text style={{ marginRight: 4 }}>
|
|
||||||
{min != null ? (
|
|
||||||
prefix + min + suffix
|
|
||||||
) : (
|
|
||||||
<Text type='secondary'>n/a</Text>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
<Space>
|
|
||||||
<Tag>Min</Tag>
|
|
||||||
</Space>
|
|
||||||
</Flex>
|
|
||||||
<Flex wrap>
|
|
||||||
<Text style={{ marginRight: 4 }}>
|
|
||||||
{max != null ? (
|
|
||||||
prefix + max + suffix
|
|
||||||
) : (
|
|
||||||
<Text type='secondary'>n/a</Text>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
<Space>
|
|
||||||
<Tag>Max</Tag>
|
|
||||||
</Space>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
MinMaxDisplay.propTypes = {
|
|
||||||
value: PropTypes.shape({
|
|
||||||
min: PropTypes.number,
|
|
||||||
max: PropTypes.number
|
|
||||||
}),
|
|
||||||
prefix: PropTypes.string,
|
|
||||||
suffix: PropTypes.string,
|
|
||||||
step: PropTypes.number
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MinMaxDisplay
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
import { Flex, InputNumber, Typography } from 'antd'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
const { Text } = Typography
|
|
||||||
|
|
||||||
const MinMaxInput = ({
|
|
||||||
value = {},
|
|
||||||
onChange,
|
|
||||||
prefix = '',
|
|
||||||
suffix = '',
|
|
||||||
min,
|
|
||||||
max,
|
|
||||||
step,
|
|
||||||
disabled = false
|
|
||||||
}) => {
|
|
||||||
const handleChange = (field, val) => {
|
|
||||||
if (!onChange) return
|
|
||||||
onChange({ ...value, [field]: val })
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex gap={'middle'} align='center' style={{ width: '100%' }}>
|
|
||||||
<InputNumber
|
|
||||||
addonBefore={<Text type='secondary'>Min:</Text>}
|
|
||||||
value={value?.min}
|
|
||||||
onChange={(val) => handleChange('min', val)}
|
|
||||||
prefix={prefix}
|
|
||||||
suffix={suffix}
|
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
step={step}
|
|
||||||
disabled={disabled}
|
|
||||||
style={{ minWidth: 120, flex: 1 }}
|
|
||||||
/>
|
|
||||||
<InputNumber
|
|
||||||
addonBefore={<Text type='secondary'>Max:</Text>}
|
|
||||||
value={value?.max}
|
|
||||||
onChange={(val) => handleChange('max', val)}
|
|
||||||
prefix={prefix}
|
|
||||||
suffix={suffix}
|
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
step={step}
|
|
||||||
disabled={disabled}
|
|
||||||
style={{ minWidth: 120, flex: 1 }}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
MinMaxInput.propTypes = {
|
|
||||||
value: PropTypes.shape({
|
|
||||||
min: PropTypes.number,
|
|
||||||
max: PropTypes.number
|
|
||||||
}),
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
prefix: PropTypes.string,
|
|
||||||
suffix: PropTypes.string,
|
|
||||||
min: PropTypes.number,
|
|
||||||
max: PropTypes.number,
|
|
||||||
step: PropTypes.number,
|
|
||||||
disabled: PropTypes.bool
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MinMaxInput
|
|
||||||
@ -3,11 +3,8 @@ import PropTypes from 'prop-types'
|
|||||||
import { Table, Skeleton, Card, Button, Flex, Typography, Modal } from 'antd'
|
import { Table, Skeleton, Card, Button, Flex, Typography, Modal } from 'antd'
|
||||||
import PlusIcon from '../../Icons/PlusIcon'
|
import PlusIcon from '../../Icons/PlusIcon'
|
||||||
import ObjectProperty from './ObjectProperty'
|
import ObjectProperty from './ObjectProperty'
|
||||||
import ObjectInfo from './ObjectInfo'
|
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import BinIcon from '../../Icons/BinIcon'
|
import BinIcon from '../../Icons/BinIcon'
|
||||||
import EyeIcon from '../../Icons/EyeIcon'
|
|
||||||
import EditIcon from '../../Icons/EditIcon'
|
|
||||||
const { Text, Link, Title } = Typography
|
const { Text, Link, Title } = Typography
|
||||||
|
|
||||||
const DEFAULT_COLUMN_WIDTHS = {
|
const DEFAULT_COLUMN_WIDTHS = {
|
||||||
@ -67,7 +64,6 @@ const ObjectChildTable = ({
|
|||||||
value = [],
|
value = [],
|
||||||
rollups = [],
|
rollups = [],
|
||||||
onChange,
|
onChange,
|
||||||
hiddenPropertyWidth = '300px',
|
|
||||||
minimal = false,
|
minimal = false,
|
||||||
label = '',
|
label = '',
|
||||||
...tableProps
|
...tableProps
|
||||||
@ -77,9 +73,6 @@ const ObjectChildTable = ({
|
|||||||
const generatedRowKeysRef = useRef(new WeakMap())
|
const generatedRowKeysRef = useRef(new WeakMap())
|
||||||
const generatedRowKeyCountRef = useRef(0)
|
const generatedRowKeyCountRef = useRef(0)
|
||||||
const [minimalModelOpen, setMinimalModelOpen] = useState(false)
|
const [minimalModelOpen, setMinimalModelOpen] = useState(false)
|
||||||
const [detailModalOpen, setDetailModalOpen] = useState(false)
|
|
||||||
const [detailModalRecord, setDetailModalRecord] = useState(null)
|
|
||||||
const [detailModalIndex, setDetailModalIndex] = useState(null)
|
|
||||||
const getFallbackRowKey = (record) => {
|
const getFallbackRowKey = (record) => {
|
||||||
if (!record || typeof record !== 'object') {
|
if (!record || typeof record !== 'object') {
|
||||||
return `object-child-table-row-${String(record)}`
|
return `object-child-table-row-${String(record)}`
|
||||||
@ -131,14 +124,15 @@ const ObjectChildTable = ({
|
|||||||
}, [columns, properties])
|
}, [columns, properties])
|
||||||
|
|
||||||
const resolvedProperties = useMemo(() => {
|
const resolvedProperties = useMemo(() => {
|
||||||
const baseProperties =
|
const explicit = orderedPropertyNames
|
||||||
columns && columns.length > 0
|
|
||||||
? orderedPropertyNames
|
|
||||||
.map((name) => propertyMap.get(name))
|
.map((name) => propertyMap.get(name))
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
: properties
|
|
||||||
|
|
||||||
return baseProperties.filter((property) => {
|
const remaining = properties.filter(
|
||||||
|
(property) => !orderedPropertyNames.includes(property.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
return [...explicit, ...remaining].filter((property) => {
|
||||||
if (!property?.name) return false
|
if (!property?.name) return false
|
||||||
if (
|
if (
|
||||||
visibleColumns &&
|
visibleColumns &&
|
||||||
@ -148,87 +142,13 @@ const ObjectChildTable = ({
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
}, [columns, orderedPropertyNames, propertyMap, properties, visibleColumns])
|
}, [orderedPropertyNames, propertyMap, properties, visibleColumns])
|
||||||
|
|
||||||
const hasHiddenProperties = useMemo(() => {
|
|
||||||
return (
|
|
||||||
Array.isArray(columns) &&
|
|
||||||
columns.length > 0 &&
|
|
||||||
Array.isArray(properties) &&
|
|
||||||
columns.length < properties.length
|
|
||||||
)
|
|
||||||
}, [columns, properties])
|
|
||||||
|
|
||||||
// When used inside antd Form.Item without Form.List, `value` will be the controlled array.
|
// When used inside antd Form.Item without Form.List, `value` will be the controlled array.
|
||||||
const itemsSource = useMemo(() => {
|
const itemsSource = useMemo(() => {
|
||||||
return value ?? []
|
return value ?? []
|
||||||
}, [value])
|
}, [value])
|
||||||
|
|
||||||
const handleRemoveItem = useCallback(
|
|
||||||
(record, index) => {
|
|
||||||
const currentItems = Array.isArray(itemsSource) ? itemsSource : []
|
|
||||||
|
|
||||||
let newItems
|
|
||||||
if (typeof rowKey === 'string' && record[rowKey] != null) {
|
|
||||||
newItems = currentItems.filter(
|
|
||||||
(item) => item[rowKey] !== record[rowKey]
|
|
||||||
)
|
|
||||||
} else if (typeof rowKey === 'function') {
|
|
||||||
const recordKey = getResolvedRecordKey(record)
|
|
||||||
newItems = currentItems.filter((item) => {
|
|
||||||
const itemKey = getResolvedRecordKey(item)
|
|
||||||
return itemKey !== recordKey
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
newItems = currentItems.filter((_, i) => i !== index)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof onChange === 'function') {
|
|
||||||
onChange(newItems)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[itemsSource, onChange, rowKey, getResolvedRecordKey]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleOpenDetailModal = useCallback((record, index) => {
|
|
||||||
setDetailModalRecord({ ...record })
|
|
||||||
setDetailModalIndex(index)
|
|
||||||
setDetailModalOpen(true)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const handleDetailPropertyChange = useCallback(
|
|
||||||
(propName, newVal) => {
|
|
||||||
const property = propertyMap.get(propName)
|
|
||||||
const resolved = resolveChangeValue(newVal, property?.type)
|
|
||||||
|
|
||||||
setDetailModalRecord((prev) =>
|
|
||||||
prev ? { ...prev, [propName]: resolved } : prev
|
|
||||||
)
|
|
||||||
|
|
||||||
if (detailModalIndex == null) return
|
|
||||||
|
|
||||||
const currentItems = Array.isArray(itemsSource) ? [...itemsSource] : []
|
|
||||||
const existingRowKey =
|
|
||||||
currentItems[detailModalIndex] != null
|
|
||||||
? getResolvedRecordKey(currentItems[detailModalIndex])
|
|
||||||
: null
|
|
||||||
const updatedItem = {
|
|
||||||
...currentItems[detailModalIndex],
|
|
||||||
[propName]: resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingRowKey) {
|
|
||||||
generatedRowKeysRef.current.set(updatedItem, existingRowKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentItems[detailModalIndex] = updatedItem
|
|
||||||
if (typeof onChange === 'function') {
|
|
||||||
onChange(currentItems)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[propertyMap, detailModalIndex, itemsSource, onChange, getResolvedRecordKey]
|
|
||||||
)
|
|
||||||
|
|
||||||
const tableColumns = useMemo(() => {
|
const tableColumns = useMemo(() => {
|
||||||
const propertyColumns = resolvedProperties.map((property) => ({
|
const propertyColumns = resolvedProperties.map((property) => ({
|
||||||
title: property.label || property.name,
|
title: property.label || property.name,
|
||||||
@ -277,32 +197,18 @@ const ObjectChildTable = ({
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const actionsColumn =
|
const deleteColumn =
|
||||||
hasHiddenProperties || (isEditing && canAddRemove)
|
isEditing && canAddRemove
|
||||||
? {
|
? {
|
||||||
title: '',
|
title: '',
|
||||||
key: 'actions',
|
key: 'delete',
|
||||||
width: hasHiddenProperties && isEditing && canAddRemove ? 70 : 40,
|
width: 10,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
render: (_text, record, index) => {
|
render: (_text, record, index) => {
|
||||||
if (record?.isSkeleton) {
|
if (record?.isSkeleton) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap={4}>
|
|
||||||
{hasHiddenProperties && (
|
|
||||||
<Button
|
|
||||||
type='text'
|
|
||||||
size='small'
|
|
||||||
icon={isEditing ? <EditIcon /> : <EyeIcon />}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
handleOpenDetailModal(record, index)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isEditing && canAddRemove && (
|
|
||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
danger
|
danger
|
||||||
@ -310,11 +216,35 @@ const ObjectChildTable = ({
|
|||||||
icon={<BinIcon />}
|
icon={<BinIcon />}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
handleRemoveItem(record, index)
|
const currentItems = Array.isArray(itemsSource)
|
||||||
|
? itemsSource
|
||||||
|
: []
|
||||||
|
|
||||||
|
// Use record's unique identifier if available, otherwise use index
|
||||||
|
let newItems
|
||||||
|
if (typeof rowKey === 'string' && record[rowKey] != null) {
|
||||||
|
// Use the unique key to find and remove the item
|
||||||
|
newItems = currentItems.filter(
|
||||||
|
(item) => item[rowKey] !== record[rowKey]
|
||||||
|
)
|
||||||
|
} else if (typeof rowKey === 'function') {
|
||||||
|
// If rowKey is a function, find the item by comparing resolved keys.
|
||||||
|
// Ant Design deprecates index-based rowKey callbacks.
|
||||||
|
const recordKey = getResolvedRecordKey(record)
|
||||||
|
newItems = currentItems.filter((item) => {
|
||||||
|
const itemKey = getResolvedRecordKey(item)
|
||||||
|
return itemKey !== recordKey
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Fallback to index-based removal
|
||||||
|
newItems = currentItems.filter((_, i) => i !== index)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof onChange === 'function') {
|
||||||
|
onChange(newItems)
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,20 +253,17 @@ const ObjectChildTable = ({
|
|||||||
return [
|
return [
|
||||||
...propertyColumns,
|
...propertyColumns,
|
||||||
...additionalColumns,
|
...additionalColumns,
|
||||||
...(actionsColumn ? [actionsColumn] : [])
|
...(deleteColumn ? [deleteColumn] : [])
|
||||||
]
|
]
|
||||||
}, [
|
}, [
|
||||||
resolvedProperties,
|
resolvedProperties,
|
||||||
additionalColumns,
|
additionalColumns,
|
||||||
hasHiddenProperties,
|
|
||||||
isEditing,
|
isEditing,
|
||||||
canAddRemove,
|
canAddRemove,
|
||||||
itemsSource,
|
itemsSource,
|
||||||
onChange,
|
onChange,
|
||||||
rowKey,
|
rowKey,
|
||||||
getResolvedRecordKey,
|
getResolvedRecordKey
|
||||||
handleOpenDetailModal,
|
|
||||||
handleRemoveItem
|
|
||||||
])
|
])
|
||||||
|
|
||||||
const skeletonData = useMemo(() => {
|
const skeletonData = useMemo(() => {
|
||||||
@ -424,8 +351,8 @@ const ObjectChildTable = ({
|
|||||||
}, [properties, rollups, objectData, value, name])
|
}, [properties, rollups, objectData, value, name])
|
||||||
|
|
||||||
const rollupColumns = useMemo(() => {
|
const rollupColumns = useMemo(() => {
|
||||||
const propertyColumns = resolvedProperties.map((property, index) => {
|
const propertyColumns = properties.map((property, index) => {
|
||||||
const nextProperty = resolvedProperties[index + 1]
|
const nextProperty = properties[index + 1]
|
||||||
var nextRollup = null
|
var nextRollup = null
|
||||||
if (nextProperty) {
|
if (nextProperty) {
|
||||||
nextRollup = rollups?.find(
|
nextRollup = rollups?.find(
|
||||||
@ -457,12 +384,12 @@ const ObjectChildTable = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const blankActionsColumn =
|
const blankDeleteColumn =
|
||||||
hasHiddenProperties || (isEditing && canAddRemove)
|
isEditing && canAddRemove
|
||||||
? {
|
? {
|
||||||
title: '',
|
title: '',
|
||||||
key: 'actions',
|
key: 'delete',
|
||||||
width: hasHiddenProperties && isEditing && canAddRemove ? 70 : 40,
|
width: 40,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
render: () => {
|
render: () => {
|
||||||
return <Flex></Flex>
|
return <Flex></Flex>
|
||||||
@ -471,15 +398,9 @@ const ObjectChildTable = ({
|
|||||||
: null
|
: null
|
||||||
return [
|
return [
|
||||||
...propertyColumns,
|
...propertyColumns,
|
||||||
...(blankActionsColumn ? [blankActionsColumn] : [])
|
...(blankDeleteColumn ? [blankDeleteColumn] : [])
|
||||||
]
|
]
|
||||||
}, [
|
}, [properties, rollups, isEditing, canAddRemove])
|
||||||
resolvedProperties,
|
|
||||||
rollups,
|
|
||||||
isEditing,
|
|
||||||
canAddRemove,
|
|
||||||
hasHiddenProperties
|
|
||||||
])
|
|
||||||
|
|
||||||
const hasRollups = useMemo(
|
const hasRollups = useMemo(
|
||||||
() => Array.isArray(rollups) && rollups.length > 0,
|
() => Array.isArray(rollups) && rollups.length > 0,
|
||||||
@ -574,37 +495,8 @@ const ObjectChildTable = ({
|
|||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
|
|
||||||
const detailModal = hasHiddenProperties ? (
|
|
||||||
<Modal
|
|
||||||
open={detailModalOpen}
|
|
||||||
onCancel={() => setDetailModalOpen(false)}
|
|
||||||
footer={null}
|
|
||||||
width='900px'
|
|
||||||
>
|
|
||||||
{label ? (
|
|
||||||
<Title
|
|
||||||
level={2}
|
|
||||||
style={{ marginTop: 0, lineHeight: '0.7', marginBottom: 20 }}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</Title>
|
|
||||||
) : null}
|
|
||||||
<ObjectInfo
|
|
||||||
propertyDefinitions={properties}
|
|
||||||
objectData={detailModalRecord}
|
|
||||||
parentData={objectData}
|
|
||||||
labelWidth={hiddenPropertyWidth}
|
|
||||||
isEditing={isEditing}
|
|
||||||
onPropertyChange={isEditing ? handleDetailPropertyChange : undefined}
|
|
||||||
column={1}
|
|
||||||
showLabels={true}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
) : null
|
|
||||||
|
|
||||||
if (isEditing === true) {
|
if (isEditing === true) {
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<Card>
|
<Card>
|
||||||
<Flex vertical gap={'middle'}>
|
<Flex vertical gap={'middle'}>
|
||||||
<Flex justify={'space-between'}>
|
<Flex justify={'space-between'}>
|
||||||
@ -622,8 +514,6 @@ const ObjectChildTable = ({
|
|||||||
{tableComponent}
|
{tableComponent}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</Card>
|
||||||
{detailModal}
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,17 +541,11 @@ const ObjectChildTable = ({
|
|||||||
</Title>
|
</Title>
|
||||||
{tableComponent}
|
{tableComponent}
|
||||||
</Modal>
|
</Modal>
|
||||||
{detailModal}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return tableComponent
|
||||||
<>
|
|
||||||
{tableComponent}
|
|
||||||
{detailModal}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectChildTable.propTypes = {
|
ObjectChildTable.propTypes = {
|
||||||
@ -690,8 +574,7 @@ ObjectChildTable.propTypes = {
|
|||||||
rollups: PropTypes.arrayOf(PropTypes.object),
|
rollups: PropTypes.arrayOf(PropTypes.object),
|
||||||
objectData: PropTypes.object,
|
objectData: PropTypes.object,
|
||||||
canAddRemove: PropTypes.bool,
|
canAddRemove: PropTypes.bool,
|
||||||
minimal: PropTypes.bool,
|
minimal: PropTypes.bool
|
||||||
hiddenPropertyWidth: PropTypes.string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectChildTable
|
export default ObjectChildTable
|
||||||
|
|||||||
@ -13,10 +13,7 @@ const ObjectInfo = ({
|
|||||||
showHyperlink,
|
showHyperlink,
|
||||||
showLabels = true,
|
showLabels = true,
|
||||||
objectData = null,
|
objectData = null,
|
||||||
parentData = null,
|
|
||||||
properties = [],
|
properties = [],
|
||||||
propertyDefinitions = null,
|
|
||||||
onPropertyChange = null,
|
|
||||||
required = undefined,
|
required = undefined,
|
||||||
visibleProperties = {},
|
visibleProperties = {},
|
||||||
objectPropertyProps = {},
|
objectPropertyProps = {},
|
||||||
@ -42,9 +39,7 @@ const ObjectInfo = ({
|
|||||||
// If properties array is empty, show all properties
|
// If properties array is empty, show all properties
|
||||||
// Otherwise, filter and order by the properties array
|
// Otherwise, filter and order by the properties array
|
||||||
let items
|
let items
|
||||||
if (Array.isArray(propertyDefinitions) && propertyDefinitions.length > 0) {
|
if (properties.length === 0) {
|
||||||
items = propertyDefinitions
|
|
||||||
} else if (properties.length === 0) {
|
|
||||||
items = allItems
|
items = allItems
|
||||||
} else {
|
} else {
|
||||||
items = properties
|
items = properties
|
||||||
@ -85,8 +80,6 @@ const ObjectInfo = ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const isControlled = typeof onPropertyChange === 'function'
|
|
||||||
|
|
||||||
// Map items to Descriptions 'items' prop format
|
// Map items to Descriptions 'items' prop format
|
||||||
const descriptionItems = items.map((item, idx) => {
|
const descriptionItems = items.map((item, idx) => {
|
||||||
const key = item.name || item.label || idx
|
const key = item.name || item.label || idx
|
||||||
@ -105,17 +98,7 @@ const ObjectInfo = ({
|
|||||||
{...objectPropertyProps}
|
{...objectPropertyProps}
|
||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
objectData={combinedObjectData}
|
objectData={combinedObjectData}
|
||||||
parentData={parentData}
|
|
||||||
showSince={true}
|
showSince={true}
|
||||||
useFormItem={isControlled ? false : objectPropertyProps.useFormItem}
|
|
||||||
value={
|
|
||||||
isControlled ? combinedObjectData?.[item.name] : undefined
|
|
||||||
}
|
|
||||||
onChange={
|
|
||||||
isControlled
|
|
||||||
? (newVal) => onPropertyChange(item.name, newVal)
|
|
||||||
: objectPropertyProps.onChange
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@ -147,11 +130,8 @@ ObjectInfo.propTypes = {
|
|||||||
bordered: PropTypes.bool,
|
bordered: PropTypes.bool,
|
||||||
items: PropTypes.arrayOf(PropTypes.object),
|
items: PropTypes.arrayOf(PropTypes.object),
|
||||||
isEditing: PropTypes.bool,
|
isEditing: PropTypes.bool,
|
||||||
type: PropTypes.string,
|
type: PropTypes.string.isRequired,
|
||||||
objectData: PropTypes.object,
|
objectData: PropTypes.object,
|
||||||
parentData: PropTypes.object,
|
|
||||||
propertyDefinitions: PropTypes.array,
|
|
||||||
onPropertyChange: PropTypes.func,
|
|
||||||
required: PropTypes.bool,
|
required: PropTypes.bool,
|
||||||
visibleProperties: PropTypes.object,
|
visibleProperties: PropTypes.object,
|
||||||
objectPropertyProps: PropTypes.object,
|
objectPropertyProps: PropTypes.object,
|
||||||
|
|||||||
@ -27,8 +27,6 @@ import { getPropertyValue } from '../../../database/ObjectModels'
|
|||||||
import PropertyChanges from './PropertyChanges'
|
import PropertyChanges from './PropertyChanges'
|
||||||
import NetGrossDisplay from './NetGrossDisplay'
|
import NetGrossDisplay from './NetGrossDisplay'
|
||||||
import NetGrossInput from './NetGrossInput'
|
import NetGrossInput from './NetGrossInput'
|
||||||
import MinMaxDisplay from './MinMaxDisplay'
|
|
||||||
import MinMaxInput from './MinMaxInput'
|
|
||||||
import InputNumberCal from './InputNumberCal'
|
import InputNumberCal from './InputNumberCal'
|
||||||
import ObjectList from './ObjectList'
|
import ObjectList from './ObjectList'
|
||||||
import VarianceDisplay from './VarianceDisplay'
|
import VarianceDisplay from './VarianceDisplay'
|
||||||
@ -49,35 +47,10 @@ import FileList from './FileList'
|
|||||||
import ObjectChildTable from './ObjectChildTable'
|
import ObjectChildTable from './ObjectChildTable'
|
||||||
import MiscId from './MiscId'
|
import MiscId from './MiscId'
|
||||||
import AddressDisplay from './AddressDisplay'
|
import AddressDisplay from './AddressDisplay'
|
||||||
import ArrayDisplay from './ArrayDisplay'
|
|
||||||
import ArrayInput from './ArrayInput'
|
|
||||||
import { round } from '../utils/Utils'
|
import { round } from '../utils/Utils'
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
const getTextValue = (value) => {
|
|
||||||
if (value == null || typeof value === 'string' || typeof value === 'number') {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === 'object') {
|
|
||||||
if (typeof value._id === 'string') {
|
|
||||||
return value._id
|
|
||||||
}
|
|
||||||
if (typeof value._id?._id === 'string') {
|
|
||||||
return value._id._id
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return JSON.stringify(value)
|
|
||||||
} catch {
|
|
||||||
return String(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return String(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeEditFormItemProps = {
|
const timeEditFormItemProps = {
|
||||||
getValueProps: (v) => ({ value: v ? dayjs(v) : null })
|
getValueProps: (v) => ({ value: v ? dayjs(v) : null })
|
||||||
}
|
}
|
||||||
@ -124,10 +97,7 @@ const ObjectProperty = ({
|
|||||||
loading = false,
|
loading = false,
|
||||||
rollups = [],
|
rollups = [],
|
||||||
showCard = true,
|
showCard = true,
|
||||||
showDownload = true,
|
|
||||||
columns = [],
|
|
||||||
style = {},
|
style = {},
|
||||||
hiddenPropertyWidth = '100px',
|
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
if (value && typeof value == 'function' && objectData) {
|
if (value && typeof value == 'function' && objectData) {
|
||||||
@ -182,11 +152,7 @@ const ObjectProperty = ({
|
|||||||
type = type(objectData, parentData)
|
type = type(objectData, parentData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columns && typeof columns == 'function' && objectData) {
|
if (!value) {
|
||||||
columns = columns(objectData, parentData)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
value = getPropertyValue(objectData, name)
|
value = getPropertyValue(objectData, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,15 +183,6 @@ const ObjectProperty = ({
|
|||||||
return (
|
return (
|
||||||
<NetGrossDisplay value={value} prefix={prefix} suffix={suffix} />
|
<NetGrossDisplay value={value} prefix={prefix} suffix={suffix} />
|
||||||
)
|
)
|
||||||
case 'minMax':
|
|
||||||
return (
|
|
||||||
<MinMaxDisplay
|
|
||||||
value={value}
|
|
||||||
prefix={prefix}
|
|
||||||
suffix={suffix}
|
|
||||||
step={step}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'secret':
|
case 'secret':
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
return <SecretDisplay value={value} {...rest} />
|
return <SecretDisplay value={value} {...rest} />
|
||||||
@ -356,9 +313,6 @@ const ObjectProperty = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'numberList':
|
|
||||||
case 'stringList':
|
|
||||||
return <ArrayDisplay value={value} prefix={prefix} suffix={suffix} />
|
|
||||||
case 'variance': {
|
case 'variance': {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
return (
|
return (
|
||||||
@ -377,7 +331,7 @@ const ObjectProperty = ({
|
|||||||
return (
|
return (
|
||||||
<Text ellipsis style={style}>
|
<Text ellipsis style={style}>
|
||||||
{prefix}
|
{prefix}
|
||||||
{getTextValue(value)}
|
{value}
|
||||||
{suffix}
|
{suffix}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
@ -478,10 +432,8 @@ const ObjectProperty = ({
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
rollups={rollups}
|
rollups={rollups}
|
||||||
size={size}
|
size={size}
|
||||||
hiddenPropertyWidth={hiddenPropertyWidth}
|
|
||||||
minimal={minimal}
|
minimal={minimal}
|
||||||
label={label}
|
label={label}
|
||||||
columns={columns}
|
|
||||||
canAddRemove={canAddRemove}
|
canAddRemove={canAddRemove}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -619,7 +571,6 @@ const ObjectProperty = ({
|
|||||||
defaultPreviewOpen={previewOpen}
|
defaultPreviewOpen={previewOpen}
|
||||||
showPreview={showPreview}
|
showPreview={showPreview}
|
||||||
showInfo={showHyperlink}
|
showInfo={showHyperlink}
|
||||||
showDownload={showDownload}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -637,8 +588,8 @@ const ObjectProperty = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
if (value != null && value !== '') {
|
if (value) {
|
||||||
return <Text {...textParams}>{getTextValue(value)}</Text>
|
return <Text {...textParams}>{value}</Text>
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Text type='secondary' {...textParams}>
|
<Text type='secondary' {...textParams}>
|
||||||
@ -669,59 +620,6 @@ const ObjectProperty = ({
|
|||||||
}
|
}
|
||||||
mergedFormItemProps.rules = rules
|
mergedFormItemProps.rules = rules
|
||||||
}
|
}
|
||||||
if (type === 'minMax' && disabled == false) {
|
|
||||||
const rules = [...(mergedFormItemProps.rules || [])]
|
|
||||||
rules.push({
|
|
||||||
validator: (_, range) => {
|
|
||||||
if (range == null) return Promise.resolve()
|
|
||||||
if (typeof range !== 'object' || Array.isArray(range)) {
|
|
||||||
return Promise.reject(new Error('Value must be a min/max range'))
|
|
||||||
}
|
|
||||||
|
|
||||||
const values = [range.min, range.max].filter((item) => item != null)
|
|
||||||
const valid = values.every(
|
|
||||||
(item) =>
|
|
||||||
typeof item === 'number' &&
|
|
||||||
Number.isFinite(item) &&
|
|
||||||
(min == null || item >= min) &&
|
|
||||||
(max == null || item <= max)
|
|
||||||
)
|
|
||||||
|
|
||||||
return valid
|
|
||||||
? Promise.resolve()
|
|
||||||
: Promise.reject(new Error('Enter valid min and max values'))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mergedFormItemProps.rules = rules
|
|
||||||
}
|
|
||||||
if ((type === 'numberList' || type === 'stringList') && disabled == false) {
|
|
||||||
const rules = [...(mergedFormItemProps.rules || [])]
|
|
||||||
rules.push({
|
|
||||||
validator: (_, items) => {
|
|
||||||
if (items == null) return Promise.resolve()
|
|
||||||
if (!Array.isArray(items)) {
|
|
||||||
return Promise.reject(new Error('Value must be a list'))
|
|
||||||
}
|
|
||||||
|
|
||||||
const valid = items.every((item) => {
|
|
||||||
if (type === 'numberList') {
|
|
||||||
return (
|
|
||||||
typeof item === 'number' &&
|
|
||||||
Number.isFinite(item) &&
|
|
||||||
(min == null || item >= min) &&
|
|
||||||
(max == null || item <= max)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return typeof item === 'string' && item.trim().length > 0
|
|
||||||
})
|
|
||||||
|
|
||||||
return valid
|
|
||||||
? Promise.resolve()
|
|
||||||
: Promise.reject(new Error('Complete or remove empty list items'))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mergedFormItemProps.rules = rules
|
|
||||||
}
|
|
||||||
// Remove name from mergedFormItemProps if present
|
// Remove name from mergedFormItemProps if present
|
||||||
if (mergedFormItemProps.name) {
|
if (mergedFormItemProps.name) {
|
||||||
delete mergedFormItemProps.name
|
delete mergedFormItemProps.name
|
||||||
@ -759,17 +657,6 @@ const ObjectProperty = ({
|
|||||||
{...inputProps}
|
{...inputProps}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case 'minMax':
|
|
||||||
return (
|
|
||||||
<MinMaxInput
|
|
||||||
prefix={prefix}
|
|
||||||
suffix={suffix}
|
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
step={step}
|
|
||||||
{...inputProps}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'secret':
|
case 'secret':
|
||||||
return (
|
return (
|
||||||
<Input.Password
|
<Input.Password
|
||||||
@ -864,26 +751,6 @@ const ObjectProperty = ({
|
|||||||
{...inputProps}
|
{...inputProps}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
case 'numberList':
|
|
||||||
return (
|
|
||||||
<ArrayInput
|
|
||||||
numeric
|
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
step={step}
|
|
||||||
placeholder={label}
|
|
||||||
disabled={disabled}
|
|
||||||
{...inputProps}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'stringList':
|
|
||||||
return (
|
|
||||||
<ArrayInput
|
|
||||||
placeholder={label}
|
|
||||||
disabled={disabled}
|
|
||||||
{...inputProps}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'text':
|
case 'text':
|
||||||
return <Input placeholder={label} {...inputProps} />
|
return <Input placeholder={label} {...inputProps} />
|
||||||
case 'codeBlock':
|
case 'codeBlock':
|
||||||
@ -964,11 +831,9 @@ const ObjectProperty = ({
|
|||||||
<ObjectChildTable
|
<ObjectChildTable
|
||||||
name={name}
|
name={name}
|
||||||
properties={properties}
|
properties={properties}
|
||||||
columns={columns}
|
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
rollups={rollups}
|
rollups={rollups}
|
||||||
hiddenPropertyWidth={hiddenPropertyWidth}
|
|
||||||
size={size}
|
size={size}
|
||||||
canAddRemove={canAddRemove}
|
canAddRemove={canAddRemove}
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
@ -1036,9 +901,7 @@ ObjectProperty.propTypes = {
|
|||||||
rollups: PropTypes.arrayOf(PropTypes.object),
|
rollups: PropTypes.arrayOf(PropTypes.object),
|
||||||
canAddRemove: PropTypes.bool,
|
canAddRemove: PropTypes.bool,
|
||||||
showCard: PropTypes.bool,
|
showCard: PropTypes.bool,
|
||||||
showDownload: PropTypes.bool,
|
style: PropTypes.object
|
||||||
style: PropTypes.object,
|
|
||||||
hiddenPropertyWidth: PropTypes.string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectProperty
|
export default ObjectProperty
|
||||||
|
|||||||
@ -217,14 +217,7 @@ const ObjectSelect = ({
|
|||||||
if (pIdx >= properties.length) {
|
if (pIdx >= properties.length) {
|
||||||
return data.map((object) => {
|
return data.map((object) => {
|
||||||
setObjectList((prev) => {
|
setObjectList((prev) => {
|
||||||
if (
|
if (prev.some((p) => p._id === object._id)) return prev
|
||||||
prev.some(
|
|
||||||
(p) =>
|
|
||||||
p._id.toString().toLowerCase() ===
|
|
||||||
object._id.toString().toLowerCase()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return prev
|
|
||||||
return [...prev, object]
|
return [...prev, object]
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
@ -242,8 +235,8 @@ const ObjectSelect = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
value: object._id.toString().toLowerCase(),
|
value: object._id,
|
||||||
key: object._id.toString().toLowerCase(),
|
key: object._id,
|
||||||
isLeaf: true,
|
isLeaf: true,
|
||||||
parentKeys,
|
parentKeys,
|
||||||
filterPath
|
filterPath
|
||||||
|
|||||||
@ -106,9 +106,7 @@ const ObjectTable = forwardRef(
|
|||||||
size = 'middle',
|
size = 'middle',
|
||||||
onStateChange,
|
onStateChange,
|
||||||
showFilterSidebar = false,
|
showFilterSidebar = false,
|
||||||
expandHeight = false,
|
expandHeight = false
|
||||||
showActions = true,
|
|
||||||
onRowAction
|
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
@ -188,17 +186,14 @@ const ObjectTable = forwardRef(
|
|||||||
const rowActions =
|
const rowActions =
|
||||||
model.actions?.filter((action) => action.row == true) || []
|
model.actions?.filter((action) => action.row == true) || []
|
||||||
|
|
||||||
const createSkeletonData = useCallback(
|
const createSkeletonData = useCallback((pageNum) => {
|
||||||
(pageNum) => {
|
|
||||||
return Array(pageSize)
|
return Array(pageSize)
|
||||||
.fill(null)
|
.fill(null)
|
||||||
.map((_, index) => ({
|
.map((_, index) => ({
|
||||||
_id: `skeleton-${pageNum}-${index}`,
|
_id: `skeleton-${pageNum}-${index}`,
|
||||||
isSkeleton: true
|
isSkeleton: true
|
||||||
}))
|
}))
|
||||||
},
|
}, [pageSize])
|
||||||
[pageSize]
|
|
||||||
)
|
|
||||||
|
|
||||||
const createSkeletonPage = useCallback(
|
const createSkeletonPage = useCallback(
|
||||||
(pageNum) => ({
|
(pageNum) => ({
|
||||||
@ -241,12 +236,6 @@ const ObjectTable = forwardRef(
|
|||||||
type={'text'}
|
type={'text'}
|
||||||
size={'small'}
|
size={'small'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (
|
|
||||||
onRowAction &&
|
|
||||||
onRowAction(action, objectData) !== false
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (action.url) {
|
if (action.url) {
|
||||||
navigate(action.url(objectData._id))
|
navigate(action.url(objectData._id))
|
||||||
}
|
}
|
||||||
@ -325,7 +314,9 @@ const ObjectTable = forwardRef(
|
|||||||
hasMore: result.hasMore
|
hasMore: result.hasMore
|
||||||
}
|
}
|
||||||
setPages((prev) =>
|
setPages((prev) =>
|
||||||
prev.map((page) => (page.pageNum === pageNum ? loadedPage : page))
|
prev.map((page) =>
|
||||||
|
page.pageNum === pageNum ? loadedPage : page
|
||||||
|
)
|
||||||
)
|
)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
return result.data || []
|
return result.data || []
|
||||||
@ -339,9 +330,9 @@ const ObjectTable = forwardRef(
|
|||||||
|
|
||||||
const findRenderedRow = useCallback((scrollTarget, id) => {
|
const findRenderedRow = useCallback((scrollTarget, id) => {
|
||||||
if (!scrollTarget || id == null) return null
|
if (!scrollTarget || id == null) return null
|
||||||
return Array.from(scrollTarget.querySelectorAll('tr[data-row-key]')).find(
|
return Array.from(
|
||||||
(row) => row.getAttribute('data-row-key') === String(id)
|
scrollTarget.querySelectorAll('tr[data-row-key]')
|
||||||
)
|
).find((row) => row.getAttribute('data-row-key') === String(id))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const captureScrollAnchor = useCallback(
|
const captureScrollAnchor = useCallback(
|
||||||
@ -369,7 +360,9 @@ const ObjectTable = forwardRef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const placeholderAnchor = closestRow(placeholderPage.items)
|
const placeholderAnchor = closestRow(placeholderPage.items)
|
||||||
const loadedPages = currentPages.filter((page) => !page.isSkeletonPage)
|
const loadedPages = currentPages.filter(
|
||||||
|
(page) => !page.isSkeletonPage
|
||||||
|
)
|
||||||
const retainedPage =
|
const retainedPage =
|
||||||
direction === 'previous'
|
direction === 'previous'
|
||||||
? loadedPages[0]
|
? loadedPages[0]
|
||||||
@ -387,7 +380,8 @@ const ObjectTable = forwardRef(
|
|||||||
(scrollTarget, anchor, loadedPage) => {
|
(scrollTarget, anchor, loadedPage) => {
|
||||||
if (!scrollTarget || !anchor) return
|
if (!scrollTarget || !anchor) return
|
||||||
|
|
||||||
const loadedItem = loadedPage.items[anchor.placeholderAnchor?.index]
|
const loadedItem =
|
||||||
|
loadedPage.items[anchor.placeholderAnchor?.index]
|
||||||
const anchorId = loadedItem?._id ?? anchor.fallbackAnchor?.id
|
const anchorId = loadedItem?._id ?? anchor.fallbackAnchor?.id
|
||||||
const previousTop = loadedItem
|
const previousTop = loadedItem
|
||||||
? anchor.placeholderAnchor?.top
|
? anchor.placeholderAnchor?.top
|
||||||
@ -469,7 +463,11 @@ const ObjectTable = forwardRef(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[captureScrollAnchor, createPageWindow, fetchPage]
|
[
|
||||||
|
captureScrollAnchor,
|
||||||
|
createPageWindow,
|
||||||
|
fetchPage
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
const loadNextPage = useCallback(
|
const loadNextPage = useCallback(
|
||||||
@ -728,7 +726,10 @@ const ObjectTable = forwardRef(
|
|||||||
]
|
]
|
||||||
|
|
||||||
if (firstResult.hasMore) {
|
if (firstResult.hasMore) {
|
||||||
setPages([...loadedPages, createSkeletonPage(pageNum + 1)])
|
setPages([
|
||||||
|
...loadedPages,
|
||||||
|
createSkeletonPage(pageNum + 1)
|
||||||
|
])
|
||||||
const secondResult = await fetchPage(pageNum + 1)
|
const secondResult = await fetchPage(pageNum + 1)
|
||||||
loadedPages.push({
|
loadedPages.push({
|
||||||
pageNum: pageNum + 1,
|
pageNum: pageNum + 1,
|
||||||
@ -1003,11 +1004,7 @@ const ObjectTable = forwardRef(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (
|
if (rowActions.length > 0 && tableData.some((item) => !item?.isSkeleton)) {
|
||||||
showActions &&
|
|
||||||
rowActions.length > 0 &&
|
|
||||||
tableData.some((item) => !item?.isSkeleton)
|
|
||||||
) {
|
|
||||||
columnsWithSkeleton.push({
|
columnsWithSkeleton.push({
|
||||||
title: (
|
title: (
|
||||||
<Flex gap='small' align='center' justify='center'>
|
<Flex gap='small' align='center' justify='center'>
|
||||||
@ -1188,9 +1185,7 @@ ObjectTable.propTypes = {
|
|||||||
size: PropTypes.string,
|
size: PropTypes.string,
|
||||||
onStateChange: PropTypes.func,
|
onStateChange: PropTypes.func,
|
||||||
showFilterSidebar: PropTypes.bool,
|
showFilterSidebar: PropTypes.bool,
|
||||||
expandHeight: PropTypes.bool,
|
expandHeight: PropTypes.bool
|
||||||
showActions: PropTypes.bool,
|
|
||||||
onRowAction: PropTypes.func
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectTable
|
export default ObjectTable
|
||||||
|
|||||||
@ -9,15 +9,13 @@ import {
|
|||||||
Divider,
|
Divider,
|
||||||
Progress,
|
Progress,
|
||||||
Button,
|
Button,
|
||||||
Dropdown,
|
Dropdown
|
||||||
Timeline
|
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import NewObjectButtons from './NewObjectButtons'
|
import NewObjectButtons from './NewObjectButtons'
|
||||||
|
|
||||||
const { Title } = Typography
|
const { Title } = Typography
|
||||||
|
|
||||||
const WizardView = ({
|
const WizardView = ({
|
||||||
sizeBarWidth = '160px',
|
|
||||||
showSteps = true,
|
showSteps = true,
|
||||||
steps,
|
steps,
|
||||||
title = 'Wizard View',
|
title = 'Wizard View',
|
||||||
@ -29,76 +27,22 @@ const WizardView = ({
|
|||||||
progress = 0,
|
progress = 0,
|
||||||
actions = [],
|
actions = [],
|
||||||
sideBarGrow = false,
|
sideBarGrow = false,
|
||||||
disabled = false,
|
disabled = false
|
||||||
showButtons = true,
|
|
||||||
showTitle = true,
|
|
||||||
currentStep: controlledCurrentStep,
|
|
||||||
currentSubStep = 0,
|
|
||||||
onStepChange,
|
|
||||||
showSkipButton = false,
|
|
||||||
onSkip = () => {}
|
|
||||||
}) => {
|
}) => {
|
||||||
const [internalCurrentStep, setInternalCurrentStep] = useState(0)
|
const [currentStep, setCurrentStep] = useState(0)
|
||||||
const currentStep = controlledCurrentStep ?? internalCurrentStep
|
|
||||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
|
|
||||||
const setCurrentStep = (step) => {
|
|
||||||
if (controlledCurrentStep === undefined) {
|
|
||||||
setInternalCurrentStep(step)
|
|
||||||
}
|
|
||||||
onStepChange?.(step)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap='middle' style={{ width: '100%' }}>
|
<Flex gap='middle' style={{ width: '100%' }}>
|
||||||
{!isMobile && showSteps == true ? (
|
{!isMobile && showSteps == true ? (
|
||||||
sideBar != null ? (
|
sideBar != null ? (
|
||||||
sideBar
|
sideBar
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div style={{ minWidth: sideBarGrow == true ? '100%' : '160px' }}>
|
||||||
style={{ minWidth: sideBarGrow == true ? '100%' : sizeBarWidth }}
|
|
||||||
>
|
|
||||||
<Steps
|
<Steps
|
||||||
current={currentStep}
|
current={currentStep}
|
||||||
items={steps.map(({ subSteps, ...step }, index) => ({
|
items={steps.map((step, index) => ({
|
||||||
...step,
|
...step,
|
||||||
description: subSteps ? (
|
|
||||||
<Timeline
|
|
||||||
className='subSteps'
|
|
||||||
items={subSteps.map((subStep, subStepIndex) => {
|
|
||||||
const complete =
|
|
||||||
index < currentStep ||
|
|
||||||
(index === currentStep && subStepIndex < currentSubStep)
|
|
||||||
const current =
|
|
||||||
index === currentStep && subStepIndex === currentSubStep
|
|
||||||
|
|
||||||
const disabled = index < currentStep
|
|
||||||
return {
|
|
||||||
...subStep,
|
|
||||||
children: subStep.title,
|
|
||||||
dot: complete ? (
|
|
||||||
<div
|
|
||||||
className={`subSteps-dot ${disabled ? 'subSteps-dot-disabled' : ''}`}
|
|
||||||
>
|
|
||||||
<CheckIcon
|
|
||||||
style={{
|
|
||||||
fontSize: '5px',
|
|
||||||
marginTop: '0px',
|
|
||||||
marginRight: '0px',
|
|
||||||
color: disabled
|
|
||||||
? 'var(--color-primary)'
|
|
||||||
: 'var(--layout-modal-bg)'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : undefined,
|
|
||||||
color: complete ? 'blue' : current ? 'blue' : 'gray'
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
step.description
|
|
||||||
),
|
|
||||||
icon:
|
icon:
|
||||||
index < currentStep ? (
|
index < currentStep ? (
|
||||||
<div className='ant-steps-item-icon'>
|
<div className='ant-steps-item-icon'>
|
||||||
@ -132,27 +76,13 @@ const WizardView = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Flex vertical gap='middle' style={{ flexGrow: 1, width: '100%' }}>
|
<Flex vertical gap='middle' style={{ flexGrow: 1, width: '100%' }}>
|
||||||
{showTitle ? (
|
|
||||||
<Title level={2} style={{ margin: '0 0 8px 0', lineHeight: 0.7 }}>
|
<Title level={2} style={{ margin: '0 0 8px 0', lineHeight: 0.7 }}>
|
||||||
{title}
|
{title}
|
||||||
</Title>
|
</Title>
|
||||||
) : null}
|
|
||||||
<div style={{ minHeight: '260px', marginBottom: 4, width: '100%' }}>
|
<div style={{ minHeight: '260px', marginBottom: 4, width: '100%' }}>
|
||||||
{steps[currentStep].content}
|
{steps[currentStep].content}
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
{showButtons || progress > 0 || actions.length > 0 ? (
|
|
||||||
<Flex
|
|
||||||
gap={'middle'}
|
|
||||||
align='center'
|
|
||||||
justify={showSkipButton ? 'space-between' : 'end'}
|
|
||||||
>
|
|
||||||
{showSkipButton && showButtons && (
|
|
||||||
<Button onClick={onSkip} disabled={disabled}>
|
|
||||||
Skip
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Flex gap={'middle'} align='center' justify='end'>
|
<Flex gap={'middle'} align='center' justify='end'>
|
||||||
{progress > 0 ? (
|
{progress > 0 ? (
|
||||||
<Progress
|
<Progress
|
||||||
@ -165,10 +95,7 @@ const WizardView = ({
|
|||||||
if (action.steps.includes(steps[currentStep].key)) {
|
if (action.steps.includes(steps[currentStep].key)) {
|
||||||
if (action.children) {
|
if (action.children) {
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown menu={{ items: action.children }} key={action.key}>
|
||||||
menu={{ items: action.children }}
|
|
||||||
key={action.key}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
onClick={action?.onClick}
|
onClick={action?.onClick}
|
||||||
disabled={action?.disabled || disabled}
|
disabled={action?.disabled || disabled}
|
||||||
@ -193,23 +120,19 @@ const WizardView = ({
|
|||||||
return null
|
return null
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{showButtons && (
|
|
||||||
<NewObjectButtons
|
<NewObjectButtons
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
currentStep={currentStep}
|
currentStep={currentStep}
|
||||||
totalSteps={steps.length}
|
totalSteps={steps.length}
|
||||||
onPrevious={() => setCurrentStep(currentStep - 1)}
|
onPrevious={() => setCurrentStep((prev) => prev - 1)}
|
||||||
onNext={() => setCurrentStep(currentStep + 1)}
|
onNext={() => setCurrentStep((prev) => prev + 1)}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
formValid={formValid}
|
formValid={formValid}
|
||||||
submitLoading={loading}
|
submitLoading={loading}
|
||||||
submitText={submitText}
|
submitText={submitText}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
) : null}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -226,15 +149,7 @@ WizardView.propTypes = {
|
|||||||
submitText: PropTypes.string,
|
submitText: PropTypes.string,
|
||||||
progress: PropTypes.number,
|
progress: PropTypes.number,
|
||||||
actions: PropTypes.array,
|
actions: PropTypes.array,
|
||||||
sideBarGrow: PropTypes.bool,
|
sideBarGrow: PropTypes.bool
|
||||||
showButtons: PropTypes.bool,
|
|
||||||
showTitle: PropTypes.bool,
|
|
||||||
currentStep: PropTypes.number,
|
|
||||||
currentSubStep: PropTypes.number,
|
|
||||||
onStepChange: PropTypes.func,
|
|
||||||
showSkipButton: PropTypes.bool,
|
|
||||||
onSkip: PropTypes.func,
|
|
||||||
sizeBarWidth: PropTypes.string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WizardView
|
export default WizardView
|
||||||
|
|||||||
@ -69,7 +69,6 @@ const AuthProvider = ({ children }) => {
|
|||||||
const [userProfile, setUserProfile] = useState(null)
|
const [userProfile, setUserProfile] = useState(null)
|
||||||
const [profileImageUrl, setProfileImageUrl] = useState(null)
|
const [profileImageUrl, setProfileImageUrl] = useState(null)
|
||||||
const profileImageUrlRef = useRef(null)
|
const profileImageUrlRef = useRef(null)
|
||||||
const processedAuthCodeRef = useRef(null)
|
|
||||||
const [showSessionExpiredModal, setShowSessionExpiredModal] = useState(false)
|
const [showSessionExpiredModal, setShowSessionExpiredModal] = useState(false)
|
||||||
const [showUnauthorizedModal, setShowUnauthorizedModal] = useState(false)
|
const [showUnauthorizedModal, setShowUnauthorizedModal] = useState(false)
|
||||||
const [showAuthErrorModal, setShowAuthErrorModal] = useState(false)
|
const [showAuthErrorModal, setShowAuthErrorModal] = useState(false)
|
||||||
@ -166,8 +165,6 @@ const AuthProvider = ({ children }) => {
|
|||||||
// Read token from cookies (web) or electron session storage if present
|
// Read token from cookies (web) or electron session storage if present
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
const authCode =
|
|
||||||
new URLSearchParams(location.search).get('authCode') || null
|
|
||||||
|
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
try {
|
try {
|
||||||
@ -230,9 +227,7 @@ const AuthProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// An auth code represents a newly completed login and must take precedence
|
if (location.pathname === '/applaunch') {
|
||||||
// over any existing persisted session.
|
|
||||||
if (location.pathname === '/applaunch' || authCode != null) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,7 +431,6 @@ const AuthProvider = ({ children }) => {
|
|||||||
setExpiresAt(nextExpiresAt)
|
setExpiresAt(nextExpiresAt)
|
||||||
setUserProfile(nextUser)
|
setUserProfile(nextUser)
|
||||||
setAuthenticated(true)
|
setAuthenticated(true)
|
||||||
setShowUnauthorizedModal(false)
|
|
||||||
|
|
||||||
// Persist session (cookies on web, electron storage on desktop)
|
// Persist session (cookies on web, electron storage on desktop)
|
||||||
const persisted = await persistSession({
|
const persisted = await persistSession({
|
||||||
@ -475,7 +469,6 @@ const AuthProvider = ({ children }) => {
|
|||||||
setAuthenticated(false)
|
setAuthenticated(false)
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
setRetreivedTokenFromCookies(true)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@ -730,11 +723,7 @@ const AuthProvider = ({ children }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const authCode =
|
const authCode =
|
||||||
new URLSearchParams(location.search).get('authCode') || null
|
new URLSearchParams(location.search).get('authCode') || null
|
||||||
if (
|
if (authCode != null) {
|
||||||
authCode != null &&
|
|
||||||
processedAuthCodeRef.current !== authCode
|
|
||||||
) {
|
|
||||||
processedAuthCodeRef.current = authCode
|
|
||||||
getLoginToken(authCode)
|
getLoginToken(authCode)
|
||||||
} else if (
|
} else if (
|
||||||
token == null &&
|
token == null &&
|
||||||
@ -811,12 +800,7 @@ const AuthProvider = ({ children }) => {
|
|||||||
Please log in to continue
|
Please log in to continue
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
open={
|
open={showUnauthorizedModal && !loading && !showAuthErrorModal}
|
||||||
showUnauthorizedModal &&
|
|
||||||
!authenticated &&
|
|
||||||
!loading &&
|
|
||||||
!showAuthErrorModal
|
|
||||||
}
|
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
setShowUnauthorizedModal(false)
|
setShowUnauthorizedModal(false)
|
||||||
loginWithSSO()
|
loginWithSSO()
|
||||||
|
|||||||
@ -12,8 +12,6 @@ const ThemeContext = createContext()
|
|||||||
|
|
||||||
const COLORS = {
|
const COLORS = {
|
||||||
colorPrimary: '#0091FF',
|
colorPrimary: '#0091FF',
|
||||||
colorPrimaryDisabledDark: '#0e3a5b',
|
|
||||||
colorPrimaryDisabledLight: '#e6f8ff',
|
|
||||||
colorSuccess: '#30D158',
|
colorSuccess: '#30D158',
|
||||||
colorWarning: '#FF9230',
|
colorWarning: '#FF9230',
|
||||||
colorError: '#FF4245',
|
colorError: '#FF4245',
|
||||||
@ -40,8 +38,6 @@ export const ThemeProvider = ({ children }) => {
|
|||||||
return savedCompact ? JSON.parse(savedCompact) : false
|
return savedCompact ? JSON.parse(savedCompact) : false
|
||||||
})
|
})
|
||||||
|
|
||||||
const [primaryColorOverride, setPrimaryColorOverride] = useState(null)
|
|
||||||
|
|
||||||
// System theme detection
|
// System theme detection
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||||
@ -128,16 +124,7 @@ export const ThemeProvider = ({ children }) => {
|
|||||||
// Set CSS custom properties for theme colors
|
// Set CSS custom properties for theme colors
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const root = document.documentElement
|
const root = document.documentElement
|
||||||
root.style.setProperty(
|
root.style.setProperty('--color-primary', COLORS.colorPrimary)
|
||||||
'--color-primary',
|
|
||||||
primaryColorOverride == null ? COLORS.colorPrimary : primaryColorOverride
|
|
||||||
)
|
|
||||||
root.style.setProperty(
|
|
||||||
'--color-primary-disabled',
|
|
||||||
isDarkMode
|
|
||||||
? COLORS.colorPrimaryDisabledDark
|
|
||||||
: COLORS.colorPrimaryDisabledLight
|
|
||||||
)
|
|
||||||
root.style.setProperty('--color-success', COLORS.colorSuccess)
|
root.style.setProperty('--color-success', COLORS.colorSuccess)
|
||||||
root.style.setProperty('--color-warning', COLORS.colorWarning)
|
root.style.setProperty('--color-warning', COLORS.colorWarning)
|
||||||
root.style.setProperty('--color-error', COLORS.colorError)
|
root.style.setProperty('--color-error', COLORS.colorError)
|
||||||
@ -152,20 +139,12 @@ export const ThemeProvider = ({ children }) => {
|
|||||||
'--layout-header-bg',
|
'--layout-header-bg',
|
||||||
isDarkMode ? '#141414' : '#ffffff'
|
isDarkMode ? '#141414' : '#ffffff'
|
||||||
)
|
)
|
||||||
root.style.setProperty(
|
}, [isDarkMode])
|
||||||
'--layout-modal-bg',
|
|
||||||
isDarkMode ? '#1f1f1f' : '#ffffff'
|
|
||||||
)
|
|
||||||
}, [isDarkMode, primaryColorOverride])
|
|
||||||
|
|
||||||
const themeConfig = {
|
const themeConfig = {
|
||||||
algorithm: getThemeAlgorithm(),
|
algorithm: getThemeAlgorithm(),
|
||||||
token: {
|
token: {
|
||||||
...COLORS,
|
...COLORS,
|
||||||
colorPrimary:
|
|
||||||
primaryColorOverride == null
|
|
||||||
? COLORS.colorPrimary
|
|
||||||
: primaryColorOverride,
|
|
||||||
borderRadius: '12px'
|
borderRadius: '12px'
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
@ -187,7 +166,6 @@ export const ThemeProvider = ({ children }) => {
|
|||||||
setThemeMode,
|
setThemeMode,
|
||||||
setDensityMode,
|
setDensityMode,
|
||||||
getColors,
|
getColors,
|
||||||
setPrimaryColorOverride,
|
|
||||||
themeConfig
|
themeConfig
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
import Icon from '@ant-design/icons'
|
|
||||||
import CustomIconSvg from '../../../assets/icons/filamentprofileicon.svg?react'
|
|
||||||
|
|
||||||
const FilamentProfileIcon = (props) => (
|
|
||||||
<Icon component={CustomIconSvg} {...props} />
|
|
||||||
)
|
|
||||||
|
|
||||||
export default FilamentProfileIcon
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import Icon from '@ant-design/icons'
|
|
||||||
import CustomIconSvg from '../../../assets/icons/hotendicon.svg?react'
|
|
||||||
|
|
||||||
const HotEndIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
|
||||||
|
|
||||||
export default HotEndIcon
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import Icon from '@ant-design/icons'
|
|
||||||
import CustomIconSvg from '../../../assets/icons/multimotionicon.svg?react'
|
|
||||||
|
|
||||||
const MultiMotionIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
|
||||||
|
|
||||||
export default MultiMotionIcon
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import Icon from '@ant-design/icons'
|
|
||||||
import CustomIconSvg from '../../../assets/icons/pictureicon.svg?react'
|
|
||||||
|
|
||||||
const PictureIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
|
||||||
|
|
||||||
export default PictureIcon
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import Icon from '@ant-design/icons'
|
|
||||||
import CustomIconSvg from '../../../assets/icons/printerprofileicon.svg?react'
|
|
||||||
|
|
||||||
const PrinterProfileIcon = (props) => (
|
|
||||||
<Icon component={CustomIconSvg} {...props} />
|
|
||||||
)
|
|
||||||
|
|
||||||
export default PrinterProfileIcon
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import Icon from '@ant-design/icons'
|
|
||||||
import CustomIconSvg from '../../../assets/icons/rulericon.svg?react'
|
|
||||||
|
|
||||||
const RulerIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
|
||||||
|
|
||||||
export default RulerIcon
|
|
||||||
@ -1,7 +1,5 @@
|
|||||||
import ProductionIcon from './ProductionIcon'
|
import ProductionIcon from './ProductionIcon'
|
||||||
import PrinterIcon from './PrinterIcon'
|
import PrinterIcon from './PrinterIcon'
|
||||||
import PrinterProfileIcon from './PrinterProfileIcon'
|
|
||||||
import FilamentProfileIcon from './FilamentProfileIcon'
|
|
||||||
import JobIcon from './JobIcon'
|
import JobIcon from './JobIcon'
|
||||||
import GCodeFileIcon from './GCodeFileIcon'
|
import GCodeFileIcon from './GCodeFileIcon'
|
||||||
import SubJobIcon from './SubJobIcon'
|
import SubJobIcon from './SubJobIcon'
|
||||||
@ -57,8 +55,6 @@ const toEmoji = (emoji) => <span aria-hidden>{emoji}</span>
|
|||||||
const sidebarIconMap = {
|
const sidebarIconMap = {
|
||||||
production: <ProductionIcon />,
|
production: <ProductionIcon />,
|
||||||
printer: <PrinterIcon />,
|
printer: <PrinterIcon />,
|
||||||
printerProfile: <PrinterProfileIcon />,
|
|
||||||
filamentProfile: <FilamentProfileIcon />,
|
|
||||||
job: <JobIcon />,
|
job: <JobIcon />,
|
||||||
subJob: <SubJobIcon />,
|
subJob: <SubJobIcon />,
|
||||||
gcodeFile: <GCodeFileIcon />,
|
gcodeFile: <GCodeFileIcon />,
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
import { Printer } from './models/Printer.js'
|
import { Printer } from './models/Printer.js'
|
||||||
import { PrinterProfile } from './models/PrinterProfile.js'
|
|
||||||
import { FilamentProfile } from './models/FilamentProfile.js'
|
|
||||||
import { Host } from './models/Host.js'
|
import { Host } from './models/Host.js'
|
||||||
import { Filament } from './models/Filament.js'
|
import { Filament } from './models/Filament.js'
|
||||||
import { Material } from './models/Material.js'
|
import { Material } from './models/Material.js'
|
||||||
@ -51,8 +49,6 @@ import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
|
|||||||
|
|
||||||
export const objectModels = [
|
export const objectModels = [
|
||||||
Printer,
|
Printer,
|
||||||
PrinterProfile,
|
|
||||||
FilamentProfile,
|
|
||||||
Host,
|
Host,
|
||||||
Filament,
|
Filament,
|
||||||
FilamentSku,
|
FilamentSku,
|
||||||
@ -104,8 +100,6 @@ export const objectModels = [
|
|||||||
// Re-export individual models for direct access
|
// Re-export individual models for direct access
|
||||||
export {
|
export {
|
||||||
Printer,
|
Printer,
|
||||||
PrinterProfile,
|
|
||||||
FilamentProfile,
|
|
||||||
Host,
|
Host,
|
||||||
Filament,
|
Filament,
|
||||||
FilamentSku,
|
FilamentSku,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -144,14 +144,9 @@ export const GCodeFile = {
|
|||||||
label: 'Name',
|
label: 'Name',
|
||||||
columnFixed: 'left',
|
columnFixed: 'left',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
value: null,
|
||||||
required: true,
|
required: true,
|
||||||
columnWidth: 200,
|
columnWidth: 200
|
||||||
value: (objectData) => {
|
|
||||||
if (objectData?.name == undefined) {
|
|
||||||
return objectData?.file?.name
|
|
||||||
}
|
|
||||||
return objectData?.name
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'file',
|
name: 'file',
|
||||||
|
|||||||
@ -1,665 +0,0 @@
|
|||||||
import PrinterProfileIcon from '../../components/Icons/PrinterProfileIcon'
|
|
||||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
|
||||||
import EditIcon from '../../components/Icons/EditIcon'
|
|
||||||
import CheckIcon from '../../components/Icons/CheckIcon'
|
|
||||||
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
|
||||||
import BinIcon from '../../components/Icons/BinIcon'
|
|
||||||
|
|
||||||
const THUMBNAIL_PROPERTIES = [
|
|
||||||
{
|
|
||||||
name: 'width',
|
|
||||||
label: 'Width',
|
|
||||||
type: 'number',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'height',
|
|
||||||
label: 'Height',
|
|
||||||
type: 'number',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 120
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const BED_EXCLUDE_AREA_PROPERTIES = [
|
|
||||||
{
|
|
||||||
name: 'x',
|
|
||||||
label: 'X',
|
|
||||||
type: 'number',
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'y',
|
|
||||||
label: 'Y',
|
|
||||||
type: 'number',
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 120
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const EXTRUDER_PROPERTIES = [
|
|
||||||
{
|
|
||||||
name: 'nozzleDiameter',
|
|
||||||
label: 'Nozzle Diameter',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.05,
|
|
||||||
columnWidth: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'nozzleVolume',
|
|
||||||
label: 'Nozzle Volume',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm³',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'nozzleType',
|
|
||||||
label: 'Nozzle Type',
|
|
||||||
type: 'text',
|
|
||||||
columnWidth: 160
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'minLayerHeight',
|
|
||||||
label: 'Min Layer Height',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 180
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'maxLayerHeight',
|
|
||||||
label: 'Max Layer Height',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 180
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'positionOffsetX',
|
|
||||||
label: 'Extruder Offset X',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 180
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'positionOffsetY',
|
|
||||||
label: 'Extruder Offset Y',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 180
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'retractionLength',
|
|
||||||
label: 'Retraction Length',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 170
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'retractionExtraLengthOnRestart',
|
|
||||||
label: 'Extra Length on Restart',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 210
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'retractionSpeed',
|
|
||||||
label: 'Retraction Speed',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm/s',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 170
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'deretractionSpeed',
|
|
||||||
label: 'Deretraction Speed',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm/s',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 180
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'retractionTravelDistanceThreshold',
|
|
||||||
label: 'Travel Distance Threshold',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 220
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'retractOnLayerChange',
|
|
||||||
label: 'Retract on Layer Change',
|
|
||||||
type: 'bool',
|
|
||||||
columnWidth: 210
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'wipeWhileRetracting',
|
|
||||||
label: 'Wipe While Retracting',
|
|
||||||
type: 'bool',
|
|
||||||
columnWidth: 190
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'wipeDistance',
|
|
||||||
label: 'Wipe Distance',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'retractAmountBeforeWipe',
|
|
||||||
label: 'Retract Amount Before Wipe',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 230
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'retractAmountAfterWipe',
|
|
||||||
label: 'Retract Amount After Wipe',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 230
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'zHopOnSurfaces',
|
|
||||||
label: 'Z-Hop On Surfaces',
|
|
||||||
type: 'select',
|
|
||||||
options: [
|
|
||||||
{ label: 'All Surfaces', value: 'All Surfaces' },
|
|
||||||
{ label: 'Top Only', value: 'Top Only' },
|
|
||||||
{ label: 'Bottom Only', value: 'Bottom Only' },
|
|
||||||
{ label: 'None', value: 'None' }
|
|
||||||
],
|
|
||||||
columnWidth: 170
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'zHopType',
|
|
||||||
label: 'Z-Hop Type',
|
|
||||||
type: 'select',
|
|
||||||
options: [
|
|
||||||
{ label: 'Auto', value: 'Auto' },
|
|
||||||
{ label: 'Normal', value: 'Normal' },
|
|
||||||
{ label: 'Spiral', value: 'Spiral' },
|
|
||||||
{ label: 'Slope', value: 'Slope' }
|
|
||||||
],
|
|
||||||
columnWidth: 140
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'zHopHeight',
|
|
||||||
label: 'Z-Hop Height',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'zHopTravelingAngle',
|
|
||||||
label: 'Traveling Angle',
|
|
||||||
type: 'number',
|
|
||||||
suffix: '°',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 160
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'zHopOnlyLiftZAbove',
|
|
||||||
label: 'Only Lift Z Above',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 170
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'zHopOnlyLiftZBelow',
|
|
||||||
label: 'Only Lift Z Below',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 170
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'materialSwitchRetractionLength',
|
|
||||||
label: 'Material Switch Retraction Length',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 250
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'materialSwitchExtraLengthOnRestart',
|
|
||||||
label: 'Material Switch Extra Length on Restart',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 290
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'longRetractionWhenCut',
|
|
||||||
label: 'Long Retraction When Cut (beta)',
|
|
||||||
type: 'bool',
|
|
||||||
columnWidth: 240
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
export const PrinterProfile = {
|
|
||||||
name: 'printerProfile',
|
|
||||||
label: 'Printer Profile',
|
|
||||||
labelPlural: 'Printer Profiles',
|
|
||||||
url: '/dashboard/production/printerprofiles',
|
|
||||||
prefix: 'PPF',
|
|
||||||
icon: PrinterProfileIcon,
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
name: 'info',
|
|
||||||
label: 'Info',
|
|
||||||
default: true,
|
|
||||||
row: true,
|
|
||||||
icon: InfoCircleIcon,
|
|
||||||
url: (_id) =>
|
|
||||||
`/dashboard/production/printerprofiles/info?printerProfileId=${_id}`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'edit',
|
|
||||||
label: 'Edit',
|
|
||||||
row: true,
|
|
||||||
icon: EditIcon,
|
|
||||||
url: (_id) =>
|
|
||||||
`/dashboard/production/printerprofiles/info?printerProfileId=${_id}&action=edit`,
|
|
||||||
visible: (objectData) => !objectData?._isEditing
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'finishEdit',
|
|
||||||
label: 'Save Edits',
|
|
||||||
icon: CheckIcon,
|
|
||||||
url: (_id) =>
|
|
||||||
`/dashboard/production/printerprofiles/info?printerProfileId=${_id}&action=finishEdit`,
|
|
||||||
visible: (objectData) => objectData?._isEditing === true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'cancelEdit',
|
|
||||||
label: 'Cancel Edits',
|
|
||||||
icon: XMarkIcon,
|
|
||||||
url: (_id) =>
|
|
||||||
`/dashboard/production/printerprofiles/info?printerProfileId=${_id}&action=cancelEdit`,
|
|
||||||
visible: (objectData) => objectData?._isEditing === true
|
|
||||||
},
|
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
|
||||||
name: 'delete',
|
|
||||||
label: 'Delete',
|
|
||||||
row: true,
|
|
||||||
icon: BinIcon,
|
|
||||||
danger: true,
|
|
||||||
url: (_id) =>
|
|
||||||
`/dashboard/production/printerprofiles/info?printerProfileId=${_id}&action=delete`
|
|
||||||
}
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
'_reference',
|
|
||||||
'name',
|
|
||||||
'printer',
|
|
||||||
'printBedWidth',
|
|
||||||
'printBedHeight',
|
|
||||||
'printableHeight',
|
|
||||||
'extruders.0.nozzleDiameter',
|
|
||||||
'extruders.0.nozzleType',
|
|
||||||
'zOffset',
|
|
||||||
'scanFirstLayer',
|
|
||||||
'auxiliaryFan',
|
|
||||||
'updatedAt'
|
|
||||||
],
|
|
||||||
filters: ['_id', 'name', 'printer'],
|
|
||||||
sorters: [
|
|
||||||
'name',
|
|
||||||
'printBedWidth',
|
|
||||||
'printBedHeight',
|
|
||||||
'printableHeight',
|
|
||||||
'zOffset',
|
|
||||||
'createdAt',
|
|
||||||
'updatedAt'
|
|
||||||
],
|
|
||||||
properties: [
|
|
||||||
{
|
|
||||||
name: '_id',
|
|
||||||
label: 'ID',
|
|
||||||
type: 'id',
|
|
||||||
objectType: 'printerProfile',
|
|
||||||
showCopy: true,
|
|
||||||
readOnly: true,
|
|
||||||
columnWidth: 140
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'createdAt',
|
|
||||||
label: 'Created At',
|
|
||||||
type: 'dateTime',
|
|
||||||
readOnly: true,
|
|
||||||
columnWidth: 175
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '_reference',
|
|
||||||
label: 'Reference',
|
|
||||||
type: 'reference',
|
|
||||||
objectType: 'printerProfile',
|
|
||||||
showCopy: true,
|
|
||||||
readOnly: true,
|
|
||||||
columnFixed: 'left',
|
|
||||||
columnWidth: 180
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'updatedAt',
|
|
||||||
label: 'Updated At',
|
|
||||||
type: 'dateTime',
|
|
||||||
readOnly: true,
|
|
||||||
columnWidth: 175
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
type: 'text',
|
|
||||||
required: true,
|
|
||||||
columnFixed: 'left',
|
|
||||||
columnWidth: 220,
|
|
||||||
value: (objectData) => {
|
|
||||||
if (objectData?.name == undefined) {
|
|
||||||
return objectData?.printer?.name
|
|
||||||
}
|
|
||||||
return objectData?.name
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'printer',
|
|
||||||
label: 'Printer',
|
|
||||||
type: 'object',
|
|
||||||
objectType: 'printer',
|
|
||||||
required: true,
|
|
||||||
showHyperlink: true,
|
|
||||||
columnWidth: 220
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'printBedWidth',
|
|
||||||
label: 'Print Bed Width',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 170
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'printBedHeight',
|
|
||||||
label: 'Print Bed Height',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 170
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'originX',
|
|
||||||
label: 'Origin X',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'originY',
|
|
||||||
label: 'Origin Y',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'bestObjectPositionX',
|
|
||||||
label: 'Best Object Position X',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'bestObjectPositionY',
|
|
||||||
label: 'Best Object Position Y',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'bedExcludeArea',
|
|
||||||
label: 'Excluded Bed Area',
|
|
||||||
type: 'objectChildren',
|
|
||||||
canAddRemove: true,
|
|
||||||
span: 2,
|
|
||||||
columns: ['x', 'y'],
|
|
||||||
properties: BED_EXCLUDE_AREA_PROPERTIES
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'printableHeight',
|
|
||||||
label: 'Printable Height',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 170
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'supportMultiBedTypes',
|
|
||||||
label: 'Support Multi Bed Types',
|
|
||||||
type: 'bool',
|
|
||||||
columnWidth: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'zOffset',
|
|
||||||
label: 'Z Offset',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 140
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'preferredOrientation',
|
|
||||||
label: 'Preferred Orientation',
|
|
||||||
type: 'number',
|
|
||||||
suffix: '°',
|
|
||||||
min: 0,
|
|
||||||
max: 360,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 190
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'extruders.0.nozzleDiameter',
|
|
||||||
label: 'Nozzle Diameter',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.05,
|
|
||||||
readOnly: true,
|
|
||||||
columnWidth: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'extruders.0.nozzleType',
|
|
||||||
label: 'Nozzle Type',
|
|
||||||
type: 'text',
|
|
||||||
readOnly: true,
|
|
||||||
columnWidth: 160
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'extruders',
|
|
||||||
label: 'Extruders',
|
|
||||||
type: 'objectChildren',
|
|
||||||
canAddRemove: true,
|
|
||||||
span: 2,
|
|
||||||
columns: [
|
|
||||||
'nozzleDiameter',
|
|
||||||
'nozzleType',
|
|
||||||
'nozzleVolume',
|
|
||||||
'minLayerHeight',
|
|
||||||
'maxLayerHeight',
|
|
||||||
'positionOffsetX',
|
|
||||||
'positionOffsetY'
|
|
||||||
],
|
|
||||||
hiddenPropertyWidth: 330,
|
|
||||||
properties: EXTRUDER_PROPERTIES
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'machineMaxAccelerationRetracting',
|
|
||||||
label: 'Max Retracting Accel',
|
|
||||||
type: 'minMax',
|
|
||||||
suffix: ' mm/s²',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 260
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'machineMaxSpeedE',
|
|
||||||
label: 'Max Extruder Speed',
|
|
||||||
type: 'minMax',
|
|
||||||
suffix: ' mm/s',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 220
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'machineMaxSpeedX',
|
|
||||||
label: 'Max X Speed',
|
|
||||||
type: 'minMax',
|
|
||||||
suffix: ' mm/s',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 190
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'machineMaxSpeedY',
|
|
||||||
label: 'Max Y Speed',
|
|
||||||
type: 'minMax',
|
|
||||||
suffix: ' mm/s',
|
|
||||||
min: 0,
|
|
||||||
step: 1,
|
|
||||||
columnWidth: 190
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'machinePauseGcode',
|
|
||||||
label: 'Pause G-code',
|
|
||||||
type: 'codeBlock',
|
|
||||||
language: 'gcode',
|
|
||||||
height: '180px',
|
|
||||||
span: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'machineStartGcode',
|
|
||||||
label: 'Start G-code',
|
|
||||||
type: 'codeBlock',
|
|
||||||
language: 'gcode',
|
|
||||||
height: '240px',
|
|
||||||
span: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'machineEndGcode',
|
|
||||||
label: 'End G-code',
|
|
||||||
type: 'codeBlock',
|
|
||||||
language: 'gcode',
|
|
||||||
height: '240px',
|
|
||||||
span: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'layerChangeGcode',
|
|
||||||
label: 'Layer Change G-code',
|
|
||||||
type: 'codeBlock',
|
|
||||||
language: 'gcode',
|
|
||||||
height: '180px',
|
|
||||||
span: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'beforeLayerChangeGcode',
|
|
||||||
label: 'Before Layer Change G-code',
|
|
||||||
type: 'codeBlock',
|
|
||||||
language: 'gcode',
|
|
||||||
height: '180px',
|
|
||||||
span: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'machineLoadFilamentTime',
|
|
||||||
label: 'Filament Load Time',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' s',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 180
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'scanFirstLayer',
|
|
||||||
label: 'Scan First Layer',
|
|
||||||
type: 'bool',
|
|
||||||
columnWidth: 150
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'machineUnloadFilamentTime',
|
|
||||||
label: 'Filament Unload Time',
|
|
||||||
type: 'number',
|
|
||||||
suffix: ' s',
|
|
||||||
min: 0,
|
|
||||||
step: 0.1,
|
|
||||||
columnWidth: 190
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'thumbnails',
|
|
||||||
label: 'Thumbnail Sizes',
|
|
||||||
type: 'objectChildren',
|
|
||||||
canAddRemove: true,
|
|
||||||
span: 2,
|
|
||||||
columns: ['width', 'height'],
|
|
||||||
properties: THUMBNAIL_PROPERTIES
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'auxiliaryFan',
|
|
||||||
label: 'Auxiliary Fan',
|
|
||||||
type: 'bool',
|
|
||||||
columnWidth: 140
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'printerNotes',
|
|
||||||
label: 'Printer Notes',
|
|
||||||
type: 'markdown',
|
|
||||||
span: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'machineMaxJunctionDeviation',
|
|
||||||
label: 'Max Junction Deviation',
|
|
||||||
type: 'minMax',
|
|
||||||
suffix: ' mm',
|
|
||||||
min: 0,
|
|
||||||
step: 0.01,
|
|
||||||
columnWidth: 240
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -24,24 +24,11 @@ const productionSidebarItems = [
|
|||||||
label: 'Sub Jobs',
|
label: 'Sub Jobs',
|
||||||
path: '/dashboard/production/subjobs'
|
path: '/dashboard/production/subjobs'
|
||||||
},
|
},
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
{
|
||||||
key: 'gcodeFiles',
|
key: 'gcodeFiles',
|
||||||
iconKey: 'gcodeFile',
|
iconKey: 'gcodeFile',
|
||||||
label: 'GCode Files',
|
label: 'GCode Files',
|
||||||
path: '/dashboard/production/gcodefiles'
|
path: '/dashboard/production/gcodefiles'
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'printerProfiles',
|
|
||||||
iconKey: 'printerProfile',
|
|
||||||
label: 'Printer Profiles',
|
|
||||||
path: '/dashboard/production/printerprofiles'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'filamentProfiles',
|
|
||||||
iconKey: 'filamentProfile',
|
|
||||||
label: 'Filament Profiles',
|
|
||||||
path: '/dashboard/production/filamentprofiles'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -13,20 +13,6 @@ const ControlPrinter = lazy(
|
|||||||
const PrinterInfo = lazy(
|
const PrinterInfo = lazy(
|
||||||
() => import('../components/Dashboard/Production/Printers/PrinterInfo.jsx')
|
() => import('../components/Dashboard/Production/Printers/PrinterInfo.jsx')
|
||||||
)
|
)
|
||||||
const PrinterProfiles = lazy(
|
|
||||||
() => import('../components/Dashboard/Production/PrinterProfiles.jsx')
|
|
||||||
)
|
|
||||||
const PrinterProfileInfo = lazy(
|
|
||||||
() =>
|
|
||||||
import('../components/Dashboard/Production/PrinterProfiles/PrinterProfileInfo.jsx')
|
|
||||||
)
|
|
||||||
const FilamentProfiles = lazy(
|
|
||||||
() => import('../components/Dashboard/Production/FilamentProfiles.jsx')
|
|
||||||
)
|
|
||||||
const FilamentProfileInfo = lazy(
|
|
||||||
() =>
|
|
||||||
import('../components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo.jsx')
|
|
||||||
)
|
|
||||||
const Jobs = lazy(() => import('../components/Dashboard/Production/Jobs.jsx'))
|
const Jobs = lazy(() => import('../components/Dashboard/Production/Jobs.jsx'))
|
||||||
const JobInfo = lazy(
|
const JobInfo = lazy(
|
||||||
() => import('../components/Dashboard/Production/Jobs/JobInfo.jsx')
|
() => import('../components/Dashboard/Production/Jobs/JobInfo.jsx')
|
||||||
@ -66,26 +52,6 @@ const ProductionRoutes = [
|
|||||||
path='production/printers/info'
|
path='production/printers/info'
|
||||||
element={<PrinterInfo />}
|
element={<PrinterInfo />}
|
||||||
/>,
|
/>,
|
||||||
<Route
|
|
||||||
key='printerprofiles'
|
|
||||||
path='production/printerprofiles'
|
|
||||||
element={<PrinterProfiles />}
|
|
||||||
/>,
|
|
||||||
<Route
|
|
||||||
key='printerprofiles-info'
|
|
||||||
path='production/printerprofiles/info'
|
|
||||||
element={<PrinterProfileInfo />}
|
|
||||||
/>,
|
|
||||||
<Route
|
|
||||||
key='filamentprofiles'
|
|
||||||
path='production/filamentprofiles'
|
|
||||||
element={<FilamentProfiles />}
|
|
||||||
/>,
|
|
||||||
<Route
|
|
||||||
key='filamentprofiles-info'
|
|
||||||
path='production/filamentprofiles/info'
|
|
||||||
element={<FilamentProfileInfo />}
|
|
||||||
/>,
|
|
||||||
<Route key='jobs' path='production/jobs' element={<Jobs />} />,
|
<Route key='jobs' path='production/jobs' element={<Jobs />} />,
|
||||||
<Route key='subjobs' path='production/subjobs' element={<SubJobs />} />,
|
<Route key='subjobs' path='production/subjobs' element={<SubJobs />} />,
|
||||||
<Route
|
<Route
|
||||||
|
|||||||
@ -6,18 +6,6 @@ const COOKIE_OPTIONS = {
|
|||||||
maxAge: 7 * 24 * 60 * 60 // 7 days in seconds
|
maxAge: 7 * 24 * 60 * 60 // 7 days in seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAuthExpiryTime = (expiresAt) => {
|
|
||||||
const numericExpiry = Number(expiresAt)
|
|
||||||
if (Number.isFinite(numericExpiry) && numericExpiry > 0) return numericExpiry
|
|
||||||
return new Date(expiresAt).getTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
const getAuthCookieMaxAge = (expiresAt) => {
|
|
||||||
const expiryTime = getAuthExpiryTime(expiresAt)
|
|
||||||
if (!Number.isFinite(expiryTime)) return COOKIE_OPTIONS.maxAge
|
|
||||||
return Math.max(Math.floor((expiryTime - Date.now()) / 1000), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a cookie with authentication data
|
* Set a cookie with authentication data
|
||||||
* @param {string} name - Cookie name
|
* @param {string} name - Cookie name
|
||||||
@ -149,10 +137,10 @@ export const validateAuthCookies = () => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = Date.now()
|
const now = new Date()
|
||||||
const expirationTime = getAuthExpiryTime(expiresAt)
|
const expirationDate = new Date(expiresAt)
|
||||||
|
|
||||||
if (!Number.isFinite(expirationTime) || expirationTime <= now) {
|
if (expirationDate <= now) {
|
||||||
// Cookies are expired, clean them up
|
// Cookies are expired, clean them up
|
||||||
clearAuthCookies()
|
clearAuthCookies()
|
||||||
return false
|
return false
|
||||||
@ -179,7 +167,9 @@ export const checkAuthCookiesExpiry = (minutesBeforeExpiry = 5) => {
|
|||||||
return { isExpiringSoon: false, timeRemaining: 0 }
|
return { isExpiringSoon: false, timeRemaining: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeRemaining = getAuthExpiryTime(expiresAt) - Date.now()
|
const now = new Date()
|
||||||
|
const expirationDate = new Date(expiresAt)
|
||||||
|
const timeRemaining = expirationDate - now
|
||||||
const minutesRemaining = timeRemaining / (1000 * 60)
|
const minutesRemaining = timeRemaining / (1000 * 60)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -250,21 +240,20 @@ export const setAuthCookies = (authData) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let success = true
|
let success = true
|
||||||
const maxAge = getAuthCookieMaxAge(authData.expires_at)
|
|
||||||
|
|
||||||
if (authData.access_token) {
|
if (authData.access_token) {
|
||||||
success =
|
success =
|
||||||
success &&
|
success &&
|
||||||
setCookie('authToken', authData.access_token, {
|
setCookie('authToken', authData.access_token, {
|
||||||
maxAge
|
maxAge: 7 * 24 * 60 * 60
|
||||||
})
|
}) // 7 days
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authData.expires_at) {
|
if (authData.expires_at) {
|
||||||
success =
|
success =
|
||||||
success &&
|
success &&
|
||||||
setCookie('authExpiresAt', authData.expires_at, {
|
setCookie('authExpiresAt', authData.expires_at, {
|
||||||
maxAge
|
maxAge: 7 * 24 * 60 * 60
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +267,7 @@ export const setAuthCookies = (authData) => {
|
|||||||
success =
|
success =
|
||||||
success &&
|
success &&
|
||||||
setCookie('user', JSON.stringify(userObject), {
|
setCookie('user', JSON.stringify(userObject), {
|
||||||
maxAge
|
maxAge: 7 * 24 * 60 * 60
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user