Compare commits
No commits in common. "84e98aa62684dfbf5be9b79b7c4f8b952e6aedda" and "24502dddfca61a334b3ff4dc11bd9fffece91b7f" have entirely different histories.
84e98aa626
...
24502dddfc
@ -1,9 +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.889695,0,0,0.889695,3,2.383378)">
|
||||
<path d="M0,33.052C0,45.309 6.996,56.09 16.234,61.178C20.659,63.638 24.162,57.594 19.732,55.013C12.395,50.857 7.182,42.577 7.182,33.052C7.182,18.948 18.465,7.622 32.569,7.622C46.673,7.622 58.03,18.948 58.03,33.052C58.03,35.325 59.805,36.728 61.717,36.728C63.565,36.728 65.191,35.303 65.191,33.052C65.191,15.177 50.444,0.462 32.569,0.462C14.726,0.462 0,15.177 0,33.052Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M13.589,33.042C13.589,39.892 17.302,45.715 21.556,48.454C25.359,50.937 28.902,45.774 25.59,43.286C22.321,41.043 20.176,37.305 20.176,33.042C20.176,26.121 25.701,20.596 32.579,20.596C39.458,20.596 44.753,26.1 45.025,32.804C45.127,34.798 46.618,36.042 48.354,36.042C50.099,36.042 51.612,34.718 51.612,32.764C51.612,22.599 43.022,14.009 32.579,14.009C22.178,14.009 13.589,22.599 13.589,33.042Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M47.066,65.874C48.64,65.243 49.395,63.472 48.712,61.887L44.195,51.408L50.357,51.285C51.793,51.304 52.485,49.84 51.462,48.796L34.618,31.611C33.647,30.639 32.221,31.207 32.19,32.582L31.859,56.934C31.817,58.42 33.513,58.935 34.483,57.946L38.738,53.572L43.036,64.259C43.647,65.781 45.481,66.537 47.066,65.874Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@ -1,13 +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.993756,0,0,0.993756,4.70027,3)">
|
||||
<path d="M21.917,58.364L7.977,58.364C2.598,58.364 0,56.673 0,53.004C0,45.534 8.171,35.818 21.434,33.487L21.434,38.985C11.428,41.086 5.926,48.099 5.926,52.158C5.926,52.741 6.224,52.965 6.958,52.965L21.434,52.965L21.434,54.927C21.434,56.202 21.603,57.347 21.917,58.364ZM22.29,28.195C17.315,25.985 13.813,20.695 13.813,14.529C13.813,6.508 19.979,0 27.481,0C35.04,0 41.156,6.405 41.156,14.478C41.156,17.712 40.199,20.706 38.573,23.144L30.955,23.144C30.844,23.144 30.734,23.146 30.625,23.148C33.464,21.732 35.494,18.445 35.494,14.478C35.494,9.257 31.873,5.399 27.481,5.399C23.126,5.399 19.474,9.334 19.474,14.524C19.474,19.579 22.791,23.478 26.82,23.866C25.711,24.303 24.771,24.923 23.999,25.691C23.298,26.389 22.72,27.223 22.29,28.195Z"/>
|
||||
<g transform="matrix(0.589451,0,0,0.589451,24.45243,26.163338)">
|
||||
<path d="M11.031,59.75L48.719,59.75C55.859,59.75 59.75,55.891 59.75,48.797L59.75,10.969C59.75,3.891 55.859,-0 48.719,-0L11.031,-0C3.906,-0 -0,3.891 -0,10.969L-0,48.797C-0,55.891 3.906,59.75 11.031,59.75ZM11.906,51.688C9.391,51.688 8.063,50.469 8.063,47.813L8.063,11.969C8.063,9.313 9.391,8.078 11.906,8.078L47.844,8.078C50.344,8.078 51.688,9.313 51.688,11.969L51.688,47.813C51.688,50.469 50.344,51.688 47.844,51.688L11.906,51.688Z" style="fill-rule:nonzero;"/>
|
||||
<g transform="matrix(0.570497,0,0,0.570497,16.522802,13.725853)">
|
||||
<path d="M6.499,56.236L41.124,56.236C44.294,56.236 46.809,54.281 46.809,50.825C46.809,47.443 44.391,45.464 41.124,45.464L18.117,45.464L18.117,45.062C22.049,43.001 23.774,38.335 23.774,33.596C23.774,32.751 23.669,32.057 23.534,31.399L37.509,31.399C39.584,31.399 41.06,30.032 41.06,28.125C41.06,26.241 39.584,24.905 37.509,24.905L22.134,24.905C21.788,23.604 21.265,21.585 21.265,19.395C21.265,13.243 26.441,10.758 32.681,10.758C34.774,10.758 36.433,10.942 37.882,11.247C38.957,11.394 40.282,11.543 41.544,11.543C44.007,11.543 46.13,10.334 46.13,7.354C46.13,5.297 45.211,3.871 43.447,2.745C39.934,0.628 34.458,0.378 30.367,0.378C18.274,0.378 7.903,5.899 7.903,17.414C7.903,19.396 8.212,21.38 9.112,24.905L3.574,24.905C1.5,24.905 0,26.241 0,28.125C0,30.071 1.514,31.399 3.574,31.399L10.467,31.399C10.73,32.486 10.797,33.332 10.797,34.157C10.797,38.814 8.74,42.769 5.065,44.806C3.057,46.118 0.808,47.931 0.808,50.875C0.808,54.298 3.219,56.236 6.499,56.236Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB |
@ -1,10 +1 @@
|
||||
<?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>
|
||||
<g transform="matrix(0.5364,0,0,0.5364,20.017716,17.05902)">
|
||||
<path d="M4.547,55.679L40.65,55.679C42.9,55.679 44.677,54.281 44.677,51.912C44.677,49.519 42.886,48.112 40.65,48.112L13.379,48.112L13.379,47.835C18.007,45.956 20.276,40.62 20.276,35.255C20.276,33.462 20.031,31.987 19.701,30.619L35.224,30.619C36.908,30.619 38.161,29.503 38.161,27.888C38.161,26.283 36.908,25.198 35.224,25.198L18.371,25.198C17.843,23.27 17.28,20.86 17.28,18.015C17.28,10.985 22.845,7.524 30.131,7.524C32.516,7.524 34.83,7.82 36.656,8.348C37.578,8.537 38.512,8.672 39.384,8.672C41.303,8.672 42.716,7.658 42.716,5.542C42.716,3.792 41.713,2.742 40.158,1.909C36.798,0.28 32.549,0.03 28.876,0.03C17.257,0.03 8.014,5.885 8.014,17.428C8.014,19.73 8.435,21.979 9.266,25.198L2.947,25.198C1.263,25.198 0,26.283 0,27.888C0,29.555 1.305,30.619 2.947,30.619L10.523,30.619C11.107,32.695 11.299,34.196 11.299,35.634C11.299,41.002 8.545,45.64 3.477,47.51C1.747,48.347 0.209,49.631 0.209,51.878C0.209,54.27 1.978,55.679 4.547,55.679Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<path d="M12.892,61L51.107,61C57.723,61 60.999,57.755 60.999,51.265L60.999,12.766C60.999,6.276 57.723,3 51.107,3L12.892,3C6.308,3 3,6.276 3,12.766L3,51.265C3,57.755 6.308,61 12.892,61ZM12.955,55.928C9.805,55.928 8.072,54.258 8.072,50.982L8.072,13.05C8.072,9.774 9.805,8.072 12.955,8.072L51.044,8.072C54.163,8.072 55.927,9.773 55.927,13.05L55.927,50.982C55.927,54.258 54.163,55.928 51.044,55.928L12.955,55.928Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
<?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><path d="M29.953,40.788l10.342,0c1.761,0 3.18,1.258 3.18,3.055c0,1.788 -1.349,3.03 -3.18,3.03l-18.004,0c-1.785,0 -3.086,-1.234 -3.086,-3.075c0,-1.581 0.953,-2.598 2.336,-3.193l0.024,-0.01c2.054,-0.833 3.153,-2.551 3.153,-4.757c0,-0.425 -0.052,-0.856 -0.133,-1.305l-2.8,0c-1.588,0 -2.701,-1.131 -2.701,-2.591c0,-1.408 1.105,-2.56 2.701,-2.56l1.544,-0c-0.168,-0.87 -0.242,-1.672 -0.242,-2.485c0,-6.288 4.756,-9.887 11.404,-9.887c2.234,0 3.578,0.148 5.274,0.766c1.53,0.469 2.646,1.469 2.646,3.106c0,0.878 -0.321,1.553 -0.828,2.023c-0.48,0.446 -1.159,0.725 -2.011,0.725c-0.479,0 -1.089,-0.115 -1.715,-0.248l-0.045,-0.01c-0.697,-0.176 -1.631,-0.301 -2.775,-0.301c-2.932,0 -5.078,1.33 -5.078,3.98c0,0.734 0.074,1.394 0.272,2.332l7.292,0c1.585,0 2.701,1.163 2.701,2.56c0,1.449 -1.124,2.591 -2.701,2.591l-6.16,0c0.024,0.361 0.034,0.743 0.034,1.147c0,1.832 -0.479,3.671 -1.444,5.108Z"/><path d="M12.892,61l38.215,0c6.616,0 9.892,-3.245 9.892,-9.735l0,-38.499c0,-6.49 -3.276,-9.766 -9.892,-9.766l-38.215,-0c-6.584,0 -9.892,3.276 -9.892,9.766l0,38.499c0,6.49 3.308,9.735 9.892,9.735Zm0.063,-5.072c-3.15,0 -4.883,-1.67 -4.883,-4.946l0,-37.932c0,-3.276 1.733,-4.978 4.883,-4.978l38.089,0c3.119,0 4.883,1.701 4.883,4.978l0,37.932c0,3.276 -1.764,4.946 -4.883,4.946l-38.089,0Z" style="fill-rule:nonzero;"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB |
@ -1 +1,14 @@
|
||||
<?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><rect x="9.624" y="3.845" width="44.752" height="56.309" style="fill-opacity:0;"/><path d="M26.257,60.138l-8.186,-0c-5.571,0 -8.446,-2.897 -8.446,-8.515l0,-39.242c0,-5.597 2.897,-8.536 8.446,-8.536l12.536,0c3.245,0 5.052,0.512 7.225,2.733l13.66,13.874c1.791,1.825 2.456,3.244 2.655,5.548l-18.685,0c-1.881,0 -3.481,0.368 -4.804,1.026c-0.718,-0.905 -1.073,-2.175 -1.073,-3.822l0,-14.263l-11.086,0c-2.532,0 -3.779,1.356 -3.779,3.758l0,38.608c0,2.423 1.247,3.736 3.758,3.736l7.523,0l0,2.541c0,0.919 0.089,1.77 0.256,2.554Zm9.331,-35.907l12.829,0l-14.339,-14.583l0,13.051c0,1.073 0.432,1.531 1.51,1.531Z"/><path d="M40.85,55.833l11.571,0c1.059,0 1.9,-0.653 1.9,-1.808c0,-1.13 -0.808,-1.792 -1.9,-1.792l-7.688,0l0,-0.134c1.314,-0.689 1.89,-2.248 1.89,-3.831c0,-0.282 -0.035,-0.515 -0.08,-0.734l4.67,0c0.693,0 1.187,-0.457 1.187,-1.094c0,-0.629 -0.493,-1.076 -1.187,-1.076l-5.138,0c-0.116,-0.435 -0.29,-1.109 -0.29,-1.841c0,-2.056 1.729,-2.886 3.815,-2.886c0.699,0 1.254,0.061 1.738,0.164c0.359,0.049 0.802,0.099 1.224,0.099c0.823,0 1.533,-0.404 1.533,-1.4c0,-0.687 -0.307,-1.164 -0.897,-1.54c-1.174,-0.707 -3.004,-0.791 -4.371,-0.791c-4.041,0 -7.507,1.845 -7.507,5.693c0,0.662 0.103,1.325 0.404,2.503l-1.851,0c-0.693,0 -1.194,0.446 -1.194,1.076c0,0.65 0.506,1.094 1.194,1.094l2.303,0c0.088,0.363 0.11,0.646 0.11,0.921c0,1.556 -0.687,2.878 -1.915,3.559c-0.671,0.438 -1.423,1.044 -1.423,2.028c0,1.144 0.806,1.792 1.902,1.792Z" style="fill-rule:nonzero;"/><g><path d="M35.462,64l22.077,0c4.182,0 6.462,-2.26 6.462,-6.416l0,-22.159c0,-4.146 -2.279,-6.425 -6.462,-6.425l-22.077,0c-4.174,0 -6.462,2.279 -6.462,6.425l0,22.159c0,4.155 2.288,6.416 6.462,6.416Zm0.513,-4.722c-1.473,0 -2.251,-0.714 -2.251,-2.27l0,-20.996c0,-1.556 0.778,-2.279 2.251,-2.279l21.051,0c1.464,0 2.252,0.723 2.252,2.279l0,20.996c0,1.556 -0.787,2.27 -2.252,2.27l-21.051,0Z" style="fill-rule:nonzero;"/></g></g></svg>
|
||||
<?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.58577,0,0,0.58577,29,29)">
|
||||
<path d="M11.031,59.75L48.719,59.75C55.859,59.75 59.75,55.891 59.75,48.797L59.75,10.969C59.75,3.891 55.859,-0 48.719,-0L11.031,-0C3.906,-0 -0,3.891 -0,10.969L-0,48.797C-0,55.891 3.906,59.75 11.031,59.75ZM11.906,51.688C9.391,51.688 8.063,50.469 8.063,47.813L8.063,11.969C8.063,9.313 9.391,8.078 11.906,8.078L47.844,8.078C50.344,8.078 51.688,9.313 51.688,11.969L51.688,47.813C51.688,50.469 50.344,51.688 47.844,51.688L11.906,51.688Z" style="fill-rule:nonzero;"/>
|
||||
<g transform="matrix(0.664312,0,0,0.664312,5.121464,12.135807)">
|
||||
<path d="M37.594,52.969C39.062,52.969 40.625,52.281 41.875,51L61.844,31.094C62.938,30 63.656,28.188 63.656,26.5C63.656,24.812 62.938,23 61.844,21.906L41.875,1.969C40.625,0.688 39.062,0 37.594,0C33.812,0 31.406,2.562 31.406,5.906C31.406,7.875 32.312,9.25 33.5,10.406L40.5,17.344L50.219,26.5L40.5,35.656L33.5,42.562C32.312,43.688 31.406,45.094 31.406,47.062C31.406,50.406 33.812,52.969 37.594,52.969ZM1.485,32.781L37.75,32.781L51.969,32.094C55.531,31.938 57.906,29.844 57.906,26.5C57.906,23.156 55.531,21.062 51.969,20.906L37.75,20.219L1.485,20.219C-2.515,20.219 -5.14,22.719 -5.14,26.5C-5.14,30.281 -2.515,32.781 1.485,32.781Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(0.739137,0,0,0.739137,-0,2.611674)">
|
||||
<path d="M35.853,52.047L25.734,52.047C20.578,52.047 17.797,48.969 17.047,43.844L12.484,12.719L3.906,12.719C1.812,12.719 0,10.906 0,8.766C0,6.625 1.812,4.828 3.906,4.828L14.172,4.828C18.125,4.828 19.609,6.437 20.047,9.625L20.402,12.109L69.062,12.109C71.922,12.109 73.438,13.75 73.438,16.031C73.438,16.5 73.344,17.062 73.281,17.516L71.172,31.781C71.146,31.963 71.118,32.143 71.087,32.319L63.084,32.319C63.272,32.028 63.394,31.657 63.453,31.219L65.078,19.094L21.4,19.094L23.397,33.062L43.157,33.063C41.542,33.617 40.191,34.464 39.103,35.547C37.914,36.73 37.007,38.224 36.457,40.031L24.393,40.031L24.844,43.188C25,44.312 25.656,45.094 26.719,45.094L35.853,45.094L35.853,52.047ZM28.422,68.437C25.078,68.437 22.359,65.766 22.359,62.391C22.359,59.063 25.078,56.359 28.422,56.359C31.781,56.359 34.453,59.063 34.453,62.391C34.453,65.766 31.781,68.437 28.422,68.437Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.6 KiB |
@ -1,16 +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.58577,0,0,0.58577,29.000019,28.999974)">
|
||||
<path d="M11.031,59.75L48.719,59.75C55.859,59.75 59.75,55.891 59.75,48.797L59.75,10.969C59.75,3.891 55.859,-0 48.719,-0L11.031,-0C3.906,-0 -0,3.891 -0,10.969L-0,48.797C-0,55.891 3.906,59.75 11.031,59.75ZM11.906,51.688C9.391,51.688 8.063,50.469 8.063,47.813L8.063,11.969C8.063,9.313 9.391,8.078 11.906,8.078L47.844,8.078C50.344,8.078 51.688,9.313 51.688,11.969L51.688,47.813C51.688,50.469 50.344,51.688 47.844,51.688L11.906,51.688Z" style="fill-rule:nonzero;"/>
|
||||
<g transform="matrix(0.570497,0,0,0.570497,16.522802,13.725853)">
|
||||
<path d="M6.499,56.236L41.124,56.236C44.294,56.236 46.809,54.281 46.809,50.825C46.809,47.443 44.391,45.464 41.124,45.464L18.117,45.464L18.117,45.062C22.049,43.001 23.774,38.335 23.774,33.596C23.774,32.751 23.669,32.057 23.534,31.399L37.509,31.399C39.584,31.399 41.06,30.032 41.06,28.125C41.06,26.241 39.584,24.905 37.509,24.905L22.134,24.905C21.788,23.604 21.265,21.585 21.265,19.395C21.265,13.243 26.441,10.758 32.681,10.758C34.774,10.758 36.433,10.942 37.882,11.247C38.957,11.394 40.282,11.543 41.544,11.543C44.007,11.543 46.13,10.334 46.13,7.354C46.13,5.297 45.211,3.871 43.447,2.745C39.934,0.628 34.458,0.378 30.367,0.378C18.274,0.378 7.903,5.899 7.903,17.414C7.903,19.396 8.212,21.38 9.112,24.905L3.574,24.905C1.5,24.905 0,26.241 0,28.125C0,30.071 1.514,31.399 3.574,31.399L10.467,31.399C10.73,32.486 10.797,33.332 10.797,34.157C10.797,38.814 8.74,42.769 5.065,44.806C3.057,46.118 0.808,47.931 0.808,50.875C0.808,54.298 3.219,56.236 6.499,56.236Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,11.133834,5.394156)">
|
||||
<rect x="0" y="0" width="74.451" height="58.962" style="fill-opacity:0;"/>
|
||||
<g transform="matrix(0.700206,0,0,0.700206,-5.133834,5.956054)">
|
||||
<path d="M28.563,58.962L10.588,58.962C3.679,58.962 0,55.326 0,48.481L0,10.522C0,3.667 3.679,0.02 10.588,0.02L63.676,0.02C70.606,0.02 74.264,3.667 74.264,10.522L74.264,20.933C74.047,20.926 73.827,20.922 73.604,20.922L67.305,20.922L67.305,11.251C67.305,8.378 65.813,6.979 63.097,6.979L11.167,6.979C8.429,6.979 6.959,8.378 6.959,11.251L6.959,47.743C6.959,50.615 8.429,52.003 11.167,52.003L28.563,52.003L28.563,58.962ZM28.857,31.097L16.175,31.097C14.981,31.097 14.107,30.204 14.107,29.051C14.107,27.929 14.981,27.055 16.175,27.055L30.296,27.055C29.634,28.235 29.144,29.582 28.857,31.097ZM16.175,19.207C14.981,19.207 14.107,18.313 14.107,17.138C14.107,16.016 14.981,15.164 16.175,15.164L58.13,15.164C59.295,15.164 60.157,16.016 60.157,17.138C60.157,18.313 59.295,19.207 58.13,19.207L16.175,19.207Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
@ -1,13 +1,10 @@
|
||||
<?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.822372,0,0,0.822372,2,2.325593)">
|
||||
<path d="M25.615,51.541L61.925,51.541C63.571,51.541 65.041,50.302 65.041,48.462C65.041,46.631 63.571,45.412 61.925,45.412L26.434,45.412C25.11,45.412 24.295,44.46 24.093,43.051L19.177,9.045C18.785,6.142 17.522,4.675 13.848,4.675L3.326,4.675C1.539,4.675 0,6.215 0,8.032C0,9.86 1.539,11.411 3.326,11.411L12.627,11.411L17.348,43.696C18.064,48.571 20.686,51.541 25.615,51.541ZM20.362,39.849L62.285,39.849C67.216,39.849 69.857,36.878 70.563,31.952L72.815,16.861C72.877,16.425 72.96,15.886 72.96,15.485C72.96,13.488 71.586,12.07 69.142,12.07L17.504,12.07L17.525,18.229L65.71,18.229L63.868,31.389C63.678,32.83 62.925,33.711 61.558,33.711L20.319,33.711L20.362,39.849ZM28.223,67.493C31.383,67.493 33.901,64.986 33.901,61.805C33.901,58.664 31.383,56.126 28.223,56.126C25.061,56.126 22.513,58.664 22.513,61.805C22.513,64.986 25.061,67.493 28.223,67.493ZM56.875,67.493C60.047,67.493 62.575,64.986 62.575,61.805C62.575,58.664 60.047,56.126 56.875,56.126C53.735,56.126 51.175,58.664 51.175,61.805C51.175,64.986 53.735,67.493 56.875,67.493Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.754667,0,0,0.754667,0,25.090254)">
|
||||
<g transform="matrix(1.26871,0,0,1.26871,0,-2.460294)">
|
||||
<path d="M3.178,17.892L14.469,11.058C16.041,10.11 16.004,8.217 14.469,7.275L3.178,0.415C1.563,-0.576 0,0.29 0,2.137L0,16.19C0,18.069 1.569,18.872 3.178,17.892Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.842778,0,0,0.842778,34.449623,-16.022037)">
|
||||
<path d="M11.031,59.75L48.719,59.75C55.859,59.75 59.75,55.891 59.75,48.797L59.75,10.969C59.75,3.891 55.859,-0 48.719,-0L11.031,-0C3.906,-0 -0,3.891 -0,10.969L-0,48.797C-0,55.891 3.906,59.75 11.031,59.75ZM11.906,51.688C9.391,51.688 8.063,50.469 8.063,47.813L8.063,11.969C8.063,9.313 9.391,8.078 11.906,8.078L47.844,8.078C50.344,8.078 51.688,9.313 51.688,11.969L51.688,47.813C51.688,50.469 50.344,51.688 47.844,51.688L11.906,51.688Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M11.031,59.75C3.906,59.75 0,55.891 0,48.797L0,10.969C0,3.891 3.906,0 11.031,0L48.719,0C55.859,0 59.75,3.891 59.75,10.969L59.75,48.797C59.75,55.891 55.859,59.75 48.719,59.75L11.031,59.75ZM11.906,51.688L47.844,51.688C50.344,51.688 51.688,50.469 51.688,47.813L51.688,11.969C51.688,9.313 50.344,8.078 47.844,8.078L11.906,8.078C9.391,8.078 8.063,9.313 8.063,11.969L8.063,47.813C8.063,50.469 9.391,51.688 11.906,51.688Z"/>
|
||||
</g>
|
||||
<path d="M3.178,17.892L14.469,11.058C16.041,10.11 16.004,8.217 14.469,7.275L3.178,0.415C1.563,-0.576 0,0.29 0,2.137L0,16.19C0,18.069 1.569,18.872 3.178,17.892ZM25.758,14.461L80.772,14.461C83.437,14.461 84.806,13.117 84.806,10.453L84.806,7.792C84.806,5.153 83.437,3.784 80.772,3.784L25.758,3.784C23.094,3.784 21.725,5.153 21.725,7.792L21.725,10.453C21.725,13.117 23.094,14.461 25.758,14.461Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.0 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.81149,0,0,0.81149,2,10.044134)">
|
||||
<path d="M14.864,42.322L22.52,42.322C24.364,42.322 25.582,41.078 25.582,39.328L25.582,33.547C25.582,31.766 24.364,30.547 22.52,30.547L14.864,30.547C13.02,30.547 11.801,31.766 11.801,33.547L11.801,39.328C11.801,41.078 13.02,42.322 14.864,42.322ZM3.51,20.305L70.459,20.305L70.459,12.998L3.51,12.998L3.51,20.305ZM10.251,54.093L63.687,54.093C70.463,54.093 73.938,50.638 73.938,43.969L73.938,10.169C73.938,3.501 70.463,0.02 63.687,0.02L10.251,0.02C3.506,0.02 0,3.501 0,10.169L0,43.969C0,50.638 3.506,54.093 10.251,54.093ZM10.606,47.97C7.701,47.97 6.122,46.471 6.122,43.429L6.122,10.709C6.122,7.667 7.701,6.142 10.606,6.142L63.332,6.142C66.186,6.142 67.816,7.667 67.816,10.709L67.816,43.429C67.816,46.471 66.186,47.97 63.332,47.97L10.606,47.97Z" style="fill-rule:nonzero;"/>
|
||||
</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.822372,0,0,0.822372,2,2.325529)">
|
||||
<path d="M25.615,51.541L61.925,51.541C63.571,51.541 65.041,50.302 65.041,48.462C65.041,46.631 63.571,45.412 61.925,45.412L26.434,45.412C25.11,45.412 24.295,44.46 24.093,43.051L19.177,9.045C18.785,6.142 17.522,4.675 13.848,4.675L3.326,4.675C1.539,4.675 0,6.215 0,8.032C0,9.86 1.539,11.411 3.326,11.411L12.627,11.411L17.348,43.696C18.064,48.571 20.686,51.541 25.615,51.541ZM20.362,39.849L62.285,39.849C67.216,39.849 69.857,36.878 70.563,31.952L72.815,16.861C72.877,16.425 72.96,15.886 72.96,15.485C72.96,13.488 71.586,12.07 69.142,12.07L17.504,12.07L17.525,18.229L65.71,18.229L63.868,31.389C63.678,32.83 62.925,33.711 61.558,33.711L20.319,33.711L20.362,39.849ZM28.223,67.493C31.383,67.493 33.901,64.986 33.901,61.805C33.901,58.664 31.383,56.126 28.223,56.126C25.061,56.126 22.513,58.664 22.513,61.805C22.513,64.986 25.061,67.493 28.223,67.493ZM56.875,67.493C60.047,67.493 62.575,64.986 62.575,61.805C62.575,58.664 60.047,56.126 56.875,56.126C53.735,56.126 51.175,58.664 51.175,61.805C51.175,64.986 53.735,67.493 56.875,67.493Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@ -32,7 +32,6 @@ import {
|
||||
ProductionRoutes,
|
||||
InventoryRoutes,
|
||||
FinanceRoutes,
|
||||
SalesRoutes,
|
||||
ManagementRoutes,
|
||||
DeveloperRoutes
|
||||
} from './routes'
|
||||
@ -99,7 +98,6 @@ const AppContent = () => {
|
||||
{ProductionRoutes}
|
||||
{InventoryRoutes}
|
||||
{FinanceRoutes}
|
||||
{SalesRoutes}
|
||||
{ManagementRoutes}
|
||||
{DeveloperRoutes}
|
||||
</Route>
|
||||
|
||||
@ -13,8 +13,7 @@ const FinanceOverview = () => {
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'FinanceOverview',
|
||||
{
|
||||
invoiceStats: true,
|
||||
paymentStats: true
|
||||
invoiceStats: true
|
||||
}
|
||||
)
|
||||
|
||||
@ -42,7 +41,6 @@ const FinanceOverview = () => {
|
||||
}
|
||||
className='no-t-padding-collapse'
|
||||
collapseKey='invoiceStats'
|
||||
canCollapse={false}
|
||||
>
|
||||
<Flex
|
||||
justify='flex-start'
|
||||
@ -53,26 +51,6 @@ const FinanceOverview = () => {
|
||||
<StatsDisplay objectType='invoice' />
|
||||
</Flex>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Payment Statistics'
|
||||
icon={null}
|
||||
active={collapseState.paymentStats}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('paymentStats', isActive)
|
||||
}
|
||||
className='no-t-padding-collapse'
|
||||
collapseKey='paymentStats'
|
||||
canCollapse={false}
|
||||
>
|
||||
<Flex
|
||||
justify='flex-start'
|
||||
gap='middle'
|
||||
wrap='wrap'
|
||||
align='flex-start'
|
||||
>
|
||||
<StatsDisplay objectType='payment' />
|
||||
</Flex>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
@ -80,3 +58,4 @@ const FinanceOverview = () => {
|
||||
}
|
||||
|
||||
export default FinanceOverview
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import DashboardSidebar from '../common/DashboardSidebar'
|
||||
import InvoiceIcon from '../../Icons/InvoiceIcon'
|
||||
import PaymentIcon from '../../Icons/PaymentIcon'
|
||||
import FinanceIcon from '../../Icons/FinanceIcon'
|
||||
|
||||
const items = [
|
||||
@ -17,19 +16,12 @@ const items = [
|
||||
label: 'Invoices',
|
||||
icon: <InvoiceIcon />,
|
||||
path: '/dashboard/finance/invoices'
|
||||
},
|
||||
{
|
||||
key: 'payments',
|
||||
label: 'Payments',
|
||||
icon: <PaymentIcon />,
|
||||
path: '/dashboard/finance/payments'
|
||||
}
|
||||
]
|
||||
|
||||
const routeKeyMap = {
|
||||
'/dashboard/finance/overview': 'overview',
|
||||
'/dashboard/finance/invoices': 'invoices',
|
||||
'/dashboard/finance/payments': 'payments'
|
||||
'/dashboard/finance/invoices': 'invoices'
|
||||
}
|
||||
|
||||
const FinanceSidebar = (props) => {
|
||||
@ -51,3 +43,4 @@ const FinanceSidebar = (props) => {
|
||||
}
|
||||
|
||||
export default FinanceSidebar
|
||||
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
import { useState, useContext } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
import { message } from 'antd'
|
||||
import MessageDialogView from '../../common/MessageDialogView.jsx'
|
||||
|
||||
const AcknowledgeInvoice = ({ onOk, objectData }) => {
|
||||
const [acknowledgeLoading, setAcknowledgeLoading] = useState(false)
|
||||
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||
|
||||
const handleAcknowledge = async () => {
|
||||
setAcknowledgeLoading(true)
|
||||
try {
|
||||
const result = await sendObjectFunction(
|
||||
objectData._id,
|
||||
'Invoice',
|
||||
'acknowledge'
|
||||
)
|
||||
if (result) {
|
||||
message.success('Invoice acknowledged successfully')
|
||||
onOk(result)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error acknowledging invoice:', error)
|
||||
} finally {
|
||||
setAcknowledgeLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MessageDialogView
|
||||
title={'Are you sure you want to acknowledge this invoice?'}
|
||||
description={`Acknowledging invoice ${objectData?.name || objectData?._reference || objectData?._id} will update its status to acknowledged.`}
|
||||
onOk={handleAcknowledge}
|
||||
okText='Acknowledge'
|
||||
okLoading={acknowledgeLoading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
AcknowledgeInvoice.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
objectData: PropTypes.object
|
||||
}
|
||||
|
||||
export default AcknowledgeInvoice
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Space, Flex, Card, Modal } from 'antd'
|
||||
import { Space, Flex, Card } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config.js'
|
||||
@ -8,11 +8,9 @@ import useCollapseState from '../../hooks/useCollapseState.js'
|
||||
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
import ViewButton from '../../common/ViewButton.jsx'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import PaymentIcon from '../../../Icons/PaymentIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
||||
import EditButtons from '../../common/EditButtons.jsx'
|
||||
@ -23,15 +21,7 @@ import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import {
|
||||
getModelByName,
|
||||
getModelProperty
|
||||
} from '../../../../database/ObjectModels.js'
|
||||
import OrderItemIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||
import ShipmentIcon from '../../../Icons/ShipmentIcon.jsx'
|
||||
import PostInvoice from './PostInvoice.jsx'
|
||||
import AcknowledgeInvoice from './AcknowledgeInvoice.jsx'
|
||||
import NewPayment from '../Payments/NewPayment.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
|
||||
const log = loglevel.getLogger('InvoiceInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
@ -41,14 +31,14 @@ const InvoiceInfo = () => {
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const invoiceId = new URLSearchParams(location.search).get('invoiceId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState('InvoiceInfo', {
|
||||
info: true,
|
||||
invoiceOrderItems: true,
|
||||
invoiceShipments: true,
|
||||
payments: true,
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
})
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'InvoiceInfo',
|
||||
{
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
@ -58,9 +48,6 @@ const InvoiceInfo = () => {
|
||||
loading: false,
|
||||
objectData: {}
|
||||
})
|
||||
const [postInvoiceOpen, setPostInvoiceOpen] = useState(false)
|
||||
const [acknowledgeInvoiceOpen, setAcknowledgeInvoiceOpen] = useState(false)
|
||||
const [newPaymentOpen, setNewPaymentOpen] = useState(false)
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
@ -82,25 +69,12 @@ const InvoiceInfo = () => {
|
||||
delete: () => {
|
||||
objectFormRef?.current?.handleDelete?.()
|
||||
return true
|
||||
},
|
||||
post: () => {
|
||||
setPostInvoiceOpen(true)
|
||||
return true
|
||||
},
|
||||
acknowledge: () => {
|
||||
setAcknowledgeInvoiceOpen(true)
|
||||
return true
|
||||
},
|
||||
newPayment: () => {
|
||||
setNewPaymentOpen(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const editDisabled =
|
||||
getModelByName('invoice')
|
||||
?.actions?.find((action) => action.name === 'edit')
|
||||
?.disabled(objectFormState.objectData) ?? false
|
||||
const editDisabled = getModelByName('invoice')
|
||||
?.actions?.find((action) => action.name === 'edit')
|
||||
?.disabled(objectFormState.objectData) ?? false
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -125,9 +99,6 @@ const InvoiceInfo = () => {
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Invoice Information' },
|
||||
{ key: 'invoiceOrderItems', label: 'Invoice Order Items' },
|
||||
{ key: 'invoiceShipments', label: 'Invoice Shipments' },
|
||||
{ key: 'payments', label: 'Payments' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
@ -200,67 +171,12 @@ const InvoiceInfo = () => {
|
||||
type='invoice'
|
||||
labelWidth='225px'
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
invoiceOrderItems: false,
|
||||
invoiceShipments: false
|
||||
}}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Invoice Order Items'
|
||||
icon={<OrderItemIcon />}
|
||||
active={collapseState.invoiceOrderItems}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('invoiceOrderItems', expanded)
|
||||
}
|
||||
collapseKey='invoiceOrderItems'
|
||||
>
|
||||
<ObjectProperty
|
||||
{...getModelProperty('invoice', 'invoiceOrderItems')}
|
||||
isEditing={isEditing}
|
||||
objectData={objectData}
|
||||
loading={loading}
|
||||
size='medium'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Invoice Shipments'
|
||||
icon={<ShipmentIcon />}
|
||||
active={collapseState.invoiceShipments}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('invoiceShipments', expanded)
|
||||
}
|
||||
collapseKey='invoiceShipments'
|
||||
>
|
||||
<ObjectProperty
|
||||
{...getModelProperty('invoice', 'invoiceShipments')}
|
||||
isEditing={isEditing}
|
||||
objectData={objectData}
|
||||
loading={loading}
|
||||
size='medium'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</ActionHandler>
|
||||
<InfoCollapse
|
||||
title='Payments'
|
||||
icon={<PaymentIcon />}
|
||||
active={collapseState.payments}
|
||||
onToggle={(expanded) => updateCollapseState('payments', expanded)}
|
||||
collapseKey='payments'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='payment'
|
||||
masterFilter={{ 'invoice._id': invoiceId }}
|
||||
visibleColumns={{ invoice: false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
@ -294,66 +210,9 @@ const InvoiceInfo = () => {
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={postInvoiceOpen}
|
||||
onCancel={() => {
|
||||
setPostInvoiceOpen(false)
|
||||
}}
|
||||
width={500}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
centered={true}
|
||||
>
|
||||
<PostInvoice
|
||||
onOk={() => {
|
||||
setPostInvoiceOpen(false)
|
||||
actions.reload()
|
||||
}}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={acknowledgeInvoiceOpen}
|
||||
onCancel={() => {
|
||||
setAcknowledgeInvoiceOpen(false)
|
||||
}}
|
||||
width={515}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
centered={true}
|
||||
>
|
||||
<AcknowledgeInvoice
|
||||
onOk={() => {
|
||||
setAcknowledgeInvoiceOpen(false)
|
||||
actions.reload()
|
||||
}}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={newPaymentOpen}
|
||||
styles={{ content: { paddingBottom: '24px' } }}
|
||||
footer={null}
|
||||
width={800}
|
||||
onCancel={() => {
|
||||
setNewPaymentOpen(false)
|
||||
}}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
<NewPayment
|
||||
onOk={() => {
|
||||
setNewPaymentOpen(false)
|
||||
actions.reload()
|
||||
}}
|
||||
reset={newPaymentOpen}
|
||||
defaultValues={{
|
||||
invoice: { ...objectFormState.objectData },
|
||||
amount: objectFormState.objectData?.grandTotalAmount
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default InvoiceInfo
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import dayjs from 'dayjs'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
@ -11,8 +10,7 @@ const NewInvoice = ({ onOk, reset, defaultValues }) => {
|
||||
reset={reset}
|
||||
defaultValues={{
|
||||
state: { type: 'draft' },
|
||||
issuedAt: new Date(),
|
||||
dueAt: dayjs().add(3, 'day').toDate(),
|
||||
invoiceType: 'sales',
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
@ -32,10 +30,27 @@ const NewInvoice = ({ onOk, reset, defaultValues }) => {
|
||||
visibleProperties={{
|
||||
orderType: true,
|
||||
order: true,
|
||||
to: true,
|
||||
from: true,
|
||||
issuedAt: true,
|
||||
dueAt: true
|
||||
vendor: true,
|
||||
invoiceDate: true,
|
||||
dueDate: true
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Optional',
|
||||
key: 'optional',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='invoice'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={false}
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
relatedOrderType: true,
|
||||
relatedOrder: true
|
||||
}}
|
||||
/>
|
||||
)
|
||||
@ -51,8 +66,6 @@ const NewInvoice = ({ onOk, reset, defaultValues }) => {
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
createdAt: false,
|
||||
invoiceOrderItems: false,
|
||||
invoiceShipments: false,
|
||||
updatedAt: false,
|
||||
_reference: false,
|
||||
totalAmount: false,
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
import { useState, useContext } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
import { message } from 'antd'
|
||||
import MessageDialogView from '../../common/MessageDialogView.jsx'
|
||||
|
||||
const PostInvoice = ({ onOk, objectData }) => {
|
||||
const [postLoading, setPostLoading] = useState(false)
|
||||
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||
|
||||
const handlePost = async () => {
|
||||
setPostLoading(true)
|
||||
try {
|
||||
const result = await sendObjectFunction(objectData._id, 'Invoice', 'post')
|
||||
if (result) {
|
||||
message.success('Invoice posted successfully')
|
||||
onOk(result)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error posting invoice:', error)
|
||||
} finally {
|
||||
setPostLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MessageDialogView
|
||||
title={'Are you sure you want to post this invoice?'}
|
||||
description={`Posting invoice ${objectData?.name || objectData?._reference || objectData?._id} will set it to sent status.`}
|
||||
onOk={handlePost}
|
||||
okText='Post'
|
||||
okLoading={postLoading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
PostInvoice.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
objectData: PropTypes.object
|
||||
}
|
||||
|
||||
export default PostInvoice
|
||||
@ -1,99 +0,0 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { Button, Flex, Space, Dropdown, Modal } from 'antd'
|
||||
import NewPayment from './Payments/NewPayment'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
|
||||
const Payments = () => {
|
||||
const [newPaymentOpen, setNewPaymentOpen] = useState(false)
|
||||
const tableRef = useRef()
|
||||
|
||||
const [viewMode, setViewMode] = useViewMode('payments')
|
||||
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
useColumnVisibility('payments')
|
||||
|
||||
const actionItems = {
|
||||
items: [
|
||||
{
|
||||
label: 'New Payment',
|
||||
key: 'newPayment',
|
||||
icon: <PlusIcon />
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
label: 'Reload List',
|
||||
key: 'reloadList',
|
||||
icon: <ReloadIcon />
|
||||
}
|
||||
],
|
||||
onClick: ({ key }) => {
|
||||
if (key === 'reloadList') {
|
||||
tableRef.current?.reload()
|
||||
} else if (key === 'newPayment') {
|
||||
setNewPaymentOpen(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex vertical={'true'} gap='large'>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='small'>
|
||||
<Dropdown menu={actionItems}>
|
||||
<Button>Actions</Button>
|
||||
</Dropdown>
|
||||
<ColumnViewButton
|
||||
type='payment'
|
||||
loading={false}
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||
onClick={() =>
|
||||
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
<ObjectTable
|
||||
ref={tableRef}
|
||||
visibleColumns={columnVisibility}
|
||||
type='payment'
|
||||
cards={viewMode === 'cards'}
|
||||
/>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={newPaymentOpen}
|
||||
styles={{ content: { paddingBottom: '24px' } }}
|
||||
footer={null}
|
||||
width={800}
|
||||
onCancel={() => {
|
||||
setNewPaymentOpen(false)
|
||||
}}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
<NewPayment
|
||||
onOk={() => {
|
||||
setNewPaymentOpen(false)
|
||||
tableRef.current?.reload()
|
||||
}}
|
||||
reset={newPaymentOpen}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Payments
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewPayment = ({ onOk, reset, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'payment'}
|
||||
reset={reset}
|
||||
defaultValues={{
|
||||
state: { type: 'draft' },
|
||||
paymentDate: new Date(),
|
||||
amount: 0,
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='payment'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={true}
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
invoice: true,
|
||||
vendor: true,
|
||||
client: true,
|
||||
paymentDate: true,
|
||||
amount: true,
|
||||
paymentMethod: true
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Summary',
|
||||
key: 'summary',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='payment'
|
||||
column={1}
|
||||
bordered={false}
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
createdAt: false,
|
||||
updatedAt: false,
|
||||
_reference: false,
|
||||
postedAt: false,
|
||||
cancelledAt: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Payment'
|
||||
onSubmit={async () => {
|
||||
const result = await handleSubmit()
|
||||
if (result) {
|
||||
onOk()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
)
|
||||
}
|
||||
|
||||
NewPayment.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewPayment
|
||||
@ -1,242 +0,0 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Space, Flex, Card, Modal } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config.js'
|
||||
import useCollapseState from '../../hooks/useCollapseState.js'
|
||||
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||
import ViewButton from '../../common/ViewButton.jsx'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
||||
import EditButtons from '../../common/EditButtons.jsx'
|
||||
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import {
|
||||
getModelByName
|
||||
} from '../../../../database/ObjectModels.js'
|
||||
import PostPayment from './PostPayment.jsx'
|
||||
|
||||
const log = loglevel.getLogger('PaymentInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
|
||||
const PaymentInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const paymentId = new URLSearchParams(location.search).get('paymentId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState('PaymentInfo', {
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
})
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
lock: null,
|
||||
loading: false,
|
||||
objectData: {}
|
||||
})
|
||||
const [postPaymentOpen, setPostPaymentOpen] = useState(false)
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.handleFetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
},
|
||||
delete: () => {
|
||||
objectFormRef?.current?.handleDelete?.()
|
||||
return true
|
||||
},
|
||||
post: () => {
|
||||
setPostPaymentOpen(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const editDisabled =
|
||||
getModelByName('payment')
|
||||
?.actions?.find((action) => action.name === 'edit')
|
||||
?.disabled(objectFormState.objectData) ?? false
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
gap='large'
|
||||
vertical='true'
|
||||
style={{
|
||||
maxHeight: '100%',
|
||||
minHeight: 0
|
||||
}}
|
||||
>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='middle'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='payment'
|
||||
id={paymentId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Payment Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
type='payment'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
</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 ||
|
||||
editDisabled
|
||||
}
|
||||
loading={objectFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<ObjectForm
|
||||
id={paymentId}
|
||||
type='payment'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<Flex vertical gap={'large'}>
|
||||
<InfoCollapse
|
||||
title='Payment Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('info', expanded)
|
||||
}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
isEditing={isEditing}
|
||||
type='payment'
|
||||
labelWidth='225px'
|
||||
objectData={objectData}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</ActionHandler>
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
active={collapseState.notes}
|
||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||
collapseKey='notes'
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel _id={paymentId} type='payment' />
|
||||
</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': paymentId }}
|
||||
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={postPaymentOpen}
|
||||
onCancel={() => {
|
||||
setPostPaymentOpen(false)
|
||||
}}
|
||||
width={500}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
centered={true}
|
||||
>
|
||||
<PostPayment
|
||||
onOk={() => {
|
||||
setPostPaymentOpen(false)
|
||||
actions.reload()
|
||||
}}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PaymentInfo
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
import { useState, useContext } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
import { message } from 'antd'
|
||||
import MessageDialogView from '../../common/MessageDialogView.jsx'
|
||||
|
||||
const PostPayment = ({ onOk, objectData }) => {
|
||||
const [postLoading, setPostLoading] = useState(false)
|
||||
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||
|
||||
const handlePost = async () => {
|
||||
setPostLoading(true)
|
||||
try {
|
||||
const result = await sendObjectFunction(objectData._id, 'Payment', 'post')
|
||||
if (result) {
|
||||
message.success('Payment posted successfully')
|
||||
onOk(result)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error posting payment:', error)
|
||||
} finally {
|
||||
setPostLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MessageDialogView
|
||||
title={'Are you sure you want to post this payment?'}
|
||||
description={`Posting payment ${objectData?.name || objectData?._reference || objectData?._id} will set it to posted status.`}
|
||||
onOk={handlePost}
|
||||
okText='Post'
|
||||
okLoading={postLoading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
PostPayment.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
objectData: PropTypes.object
|
||||
}
|
||||
|
||||
export default PostPayment
|
||||
|
||||
@ -42,7 +42,6 @@ const InventoryOverview = () => {
|
||||
}
|
||||
className='no-t-padding-collapse'
|
||||
collapseKey='inventoryStats'
|
||||
canCollapse={false}
|
||||
>
|
||||
<Flex
|
||||
justify='flex-start'
|
||||
|
||||
@ -43,7 +43,6 @@ const items = [
|
||||
icon: <PurchaseOrderIcon />,
|
||||
path: '/dashboard/inventory/purchaseorders'
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: 'orderitems',
|
||||
label: 'Order Items',
|
||||
|
||||
@ -33,15 +33,7 @@ const NewOrderItem = ({ onOk, reset, defaultValues }) => {
|
||||
itemAmount: false,
|
||||
totalAmount: false,
|
||||
totalAmountWithTax: false,
|
||||
quantity: false,
|
||||
invoicedAmount: false,
|
||||
invoicedAmountWithTax: false,
|
||||
invoicedQuantity: false,
|
||||
invoicedAmountRemaining: false,
|
||||
invoicedAmountWithTaxRemaining: false,
|
||||
invoicedQuantityRemaining: false,
|
||||
orderedAt: false,
|
||||
receivedAt: false
|
||||
quantity: false
|
||||
}}
|
||||
/>
|
||||
)
|
||||
@ -79,16 +71,7 @@ const NewOrderItem = ({ onOk, reset, defaultValues }) => {
|
||||
required={false}
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
shipment: true,
|
||||
invoicedAmount: false,
|
||||
invoicedAmountWithTax: false,
|
||||
invoicedQuantity: false,
|
||||
invoicedAmountRemaining: false,
|
||||
invoicedAmountWithTaxRemaining: false,
|
||||
invoicedQuantityRemaining: false,
|
||||
orderedAt: false,
|
||||
receivedAt: false,
|
||||
syncAmount: false
|
||||
shipment: true
|
||||
}}
|
||||
/>
|
||||
)
|
||||
@ -105,15 +88,7 @@ const NewOrderItem = ({ onOk, reset, defaultValues }) => {
|
||||
_id: false,
|
||||
createdAt: false,
|
||||
updatedAt: false,
|
||||
_reference: false,
|
||||
invoicedAmount: false,
|
||||
invoicedAmountWithTax: false,
|
||||
invoicedQuantity: false,
|
||||
invoicedAmountRemaining: false,
|
||||
invoicedAmountWithTaxRemaining: false,
|
||||
invoicedQuantityRemaining: false,
|
||||
orderedAt: false,
|
||||
receivedAt: false
|
||||
_reference: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
|
||||
@ -155,7 +155,7 @@ const OrderItemInfo = () => {
|
||||
isEditing={isEditing}
|
||||
type='orderItem'
|
||||
objectData={objectData}
|
||||
labelWidth='275px'
|
||||
labelWidth='200px'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
|
||||
@ -53,8 +53,7 @@ const NewPurchaseOrder = ({ onOk, reset, defaultValues }) => {
|
||||
acknowledgedAt: false,
|
||||
shippingAmount: false,
|
||||
shippingAmountWithTax: false,
|
||||
grandTotalAmount: false,
|
||||
completedAt: false
|
||||
grandTotalAmount: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
|
||||
@ -28,10 +28,7 @@ import PostPurchaseOrder from './PostPurchaseOrder.jsx'
|
||||
import AcknowledgePurchaseOrder from './AcknowledgePurchaseOrder.jsx'
|
||||
import CancelPurchaseOrder from './CancelPurchaseOrder.jsx'
|
||||
import ShipmentIcon from '../../../Icons/ShipmentIcon.jsx'
|
||||
import InvoiceIcon from '../../../Icons/InvoiceIcon.jsx'
|
||||
import StockEventIcon from '../../../Icons/StockEventIcon.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import NewInvoice from '../../Finance/Invoices/NewInvoice.jsx'
|
||||
|
||||
const log = loglevel.getLogger('PurchaseOrderInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
@ -48,7 +45,6 @@ const PurchaseOrderInfo = () => {
|
||||
const [acknowledgePurchaseOrderOpen, setAcknowledgePurchaseOrderOpen] =
|
||||
useState(false)
|
||||
const [cancelPurchaseOrderOpen, setCancelPurchaseOrderOpen] = useState(false)
|
||||
const [newInvoiceOpen, setNewInvoiceOpen] = useState(false)
|
||||
const purchaseOrderId = new URLSearchParams(location.search).get(
|
||||
'purchaseOrderId'
|
||||
)
|
||||
@ -57,9 +53,7 @@ const PurchaseOrderInfo = () => {
|
||||
{
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: true,
|
||||
invoices: true,
|
||||
stockEvents: true
|
||||
auditLogs: true
|
||||
}
|
||||
)
|
||||
|
||||
@ -104,10 +98,6 @@ const PurchaseOrderInfo = () => {
|
||||
setNewShipmentOpen(true)
|
||||
return true
|
||||
},
|
||||
newInvoice: () => {
|
||||
setNewInvoiceOpen(true)
|
||||
return true
|
||||
},
|
||||
post: () => {
|
||||
setPostPurchaseOrderOpen(true)
|
||||
return true
|
||||
@ -151,8 +141,6 @@ const PurchaseOrderInfo = () => {
|
||||
{ key: 'info', label: 'Purchase Order Information' },
|
||||
{ key: 'orderItems', label: 'Order Items' },
|
||||
{ key: 'shipments', label: 'Shipments' },
|
||||
{ key: 'invoices', label: 'Invoices' },
|
||||
{ key: 'stockEvents', label: 'Stock Events' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
@ -268,48 +256,6 @@ const PurchaseOrderInfo = () => {
|
||||
ref={shipmentsTableRef}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Invoices'
|
||||
icon={<InvoiceIcon />}
|
||||
active={collapseState.invoices}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('invoices', expanded)
|
||||
}
|
||||
collapseKey='invoices'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='invoice'
|
||||
masterFilter={{
|
||||
'order._id': purchaseOrderId,
|
||||
orderType: 'purchaseOrder'
|
||||
}}
|
||||
visibleColumns={{ order: false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Stock Events'
|
||||
icon={<StockEventIcon />}
|
||||
active={collapseState.stockEvents}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('stockEvents', expanded)
|
||||
}
|
||||
collapseKey='stockEvents'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='stockEvent'
|
||||
masterFilter={{
|
||||
'owner._id': purchaseOrderId
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
)}
|
||||
</ObjectForm>
|
||||
@ -388,26 +334,6 @@ const PurchaseOrderInfo = () => {
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={newInvoiceOpen}
|
||||
onCancel={() => {
|
||||
setNewInvoiceOpen(false)
|
||||
}}
|
||||
width={800}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
<NewInvoice
|
||||
onOk={() => {
|
||||
setNewInvoiceOpen(false)
|
||||
}}
|
||||
reset={newInvoiceOpen}
|
||||
defaultValues={{
|
||||
orderType: 'purchaseOrder',
|
||||
order: { ...objectFormState.objectData }
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={postPurchaseOrderOpen}
|
||||
onCancel={() => {
|
||||
|
||||
@ -85,11 +85,7 @@ const NewShipment = ({ onOk, reset, defaultValues }) => {
|
||||
shippedAt: false,
|
||||
expectedAt: false,
|
||||
deliveredAt: false,
|
||||
taxRecord: false,
|
||||
invoicedAmount: false,
|
||||
invoicedAmountWithTax: false,
|
||||
invoicedAmountRemaining: false,
|
||||
invoicedAmountWithTaxRemaining: false
|
||||
taxRecord: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
|
||||
@ -5,7 +5,6 @@ import { useLocation } from 'react-router-dom'
|
||||
import ProductionSidebar from './Production/ProductionSidebar'
|
||||
import InventorySidebar from './Inventory/InventorySidebar'
|
||||
import FinanceSidebar from './Finance/FinanceSidebar'
|
||||
import SalesSidebar from './Sales/SalesSidebar'
|
||||
import ManagementSidebar from './Management/ManagementSidebar'
|
||||
import DashboardNavigation from './common/DashboardNavigation'
|
||||
import DashboardBreadcrumb from './common/DashboardBreadcrumb'
|
||||
@ -20,7 +19,6 @@ const DashboardLayout = ({ children }) => {
|
||||
const isProduction = location.pathname.startsWith('/dashboard/production')
|
||||
const isInventory = location.pathname.startsWith('/dashboard/inventory')
|
||||
const isFinance = location.pathname.startsWith('/dashboard/finance')
|
||||
const isSales = location.pathname.startsWith('/dashboard/sales')
|
||||
const isManagement = location.pathname.startsWith('/dashboard/management')
|
||||
const isDeveloper = location.pathname.startsWith('/dashboard/developer')
|
||||
|
||||
@ -40,8 +38,6 @@ const DashboardLayout = ({ children }) => {
|
||||
<InventorySidebar />
|
||||
) : isFinance ? (
|
||||
<FinanceSidebar />
|
||||
) : isSales ? (
|
||||
<SalesSidebar />
|
||||
) : isManagement ? (
|
||||
<ManagementSidebar />
|
||||
) : isDeveloper ? (
|
||||
|
||||
@ -1,95 +0,0 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { Button, Flex, Space, Modal, Dropdown } from 'antd'
|
||||
import NewClient from './Clients/NewClient'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
|
||||
const Clients = () => {
|
||||
const [newClientOpen, setNewClientOpen] = useState(false)
|
||||
const tableRef = useRef()
|
||||
|
||||
const [viewMode, setViewMode] = useViewMode('client')
|
||||
|
||||
const [columnVisibility, setColumnVisibility] = useColumnVisibility('client')
|
||||
|
||||
const actionItems = {
|
||||
items: [
|
||||
{
|
||||
label: 'New Client',
|
||||
key: 'newClient',
|
||||
icon: <PlusIcon />
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
label: 'Reload List',
|
||||
key: 'reloadList',
|
||||
icon: <ReloadIcon />
|
||||
}
|
||||
],
|
||||
onClick: ({ key }) => {
|
||||
if (key === 'reloadList') {
|
||||
tableRef.current?.reload()
|
||||
} else if (key === 'newClient') {
|
||||
setNewClientOpen(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex vertical={'true'} gap='large'>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='small'>
|
||||
<Dropdown menu={actionItems}>
|
||||
<Button>Actions</Button>
|
||||
</Dropdown>
|
||||
<ColumnViewButton
|
||||
type='client'
|
||||
loading={false}
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||
onClick={() =>
|
||||
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
<ObjectTable
|
||||
ref={tableRef}
|
||||
visibleColumns={columnVisibility}
|
||||
type='client'
|
||||
cards={viewMode === 'cards'}
|
||||
/>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={newClientOpen}
|
||||
onCancel={() => setNewClientOpen(false)}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
width={700}
|
||||
>
|
||||
<NewClient
|
||||
onOk={() => {
|
||||
setNewClientOpen(false)
|
||||
tableRef.current?.reload()
|
||||
}}
|
||||
reset={!newClientOpen}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Clients
|
||||
|
||||
@ -1,194 +0,0 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Space, Flex, Card } from 'antd'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config'
|
||||
import useCollapseState from '../../hooks/useCollapseState'
|
||||
import NotesPanel from '../../common/NotesPanel'
|
||||
import InfoCollapse from '../../common/InfoCollapse'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import ViewButton from '../../common/ViewButton'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm'
|
||||
import EditButtons from '../../common/EditButtons'
|
||||
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
|
||||
const log = loglevel.getLogger('ClientInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
|
||||
const ClientInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const clientId = new URLSearchParams(location.search).get('clientId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState('ClientInfo', {
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
})
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
lock: null,
|
||||
loading: false,
|
||||
objectData: {}
|
||||
})
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.handleFetchObject?.()
|
||||
return true
|
||||
},
|
||||
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='client'
|
||||
id={clientId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Client Information' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
type='client'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
</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>
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
title='Client Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectForm
|
||||
id={clientId}
|
||||
type='client'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
isEditing={isEditing}
|
||||
type='client'
|
||||
objectData={objectData}
|
||||
/>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</InfoCollapse>
|
||||
</ActionHandler>
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
active={collapseState.notes}
|
||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||
collapseKey='notes'
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel _id={clientId} type='client' />
|
||||
</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': clientId }}
|
||||
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ClientInfo
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewClient = ({ onOk, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'client'}
|
||||
defaultValues={{ active: true, ...defaultValues }}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='client'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={true}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Optional',
|
||||
key: 'optional',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='client'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Summary',
|
||||
key: 'summary',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='client'
|
||||
column={1}
|
||||
bordered={false}
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
createdAt: false,
|
||||
updatedAt: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Client'
|
||||
onSubmit={async () => {
|
||||
const result = await handleSubmit()
|
||||
if (result) {
|
||||
onOk()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
)
|
||||
}
|
||||
|
||||
NewClient.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewClient
|
||||
|
||||
@ -1,99 +0,0 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { Button, Flex, Space, Modal, Dropdown } from 'antd'
|
||||
import NewSalesOrder from './SalesOrders/NewSalesOrder'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
|
||||
const SalesOrders = () => {
|
||||
const [newSalesOrderOpen, setNewSalesOrderOpen] = useState(false)
|
||||
const tableRef = useRef()
|
||||
|
||||
const [viewMode, setViewMode] = useViewMode('salesOrders')
|
||||
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
useColumnVisibility('salesOrders')
|
||||
|
||||
const actionItems = {
|
||||
items: [
|
||||
{
|
||||
label: 'New Sales Order',
|
||||
key: 'newSalesOrder',
|
||||
icon: <PlusIcon />
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
label: 'Reload List',
|
||||
key: 'reloadList',
|
||||
icon: <ReloadIcon />
|
||||
}
|
||||
],
|
||||
onClick: ({ key }) => {
|
||||
if (key === 'reloadList') {
|
||||
tableRef.current?.reload()
|
||||
} else if (key === 'newSalesOrder') {
|
||||
setNewSalesOrderOpen(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex vertical={'true'} gap='large'>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='small'>
|
||||
<Dropdown menu={actionItems}>
|
||||
<Button>Actions</Button>
|
||||
</Dropdown>
|
||||
<ColumnViewButton
|
||||
type='salesOrder'
|
||||
loading={false}
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||
onClick={() =>
|
||||
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
<ObjectTable
|
||||
ref={tableRef}
|
||||
visibleColumns={columnVisibility}
|
||||
type='salesOrder'
|
||||
cards={viewMode === 'cards'}
|
||||
/>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={newSalesOrderOpen}
|
||||
styles={{ content: { paddingBottom: '24px' } }}
|
||||
footer={null}
|
||||
width={800}
|
||||
onCancel={() => {
|
||||
setNewSalesOrderOpen(false)
|
||||
}}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
<NewSalesOrder
|
||||
onOk={() => {
|
||||
setNewSalesOrderOpen(false)
|
||||
tableRef.current?.reload()
|
||||
}}
|
||||
reset={newSalesOrderOpen}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SalesOrders
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
import { useState, useContext } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
import { message } from 'antd'
|
||||
import MessageDialogView from '../../common/MessageDialogView.jsx'
|
||||
|
||||
const CancelSalesOrder = ({ onOk, objectData }) => {
|
||||
const [cancelLoading, setCancelLoading] = useState(false)
|
||||
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||
|
||||
const handleCancel = async () => {
|
||||
setCancelLoading(true)
|
||||
try {
|
||||
const result = await sendObjectFunction(
|
||||
objectData._id,
|
||||
'SalesOrder',
|
||||
'cancel'
|
||||
)
|
||||
if (result) {
|
||||
message.success('Sales order cancelled successfully')
|
||||
onOk(result)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error cancelling sales order:', error)
|
||||
} finally {
|
||||
setCancelLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MessageDialogView
|
||||
title={'Are you sure you want to cancel this sales order?'}
|
||||
description={`Cancelling sales order ${objectData?.name || objectData?._reference || objectData?._id} will update its status to cancelled.`}
|
||||
onOk={handleCancel}
|
||||
okText='Cancel'
|
||||
okLoading={cancelLoading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
CancelSalesOrder.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
objectData: PropTypes.object
|
||||
}
|
||||
|
||||
export default CancelSalesOrder
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
import { useState, useContext } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
import { message } from 'antd'
|
||||
import MessageDialogView from '../../common/MessageDialogView.jsx'
|
||||
|
||||
const ConfirmSalesOrder = ({ onOk, objectData }) => {
|
||||
const [confirmLoading, setConfirmLoading] = useState(false)
|
||||
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||
|
||||
const handleConfirm = async () => {
|
||||
setConfirmLoading(true)
|
||||
try {
|
||||
const result = await sendObjectFunction(
|
||||
objectData._id,
|
||||
'SalesOrder',
|
||||
'confirm'
|
||||
)
|
||||
if (result) {
|
||||
message.success('Sales order confirmed successfully')
|
||||
onOk(result)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error confirming sales order:', error)
|
||||
} finally {
|
||||
setConfirmLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MessageDialogView
|
||||
title={'Are you sure you want to confirm this sales order?'}
|
||||
description={`Confirming sales order ${objectData?.name || objectData?._reference || objectData?._id} will update its status to confirmed.`}
|
||||
onOk={handleConfirm}
|
||||
okText='Confirm'
|
||||
okLoading={confirmLoading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
ConfirmSalesOrder.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
objectData: PropTypes.object
|
||||
}
|
||||
|
||||
export default ConfirmSalesOrder
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
const NewSalesOrder = ({ onOk, reset, defaultValues }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type={'salesOrder'}
|
||||
reset={reset}
|
||||
defaultValues={{
|
||||
state: { type: 'draft' },
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='salesOrder'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={true}
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
_reference: false,
|
||||
items: false,
|
||||
cost: false
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Summary',
|
||||
key: 'summary',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='salesOrder'
|
||||
column={1}
|
||||
bordered={false}
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
createdAt: false,
|
||||
updatedAt: false,
|
||||
_reference: false,
|
||||
totalAmount: false,
|
||||
totalAmountWithTax: false,
|
||||
totalTaxAmount: false,
|
||||
postedAt: false,
|
||||
confirmedAt: false,
|
||||
shippingAmount: false,
|
||||
shippingAmountWithTax: false,
|
||||
grandTotalAmount: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New Sales Order'
|
||||
onSubmit={async () => {
|
||||
const result = await handleSubmit()
|
||||
if (result) {
|
||||
onOk()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
)
|
||||
}
|
||||
|
||||
NewSalesOrder.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewSalesOrder
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
import { useState, useContext } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
import { message } from 'antd'
|
||||
import MessageDialogView from '../../common/MessageDialogView.jsx'
|
||||
|
||||
const PostSalesOrder = ({ onOk, objectData }) => {
|
||||
const [postLoading, setPostLoading] = useState(false)
|
||||
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||
|
||||
const handlePost = async () => {
|
||||
setPostLoading(true)
|
||||
try {
|
||||
const result = await sendObjectFunction(
|
||||
objectData._id,
|
||||
'SalesOrder',
|
||||
'post'
|
||||
)
|
||||
if (result) {
|
||||
message.success('Sales order posted successfully')
|
||||
onOk(result)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error posting sales order:', error)
|
||||
} finally {
|
||||
setPostLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<MessageDialogView
|
||||
title={'Are you sure you want to post this sales order?'}
|
||||
description={`Posting sales order ${objectData?.name || objectData?._reference || objectData?._id} will finalize it and update inventory levels where applicable.`}
|
||||
onOk={handlePost}
|
||||
okText='Post'
|
||||
okLoading={postLoading}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
PostSalesOrder.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
objectData: PropTypes.object
|
||||
}
|
||||
|
||||
export default PostSalesOrder
|
||||
|
||||
@ -1,466 +0,0 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Space, Flex, Card, Modal } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import loglevel from 'loglevel'
|
||||
import config from '../../../../config.js'
|
||||
import useCollapseState from '../../hooks/useCollapseState.js'
|
||||
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||
import ViewButton from '../../common/ViewButton.jsx'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
||||
import EditButtons from '../../common/EditButtons.jsx'
|
||||
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import OrderItemsIcon from '../../../Icons/OrderItemIcon.jsx'
|
||||
import NewOrderItem from '../../Inventory/OrderItems/NewOrderItem.jsx'
|
||||
import NewShipment from '../../Inventory/Shipments/NewShipment.jsx'
|
||||
import PostSalesOrder from './PostSalesOrder.jsx'
|
||||
import ConfirmSalesOrder from './ConfirmSalesOrder.jsx'
|
||||
import CancelSalesOrder from './CancelSalesOrder.jsx'
|
||||
import ShipmentIcon from '../../../Icons/ShipmentIcon.jsx'
|
||||
import InvoiceIcon from '../../../Icons/InvoiceIcon.jsx'
|
||||
import StockEventIcon from '../../../Icons/StockEventIcon.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import NewInvoice from '../../Finance/Invoices/NewInvoice.jsx'
|
||||
|
||||
const log = loglevel.getLogger('SalesOrderInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
|
||||
const SalesOrderInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const orderItemsTableRef = useRef(null)
|
||||
const shipmentsTableRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const [newOrderItemOpen, setNewOrderItemOpen] = useState(false)
|
||||
const [newShipmentOpen, setNewShipmentOpen] = useState(false)
|
||||
const [postSalesOrderOpen, setPostSalesOrderOpen] = useState(false)
|
||||
const [confirmSalesOrderOpen, setConfirmSalesOrderOpen] = useState(false)
|
||||
const [cancelSalesOrderOpen, setCancelSalesOrderOpen] = useState(false)
|
||||
const [newInvoiceOpen, setNewInvoiceOpen] = useState(false)
|
||||
const salesOrderId = new URLSearchParams(location.search).get('salesOrderId')
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'SalesOrderInfo',
|
||||
{
|
||||
info: true,
|
||||
notes: true,
|
||||
auditLogs: true,
|
||||
invoices: true,
|
||||
stockEvents: true
|
||||
}
|
||||
)
|
||||
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
lock: null,
|
||||
loading: false,
|
||||
objectData: {}
|
||||
})
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.handleFetchObject?.()
|
||||
return true
|
||||
},
|
||||
edit: () => {
|
||||
orderItemsTableRef?.current?.startEditing?.()
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
orderItemsTableRef?.current?.cancelEditing?.()
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
orderItemsTableRef?.current?.handleUpdate?.()
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
},
|
||||
delete: () => {
|
||||
objectFormRef?.current?.handleDelete?.()
|
||||
return true
|
||||
},
|
||||
newOrderItem: () => {
|
||||
setNewOrderItemOpen(true)
|
||||
return true
|
||||
},
|
||||
newShipment: () => {
|
||||
setNewShipmentOpen(true)
|
||||
return true
|
||||
},
|
||||
newInvoice: () => {
|
||||
setNewInvoiceOpen(true)
|
||||
return true
|
||||
},
|
||||
post: () => {
|
||||
setPostSalesOrderOpen(true)
|
||||
return true
|
||||
},
|
||||
confirm: () => {
|
||||
setConfirmSalesOrderOpen(true)
|
||||
return true
|
||||
},
|
||||
cancel: () => {
|
||||
setCancelSalesOrderOpen(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const editDisabled = getModelByName('salesOrder')
|
||||
.actions.find((action) => action.name === 'edit')
|
||||
.disabled(objectFormState.objectData)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
gap='large'
|
||||
vertical='true'
|
||||
style={{
|
||||
maxHeight: '100%',
|
||||
minHeight: 0
|
||||
}}
|
||||
>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='middle'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='salesOrder'
|
||||
id={salesOrderId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Sales Order Information' },
|
||||
{ key: 'orderItems', label: 'Order Items' },
|
||||
{ key: 'shipments', label: 'Shipments' },
|
||||
{ key: 'invoices', label: 'Invoices' },
|
||||
{ key: 'stockEvents', label: 'Stock Events' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
type='salesOrder'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
</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 ||
|
||||
editDisabled
|
||||
}
|
||||
loading={objectFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<ObjectForm
|
||||
id={salesOrderId}
|
||||
type='salesOrder'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<Flex vertical gap={'large'}>
|
||||
<InfoCollapse
|
||||
title='Sales Order Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('info', expanded)
|
||||
}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
isEditing={isEditing}
|
||||
type='salesOrder'
|
||||
labelWidth='225px'
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
items: false
|
||||
}}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Sales Order Items'
|
||||
icon={<OrderItemsIcon />}
|
||||
active={collapseState.orderItems}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('orderItems', expanded)
|
||||
}
|
||||
collapseKey='orderItems'
|
||||
>
|
||||
<ObjectTable
|
||||
type='orderItem'
|
||||
masterFilter={{
|
||||
'order._id': salesOrderId,
|
||||
orderType: 'salesOrder'
|
||||
}}
|
||||
visibleColumns={{ order: false }}
|
||||
ref={orderItemsTableRef}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Shipments'
|
||||
icon={<ShipmentIcon />}
|
||||
active={collapseState.shipments}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('shipments', expanded)
|
||||
}
|
||||
collapseKey='shipments'
|
||||
>
|
||||
<ObjectTable
|
||||
type='shipment'
|
||||
masterFilter={{
|
||||
'order._id': salesOrderId,
|
||||
orderType: 'salesOrder'
|
||||
}}
|
||||
visibleColumns={{ order: false }}
|
||||
ref={shipmentsTableRef}
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Invoices'
|
||||
icon={<InvoiceIcon />}
|
||||
active={collapseState.invoices}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('invoices', expanded)
|
||||
}
|
||||
collapseKey='invoices'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='invoice'
|
||||
masterFilter={{
|
||||
'order._id': salesOrderId,
|
||||
orderType: 'salesOrder'
|
||||
}}
|
||||
visibleColumns={{ order: false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Stock Events'
|
||||
icon={<StockEventIcon />}
|
||||
active={collapseState.stockEvents}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('stockEvents', expanded)
|
||||
}
|
||||
collapseKey='stockEvents'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='stockEvent'
|
||||
masterFilter={{
|
||||
'owner._id': salesOrderId
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</ActionHandler>
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
active={collapseState.notes}
|
||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||
collapseKey='notes'
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel _id={salesOrderId} type='salesOrder' />
|
||||
</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': salesOrderId }}
|
||||
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
<Modal
|
||||
open={newOrderItemOpen}
|
||||
onCancel={() => {
|
||||
setNewOrderItemOpen(false)
|
||||
}}
|
||||
width={800}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
<NewOrderItem
|
||||
onOk={() => {
|
||||
setNewOrderItemOpen(false)
|
||||
}}
|
||||
reset={newOrderItemOpen}
|
||||
defaultValues={{
|
||||
order: { _id: salesOrderId },
|
||||
orderType: 'salesOrder',
|
||||
syncAmount: 'itemPrice'
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={newShipmentOpen}
|
||||
onCancel={() => {
|
||||
setNewShipmentOpen(false)
|
||||
}}
|
||||
width={800}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
<NewShipment
|
||||
onOk={() => {
|
||||
setNewShipmentOpen(false)
|
||||
}}
|
||||
reset={newShipmentOpen}
|
||||
defaultValues={{
|
||||
orderType: 'salesOrder',
|
||||
order: { _id: salesOrderId }
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={newInvoiceOpen}
|
||||
onCancel={() => {
|
||||
setNewInvoiceOpen(false)
|
||||
}}
|
||||
width={800}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
>
|
||||
<NewInvoice
|
||||
onOk={() => {
|
||||
setNewInvoiceOpen(false)
|
||||
}}
|
||||
reset={newInvoiceOpen}
|
||||
defaultValues={{
|
||||
orderType: 'salesOrder',
|
||||
order: { ...objectFormState.objectData }
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={postSalesOrderOpen}
|
||||
onCancel={() => {
|
||||
setPostSalesOrderOpen(false)
|
||||
}}
|
||||
width={500}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
centered={true}
|
||||
>
|
||||
<PostSalesOrder
|
||||
onOk={() => {
|
||||
setPostSalesOrderOpen(false)
|
||||
actions.reload()
|
||||
}}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={confirmSalesOrderOpen}
|
||||
onCancel={() => {
|
||||
setConfirmSalesOrderOpen(false)
|
||||
}}
|
||||
width={515}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
centered={true}
|
||||
>
|
||||
<ConfirmSalesOrder
|
||||
onOk={() => {
|
||||
setConfirmSalesOrderOpen(false)
|
||||
actions.reload()
|
||||
}}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
open={cancelSalesOrderOpen}
|
||||
onCancel={() => {
|
||||
setCancelSalesOrderOpen(false)
|
||||
}}
|
||||
width={515}
|
||||
footer={null}
|
||||
destroyOnHidden={true}
|
||||
centered={true}
|
||||
>
|
||||
<CancelSalesOrder
|
||||
onOk={() => {
|
||||
setCancelSalesOrderOpen(false)
|
||||
actions.reload()
|
||||
}}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SalesOrderInfo
|
||||
@ -1,62 +0,0 @@
|
||||
import { useContext } from 'react'
|
||||
import { Flex } from 'antd'
|
||||
import useCollapseState from '../hooks/useCollapseState'
|
||||
import StatsDisplay from '../common/StatsDisplay'
|
||||
import InfoCollapse from '../common/InfoCollapse'
|
||||
import ScrollBox from '../common/ScrollBox'
|
||||
|
||||
import { ApiServerContext } from '../context/ApiServerContext'
|
||||
|
||||
const SalesOverview = () => {
|
||||
const { connected } = useContext(ApiServerContext)
|
||||
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'SalesOverview',
|
||||
{
|
||||
clientStats: true,
|
||||
salesOrderStats: true
|
||||
}
|
||||
)
|
||||
|
||||
if (!connected) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
gap='large'
|
||||
vertical='true'
|
||||
style={{
|
||||
maxHeight: '100%',
|
||||
minHeight: 0
|
||||
}}
|
||||
>
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<InfoCollapse
|
||||
title='Sales Order Statistics'
|
||||
icon={null}
|
||||
active={collapseState.salesOrderStats}
|
||||
onToggle={(isActive) =>
|
||||
updateCollapseState('salesOrderStats', isActive)
|
||||
}
|
||||
className='no-t-padding-collapse'
|
||||
collapseKey='salesOrderStats'
|
||||
canCollapse={false}
|
||||
>
|
||||
<Flex
|
||||
justify='flex-start'
|
||||
gap='middle'
|
||||
wrap='wrap'
|
||||
align='flex-start'
|
||||
>
|
||||
<StatsDisplay objectType='salesOrder' />
|
||||
</Flex>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export default SalesOverview
|
||||
@ -1,54 +0,0 @@
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import DashboardSidebar from '../common/DashboardSidebar'
|
||||
import ClientIcon from '../../Icons/ClientIcon'
|
||||
import SalesIcon from '../../Icons/SalesIcon'
|
||||
|
||||
import SalesOrderIcon from '../../Icons/SalesOrderIcon'
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: 'overview',
|
||||
label: 'Overview',
|
||||
icon: <SalesIcon />,
|
||||
path: '/dashboard/sales/overview'
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: 'clients',
|
||||
label: 'Clients',
|
||||
icon: <ClientIcon />,
|
||||
path: '/dashboard/sales/clients'
|
||||
},
|
||||
{
|
||||
key: 'salesorders',
|
||||
label: 'Sales Orders',
|
||||
icon: <SalesOrderIcon />,
|
||||
path: '/dashboard/sales/salesorders'
|
||||
}
|
||||
]
|
||||
|
||||
const routeKeyMap = {
|
||||
'/dashboard/sales/overview': 'overview',
|
||||
'/dashboard/sales/clients': 'clients',
|
||||
'/dashboard/sales/salesorders': 'salesorders'
|
||||
}
|
||||
|
||||
const SalesSidebar = (props) => {
|
||||
const location = useLocation()
|
||||
const selectedKey = (() => {
|
||||
const match = Object.keys(routeKeyMap).find((path) => {
|
||||
const pathSplit = path.split('/')
|
||||
const locationPathSplit = location.pathname.split('/')
|
||||
if (pathSplit.length > locationPathSplit.length) return false
|
||||
for (let i = 0; i < pathSplit.length; i++) {
|
||||
if (pathSplit[i] !== locationPathSplit[i]) return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return match ? routeKeyMap[match] : 'overview'
|
||||
})()
|
||||
|
||||
return <DashboardSidebar items={items} selectedKey={selectedKey} {...props} />
|
||||
}
|
||||
|
||||
export default SalesSidebar
|
||||
@ -11,7 +11,6 @@ const breadcrumbNameMap = {
|
||||
management: 'Management',
|
||||
developer: 'Developer',
|
||||
finance: 'Finance',
|
||||
sales: 'Sales',
|
||||
overview: 'Overview',
|
||||
info: 'Info',
|
||||
design: 'Design',
|
||||
|
||||
@ -31,7 +31,6 @@ import MenuIcon from '../../Icons/MenuIcon'
|
||||
import ProductionIcon from '../../Icons/ProductionIcon'
|
||||
import InventoryIcon from '../../Icons/InventoryIcon'
|
||||
import FinanceIcon from '../../Icons/FinanceIcon'
|
||||
import SalesIcon from '../../Icons/SalesIcon'
|
||||
import PersonIcon from '../../Icons/PersonIcon'
|
||||
import CloudIcon from '../../Icons/CloudIcon'
|
||||
import BellIcon from '../../Icons/BellIcon'
|
||||
@ -72,11 +71,6 @@ const DashboardNavigation = () => {
|
||||
label: 'Inventory',
|
||||
icon: <InventoryIcon />
|
||||
},
|
||||
{
|
||||
key: 'sales',
|
||||
label: 'Sales',
|
||||
icon: <SalesIcon />
|
||||
},
|
||||
{
|
||||
key: 'finance',
|
||||
label: 'Finance',
|
||||
@ -147,8 +141,6 @@ const DashboardNavigation = () => {
|
||||
navigate('/dashboard/inventory/overview')
|
||||
} else if (key === 'finance') {
|
||||
navigate('/dashboard/finance/overview')
|
||||
} else if (key === 'sales') {
|
||||
navigate('/dashboard/sales/overview')
|
||||
} else if (key === 'management') {
|
||||
navigate('/dashboard/management/filaments')
|
||||
}
|
||||
|
||||
@ -141,10 +141,6 @@ const NewObjectForm = ({ type, style, defaultValues = {}, children }) => {
|
||||
const computedValuesObject = buildObjectFromEntries(computedEntries)
|
||||
const initialFormData = merge({}, defaultValues, computedValuesObject)
|
||||
form.setFieldsValue(initialFormData)
|
||||
form
|
||||
.validateFields({ validateOnly: true })
|
||||
.then(() => setFormValid(true))
|
||||
.catch(() => setFormValid(false))
|
||||
setObjectData((prev) => merge({}, prev, initialFormData))
|
||||
}
|
||||
}, [form, defaultValues, calculateComputedValues, model])
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMemo, useEffect, useRef } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Table, Skeleton, Card, Button, Flex, Typography } from 'antd'
|
||||
import { Table, Skeleton, Card, Button, Flex, Form, Typography } from 'antd'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import ObjectProperty from './ObjectProperty'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
@ -21,14 +21,6 @@ const getDefaultWidth = (type) => {
|
||||
return DEFAULT_COLUMN_WIDTHS[type] || 200
|
||||
}
|
||||
|
||||
const resolveChangeValue = (val, type) => {
|
||||
if (type === 'bool') return val
|
||||
if (val?.target && typeof val.target === 'object') {
|
||||
return val.target.value
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
const createSkeletonRows = (rowCount, keyPrefix, keyName) => {
|
||||
return Array.from({ length: rowCount }).map((_, index) => {
|
||||
const skeletonKey = `${keyPrefix}-${index}`
|
||||
@ -59,6 +51,7 @@ const ObjectChildTable = ({
|
||||
additionalColumns = [],
|
||||
emptyText = 'No items',
|
||||
isEditing = false,
|
||||
formListName,
|
||||
value = [],
|
||||
rollups = [],
|
||||
onChange,
|
||||
@ -110,44 +103,34 @@ const ObjectChildTable = ({
|
||||
return value ?? []
|
||||
}, [value])
|
||||
|
||||
// When used with antd Form.List, grab the form instance so we can read
|
||||
// the latest row values and pass them into ObjectProperty as objectData.
|
||||
// Assumes this component is rendered within a Form context when editing.
|
||||
const formInstance = Form.useFormInstance()
|
||||
|
||||
const listNamePath = useMemo(() => {
|
||||
if (!formListName) return null
|
||||
return Array.isArray(formListName) ? formListName : [formListName]
|
||||
}, [formListName])
|
||||
|
||||
const tableColumns = useMemo(() => {
|
||||
const propertyColumns = resolvedProperties.map((property) => ({
|
||||
title: property.label || property.name,
|
||||
dataIndex: property.name,
|
||||
key: property.name,
|
||||
width: property.columnWidth || getDefaultWidth(property.type),
|
||||
render: (_text, record, index) => {
|
||||
render: (_text, record) => {
|
||||
if (record?.isSkeleton) {
|
||||
return (
|
||||
<Skeleton.Input active size='small' style={{ width: '100%' }} />
|
||||
)
|
||||
}
|
||||
|
||||
const handleCellChange = (newVal) => {
|
||||
const resolved = resolveChangeValue(newVal, property.type)
|
||||
const currentItems = Array.isArray(itemsSource)
|
||||
? [...itemsSource]
|
||||
: []
|
||||
const updatedItem = {
|
||||
...currentItems[index],
|
||||
[property.name]: resolved
|
||||
}
|
||||
currentItems[index] = updatedItem
|
||||
if (typeof onChange === 'function') {
|
||||
onChange(currentItems)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<ObjectProperty
|
||||
{...property}
|
||||
longId={false}
|
||||
objectData={record}
|
||||
isEditing={isEditing}
|
||||
useFormItem={false}
|
||||
name={undefined}
|
||||
value={record[property.name]}
|
||||
onChange={handleCellChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -433,6 +416,122 @@ const ObjectChildTable = ({
|
||||
</Flex>
|
||||
)
|
||||
|
||||
// When editing and a Form.List name is provided, bind rows via Form.List
|
||||
// instead of the manual value/onChange mechanism.
|
||||
if (isEditing === true && formListName) {
|
||||
return (
|
||||
<Form.List name={formListName}>
|
||||
{(fields, { add, remove }) => {
|
||||
const listDataSource = fields.map((field, index) => ({
|
||||
_field: field,
|
||||
_index: index,
|
||||
key: field.key
|
||||
}))
|
||||
const listColumns = resolvedProperties.map((property) => ({
|
||||
title: property.label || property.name,
|
||||
dataIndex: property.name,
|
||||
key: property.name,
|
||||
width: property.columnWidth || getDefaultWidth(property.type),
|
||||
render: (_text, record) => {
|
||||
const field = record?._field
|
||||
if (!field) return null
|
||||
|
||||
// Resolve the most up-to-date row data for this index from the form
|
||||
let rowObjectData = undefined
|
||||
if (formInstance && listNamePath) {
|
||||
const namePath = [...listNamePath, field.name]
|
||||
rowObjectData = formInstance.getFieldValue(namePath)
|
||||
}
|
||||
return (
|
||||
<ObjectProperty
|
||||
{...property}
|
||||
// Bind directly to this list item + property via NamePath
|
||||
name={[field.name, property.name]}
|
||||
longId={false}
|
||||
isEditing={true}
|
||||
objectData={rowObjectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}))
|
||||
|
||||
const deleteColumn = {
|
||||
title: '',
|
||||
key: 'delete',
|
||||
width: 10,
|
||||
fixed: 'right',
|
||||
render: (_text, record) => {
|
||||
const field = record?._field
|
||||
const index = record?._index
|
||||
if (!field || index == null) return null
|
||||
return (
|
||||
<Button
|
||||
type='text'
|
||||
danger
|
||||
size='small'
|
||||
icon={<BinIcon />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
if (typeof remove === 'function') {
|
||||
remove(index)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const listTable = (
|
||||
<Flex vertical>
|
||||
<div ref={mainTableWrapperRef}>
|
||||
<Table
|
||||
dataSource={listDataSource}
|
||||
columns={[...listColumns, ...additionalColumns, deleteColumn]}
|
||||
pagination={false}
|
||||
size={size}
|
||||
loading={loading}
|
||||
rowKey={(record) => record.key ?? record._index}
|
||||
scroll={scrollConfig}
|
||||
locale={{ emptyText }}
|
||||
className={hasRollups ? 'child-table-rollups' : 'child-table'}
|
||||
style={{ maxWidth, minWidth: 0 }}
|
||||
{...tableProps}
|
||||
/>
|
||||
</div>
|
||||
{rollupTable}
|
||||
</Flex>
|
||||
)
|
||||
|
||||
const handleAddListItem = () => {
|
||||
const newItem = {}
|
||||
resolvedProperties.forEach((property) => {
|
||||
if (property?.name) {
|
||||
newItem[property.name] = null
|
||||
}
|
||||
})
|
||||
add(newItem)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card style={{ minWidth: 0 }}>
|
||||
<Flex vertical gap={'middle'}>
|
||||
<Flex justify={'space-between'}>
|
||||
<Button>Actions</Button>
|
||||
<Button
|
||||
type='primary'
|
||||
icon={<PlusIcon />}
|
||||
onClick={handleAddListItem}
|
||||
/>
|
||||
</Flex>
|
||||
{listTable}
|
||||
</Flex>
|
||||
</Card>
|
||||
)
|
||||
}}
|
||||
</Form.List>
|
||||
)
|
||||
}
|
||||
|
||||
if (isEditing === true) {
|
||||
return (
|
||||
<Card>
|
||||
@ -472,6 +571,7 @@ ObjectChildTable.propTypes = {
|
||||
additionalColumns: PropTypes.arrayOf(PropTypes.object),
|
||||
emptyText: PropTypes.node,
|
||||
isEditing: PropTypes.bool,
|
||||
formListName: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
value: PropTypes.arrayOf(PropTypes.object),
|
||||
onChange: PropTypes.func,
|
||||
maxWidth: PropTypes.string,
|
||||
|
||||
@ -14,17 +14,10 @@ import { useMessageContext } from '../context/MessageContext'
|
||||
import PropTypes from 'prop-types'
|
||||
import DeleteObjectModal from './DeleteObjectModal'
|
||||
import merge from 'lodash/merge'
|
||||
import mergeWith from 'lodash/mergeWith'
|
||||
import set from 'lodash/set'
|
||||
import { getModelByName } from '../../../database/ObjectModels'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
const arrayReplaceCustomizer = (objValue, srcValue) => {
|
||||
if (Array.isArray(srcValue)) {
|
||||
return srcValue
|
||||
}
|
||||
}
|
||||
|
||||
const buildObjectFromEntries = (entries = []) => {
|
||||
return entries.reduce((acc, entry) => {
|
||||
const { namePath, value } = entry || {}
|
||||
@ -292,14 +285,14 @@ const ObjectForm = forwardRef(
|
||||
const lockEvent = await fetchObjectLock(id, type)
|
||||
setLock(lockEvent)
|
||||
onStateChangeRef.current({ lock: lockEvent })
|
||||
|
||||
setObjectData({ ...data, _isEditing: isEditingRef.current })
|
||||
serverObjectData.current = data
|
||||
|
||||
// Calculate and set computed values on initial load
|
||||
const computedEntries = calculateComputedValues(data, model)
|
||||
const computedValuesObject = buildObjectFromEntries(computedEntries)
|
||||
const initialFormData = merge({}, data, computedValuesObject)
|
||||
setObjectData({ ...initialFormData, _isEditing: isEditingRef.current })
|
||||
|
||||
form.setFieldsValue(initialFormData)
|
||||
setFetchLoading(false)
|
||||
onStateChangeRef.current({ loading: false })
|
||||
@ -325,9 +318,7 @@ const ObjectForm = forwardRef(
|
||||
|
||||
// Update event handler
|
||||
const updateObjectEventHandler = useCallback((value) => {
|
||||
setObjectData((prev) =>
|
||||
mergeWith({}, prev, value, arrayReplaceCustomizer)
|
||||
)
|
||||
setObjectData((prev) => merge({}, prev, value))
|
||||
}, [])
|
||||
|
||||
// Update event handler
|
||||
@ -549,22 +540,15 @@ const ObjectForm = forwardRef(
|
||||
}
|
||||
|
||||
const computedValuesObject = buildObjectFromEntries(computedEntries)
|
||||
const mergedFormValues = mergeWith(
|
||||
const mergedFormValues = merge(
|
||||
{},
|
||||
allFormValues,
|
||||
arrayReplaceCustomizer
|
||||
computedValuesObject
|
||||
)
|
||||
merge(mergedFormValues, computedValuesObject)
|
||||
|
||||
mergedFormValues._isEditing = isEditingRef.current
|
||||
|
||||
setObjectData((prev) => {
|
||||
return mergeWith(
|
||||
{},
|
||||
prev,
|
||||
mergedFormValues,
|
||||
arrayReplaceCustomizer
|
||||
)
|
||||
return merge({}, prev, mergedFormValues)
|
||||
})
|
||||
}}
|
||||
>
|
||||
|
||||
@ -61,7 +61,6 @@ const MATERIAL_OPTIONS = [
|
||||
const ObjectProperty = ({
|
||||
type = 'text',
|
||||
prefix,
|
||||
size,
|
||||
suffix,
|
||||
value,
|
||||
min,
|
||||
@ -86,7 +85,6 @@ const ObjectProperty = ({
|
||||
minimal = false,
|
||||
previewOpen = false,
|
||||
showPreview = true,
|
||||
useFormItem = true,
|
||||
options = [],
|
||||
roundNumber = false,
|
||||
fixedNumber = false,
|
||||
@ -412,7 +410,6 @@ const ObjectProperty = ({
|
||||
maxWidth={maxWidth}
|
||||
loading={loading}
|
||||
rollups={rollups}
|
||||
size={size}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -616,219 +613,254 @@ const ObjectProperty = ({
|
||||
mergedFormItemProps.onChange = onChange
|
||||
}
|
||||
|
||||
const inputProps = useFormItem
|
||||
? {}
|
||||
: {
|
||||
value,
|
||||
onChange,
|
||||
disabled
|
||||
}
|
||||
|
||||
const renderInput = () => {
|
||||
switch (type) {
|
||||
case 'netGross':
|
||||
return (
|
||||
switch (type) {
|
||||
case 'netGross':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<NetGrossInput
|
||||
difference={difference}
|
||||
prefix={prefix}
|
||||
suffix={suffix}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
case 'secret':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'secret':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<Input.Password
|
||||
placeholder={label}
|
||||
disabled={disabled}
|
||||
{...mergedFormItemProps}
|
||||
iconRender={(visible) =>
|
||||
visible ? <EyeSlashIcon /> : <EyeIcon />
|
||||
}
|
||||
{...mergedFormItemProps}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
case 'wsprotocol':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'wsprotocol':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<Select
|
||||
defaultValue='ws'
|
||||
disabled={disabled}
|
||||
options={[
|
||||
{ value: 'ws', label: 'Websocket' },
|
||||
{ value: 'wss', label: 'Websocket Secure' }
|
||||
]}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
case 'select':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'select':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<CustomSelect
|
||||
placeholder={'Select a ' + label.toLowerCase() + '...'}
|
||||
disabled={disabled}
|
||||
options={Array.isArray(options) ? options : []}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
case 'priceMode':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'priceMode':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<Select
|
||||
defaultValue='margin'
|
||||
disabled={disabled}
|
||||
options={[
|
||||
{ value: 'margin', label: 'Margin %' },
|
||||
{ value: 'amount', label: '£ Amount' }
|
||||
]}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
case 'bool':
|
||||
return (
|
||||
<Switch
|
||||
{...inputProps}
|
||||
{...(useFormItem ? {} : { checked: value })}
|
||||
/>
|
||||
)
|
||||
case 'dateTime':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'bool':
|
||||
return (
|
||||
<Form.Item
|
||||
name={formItemName}
|
||||
{...mergedFormItemProps}
|
||||
valuePropName='checked'
|
||||
>
|
||||
<Switch disabled={disabled} />
|
||||
</Form.Item>
|
||||
)
|
||||
case 'dateTime':
|
||||
return (
|
||||
<Form.Item
|
||||
name={formItemName}
|
||||
{...mergedFormItemProps}
|
||||
getValueProps={(v) => ({ value: v ? dayjs(v) : null })}
|
||||
>
|
||||
<DatePicker
|
||||
showTime
|
||||
style={{ width: '100%' }}
|
||||
{...inputProps}
|
||||
{...(useFormItem ? {} : { value: value ? dayjs(value) : null })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
case 'country':
|
||||
return <CountrySelect {...inputProps} />
|
||||
case 'color':
|
||||
return (
|
||||
<ColorSelector
|
||||
required={required}
|
||||
{...inputProps}
|
||||
{...(useFormItem ? {} : { value })}
|
||||
/>
|
||||
)
|
||||
case 'weight':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'country':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<CountrySelect disabled={disabled} />
|
||||
</Form.Item>
|
||||
)
|
||||
case 'color':
|
||||
return (
|
||||
<Form.Item
|
||||
name={formItemName}
|
||||
{...mergedFormItemProps}
|
||||
valuePropName='value'
|
||||
getValueFromEvent={(v) => v}
|
||||
>
|
||||
<ColorSelector required={required} disabled={disabled} />
|
||||
</Form.Item>
|
||||
)
|
||||
case 'weight':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<InputNumber
|
||||
suffix='g'
|
||||
style={{ width: '100%' }}
|
||||
placeholder={label}
|
||||
{...inputProps}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
case 'number':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'number':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<InputNumber
|
||||
placeholder={label}
|
||||
disabled={disabled}
|
||||
prefix={prefix}
|
||||
suffix={suffix}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
{...mergedFormItemProps}
|
||||
style={{ width: '100%' }}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
case 'text':
|
||||
return <Input placeholder={label} {...inputProps} />
|
||||
case 'codeBlock':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'text':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<Input
|
||||
placeholder={label}
|
||||
{...mergedFormItemProps}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
case 'codeBlock':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<CodeBlockEditor
|
||||
code={value}
|
||||
language={language}
|
||||
disabled={disabled}
|
||||
height={height}
|
||||
minimal={minimal}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
case 'markdown':
|
||||
return <MarkdownInput {...inputProps} />
|
||||
case 'material':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'markdown':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<MarkdownInput value={value} />
|
||||
</Form.Item>
|
||||
)
|
||||
case 'material':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<Select
|
||||
options={MATERIAL_OPTIONS}
|
||||
placeholder={label}
|
||||
{...inputProps}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
case 'id':
|
||||
// id is not editable, just show view mode
|
||||
if (value) {
|
||||
return <IdDisplay id={value} type={objectType} {...rest} />
|
||||
} else {
|
||||
return (
|
||||
<Text type='secondary' {...textParams}>
|
||||
n/a
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
case 'object':
|
||||
</Form.Item>
|
||||
)
|
||||
case 'id':
|
||||
// id is not editable, just show view mode
|
||||
if (value) {
|
||||
return <IdDisplay id={value} type={objectType} {...rest} />
|
||||
} else {
|
||||
return (
|
||||
<Text type='secondary' {...textParams}>
|
||||
n/a
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
case 'object':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<ObjectSelect
|
||||
type={objectType}
|
||||
disabled={disabled}
|
||||
masterFilter={masterFilter}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
case 'objectType':
|
||||
return (
|
||||
<ObjectTypeSelect masterFilter={masterFilter} {...inputProps} />
|
||||
)
|
||||
case 'objectList':
|
||||
return <ObjectSelect type={objectType} multiple {...inputProps} />
|
||||
case 'tags':
|
||||
return <TagsInput {...inputProps} />
|
||||
case 'file':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'objectType':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<ObjectTypeSelect disabled={disabled} masterFilter={masterFilter} />
|
||||
</Form.Item>
|
||||
)
|
||||
case 'objectList':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<ObjectSelect type={objectType} multiple disabled={disabled} />
|
||||
</Form.Item>
|
||||
)
|
||||
case 'tags':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<TagsInput />
|
||||
</Form.Item>
|
||||
)
|
||||
case 'file':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<FileUpload
|
||||
value={value}
|
||||
multiple={false}
|
||||
defaultPreviewOpen={previewOpen}
|
||||
showPreview={showPreview}
|
||||
showInfo={showHyperlink}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
case 'fileList':
|
||||
return (
|
||||
</Form.Item>
|
||||
)
|
||||
case 'fileList':
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<FileUpload
|
||||
value={value}
|
||||
multiple={true}
|
||||
defaultPreviewOpen={previewOpen}
|
||||
showPreview={showPreview}
|
||||
showInfo={showHyperlink}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
case 'objectChildren': {
|
||||
return (
|
||||
<ObjectChildTable
|
||||
properties={properties}
|
||||
objectData={objectData}
|
||||
isEditing={true}
|
||||
rollups={rollups}
|
||||
size={size}
|
||||
{...inputProps}
|
||||
/>
|
||||
)
|
||||
}
|
||||
default:
|
||||
return <Input placeholder={label} {...inputProps} />
|
||||
</Form.Item>
|
||||
)
|
||||
case 'objectChildren': {
|
||||
return (
|
||||
<ObjectChildTable
|
||||
value={value}
|
||||
properties={properties}
|
||||
objectData={objectData}
|
||||
isEditing={true}
|
||||
formListName={formItemName}
|
||||
rollups={rollups}
|
||||
/>
|
||||
)
|
||||
}
|
||||
default:
|
||||
return (
|
||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||
<Input placeholder={label} {...mergedFormItemProps} />
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
if (!useFormItem) {
|
||||
return renderInput()
|
||||
}
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
name={formItemName}
|
||||
{...mergedFormItemProps}
|
||||
{...(type === 'bool' ? { valuePropName: 'checked' } : {})}
|
||||
{...(type === 'color'
|
||||
? { valuePropName: 'value', getValueFromEvent: (v) => v }
|
||||
: {})}
|
||||
{...(type === 'dateTime'
|
||||
? { getValueProps: (v) => ({ value: v ? dayjs(v) : null }) }
|
||||
: {})}
|
||||
>
|
||||
{renderInput()}
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
|
||||
const property = renderProperty()
|
||||
@ -860,7 +892,6 @@ ObjectProperty.propTypes = {
|
||||
height: PropTypes.string,
|
||||
previewOpen: PropTypes.bool,
|
||||
showPreview: PropTypes.bool,
|
||||
useFormItem: PropTypes.bool,
|
||||
showHyperlink: PropTypes.bool,
|
||||
options: PropTypes.array,
|
||||
showSince: PropTypes.bool,
|
||||
|
||||
@ -41,7 +41,6 @@ import { useNavigate } from 'react-router-dom'
|
||||
import QuestionCircleIcon from '../../Icons/QuestionCircleIcon'
|
||||
import { AuthContext } from '../context/AuthContext'
|
||||
import { ElectronContext } from '../context/ElectronContext'
|
||||
import ActionsIcon from '../../Icons/ActionsIcon'
|
||||
|
||||
const logger = loglevel.getLogger('DasboardTable')
|
||||
logger.setLevel(config.logLevel)
|
||||
@ -759,11 +758,11 @@ const ObjectTable = forwardRef(
|
||||
}
|
||||
})
|
||||
|
||||
if (rowActions.length > 0 && tableData.some((item) => !item.isSkeleton)) {
|
||||
if (rowActions.length > 0) {
|
||||
columnsWithSkeleton.push({
|
||||
title: (
|
||||
<Flex gap='small' align='center' justify='center'>
|
||||
<ActionsIcon />
|
||||
{'Actions'}
|
||||
</Flex>
|
||||
),
|
||||
key: 'actions',
|
||||
|
||||
@ -101,13 +101,9 @@ const StateTag = ({ state, showBadge = true, style = {} }) => {
|
||||
text = 'Sent'
|
||||
break
|
||||
case 'acknowledged':
|
||||
status = 'purple'
|
||||
status = 'processing'
|
||||
text = 'Acknowledged'
|
||||
break
|
||||
case 'confirmed':
|
||||
status = 'purple'
|
||||
text = 'Confirmed'
|
||||
break
|
||||
case 'ordered':
|
||||
status = 'cyan'
|
||||
text = 'Ordered'
|
||||
|
||||
@ -8,33 +8,25 @@ const { Text } = Typography
|
||||
|
||||
const formatTimeDifference = (dateTime) => {
|
||||
const now = dayjs()
|
||||
const target = dayjs(dateTime)
|
||||
const isFuture = target.isAfter(now)
|
||||
const diff = dayjs(dateTime)
|
||||
|
||||
// If future, calculate from target to now; if past, from now to target
|
||||
const baseDate = isFuture ? target : now
|
||||
const compareDate = isFuture ? now : target
|
||||
|
||||
const years = baseDate.diff(compareDate, 'year')
|
||||
const months = baseDate.diff(compareDate.add(years, 'year'), 'month')
|
||||
const weeks = baseDate.diff(
|
||||
compareDate.add(years, 'year').add(months, 'month'),
|
||||
'week'
|
||||
)
|
||||
const days = baseDate.diff(
|
||||
compareDate.add(years, 'year').add(months, 'month').add(weeks, 'week'),
|
||||
const years = now.diff(diff, 'year')
|
||||
const months = now.diff(diff.add(years, 'year'), 'month')
|
||||
const weeks = now.diff(diff.add(years, 'year').add(months, 'month'), 'week')
|
||||
const days = now.diff(
|
||||
diff.add(years, 'year').add(months, 'month').add(weeks, 'week'),
|
||||
'day'
|
||||
)
|
||||
const hours = baseDate.diff(
|
||||
compareDate
|
||||
const hours = now.diff(
|
||||
diff
|
||||
.add(years, 'year')
|
||||
.add(months, 'month')
|
||||
.add(weeks, 'week')
|
||||
.add(days, 'day'),
|
||||
'hour'
|
||||
)
|
||||
const minutes = baseDate.diff(
|
||||
compareDate
|
||||
const minutes = now.diff(
|
||||
diff
|
||||
.add(years, 'year')
|
||||
.add(months, 'month')
|
||||
.add(weeks, 'week')
|
||||
@ -42,8 +34,8 @@ const formatTimeDifference = (dateTime) => {
|
||||
.add(hours, 'hour'),
|
||||
'minute'
|
||||
)
|
||||
const seconds = baseDate.diff(
|
||||
compareDate
|
||||
const seconds = now.diff(
|
||||
diff
|
||||
.add(years, 'year')
|
||||
.add(months, 'month')
|
||||
.add(weeks, 'week')
|
||||
@ -53,24 +45,21 @@ const formatTimeDifference = (dateTime) => {
|
||||
'second'
|
||||
)
|
||||
|
||||
let timeStr = ''
|
||||
if (years > 0) {
|
||||
timeStr = `${years} ${years === 1 ? 'year' : 'years'}`
|
||||
return `${years}y`
|
||||
} else if (months > 0) {
|
||||
timeStr = `${months} ${months === 1 ? 'month' : 'months'}`
|
||||
return `${months}mo`
|
||||
} else if (weeks > 0) {
|
||||
timeStr = `${weeks} ${weeks === 1 ? 'week' : 'weeks'}`
|
||||
return `${weeks}w`
|
||||
} else if (days > 0) {
|
||||
timeStr = `${days} ${days === 1 ? 'day' : 'days'}`
|
||||
return `${days}d`
|
||||
} else if (hours > 0) {
|
||||
timeStr = `${hours} ${hours === 1 ? 'hour' : 'hours'}`
|
||||
return `${hours}h`
|
||||
} else if (minutes > 0) {
|
||||
timeStr = `${minutes} ${minutes === 1 ? 'minute' : 'minutes'}`
|
||||
return `${minutes}m`
|
||||
} else {
|
||||
timeStr = `${seconds} ${seconds === 1 ? 'second' : 'seconds'}`
|
||||
return `${seconds}s`
|
||||
}
|
||||
|
||||
return isFuture ? `in ${timeStr} time` : `${timeStr} ago`
|
||||
}
|
||||
|
||||
const TimeDisplay = ({
|
||||
@ -109,7 +98,7 @@ const TimeDisplay = ({
|
||||
return (
|
||||
<Flex align={'center'} gap={'small'}>
|
||||
<Text type={type}>{formattedDate}</Text>
|
||||
{showSince ? <Tag style={{ margin: 0 }}>{timeAgo}</Tag> : null}
|
||||
{showSince ? <Tag style={{ margin: 0 }}>{timeAgo + ' ago'}</Tag> : null}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/actionsicon.svg?react'
|
||||
|
||||
const ActionsIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default ActionsIcon
|
||||
@ -1,6 +0,0 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/clienticon.svg?react'
|
||||
|
||||
const ClientIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default ClientIcon
|
||||
@ -1,6 +0,0 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/paymenticon.svg?react'
|
||||
|
||||
const PaymentIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default PaymentIcon
|
||||
@ -1,6 +0,0 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/salesicon.svg?react'
|
||||
|
||||
const SalesIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default SalesIcon
|
||||
@ -1,6 +0,0 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/salesordericon.svg?react'
|
||||
|
||||
const SalesOrderIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default SalesOrderIcon
|
||||
@ -31,9 +31,6 @@ import { DocumentJob } from './models/DocumentJob.js'
|
||||
import { TaxRate } from './models/TaxRate.js'
|
||||
import { TaxRecord } from './models/TaxRecord.js'
|
||||
import { Invoice } from './models/Invoice.js'
|
||||
import { Payment } from './models/Payment.js'
|
||||
import { Client } from './models/Client.js'
|
||||
import { SalesOrder } from './models/SalesOrder.js'
|
||||
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
|
||||
|
||||
export const objectModels = [
|
||||
@ -69,10 +66,7 @@ export const objectModels = [
|
||||
DocumentJob,
|
||||
TaxRate,
|
||||
TaxRecord,
|
||||
Invoice,
|
||||
Payment,
|
||||
Client,
|
||||
SalesOrder
|
||||
Invoice
|
||||
]
|
||||
|
||||
// Re-export individual models for direct access
|
||||
@ -109,10 +103,7 @@ export {
|
||||
DocumentJob,
|
||||
TaxRate,
|
||||
TaxRecord,
|
||||
Invoice,
|
||||
Payment,
|
||||
Client,
|
||||
SalesOrder
|
||||
Invoice
|
||||
}
|
||||
|
||||
export function getModelByName(name, ignoreCase = false) {
|
||||
|
||||
@ -7,7 +7,7 @@ export const AuditLog = {
|
||||
icon: AuditLogIcon,
|
||||
actions: [],
|
||||
columns: [
|
||||
'_reference',
|
||||
'_id',
|
||||
'owner',
|
||||
'parent',
|
||||
'operation',
|
||||
|
||||
@ -1,215 +0,0 @@
|
||||
import ClientIcon from '../../components/Icons/ClientIcon'
|
||||
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 ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||
import BinIcon from '../../components/Icons/BinIcon'
|
||||
|
||||
export const Client = {
|
||||
name: 'client',
|
||||
label: 'Client',
|
||||
prefix: 'CLI',
|
||||
icon: ClientIcon,
|
||||
actions: [
|
||||
{
|
||||
name: 'info',
|
||||
label: 'Info',
|
||||
default: true,
|
||||
row: true,
|
||||
icon: InfoCircleIcon,
|
||||
url: (_id) => `/dashboard/sales/clients/info?clientId=${_id}`
|
||||
},
|
||||
{
|
||||
name: 'reload',
|
||||
label: 'Reload',
|
||||
icon: ReloadIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/clients/info?clientId=${_id}&action=reload`
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
label: 'Edit',
|
||||
row: true,
|
||||
icon: EditIcon,
|
||||
url: (_id) => `/dashboard/sales/clients/info?clientId=${_id}&action=edit`,
|
||||
visible: (objectData) => {
|
||||
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'finishEdit',
|
||||
label: 'Save Edits',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/clients/info?clientId=${_id}&action=finishEdit`,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancelEdit',
|
||||
label: 'Cancel Edits',
|
||||
icon: XMarkIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/clients/info?clientId=${_id}&action=cancelEdit`,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
name: 'delete',
|
||||
label: 'Delete',
|
||||
icon: BinIcon,
|
||||
danger: true,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/clients/info?clientId=${_id}&action=delete`
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'name',
|
||||
'_reference',
|
||||
'country',
|
||||
'email',
|
||||
'phone',
|
||||
'active',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
filters: [
|
||||
'name',
|
||||
'_id',
|
||||
'country',
|
||||
'email',
|
||||
'phone',
|
||||
'active',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
sorters: [
|
||||
'name',
|
||||
'country',
|
||||
'email',
|
||||
'phone',
|
||||
'active',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'_id'
|
||||
],
|
||||
group: [],
|
||||
properties: [
|
||||
{
|
||||
name: '_id',
|
||||
label: 'ID',
|
||||
columnFixed: 'left',
|
||||
type: 'id',
|
||||
objectType: 'client',
|
||||
showCopy: true
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
columnFixed: 'left',
|
||||
required: true,
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'active',
|
||||
label: 'Active',
|
||||
type: 'bool',
|
||||
readOnly: false,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'country',
|
||||
label: 'Country',
|
||||
type: 'country',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
columnWidth: 300,
|
||||
type: 'email',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'phone',
|
||||
label: 'Phone',
|
||||
type: 'phone',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'tags',
|
||||
label: 'Tags',
|
||||
type: 'array',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.building',
|
||||
label: 'Building',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.addressLine1',
|
||||
label: 'Address Line 1',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.addressLine2',
|
||||
label: 'Address Line 2',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.city',
|
||||
label: 'City',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.state',
|
||||
label: 'State',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.postcode',
|
||||
label: 'Postcode',
|
||||
type: 'text',
|
||||
readOnly: false,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'address.country',
|
||||
label: 'Country',
|
||||
type: 'country',
|
||||
readOnly: false,
|
||||
required: false
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -68,7 +68,7 @@ export const Courier = {
|
||||
`/dashboard/management/couriers/info?courierId=${_id}&action=delete`
|
||||
}
|
||||
],
|
||||
columns: ['name', '_reference', 'country', 'email', 'website', 'createdAt'],
|
||||
columns: ['name', '_id', 'country', 'email', 'website', 'createdAt'],
|
||||
filters: ['name', '_id', 'country', 'email'],
|
||||
sorters: ['name', 'country', 'email', 'createdAt', '_id'],
|
||||
group: ['country'],
|
||||
|
||||
@ -69,14 +69,7 @@ export const CourierService = {
|
||||
`/dashboard/management/courierservices/info?courierServiceId=${_id}&action=delete`
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'name',
|
||||
'_reference',
|
||||
'courier',
|
||||
'tracked',
|
||||
'deliveryTime',
|
||||
'active'
|
||||
],
|
||||
columns: ['name', '_id', 'courier', 'tracked', 'deliveryTime', 'active'],
|
||||
filters: ['name', '_id', 'courier', 'active', 'deliveryTime', 'tracked'],
|
||||
sorters: [
|
||||
'name',
|
||||
|
||||
@ -61,7 +61,7 @@ export const DocumentJob = {
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: ['name', '_reference', 'state', 'createdAt', 'updatedAt'],
|
||||
columns: ['name', '_id', 'state', 'createdAt', 'updatedAt'],
|
||||
filters: ['name', '_id', 'state'],
|
||||
sorters: ['name', 'state', 'createdAt', 'updatedAt'],
|
||||
properties: [
|
||||
|
||||
@ -60,15 +60,7 @@ export const DocumentPrinter = {
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'name',
|
||||
'_reference',
|
||||
'state',
|
||||
'host',
|
||||
'tags',
|
||||
'connectedAt',
|
||||
'updatedAt'
|
||||
],
|
||||
columns: ['name', '_id', 'state', 'host', 'tags', 'connectedAt', 'updatedAt'],
|
||||
filters: ['name', '_id'],
|
||||
sorters: ['name', 'documentSize', 'connectedAt', 'updatedAt'],
|
||||
properties: [
|
||||
|
||||
@ -62,7 +62,7 @@ export const DocumentSize = {
|
||||
],
|
||||
columns: [
|
||||
'name',
|
||||
'_reference',
|
||||
'_id',
|
||||
'width',
|
||||
'height',
|
||||
'infiniteHeight',
|
||||
|
||||
@ -71,7 +71,7 @@ export const DocumentTemplate = {
|
||||
],
|
||||
columns: [
|
||||
'name',
|
||||
'_reference',
|
||||
'_id',
|
||||
'active',
|
||||
'global',
|
||||
'objectType',
|
||||
|
||||
@ -59,7 +59,7 @@ export const Filament = {
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'_reference',
|
||||
'_id',
|
||||
'name',
|
||||
'type',
|
||||
'color',
|
||||
|
||||
@ -18,7 +18,7 @@ export const FilamentStock = {
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'_reference',
|
||||
'_id',
|
||||
'state',
|
||||
'currentWeight',
|
||||
'startingWeight',
|
||||
|
||||
@ -78,7 +78,7 @@ export const File = {
|
||||
}
|
||||
],
|
||||
url: (id) => `/dashboard/management/files/info?fileId=${id}`,
|
||||
columns: ['name', '_reference', 'type', 'size', 'temp', 'createdAt'],
|
||||
columns: ['name', '_id', 'type', 'size', 'temp', 'createdAt'],
|
||||
filters: ['name', '_id', 'type', 'temp'],
|
||||
sorters: ['name', 'type', 'size', 'createdAt', 'temp'],
|
||||
group: ['type'],
|
||||
|
||||
@ -70,7 +70,7 @@ export const GCodeFile = {
|
||||
|
||||
columns: [
|
||||
'name',
|
||||
'_reference',
|
||||
'_id',
|
||||
'filament',
|
||||
'gcodeFileInfo.estimatedPrintingTimeNormalMode',
|
||||
'gcodeFileInfo.sparseInfillDensity',
|
||||
|
||||
@ -67,7 +67,7 @@ export const Host = {
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: ['name', '_reference', 'state', 'tags', 'connectedAt'],
|
||||
columns: ['name', '_id', 'state', 'tags', 'connectedAt'],
|
||||
filters: ['name', '_id', 'state', 'tags'],
|
||||
sorters: ['name', 'state', 'connectedAt'],
|
||||
group: ['tags'],
|
||||
|
||||
@ -4,7 +4,6 @@ import CheckIcon from '../../components/Icons/CheckIcon'
|
||||
import EditIcon from '../../components/Icons/EditIcon'
|
||||
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||
import BinIcon from '../../components/Icons/BinIcon'
|
||||
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||
|
||||
export const Invoice = {
|
||||
name: 'invoice',
|
||||
@ -73,38 +72,28 @@ export const Invoice = {
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
name: 'newPayment',
|
||||
label: 'New Payment',
|
||||
type: 'button',
|
||||
icon: PlusIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/invoices/info?invoiceId=${_id}&action=newPayment`,
|
||||
disabled: (objectData) => {
|
||||
const allowedStates = ['acknowledged', 'partiallyPaid', 'overdue']
|
||||
return !allowedStates.includes(objectData?.state?.type)
|
||||
}
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
name: 'post',
|
||||
label: 'Post',
|
||||
name: 'send',
|
||||
label: 'Send',
|
||||
type: 'button',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/invoices/info?invoiceId=${_id}&action=post`,
|
||||
`/dashboard/finance/invoices/info?invoiceId=${_id}&action=send`,
|
||||
visible: (objectData) => {
|
||||
return objectData?.state?.type == 'draft'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'acknowledge',
|
||||
label: 'Acknowledge',
|
||||
name: 'markPaid',
|
||||
label: 'Mark Paid',
|
||||
type: 'button',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/invoices/info?invoiceId=${_id}&action=acknowledge`,
|
||||
`/dashboard/finance/invoices/info?invoiceId=${_id}&action=markPaid`,
|
||||
visible: (objectData) => {
|
||||
return objectData?.state?.type == 'sent'
|
||||
return (
|
||||
objectData?.state?.type == 'sent' ||
|
||||
objectData?.state?.type == 'partiallyPaid'
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -116,28 +105,29 @@ export const Invoice = {
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/invoices/info?invoiceId=${_id}&action=cancel`,
|
||||
disabled: (objectData) => {
|
||||
const allowedStates = [
|
||||
'acknowledged',
|
||||
'partiallyPaid',
|
||||
'overdue',
|
||||
'sent'
|
||||
]
|
||||
return !allowedStates.includes(objectData?.state?.type)
|
||||
return (
|
||||
objectData?.state?.type == 'cancelled' ||
|
||||
objectData?.state?.type == 'paid'
|
||||
)
|
||||
},
|
||||
visible: (objectData) => {
|
||||
return objectData?.state?.type != 'draft'
|
||||
return (
|
||||
objectData?.state?.type == 'draft' ||
|
||||
objectData?.state?.type == 'sent'
|
||||
)
|
||||
}
|
||||
}
|
||||
],
|
||||
group: ['orderType'],
|
||||
filters: ['to', 'from', 'orderType'],
|
||||
group: ['vendor', 'customer', 'invoiceType'],
|
||||
filters: ['vendor', 'customer', 'invoiceType'],
|
||||
sorters: ['createdAt', 'state', 'updatedAt', 'invoiceDate', 'dueDate'],
|
||||
columns: [
|
||||
'_id',
|
||||
'_reference',
|
||||
'state',
|
||||
'orderType',
|
||||
'to',
|
||||
'from',
|
||||
'invoiceType',
|
||||
'vendor',
|
||||
'customer',
|
||||
'invoiceDate',
|
||||
'dueDate',
|
||||
'totalAmount',
|
||||
@ -169,7 +159,6 @@ export const Invoice = {
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
columnFixed: 'left',
|
||||
required: true,
|
||||
objectType: 'invoice',
|
||||
showCopy: true,
|
||||
@ -183,11 +172,27 @@ export const Invoice = {
|
||||
},
|
||||
{ name: 'state', label: 'State', type: 'state', readOnly: true },
|
||||
{
|
||||
name: 'issuedAt',
|
||||
label: 'Issued At',
|
||||
type: 'dateTime',
|
||||
readOnly: false,
|
||||
required: true
|
||||
name: 'invoiceDate',
|
||||
label: 'Invoice Date',
|
||||
type: 'date',
|
||||
readOnly: false
|
||||
},
|
||||
{
|
||||
name: 'dueDate',
|
||||
label: 'Due Date',
|
||||
type: 'date',
|
||||
readOnly: false
|
||||
},
|
||||
{
|
||||
name: 'vendor',
|
||||
label: 'Vendor',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'vendor',
|
||||
showHyperlink: true,
|
||||
visible: (objectData) => {
|
||||
return objectData?.invoiceType === 'purchase' || objectData?.vendor
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'orderType',
|
||||
@ -196,12 +201,6 @@ export const Invoice = {
|
||||
masterFilter: ['purchaseOrder', 'salesOrder'],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'dueAt',
|
||||
label: 'Due At',
|
||||
type: 'dateTime',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'order',
|
||||
label: 'Order',
|
||||
@ -209,62 +208,38 @@ export const Invoice = {
|
||||
objectType: (objectData) => {
|
||||
return objectData?.orderType
|
||||
},
|
||||
masterFilter: (objectData) => {
|
||||
return {
|
||||
vendor: objectData?.vendor?._id
|
||||
}
|
||||
},
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'postedAt',
|
||||
label: 'Posted At',
|
||||
name: 'sentAt',
|
||||
label: 'Sent At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'from',
|
||||
label: 'From',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'vendor',
|
||||
showHyperlink: true,
|
||||
readOnly: true,
|
||||
value: (objectData) => {
|
||||
if (objectData?.orderType == 'purchaseOrder') {
|
||||
return objectData?.order?.vendor
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'acknowledgedAt',
|
||||
label: 'Acknowledged At',
|
||||
name: 'paidAt',
|
||||
label: 'Paid At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
|
||||
{
|
||||
name: 'to',
|
||||
label: 'To',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'client',
|
||||
showHyperlink: true,
|
||||
readOnly: true,
|
||||
value: (objectData) => {
|
||||
if (objectData?.orderType == 'salesOrder') {
|
||||
return objectData?.order?.client
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancelledAt',
|
||||
label: 'Cancelled At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
|
||||
{
|
||||
name: 'overdueAt',
|
||||
label: 'Overdue At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'totalTaxAmount',
|
||||
label: 'Total Tax Amount',
|
||||
@ -274,12 +249,6 @@ export const Invoice = {
|
||||
readOnly: true,
|
||||
columnWidth: 175
|
||||
},
|
||||
{
|
||||
name: 'paidAt',
|
||||
label: 'Paid At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'totalAmountWithTax',
|
||||
label: 'Total Amount w/ Tax',
|
||||
@ -324,294 +293,44 @@ export const Invoice = {
|
||||
roundNumber: 2,
|
||||
columnWidth: 175,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'invoiceOrderItems',
|
||||
label: 'Invoice Order Items',
|
||||
type: 'objectChildren',
|
||||
objectType: 'orderItem',
|
||||
properties: [
|
||||
{
|
||||
name: 'orderItem',
|
||||
label: 'Order Item',
|
||||
type: 'object',
|
||||
objectType: 'orderItem',
|
||||
required: true,
|
||||
columnWidth: 300,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'invoiceQuantity',
|
||||
label: 'Quantity',
|
||||
type: 'number',
|
||||
required: true,
|
||||
columnWidth: 175
|
||||
},
|
||||
{
|
||||
name: 'invoiceAmount',
|
||||
label: 'Invoice Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
required: true,
|
||||
columnWidth: 150
|
||||
},
|
||||
{
|
||||
name: 'taxRate',
|
||||
label: 'Tax Rate',
|
||||
type: 'object',
|
||||
objectType: 'taxRate',
|
||||
required: false,
|
||||
showHyperlink: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'invoiceAmountWithTax',
|
||||
label: 'Invoice Amount w/ Tax',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
required: true,
|
||||
readOnly: true,
|
||||
columnWidth: 200,
|
||||
value: (objectData) => {
|
||||
const invoiceAmount = objectData?.invoiceAmount || 0
|
||||
if (objectData?.taxRate?.rateType == 'percentage') {
|
||||
return (
|
||||
(invoiceAmount * (1 + objectData?.taxRate?.rate / 100)).toFixed(
|
||||
2
|
||||
) || undefined
|
||||
)
|
||||
} else if (objectData?.taxRate?.rateType == 'amount') {
|
||||
return (
|
||||
(invoiceAmount + objectData?.taxRate?.rate).toFixed(2) ||
|
||||
undefined
|
||||
)
|
||||
} else {
|
||||
return invoiceAmount || 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
rollups: [
|
||||
{
|
||||
name: 'totalQuantity',
|
||||
label: 'Total Quantity',
|
||||
type: 'number',
|
||||
property: 'invoiceQuantity',
|
||||
value: (objectData) => {
|
||||
return objectData?.invoiceOrderItems?.reduce(
|
||||
(acc, item) => acc + (item.invoiceQuantity || 0),
|
||||
0
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalAmount',
|
||||
label: 'Total Amount',
|
||||
type: 'number',
|
||||
property: 'invoiceAmount',
|
||||
prefix: '£',
|
||||
fixedNumber: 2,
|
||||
value: (objectData) => {
|
||||
return objectData?.invoiceOrderItems
|
||||
?.reduce(
|
||||
(acc, item) =>
|
||||
acc + (Number.parseFloat(item.invoiceAmount) || 0),
|
||||
0
|
||||
)
|
||||
.toFixed(2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalAmountWithTax',
|
||||
label: 'Total Amount w/ Tax',
|
||||
type: 'number',
|
||||
property: 'invoiceAmountWithTax',
|
||||
prefix: '£',
|
||||
fixedNumber: 2,
|
||||
value: (objectData) => {
|
||||
return objectData?.invoiceOrderItems
|
||||
?.reduce(
|
||||
(acc, item) =>
|
||||
acc + (Number.parseFloat(item.invoiceAmountWithTax) || 0),
|
||||
0
|
||||
)
|
||||
.toFixed(2)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'invoiceShipments',
|
||||
label: 'Invoice Shipments',
|
||||
type: 'objectChildren',
|
||||
objectType: 'shipment',
|
||||
properties: [
|
||||
{
|
||||
name: 'shipment',
|
||||
label: 'Shipment',
|
||||
type: 'object',
|
||||
objectType: 'shipment',
|
||||
required: true,
|
||||
columnWidth: 300,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'invoiceAmount',
|
||||
label: 'Invoice Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
columnWidth: 175,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'taxRate',
|
||||
label: 'Tax Rate',
|
||||
type: 'object',
|
||||
objectType: 'taxRate',
|
||||
required: false,
|
||||
showHyperlink: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'invoiceAmountWithTax',
|
||||
label: 'Invoice Amount w/ Tax',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
required: true,
|
||||
readOnly: true,
|
||||
columnWidth: 200,
|
||||
value: (objectData) => {
|
||||
const invoiceAmount = objectData?.invoiceAmount || 0
|
||||
if (objectData?.taxRate?.rateType == 'percentage') {
|
||||
return (
|
||||
(invoiceAmount * (1 + objectData?.taxRate?.rate / 100)).toFixed(
|
||||
2
|
||||
) || undefined
|
||||
)
|
||||
} else if (objectData?.taxRate?.rateType == 'amount') {
|
||||
return (
|
||||
(invoiceAmount + objectData?.taxRate?.rate).toFixed(2) ||
|
||||
undefined
|
||||
)
|
||||
} else {
|
||||
return invoiceAmount || 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
rollups: [
|
||||
{
|
||||
name: 'totalAmount',
|
||||
label: 'Total Amount',
|
||||
type: 'number',
|
||||
property: 'invoiceAmount',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
value: (objectData) => {
|
||||
return objectData?.invoiceShipments
|
||||
?.reduce(
|
||||
(acc, shipment) => acc + (shipment.invoiceAmount || 0),
|
||||
0
|
||||
)
|
||||
.toFixed(2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'totalAmountWithTax',
|
||||
label: 'Total Amount w/ Tax',
|
||||
type: 'number',
|
||||
property: 'invoiceAmountWithTax',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
value: (objectData) => {
|
||||
return objectData?.invoiceShipments
|
||||
?.reduce(
|
||||
(acc, shipment) =>
|
||||
acc + (Number.parseFloat(shipment.invoiceAmountWithTax) || 0),
|
||||
0
|
||||
)
|
||||
.toFixed(2)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
stats: [
|
||||
{
|
||||
name: 'draft.draftCount.count',
|
||||
name: 'draft.count',
|
||||
label: 'Draft',
|
||||
type: 'number',
|
||||
color: 'default'
|
||||
},
|
||||
{
|
||||
name: 'draft.draftGrandTotalAmount.sum',
|
||||
label: 'Draft Grand Total Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
color: 'default'
|
||||
},
|
||||
{
|
||||
name: 'sent.sentCount.count',
|
||||
name: 'sent.count',
|
||||
label: 'Sent',
|
||||
type: 'number',
|
||||
color: 'cyan'
|
||||
},
|
||||
{
|
||||
name: 'acknowledged.acknowledgedCount.count',
|
||||
label: 'Acknowledged',
|
||||
type: 'number',
|
||||
color: 'purple'
|
||||
},
|
||||
{
|
||||
name: 'due.dueCount.count',
|
||||
label: 'Due',
|
||||
type: 'number',
|
||||
color: 'warning',
|
||||
sum: [
|
||||
'sent.sentCount.count',
|
||||
'partiallyPaid.partiallyPaidCount.count',
|
||||
'overdue.overdueCount.count',
|
||||
'acknowledged.acknowledgedCount.count'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'due.dueGrandTotalAmount.sum',
|
||||
label: 'Due Grand Total Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
color: 'warning',
|
||||
sum: [
|
||||
'sent.sentGrandTotalAmount.sum',
|
||||
'partiallyPaid.partiallyPaidGrandTotalAmount.sum',
|
||||
'overdue.overdueGrandTotalAmount.sum',
|
||||
'acknowledged.acknowledgedGrandTotalAmount.sum'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'partiallyPaid.partiallyPaidCount.count',
|
||||
name: 'partiallyPaid.count',
|
||||
label: 'Partially Paid',
|
||||
type: 'number',
|
||||
color: 'processing'
|
||||
},
|
||||
{
|
||||
name: 'overdue.overdueCount.count',
|
||||
name: 'paid.count',
|
||||
label: 'Paid',
|
||||
type: 'number',
|
||||
color: 'success'
|
||||
},
|
||||
{
|
||||
name: 'overdue.count',
|
||||
label: 'Overdue',
|
||||
type: 'number',
|
||||
color: 'error'
|
||||
},
|
||||
{
|
||||
name: 'overdue.overdueGrandTotalAmount.sum',
|
||||
label: 'Overdue Grand Total Amount',
|
||||
name: 'cancelled.count',
|
||||
label: 'Cancelled',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
color: 'error'
|
||||
color: 'default'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ export const Job = {
|
||||
url: (_id) => `/dashboard/production/jobs/info?jobId=${_id}&action=reload`
|
||||
}
|
||||
],
|
||||
columns: ['_reference', 'quantity', 'state', 'gcodeFile', 'createdAt'],
|
||||
columns: ['_id', 'quantity', 'state', 'gcodeFile', 'createdAt'],
|
||||
filters: ['state', '_id', 'gcodeFile', 'quantity'],
|
||||
sorters: ['createdAt', 'state', 'quantity', 'gcodeFile'],
|
||||
properties: [
|
||||
|
||||
@ -34,7 +34,6 @@ export const Note = {
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
columnFixed: 'left',
|
||||
objectType: 'note',
|
||||
showCopy: true,
|
||||
readOnly: true
|
||||
|
||||
@ -58,7 +58,7 @@ export const NoteType = {
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: ['name', '_reference', 'color', 'active', 'createdAt', 'updatedAt'],
|
||||
columns: ['name', '_id', 'color', 'active', 'createdAt', 'updatedAt'],
|
||||
filters: ['name', '_id', 'color', 'active'],
|
||||
sorters: ['name', 'color', 'active', 'createdAt', 'updatedAt'],
|
||||
properties: [
|
||||
|
||||
@ -81,8 +81,8 @@ export const OrderItem = {
|
||||
filters: ['itemType', 'item', 'order'],
|
||||
sorters: ['createdAt', 'updatedAt', 'itemAmount', 'quantity'],
|
||||
columns: [
|
||||
'_id',
|
||||
'_reference',
|
||||
'name',
|
||||
'state',
|
||||
'itemType',
|
||||
'item',
|
||||
@ -93,14 +93,6 @@ export const OrderItem = {
|
||||
'totalAmountWithTax',
|
||||
'order',
|
||||
'shipment',
|
||||
'invoicedAmount',
|
||||
'invoicedAmountWithTax',
|
||||
'invoicedQuantity',
|
||||
'invoicedAmountRemaining',
|
||||
'invoicedAmountWithTaxRemaining',
|
||||
'invoicedQuantityRemaining',
|
||||
'orderedAt',
|
||||
'receivedAt',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
@ -124,7 +116,6 @@ export const OrderItem = {
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
columnFixed: 'left',
|
||||
objectType: 'orderItem',
|
||||
showCopy: true,
|
||||
readOnly: true
|
||||
@ -135,30 +126,7 @@ export const OrderItem = {
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: 'text',
|
||||
readOnly: true,
|
||||
value: (objectData) => {
|
||||
return objectData?.item?.name
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'orderedAt',
|
||||
label: 'Ordered At',
|
||||
type: 'dateTime',
|
||||
required: false,
|
||||
readOnly: true
|
||||
},
|
||||
{ name: 'state', label: 'State', type: 'state', readOnly: true },
|
||||
{
|
||||
name: 'receivedAt',
|
||||
label: 'Received At',
|
||||
type: 'dateTime',
|
||||
required: false,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'orderType',
|
||||
label: 'Order Type',
|
||||
@ -198,7 +166,7 @@ export const OrderItem = {
|
||||
name: 'itemType',
|
||||
label: 'Item Type',
|
||||
type: 'objectType',
|
||||
masterFilter: ['part', 'packaging', 'filament', 'product'],
|
||||
masterFilter: ['part', 'packaging', 'filament'],
|
||||
required: true,
|
||||
columnWidth: 175
|
||||
},
|
||||
@ -233,7 +201,6 @@ export const OrderItem = {
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
fixedNumber: 2,
|
||||
readOnly: (objectData) => {
|
||||
return objectData?.syncAmount != null
|
||||
},
|
||||
@ -264,7 +231,6 @@ export const OrderItem = {
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 150,
|
||||
fixedNumber: 2,
|
||||
readOnly: true,
|
||||
value: (objectData) => {
|
||||
if (objectData?.itemAmount && objectData?.quantity) {
|
||||
@ -306,7 +272,6 @@ export const OrderItem = {
|
||||
label: 'Total Amount w/ Tax',
|
||||
type: 'number',
|
||||
required: true,
|
||||
fixedNumber: 2,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
@ -333,70 +298,6 @@ export const OrderItem = {
|
||||
return totalAmount || 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'invoicedAmount',
|
||||
label: 'Invoiced Amount',
|
||||
type: 'number',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
fixedNumber: 2,
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 150
|
||||
},
|
||||
{
|
||||
name: 'invoicedAmountWithTax',
|
||||
label: 'Invoiced Amount w/ Tax',
|
||||
type: 'number',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
fixedNumber: 2,
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'invoicedQuantity',
|
||||
label: 'Invoiced Quantity',
|
||||
type: 'number',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
columnWidth: 150
|
||||
},
|
||||
{
|
||||
name: 'invoicedAmountRemaining',
|
||||
label: 'Remaining Invoiced Amount',
|
||||
type: 'number',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 225,
|
||||
fixedNumber: 2
|
||||
},
|
||||
{
|
||||
name: 'invoicedAmountWithTaxRemaining',
|
||||
label: 'Remaining Invoiced Amount w/ Tax',
|
||||
type: 'number',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
fixedNumber: 2,
|
||||
step: 0.01,
|
||||
columnWidth: 275
|
||||
},
|
||||
{
|
||||
name: 'invoicedQuantityRemaining',
|
||||
label: 'Remaining Invoiced Quantity',
|
||||
type: 'number',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
columnWidth: 225
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ export const Part = {
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: ['name', '_reference', 'product', 'globalPricing', 'createdAt'],
|
||||
columns: ['name', '_id', 'product', 'globalPricing', 'createdAt'],
|
||||
filters: ['name', '_id', 'product', 'globalPricing'],
|
||||
sorters: ['name', 'email', 'role', 'createdAt', '_id'],
|
||||
properties: [
|
||||
|
||||
@ -20,7 +20,7 @@ export const PartStock = {
|
||||
filters: ['_id', 'part', 'startingQuantity', 'currentQuantity'],
|
||||
sorters: ['part', 'startingQuantity', 'currentQuantity'],
|
||||
columns: [
|
||||
'_reference',
|
||||
'_id',
|
||||
'state',
|
||||
'startingQuantity',
|
||||
'currentQuantity',
|
||||
|
||||
@ -1,252 +0,0 @@
|
||||
import PaymentIcon from '../../components/Icons/PaymentIcon'
|
||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||
import EditIcon from '../../components/Icons/EditIcon'
|
||||
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||
import BinIcon from '../../components/Icons/BinIcon'
|
||||
|
||||
export const Payment = {
|
||||
name: 'payment',
|
||||
label: 'Payment',
|
||||
prefix: 'PAY',
|
||||
icon: PaymentIcon,
|
||||
actions: [
|
||||
{
|
||||
name: 'info',
|
||||
label: 'Info',
|
||||
default: true,
|
||||
row: true,
|
||||
icon: InfoCircleIcon,
|
||||
url: (_id) => `/dashboard/finance/payments/info?paymentId=${_id}`
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
label: 'Edit',
|
||||
type: 'button',
|
||||
icon: EditIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/payments/info?paymentId=${_id}&action=edit`,
|
||||
visible: (objectData) => {
|
||||
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||
},
|
||||
disabled: (objectData) => {
|
||||
return objectData?.state?.type != 'draft'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancelEdit',
|
||||
label: 'Cancel Edit',
|
||||
type: 'button',
|
||||
icon: XMarkIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/payments/info?paymentId=${_id}&action=cancelEdit`,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'finishEdit',
|
||||
label: 'Finish Edit',
|
||||
type: 'button',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/payments/info?paymentId=${_id}&action=finishEdit`,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
label: 'Delete',
|
||||
type: 'button',
|
||||
icon: BinIcon,
|
||||
danger: true,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/payments/info?paymentId=${_id}&action=delete`,
|
||||
visible: (objectData) => {
|
||||
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||
},
|
||||
disabled: (objectData) => {
|
||||
return objectData?.state?.type != 'draft'
|
||||
}
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
name: 'post',
|
||||
label: 'Post',
|
||||
type: 'button',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/payments/info?paymentId=${_id}&action=post`,
|
||||
visible: (objectData) => {
|
||||
return objectData?.state?.type == 'draft'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancel',
|
||||
label: 'Cancel',
|
||||
type: 'button',
|
||||
icon: XMarkIcon,
|
||||
danger: true,
|
||||
url: (_id) =>
|
||||
`/dashboard/finance/payments/info?paymentId=${_id}&action=cancel`,
|
||||
disabled: (objectData) => {
|
||||
return objectData?.state?.type == 'cancelled'
|
||||
},
|
||||
visible: (objectData) => {
|
||||
return (
|
||||
objectData?.state?.type == 'draft' ||
|
||||
objectData?.state?.type == 'posted'
|
||||
)
|
||||
}
|
||||
}
|
||||
],
|
||||
group: ['vendor', 'client', 'invoice'],
|
||||
filters: ['vendor', 'client', 'invoice'],
|
||||
sorters: ['createdAt', 'state', 'updatedAt', 'paymentDate'],
|
||||
columns: [
|
||||
'_reference',
|
||||
'state',
|
||||
'invoice',
|
||||
'vendor',
|
||||
'client',
|
||||
'paymentDate',
|
||||
'amount',
|
||||
'paymentMethod',
|
||||
'createdAt',
|
||||
'updatedAt'
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
name: '_id',
|
||||
label: 'ID',
|
||||
type: 'id',
|
||||
columnFixed: 'left',
|
||||
objectType: 'payment',
|
||||
columnWidth: 140,
|
||||
showCopy: true
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
columnFixed: 'left',
|
||||
required: true,
|
||||
objectType: 'payment',
|
||||
showCopy: true,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{ name: 'state', label: 'State', type: 'state', readOnly: true },
|
||||
{
|
||||
name: 'invoice',
|
||||
label: 'Invoice',
|
||||
type: 'object',
|
||||
objectType: 'invoice',
|
||||
required: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'useRemainingAmount',
|
||||
label: 'Use Remaining Amount',
|
||||
type: 'boolean',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'vendor',
|
||||
label: 'Vendor',
|
||||
type: 'object',
|
||||
objectType: 'vendor',
|
||||
showHyperlink: true,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'client',
|
||||
label: 'Client',
|
||||
type: 'object',
|
||||
objectType: 'client',
|
||||
showHyperlink: true,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'paymentDate',
|
||||
label: 'Payment Date',
|
||||
type: 'dateTime',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'postedAt',
|
||||
label: 'Posted At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'cancelledAt',
|
||||
label: 'Cancelled At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'amount',
|
||||
label: 'Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
required: true,
|
||||
columnWidth: 150
|
||||
},
|
||||
{
|
||||
name: 'paymentMethod',
|
||||
label: 'Payment Method',
|
||||
type: 'string',
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'notes',
|
||||
label: 'Notes',
|
||||
type: 'text',
|
||||
required: false
|
||||
}
|
||||
],
|
||||
stats: [
|
||||
{
|
||||
name: 'draft.draftCount.count',
|
||||
label: 'Draft',
|
||||
type: 'number',
|
||||
color: 'default'
|
||||
},
|
||||
{
|
||||
name: 'draft.draftAmount.sum',
|
||||
label: 'Draft Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
color: 'default'
|
||||
},
|
||||
{
|
||||
name: 'posted.postedCount.count',
|
||||
label: 'Posted',
|
||||
type: 'number',
|
||||
color: 'cyan'
|
||||
},
|
||||
{
|
||||
name: 'posted.postedAmount.sum',
|
||||
label: 'Posted Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
color: 'cyan'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -212,15 +212,7 @@ export const Printer = {
|
||||
]
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'name',
|
||||
'_reference',
|
||||
'state',
|
||||
'host',
|
||||
'tags',
|
||||
'connectedAt',
|
||||
'updatedAt'
|
||||
],
|
||||
columns: ['name', '_id', 'state', 'host', 'tags', 'connectedAt', 'updatedAt'],
|
||||
filters: ['name', '_id', 'state', 'tags'],
|
||||
sorters: ['name', 'state', 'connectedAt'],
|
||||
group: ['tags'],
|
||||
|
||||
@ -58,7 +58,7 @@ export const Product = {
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: ['_reference', 'name', 'tags', 'vendor', 'price', 'createdAt', 'updatedAt'],
|
||||
columns: ['_id', 'name', 'tags', 'vendor', 'price', 'createdAt', 'updatedAt'],
|
||||
filters: ['_id', 'name', 'type', 'color', 'cost', 'vendor'],
|
||||
sorters: ['name', 'createdAt', 'type', 'vendor', 'cost', 'updatedAt'],
|
||||
properties: [
|
||||
|
||||
@ -103,13 +103,7 @@ export const PurchaseOrder = {
|
||||
url: (_id) =>
|
||||
`/dashboard/inventory/purchaseorders/info?purchaseOrderId=${_id}&action=newInvoice`,
|
||||
disabled: (objectData) => {
|
||||
const allowedStates = [
|
||||
'received',
|
||||
'shipped',
|
||||
'partiallyReceived',
|
||||
'partiallyShipped'
|
||||
]
|
||||
return !allowedStates.includes(objectData?.state?.type)
|
||||
return objectData?.state?.type != 'received'
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -175,6 +169,7 @@ export const PurchaseOrder = {
|
||||
filters: ['vendor'],
|
||||
sorters: ['createdAt', 'state', 'updatedAt'],
|
||||
columns: [
|
||||
'_id',
|
||||
'_reference',
|
||||
'state',
|
||||
'vendor',
|
||||
@ -208,7 +203,6 @@ export const PurchaseOrder = {
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
columnFixed: 'left',
|
||||
required: true,
|
||||
objectType: 'purchaseOrder',
|
||||
showCopy: true,
|
||||
@ -246,7 +240,7 @@ export const PurchaseOrder = {
|
||||
columnWidth: 175
|
||||
},
|
||||
{
|
||||
name: 'completedAt',
|
||||
name: 'CompletedAt',
|
||||
label: 'Completed At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
@ -314,7 +308,7 @@ export const PurchaseOrder = {
|
||||
name: 'acknowledged.count',
|
||||
label: 'Acknowledged',
|
||||
type: 'number',
|
||||
color: 'purple'
|
||||
color: 'processing'
|
||||
},
|
||||
{
|
||||
name: 'partiallyShipped.count',
|
||||
|
||||
@ -1,343 +0,0 @@
|
||||
import SalesOrderIcon from '../../components/Icons/SalesOrderIcon'
|
||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||
import EditIcon from '../../components/Icons/EditIcon'
|
||||
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||
import BinIcon from '../../components/Icons/BinIcon'
|
||||
|
||||
export const SalesOrder = {
|
||||
name: 'salesOrder',
|
||||
label: 'Sales Order',
|
||||
prefix: 'SOR',
|
||||
icon: SalesOrderIcon,
|
||||
actions: [
|
||||
{
|
||||
name: 'info',
|
||||
label: 'Info',
|
||||
default: true,
|
||||
row: true,
|
||||
icon: InfoCircleIcon,
|
||||
url: (_id) => `/dashboard/sales/salesorders/info?salesOrderId=${_id}`
|
||||
},
|
||||
{
|
||||
name: 'edit',
|
||||
label: 'Edit',
|
||||
type: 'button',
|
||||
icon: EditIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=edit`,
|
||||
visible: (objectData) => {
|
||||
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||
},
|
||||
disabled: (objectData) => {
|
||||
return objectData?.state?.type != 'draft'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancelEdit',
|
||||
label: 'Cancel Edit',
|
||||
type: 'button',
|
||||
icon: XMarkIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=cancelEdit`,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'finishEdit',
|
||||
label: 'Finish Edit',
|
||||
type: 'button',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=finishEdit`,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'delete',
|
||||
label: 'Delete',
|
||||
type: 'button',
|
||||
icon: BinIcon,
|
||||
danger: true,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=delete`,
|
||||
visible: (objectData) => {
|
||||
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||
},
|
||||
disabled: (objectData) => {
|
||||
return objectData?.state?.type != 'draft'
|
||||
}
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
name: 'New Order Item',
|
||||
label: 'New Order Item',
|
||||
type: 'button',
|
||||
icon: PlusIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=newOrderItem`,
|
||||
disabled: (objectData) => {
|
||||
return objectData?.state?.type != 'draft'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'New Shipment',
|
||||
label: 'New Shipment',
|
||||
type: 'button',
|
||||
icon: PlusIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=newShipment`,
|
||||
disabled: (objectData) => {
|
||||
return objectData?.state?.type != 'draft'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'New Invoice',
|
||||
label: 'New Invoice',
|
||||
type: 'button',
|
||||
icon: PlusIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=newInvoice`,
|
||||
disabled: (objectData) => {
|
||||
const allowedStates = [
|
||||
'delivered',
|
||||
'sent',
|
||||
'confirmed',
|
||||
'shipped',
|
||||
'partiallyDelivered',
|
||||
'partiallyShipped'
|
||||
]
|
||||
return !allowedStates.includes(objectData?.state?.type)
|
||||
}
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
name: 'post',
|
||||
label: 'Post',
|
||||
type: 'button',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=post`,
|
||||
visible: (objectData) => {
|
||||
return objectData?.state?.type == 'draft'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'confirm',
|
||||
label: 'Confirm',
|
||||
type: 'button',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=confirm`,
|
||||
visible: (objectData) => {
|
||||
return objectData?.state?.type == 'sent'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'complete',
|
||||
label: 'Complete',
|
||||
type: 'button',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=complete`,
|
||||
disabled: (objectData) => {
|
||||
return objectData?.state?.type != 'delivered'
|
||||
},
|
||||
visible: (objectData) => {
|
||||
return objectData?.state?.type == 'delivered'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancel',
|
||||
label: 'Cancel',
|
||||
type: 'button',
|
||||
icon: XMarkIcon,
|
||||
danger: true,
|
||||
url: (_id) =>
|
||||
`/dashboard/sales/salesorders/info?salesOrderId=${_id}&action=cancel`,
|
||||
disabled: (objectData) => {
|
||||
return objectData?.state?.type == 'cancelled'
|
||||
},
|
||||
visible: (objectData) => {
|
||||
return (
|
||||
objectData?.state?.type != 'draft' &&
|
||||
objectData?.state?.type != 'completed' &&
|
||||
objectData?.state?.type != 'delivered'
|
||||
)
|
||||
}
|
||||
}
|
||||
],
|
||||
group: ['client'],
|
||||
filters: ['client'],
|
||||
sorters: ['createdAt', 'state', 'updatedAt'],
|
||||
columns: [
|
||||
'_reference',
|
||||
'state',
|
||||
'client',
|
||||
'totalAmount',
|
||||
'totalAmountWithTax',
|
||||
'totalTaxAmount',
|
||||
'shippingAmount',
|
||||
'shippingAmountWithTax',
|
||||
'grandTotalAmount',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'client'
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
name: '_id',
|
||||
label: 'ID',
|
||||
type: 'id',
|
||||
columnFixed: 'left',
|
||||
objectType: 'salesOrder',
|
||||
columnWidth: 140,
|
||||
showCopy: true
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
columnFixed: 'left',
|
||||
required: true,
|
||||
objectType: 'salesOrder',
|
||||
showCopy: true,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{ name: 'state', label: 'State', type: 'state', readOnly: true },
|
||||
{ name: 'postedAt', label: 'Posted At', type: 'dateTime', readOnly: true },
|
||||
{
|
||||
name: 'client',
|
||||
label: 'Client',
|
||||
required: true,
|
||||
type: 'object',
|
||||
objectType: 'client',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'confirmedAt',
|
||||
label: 'Confirmed At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'totalTaxAmount',
|
||||
label: 'Total Tax Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
readOnly: true,
|
||||
columnWidth: 175
|
||||
},
|
||||
{
|
||||
name: 'completedAt',
|
||||
label: 'Completed At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'totalAmountWithTax',
|
||||
label: 'Total Amount w/ Tax',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
readOnly: true,
|
||||
columnWidth: 175,
|
||||
roundNumber: 2
|
||||
},
|
||||
{
|
||||
name: 'shippingAmount',
|
||||
label: 'Shipping Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
readOnly: true,
|
||||
columnWidth: 150
|
||||
},
|
||||
{
|
||||
name: 'shippingAmountWithTax',
|
||||
label: 'Shipping Amount w/ Tax',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
readOnly: true,
|
||||
roundNumber: 2,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'totalAmount',
|
||||
label: 'Total Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
readOnly: true,
|
||||
columnWidth: 150
|
||||
},
|
||||
{
|
||||
name: 'grandTotalAmount',
|
||||
label: 'Grand Total Amount',
|
||||
type: 'number',
|
||||
prefix: '£',
|
||||
roundNumber: 2,
|
||||
columnWidth: 175,
|
||||
readOnly: true
|
||||
}
|
||||
],
|
||||
stats: [
|
||||
{
|
||||
name: 'draft.count',
|
||||
label: 'Draft',
|
||||
type: 'number',
|
||||
color: 'default'
|
||||
},
|
||||
{
|
||||
name: 'sent.count',
|
||||
label: 'Sent',
|
||||
type: 'number',
|
||||
color: 'cyan'
|
||||
},
|
||||
{
|
||||
name: 'confirmed.count',
|
||||
label: 'Confirmed',
|
||||
type: 'number',
|
||||
color: 'purple'
|
||||
},
|
||||
{
|
||||
name: 'partiallyShipped.count',
|
||||
label: 'Partially Shipped',
|
||||
type: 'number',
|
||||
color: 'processing'
|
||||
},
|
||||
{
|
||||
name: 'shipped.count',
|
||||
label: 'Shipped',
|
||||
type: 'number',
|
||||
color: 'processing'
|
||||
},
|
||||
{
|
||||
name: 'partiallyDelivered.count',
|
||||
label: 'Partially Delivered',
|
||||
type: 'number',
|
||||
color: 'success'
|
||||
},
|
||||
{
|
||||
name: 'delivered.count',
|
||||
label: 'Delivered',
|
||||
type: 'number',
|
||||
color: 'success'
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -123,6 +123,7 @@ export const Shipment = {
|
||||
'deliveredAt'
|
||||
],
|
||||
columns: [
|
||||
'_id',
|
||||
'_reference',
|
||||
'state',
|
||||
'orderType',
|
||||
@ -131,10 +132,6 @@ export const Shipment = {
|
||||
'amountWithTax',
|
||||
'taxRate',
|
||||
'taxAmount',
|
||||
'invoicedAmount',
|
||||
'invoicedAmountWithTax',
|
||||
'invoicedAmountRemaining',
|
||||
'invoicedAmountWithTaxRemaining',
|
||||
'trackingNumber',
|
||||
'shippedAt',
|
||||
'expectedAt',
|
||||
@ -162,7 +159,6 @@ export const Shipment = {
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
columnFixed: 'left',
|
||||
objectType: 'shipment',
|
||||
showCopy: true,
|
||||
readOnly: true
|
||||
@ -292,54 +288,6 @@ export const Shipment = {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'invoicedAmount',
|
||||
label: 'Invoiced Amount',
|
||||
type: 'number',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
fixedNumber: 2,
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 150
|
||||
},
|
||||
{
|
||||
name: 'invoicedAmountWithTax',
|
||||
label: 'Invoiced Amount w/ Tax',
|
||||
type: 'number',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
fixedNumber: 2,
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'invoicedAmountRemaining',
|
||||
label: 'Remaining Invoiced Amount',
|
||||
type: 'number',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
step: 0.01,
|
||||
columnWidth: 225,
|
||||
fixedNumber: 2
|
||||
},
|
||||
{
|
||||
name: 'invoicedAmountWithTaxRemaining',
|
||||
label: 'Remaining Invoiced Amount w/ Tax',
|
||||
type: 'number',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
prefix: '£',
|
||||
min: 0,
|
||||
fixedNumber: 2,
|
||||
step: 0.01,
|
||||
columnWidth: 275
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ export const StockAudit = {
|
||||
}
|
||||
],
|
||||
url: (id) => `/dashboard/inventory/stockaudits/info?stockAuditId=${id}`,
|
||||
columns: ['_reference', 'state', 'createdAt', 'updatedAt'],
|
||||
columns: ['_id', 'state', 'createdAt', 'updatedAt'],
|
||||
filters: ['_id'],
|
||||
sorters: ['createdAt', 'updatedAt'],
|
||||
group: ['state'],
|
||||
|
||||
@ -6,7 +6,7 @@ export const StockEvent = {
|
||||
prefix: 'SEV',
|
||||
icon: StockEventIcon,
|
||||
actions: [],
|
||||
columns: ['_reference', 'owner', 'parent', 'value', 'createdAt'],
|
||||
columns: ['_id', 'owner', 'parent', 'value', 'createdAt'],
|
||||
filters: ['_id', 'owner', 'parent'],
|
||||
sorters: ['createdAt'],
|
||||
properties: [
|
||||
@ -26,16 +26,6 @@ export const StockEvent = {
|
||||
value: null,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: '_reference',
|
||||
label: 'Reference',
|
||||
type: 'reference',
|
||||
columnFixed: 'left',
|
||||
objectType: 'stockEvent',
|
||||
value: null,
|
||||
showCopy: true,
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
@ -51,7 +41,6 @@ export const StockEvent = {
|
||||
return objectData.ownerType
|
||||
},
|
||||
columnFixed: 'left',
|
||||
columnWidth: 200,
|
||||
value: null,
|
||||
showCopy: true,
|
||||
showHyperlink: true
|
||||
@ -64,7 +53,6 @@ export const StockEvent = {
|
||||
return objectData?.parentType
|
||||
},
|
||||
value: null,
|
||||
columnWidth: 200,
|
||||
showCopy: true,
|
||||
showHyperlink: true
|
||||
},
|
||||
|
||||
@ -29,7 +29,7 @@ export const SubJob = {
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: ['_reference', 'printer', 'job', 'state', 'createdAt'],
|
||||
columns: ['_id', 'printer', 'job', 'state', 'createdAt'],
|
||||
filters: ['state', '_id', 'job', 'printer'],
|
||||
sorters: ['createdAt', 'state'],
|
||||
group: ['job'],
|
||||
|
||||
@ -70,7 +70,7 @@ export const TaxRate = {
|
||||
],
|
||||
columns: [
|
||||
'name',
|
||||
'_reference',
|
||||
'_id',
|
||||
'rate',
|
||||
'rateType',
|
||||
'active',
|
||||
|
||||
@ -69,7 +69,7 @@ export const TaxRecord = {
|
||||
}
|
||||
],
|
||||
columns: [
|
||||
'_reference',
|
||||
'_id',
|
||||
'taxRate',
|
||||
'transactionType',
|
||||
'transaction',
|
||||
|
||||
@ -24,7 +24,7 @@ export const User = {
|
||||
`/dashboard/management/users/info?userId=${_id}&action=reload`
|
||||
}
|
||||
],
|
||||
columns: ['name', '_reference', 'username', 'email', 'role', 'createdAt'],
|
||||
columns: ['name', '_id', 'username', 'email', 'role', 'createdAt'],
|
||||
filters: ['name', '_id', 'email', 'role'],
|
||||
sorters: ['name', 'email', 'role', 'createdAt', '_id'],
|
||||
properties: [
|
||||
|
||||
@ -70,7 +70,7 @@ export const Vendor = {
|
||||
],
|
||||
columns: [
|
||||
'name',
|
||||
'_reference',
|
||||
'_id',
|
||||
'country',
|
||||
'email',
|
||||
'website',
|
||||
|
||||
@ -7,12 +7,6 @@ const Invoices = lazy(
|
||||
const InvoiceInfo = lazy(
|
||||
() => import('../components/Dashboard/Finance/Invoices/InvoiceInfo.jsx')
|
||||
)
|
||||
const Payments = lazy(
|
||||
() => import('../components/Dashboard/Finance/Payments.jsx')
|
||||
)
|
||||
const PaymentInfo = lazy(
|
||||
() => import('../components/Dashboard/Finance/Payments/PaymentInfo.jsx')
|
||||
)
|
||||
const FinanceOverview = lazy(
|
||||
() => import('../components/Dashboard/Finance/FinanceOverview.jsx')
|
||||
)
|
||||
@ -28,12 +22,6 @@ const FinanceRoutes = [
|
||||
key='invoices-info'
|
||||
path='finance/invoices/info'
|
||||
element={<InvoiceInfo />}
|
||||
/>,
|
||||
<Route key='payments' path='finance/payments' element={<Payments />} />,
|
||||
<Route
|
||||
key='payments-info'
|
||||
path='finance/payments/info'
|
||||
element={<PaymentInfo />}
|
||||
/>
|
||||
]
|
||||
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
import { lazy } from 'react'
|
||||
import { Route } from 'react-router-dom'
|
||||
|
||||
const Clients = lazy(
|
||||
() => import('../components/Dashboard/Sales/Clients.jsx')
|
||||
)
|
||||
const ClientInfo = lazy(
|
||||
() => import('../components/Dashboard/Sales/Clients/ClientInfo.jsx')
|
||||
)
|
||||
const SalesOrders = lazy(
|
||||
() => import('../components/Dashboard/Sales/SalesOrders.jsx')
|
||||
)
|
||||
const SalesOrderInfo = lazy(
|
||||
() => import('../components/Dashboard/Sales/SalesOrders/SalesOrderInfo.jsx')
|
||||
)
|
||||
const SalesOverview = lazy(
|
||||
() => import('../components/Dashboard/Sales/SalesOverview.jsx')
|
||||
)
|
||||
|
||||
const SalesRoutes = [
|
||||
<Route
|
||||
key='overview'
|
||||
path='sales/overview'
|
||||
element={<SalesOverview />}
|
||||
/>,
|
||||
<Route key='clients' path='sales/clients' element={<Clients />} />,
|
||||
<Route
|
||||
key='clients-info'
|
||||
path='sales/clients/info'
|
||||
element={<ClientInfo />}
|
||||
/>,
|
||||
<Route key='salesorders' path='sales/salesorders' element={<SalesOrders />} />,
|
||||
<Route
|
||||
key='salesorders-info'
|
||||
path='sales/salesorders/info'
|
||||
element={<SalesOrderInfo />}
|
||||
/>
|
||||
]
|
||||
|
||||
export default SalesRoutes
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
export { default as ProductionRoutes } from './ProductionRoutes'
|
||||
export { default as InventoryRoutes } from './InventoryRoutes'
|
||||
export { default as FinanceRoutes } from './FinanceRoutes'
|
||||
export { default as SalesRoutes } from './SalesRoutes'
|
||||
export { default as ManagementRoutes } from './ManagementRoutes'
|
||||
export { default as DeveloperRoutes } from './DeveloperRoutes'
|
||||
|
||||