Compare commits

..

1 Commits

Author SHA1 Message Date
550f0eca06 Started electrobun migration.
Some checks failed
farmcontrol/farmcontrol-ui/pipeline/head There was a failure building this commit
2026-03-01 02:56:25 +00:00
223 changed files with 10251 additions and 11244 deletions

4
.gitignore vendored
View File

@ -27,6 +27,4 @@ yarn-error.log*
/design_files/*.af~lock~
stats.html
test-results.xml
stats.html

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(0.913777,0,0,0.913777,-11.957695,-8.458487)">
<path d="M56.866,48.187C56.775,48.817 56.734,49.463 56.734,50.118C56.734,52.243 57.143,54.279 57.906,56.141C54.515,53.972 49.815,52.352 44,52.352C31.125,52.352 23.734,60.274 23.734,64.477C23.734,64.93 23.953,65.165 24.547,65.165L63.031,65.165L63.031,71.774L24.766,71.774C19.047,71.774 16.219,69.852 16.219,65.774C16.219,56.587 27.547,45.727 44,45.727C48.778,45.727 53.123,46.643 56.866,48.187ZM57.984,27.368C57.984,35.743 51.766,42.477 44,42.477C36.25,42.477 30.031,35.758 30.031,27.399C30.031,19.227 36.328,12.54 44,12.54C51.719,12.54 57.984,19.165 57.984,27.368ZM36.812,27.384C36.812,32.352 40.109,35.868 44,35.868C47.938,35.868 51.203,32.321 51.203,27.368C51.203,22.587 47.922,19.149 44,19.149C40.125,19.149 36.812,22.634 36.812,27.384Z" style="fill-rule:nonzero;"/>
<path d="M72.469,39.508C66.531,39.508 61.812,44.29 61.812,50.118C61.812,54.462 64.266,58.212 68.109,59.899L68.109,74.743C68.109,75.29 68.359,75.821 68.766,76.243L71.609,78.915C72.141,79.399 72.969,79.462 73.562,78.868L78.5,73.946C79.125,73.305 79.062,72.383 78.484,71.774L76.031,69.258L79.641,65.649C80.234,65.055 80.25,64.118 79.609,63.43L76.234,60.087C80.641,57.962 83.125,54.399 83.125,50.118C83.125,44.29 78.375,39.508 72.469,39.508ZM72.453,43.696C74.094,43.696 75.422,45.055 75.422,46.696C75.422,48.321 74.094,49.696 72.453,49.696C70.844,49.696 69.469,48.321 69.469,46.696C69.469,45.055 70.797,43.696 72.453,43.696Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,11 +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.865066,0,0,0.865066,3,7.501025)">
<path d="M3.656,56.641L63.359,56.641C65.406,56.641 67.047,54.906 67.047,52.891C67.047,50.813 65.422,49.125 63.359,49.125L3.656,49.125C1.609,49.125 0,50.859 0,52.891C0,54.875 1.625,56.641 3.656,56.641Z" style="fill-rule:nonzero;"/>
<path d="M3.656,40.297L63.359,40.297C65.406,40.297 67.047,38.563 67.047,36.547C67.047,34.453 65.422,32.781 63.359,32.781L3.656,32.781C1.609,32.781 0,34.484 0,36.547C0,38.516 1.625,40.297 3.656,40.297Z" style="fill-rule:nonzero;"/>
<path d="M46.469,23.891L63.359,23.938C65.406,23.953 67.047,22.188 67.047,20.172C67.047,18.11 65.422,16.438 63.359,16.422L46.469,16.375C44.422,16.36 42.812,18.094 42.812,20.125C42.812,22.094 44.438,23.891 46.469,23.891Z" style="fill-rule:nonzero;"/>
<path d="M46.469,7.516L63.359,7.578C65.406,7.594 67.047,5.844 67.047,3.828C67.047,1.75 65.422,0.078 63.359,0.063L46.469,0C44.422,-0.016 42.812,1.734 42.812,3.766C42.812,5.75 44.438,7.516 46.469,7.516Z" style="fill-rule:nonzero;"/>
<path d="M9.047,24.594C13.453,24.594 16.984,21.5 16.984,17.063C16.984,12.828 14.078,9.906 10.219,9.906C7.75,9.906 5.719,11 4.703,13.61L5.406,14.063C5.594,9.063 9.016,5.547 14.219,5.203C15.516,5.109 16.5,4.063 16.5,2.781C16.5,1.25 15.172,0.266 13.453,0.266C6.359,0.266 0.047,6.188 0.047,14.406C0.047,20.594 3.797,24.594 9.047,24.594ZM29.297,24.594C33.672,24.594 37.219,21.5 37.219,17.063C37.219,12.828 34.312,9.906 30.453,9.906C27.984,9.906 25.953,11 24.922,13.61L25.641,14.063C25.812,9.063 29.234,5.531 34.422,5.203C35.734,5.109 36.75,4.063 36.75,2.781C36.75,1.25 35.453,0.266 33.688,0.266C26.609,0.266 20.266,6.188 20.266,14.406C20.266,20.594 24.047,24.594 29.297,24.594Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -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(1,0,0,1,12.2656,8.9375)">
<path d="M6.125,46.125L23.078,46.125C33.078,46.125 39.469,40.859 39.469,32.844C39.469,26.656 34.562,22.031 28.062,21.766L28.062,21.516C33.484,20.797 37.344,16.781 37.344,11.578C37.344,4.391 31.75,0.016 22.484,0.016L6.125,0.016C2.344,0.016 0,2.234 0,6.109L0,40.047C0,43.922 2.344,46.125 6.125,46.125ZM12.953,37.828L12.953,26.062L18.5,26.062C23.469,26.062 26.312,28.125 26.312,31.844C26.312,35.734 23.922,37.828 18.938,37.828L12.953,37.828ZM12.953,18.844L12.953,8.391L18.219,8.391C22.25,8.391 24.672,10.297 24.672,13.531C24.672,16.828 22.047,18.844 17.672,18.844L12.953,18.844Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,12 +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.865066,0,0,0.865066,3,10.731571)">
<path d="M21.156,48.391L63.359,48.391C65.406,48.391 67.047,46.656 67.047,44.625C67.047,42.547 65.422,40.875 63.359,40.875L21.156,40.875C19.078,40.875 17.484,42.594 17.484,44.625C17.484,46.609 19.109,48.391 21.156,48.391Z" style="fill-rule:nonzero;"/>
<path d="M5.203,49.828C8.078,49.828 10.422,47.484 10.422,44.625C10.422,41.766 8.078,39.422 5.203,39.422C2.344,39.422 0,41.766 0,44.625C0,47.484 2.344,49.828 5.203,49.828Z" style="fill-rule:nonzero;"/>
<path d="M21.156,28.672L63.359,28.672C65.406,28.672 67.047,26.938 67.047,24.922C67.047,22.844 65.422,21.156 63.359,21.156L21.156,21.156C19.078,21.156 17.484,22.891 17.484,24.922C17.484,26.906 19.109,28.672 21.156,28.672Z" style="fill-rule:nonzero;"/>
<path d="M5.203,30.125C8.078,30.125 10.422,27.797 10.422,24.922C10.422,22.047 8.078,19.719 5.203,19.719C2.344,19.719 0,22.047 0,24.922C0,27.797 2.344,30.125 5.203,30.125Z" style="fill-rule:nonzero;"/>
<path d="M21.156,8.969L63.359,8.969C65.406,8.969 67.047,7.234 67.047,5.203C67.047,3.141 65.422,1.453 63.359,1.453L21.156,1.453C19.078,1.453 17.484,3.172 17.484,5.203C17.484,7.203 19.109,8.969 21.156,8.969Z" style="fill-rule:nonzero;"/>
<path d="M5.203,10.422C8.078,10.422 10.422,8.078 10.422,5.203C10.422,2.344 8.078,0 5.203,0C2.344,0 0,2.344 0,5.203C0,8.078 2.344,10.422 5.203,10.422Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,12 +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 12 12" 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.156042,0,0,0.156042,1.80453,0.721021)">
<rect x="0" y="0" width="53.774" height="67.661" style="fill-opacity:0;"/>
<path d="M10.149,67.641L43.438,67.641C50.137,67.641 53.586,64.135 53.586,57.41L53.586,29.058C53.586,24.717 53.028,22.725 50.308,19.954L33.894,3.284C31.282,0.616 29.111,0 25.212,0L10.149,0C3.481,0 0,3.532 0,10.257L0,57.41C0,64.161 3.455,67.641 10.149,67.641ZM10.637,61.519C7.621,61.519 6.122,59.941 6.122,57.029L6.122,10.637C6.122,7.752 7.621,6.122 10.663,6.122L23.984,6.122L23.984,23.261C23.984,27.733 26.167,29.895 30.619,29.895L47.464,29.895L47.464,57.029C47.464,59.941 45.965,61.519 42.929,61.519L10.637,61.519ZM31.198,24.496C29.903,24.496 29.384,23.945 29.384,22.656L29.384,6.973L46.613,24.496L31.198,24.496Z" style="fill-rule:nonzero;"/>
<g transform="matrix(12.808657,0,0,12.808657,-40.118025,-49.663642)">
<path d="M4.662,8.034C4.545,8.034 4.448,8.015 4.37,7.978C4.293,7.94 4.236,7.89 4.2,7.825C4.163,7.761 4.145,7.69 4.145,7.612C4.145,7.528 4.166,7.454 4.208,7.389C4.25,7.325 4.316,7.274 4.405,7.236C4.495,7.198 4.607,7.178 4.743,7.178L5.084,7.178C5.084,7.115 5.076,7.063 5.06,7.022C5.045,6.981 5.019,6.95 4.984,6.93C4.948,6.909 4.9,6.899 4.838,6.899C4.773,6.899 4.718,6.912 4.673,6.938C4.629,6.964 4.601,7.005 4.59,7.061L4.187,7.061C4.197,6.961 4.23,6.873 4.286,6.798C4.343,6.724 4.419,6.665 4.514,6.622C4.609,6.58 4.718,6.558 4.841,6.558C4.975,6.558 5.092,6.58 5.19,6.624C5.289,6.668 5.366,6.731 5.421,6.815C5.476,6.899 5.503,7.003 5.503,7.128L5.503,8L5.154,8L5.104,7.796C5.083,7.831 5.059,7.864 5.031,7.892C5.003,7.921 4.971,7.946 4.933,7.968C4.896,7.989 4.855,8.006 4.81,8.017C4.766,8.028 4.716,8.034 4.662,8.034ZM4.766,7.715C4.81,7.715 4.849,7.708 4.883,7.693C4.917,7.678 4.945,7.657 4.97,7.631C4.994,7.605 5.014,7.575 5.03,7.54C5.046,7.506 5.057,7.468 5.065,7.427L5.065,7.424L4.794,7.424C4.747,7.424 4.708,7.43 4.678,7.443C4.647,7.455 4.624,7.472 4.609,7.494C4.594,7.517 4.587,7.543 4.587,7.572C4.587,7.604 4.595,7.631 4.611,7.652C4.626,7.674 4.648,7.689 4.675,7.7C4.702,7.71 4.732,7.715 4.766,7.715Z" style="fill-rule:nonzero;"/>
<path d="M5.835,7.521L6.43,7.521L6.077,8.432L5.637,8.432L5.835,7.521Z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,17 +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.124,0,0,0.124,1.867256,1)">
<path d="M69.43,159.719C69.43,125.199 97.41,97.219 131.922,97.219L486.012,97.219L486.012,458.328C486.012,481.34 467.359,500 444.352,500L152.738,500C106.73,500 69.43,462.691 69.43,416.672L69.43,159.719Z" style="fill:rgb(38,120,43);fill-rule:nonzero;"/>
<path d="M69.43,83.328C69.43,37.309 106.73,0 152.738,0L319.371,0L319.371,166.672L152.738,166.672C106.73,166.672 69.43,203.98 69.43,250L69.43,83.328Z" style="fill:rgb(115,218,95);fill-rule:nonzero;"/>
<g transform="matrix(1.243478,0,0,1.243478,-118.330239,0)">
<path d="M332.245,0L444.34,0C467.348,0 486,18.652 486,41.66L486,125.012C486,148.02 467.348,166.672 444.34,166.672L332.245,166.672C309.237,166.672 290.585,148.02 290.585,125.012L290.585,41.66C290.585,18.652 309.237,0 332.245,0Z" style="fill:rgb(143,233,111);fill-rule:nonzero;"/>
</g>
<g transform="matrix(1.31598,0,0,1.31598,0,-144.822643)">
<path d="M45.129,236.109L177.039,236.109C201.965,236.109 222.172,256.316 222.172,281.238L222.172,413.199C222.172,438.125 201.965,458.328 177.039,458.328L45.129,458.328C20.207,458.328 0,438.125 0,413.199L0,281.238C0,256.316 20.207,236.109 45.129,236.109Z" style="fill:rgb(11,78,46);fill-rule:nonzero;"/>
<g transform="matrix(1.2548,0,0,1.2548,-33.009557,-88.471875)">
<path d="M169.48,410.711L135.23,410.711L113.73,370.238C112.961,368.82 112.371,367.699 111.961,366.871C111.609,365.988 111.219,364.98 110.809,363.859L110.461,363.859C109.93,365.281 109.43,366.43 108.961,367.309C108.488,368.199 107.93,369.289 107.281,370.59L84.98,410.699L52.68,410.699L91.441,347.121L55.34,283.719L89.141,283.719L108.25,319.852C109.02,321.328 109.672,322.629 110.199,323.75C110.789,324.809 111.379,326.078 111.969,327.559L112.32,327.559C113.141,325.852 113.789,324.488 114.27,323.488C114.801,322.488 115.512,321.16 116.391,319.512L136.211,283.738L168.422,283.738L131.789,346.172L169.488,410.719L169.48,410.711Z" style="fill:rgb(254,254,254);fill-rule:nonzero;"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -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.760492,0,0,0.760492,9.844746,-1.023725)">
<path d="M19.578,35.125L14.438,35.125C10.359,35.125 8.062,37.422 8.062,41.5L8.062,64.734C8.062,68.828 10.359,71.125 14.438,71.125L43.828,71.125C47.906,71.125 50.203,68.828 50.203,64.734L50.203,41.5C50.203,37.422 47.906,35.125 43.828,35.125L38.672,35.125L38.672,27.062L44.266,27.062C53.234,27.062 58.266,32.062 58.266,41.047L58.266,65.188C58.266,74.156 53.234,79.188 44.266,79.188L14,79.188C5.016,79.188 0,74.156 0,65.188L0,41.047C0,32.062 5.016,27.062 14,27.062L19.578,27.062L19.578,35.125Z" style="fill-rule:nonzero;"/>
<path d="M32.423,14.141L32.75,18.578L32.75,49.219C32.75,51.125 31.141,52.766 29.125,52.766C27.109,52.766 25.516,51.125 25.516,49.219L25.516,18.578L25.851,14.122L29.125,9.438L32.423,14.141Z" style="fill-rule:nonzero;"/>
<path d="M18.172,21.328C19.078,21.328 19.938,20.953 20.547,20.297L24.375,16.234L29.125,9.438L33.891,16.234L37.703,20.297C38.312,20.953 39.156,21.328 40.062,21.328C41.703,21.328 43.188,20.094 43.188,18.328C43.188,17.406 42.859,16.734 42.234,16.109L31.984,6.281C31.047,5.359 30.109,5.031 29.125,5.031C28.156,5.031 27.234,5.359 26.266,6.281L16.031,16.109C15.422,16.734 15.062,17.406 15.062,18.328C15.062,20.094 16.531,21.328 18.172,21.328Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -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.011907,-0,-0,-0.011907,-0.017623,64)">
<path d="M2168,4709C1894,4666 1703,4555 1616,4389C1580,4321 1580,4184 1616,4116C1686,3983 1795,3902 1990,3838C2374,3713 2898,3806 3075,4030C3139,4112 3155,4156 3155,4253C3155,4349 3139,4393 3075,4475C2930,4659 2521,4765 2168,4709ZM2531,4395C2672,4372 2780,4328 2829,4275C2848,4253 2848,4252 2829,4230C2762,4157 2554,4095 2373,4095C2192,4095 1984,4157 1917,4230C1898,4252 1898,4253 1917,4275C2010,4378 2296,4434 2531,4395Z" style="fill-rule:nonzero;"/>
<path d="M4413.328,0.003L3970,0C2805,0 2804,0 2641,56C2546.293,88.172 2456.943,133.018 2375.17,188.649L752.515,188.649C440.617,188.649 187.395,441.871 187.395,753.769L187.395,2042.949C187.395,2333.828 407.638,2573.672 690.318,2604.678C623.112,2767.055 626.105,2931.521 701,3087L731,3149L707,3196C648,3311 626,3466 653,3579L665,3631L598,3692C381,3894 287,4149 343,4391C403,4658 606,4880 958,5066C1270,5230 1637,5326 2121,5369C2228,5379 2689,5366 2814,5350C3439,5270 3943,5054 4214,4748C4509,4415 4483,4003 4148,3692L4081,3631L4093,3579C4120,3465 4098,3312 4038,3194C4014,3145 4014,3144 4033,3113C4078,3041 4100,2950 4100,2835C4100,2738.782 4090.9,2682.466 4058.642,2608.068L4625.617,2608.068C4937.515,2608.068 5190.736,2354.846 5190.736,2042.949L5190.736,753.769C5190.736,611.423 5137.993,481.298 5051,381.889L5051.021,155.223C5051.021,69.541 4981.4,0.003 4895.817,0.003L4413.328,0.003ZM1063.992,2608.068L1634.782,2608.068C1394.851,2668.367 1190.699,2752.015 1034,2856C966,2901 967,2901 960,2872C944,2809 979,2712 1045,2630C1050.866,2622.695 1057.204,2615.38 1063.992,2608.068ZM1078,3393C1025,3416 976,3438 970,3440C961,3444 960,3436 965,3411C1016,3186 1382,2969 1856,2883C2036,2850 2146,2841 2373,2841C2600,2841 2710,2850 2890,2883C3364,2969 3730,3186 3781,3411C3786,3436 3785,3444 3776,3440C3770,3438 3721,3416 3668,3393C3424,3286 3132,3213 2772,3169C2627,3151 2119,3151 1974,3169C1614,3213 1322,3286 1078,3393ZM4854.788,2042.949C4854.788,2169.432 4752.1,2272.12 4625.617,2272.12L752.515,2272.12C626.032,2272.12 523.344,2169.432 523.344,2042.949L523.344,753.769C523.344,627.286 626.032,524.597 752.515,524.597L4625.617,524.597C4752.1,524.597 4854.788,627.286 4854.788,753.769L4854.788,2042.949ZM2621,5050C1966,5103 1283,4947 897,4656C795,4579 733,4507 685,4411C650,4342 646,4324 646,4253C646,4146 685,4063 783,3960C1103,3621 1884,3416 2631,3476C3229,3524 3721,3702 3963,3960C4061,4063 4100,4146 4100,4253C4100,4324 4096,4342 4061,4411C4013,4507 3951,4579 3849,4656C3560,4873 3138,5009 2621,5050ZM3111.507,2608.068L3680.692,2608.068C3712.673,2642.169 3737.999,2677.689 3757,2715C3782,2766 3797,2857 3783,2881C3775,2896 3769,2895 3733,2870C3569.742,2759.218 3359.738,2670.745 3111.507,2608.068Z"/>
<g transform="matrix(143.743961,-0,-0,-143.743961,-3687.186324,8190.967756)">
<path d="M34.865,52.259C37.413,52.259 38.935,51.026 38.935,49.082C38.935,47.56 37.996,46.722 35.931,46.326L34.939,46.139C33.887,45.938 33.451,45.656 33.451,45.147C33.451,44.577 33.974,44.174 34.865,44.174C35.576,44.174 36.112,44.409 36.428,45.012C36.716,45.482 37.051,45.676 37.587,45.676C38.204,45.669 38.62,45.287 38.62,44.717C38.62,44.516 38.586,44.355 38.519,44.188C38.05,42.961 36.696,42.25 34.845,42.25C32.62,42.25 31.004,43.45 31.004,45.314C31.004,46.789 32.01,47.734 33.934,48.096L34.933,48.284C36.079,48.505 36.488,48.78 36.488,49.316C36.488,49.906 35.864,50.335 34.906,50.335C34.128,50.335 33.478,50.081 33.149,49.477C32.834,48.995 32.512,48.827 32.036,48.827C31.413,48.827 30.97,49.243 30.97,49.859C30.97,50.061 31.011,50.268 31.098,50.469C31.5,51.468 32.767,52.259 34.865,52.259Z" style="fill-rule:nonzero;"/>
<path d="M41.308,52.239C42.086,52.239 42.535,51.777 42.535,50.959L42.535,49.357L43.433,48.418L45.941,51.549C46.336,52.052 46.691,52.246 47.208,52.246C47.878,52.246 48.388,51.73 48.388,51.059C48.388,50.718 48.22,50.349 47.818,49.853L45.344,46.823L47.657,44.382C47.985,44.027 48.113,43.759 48.113,43.397C48.113,42.767 47.596,42.264 46.953,42.264C46.537,42.264 46.229,42.425 45.88,42.814L42.589,46.46L42.535,46.46L42.535,43.551C42.535,42.733 42.086,42.27 41.308,42.27C40.53,42.27 40.075,42.733 40.075,43.551L40.075,50.959C40.075,51.777 40.53,52.239 41.308,52.239Z" style="fill-rule:nonzero;"/>
<path d="M53.583,52.259C56.097,52.259 57.746,50.832 57.746,48.659L57.746,43.551C57.746,42.733 57.297,42.27 56.513,42.27C55.735,42.27 55.286,42.733 55.286,43.551L55.286,48.398C55.286,49.524 54.676,50.195 53.583,50.195C52.484,50.195 51.874,49.524 51.874,48.398L51.874,43.551C51.874,42.733 51.424,42.27 50.647,42.27C49.869,42.27 49.413,42.733 49.413,43.551L49.413,48.659C49.413,50.832 51.062,52.259 53.583,52.259Z" style="fill-rule:nonzero;"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -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.889695,0,0,0.889695,3,3.004404)">
<path d="M65.191,32.59C65.191,50.56 50.592,65.181 32.59,65.181C14.621,65.181 0,50.56 0,32.59C0,14.599 14.621,0 32.59,0C50.592,0 65.191,14.599 65.191,32.59ZM24.761,42.45C22.996,42.45 21.767,43.565 21.767,45.227C21.767,46.89 22.996,48.026 24.761,48.026L40.494,48.026C42.259,48.026 43.509,46.89 43.509,45.227C43.509,43.565 42.259,42.45 40.494,42.45L24.761,42.45ZM20.339,31.642C18.552,31.642 17.323,32.777 17.323,34.44C17.323,36.144 18.552,37.249 20.339,37.249L44.937,37.249C46.703,37.249 47.931,36.144 47.931,34.44C47.931,32.777 46.703,31.642 44.937,31.642L20.339,31.642ZM16.054,20.886C14.279,20.886 13.039,22.001 13.039,23.684C13.039,25.358 14.279,26.472 16.054,26.472L49.222,26.472C50.976,26.472 52.237,25.358 52.237,23.684C52.237,22.001 50.976,20.886 49.222,20.886L16.054,20.886Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(0.889695,0,0,0.889695,3,3.004404)">
<path d="M32.59,65.181C50.592,65.181 65.191,50.582 65.191,32.59C65.191,14.599 50.592,0 32.59,0C14.599,0 0,14.599 0,32.59C0,50.582 14.599,65.181 32.59,65.181ZM32.59,58.02C18.529,58.02 7.161,46.652 7.161,32.59C7.161,18.529 18.529,7.161 32.59,7.161C46.652,7.161 58.03,18.529 58.03,32.59C58.03,46.652 46.652,58.02 32.59,58.02Z" style="fill-rule:nonzero;"/>
<path d="M16.733,26.802L48.5,26.802C50.183,26.802 51.362,25.748 51.362,24.146C51.362,22.565 50.183,21.511 48.5,21.511L16.733,21.511C15.029,21.511 13.871,22.565 13.871,24.146C13.871,25.748 15.029,26.802 16.733,26.802ZM20.832,37.096L44.402,37.096C46.065,37.096 47.233,36.042 47.233,34.44C47.233,32.869 46.065,31.805 44.402,31.805L20.832,31.805C19.147,31.805 17.98,32.869 17.98,34.44C17.98,36.042 19.147,37.096 20.832,37.096ZM25.068,47.411L40.145,47.411C41.808,47.411 42.996,46.336 42.996,44.755C42.996,43.184 41.808,42.121 40.145,42.121L25.068,42.121C23.405,42.121 22.237,43.184 22.237,44.755C22.237,46.336 23.405,47.411 25.068,47.411Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -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.929807,0,0,0.929807,1,12.754528)">
<path d="M16.503,41.397L50.239,41.397C52.648,41.397 54.536,39.324 54.536,36.987C54.536,34.578 52.671,32.569 50.239,32.569L16.503,32.569C14.094,32.569 12.212,34.626 12.212,36.987C12.212,39.276 14.118,41.397 16.503,41.397Z" style="fill-rule:nonzero;"/>
<path d="M10.407,25.112L56.342,25.112C58.727,25.112 60.608,23.039 60.608,20.702C60.608,18.269 58.751,16.285 56.342,16.285L10.407,16.285C8.022,16.285 6.133,18.341 6.133,20.702C6.133,22.992 8.046,25.112 10.407,25.112Z" style="fill-rule:nonzero;"/>
<path d="M4.267,8.827L62.414,8.827C64.823,8.827 66.68,6.755 66.68,4.417C66.68,1.984 64.847,0 62.414,0L4.267,0C1.882,0 0,2.032 0,4.417C0,6.683 1.905,8.827 4.267,8.827Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(0.624686,0,0,0.624686,3,17.237995)">
<path d="M45.17,47.212C46.58,47.212 47.578,46.42 47.578,45.039C47.578,44.432 47.455,44.033 47.022,43.038C43.627,36.904 41.807,30.56 41.807,23.653C41.807,16.937 43.541,10.39 47.022,4.224C47.455,3.24 47.578,2.84 47.578,2.233C47.578,0.895 46.58,0.05 45.17,0.05C43.622,0.05 42.412,0.807 41.094,2.698C36.763,8.399 34.637,15.598 34.637,23.632C34.637,31.696 36.75,38.726 41.094,44.574C42.402,46.475 43.622,47.212 45.17,47.212ZM53.441,39.915C54.789,39.915 55.627,39.472 56.736,37.85L63.381,28.189L63.526,28.189L70.367,38.036C71.337,39.448 72.177,39.915 73.434,39.915C75.47,39.915 76.876,38.574 76.876,36.677C76.876,35.855 76.63,35.133 76.075,34.383L68.175,23.647L76.006,13.227C76.629,12.367 76.927,11.646 76.927,10.722C76.927,8.851 75.474,7.596 73.587,7.596C72.104,7.596 71.3,8.297 70.379,9.72L64.033,19.158L63.877,19.158L57.501,9.689C56.548,8.256 55.66,7.596 54.075,7.596C52.07,7.596 50.568,9.08 50.568,10.866C50.568,11.932 50.899,12.683 51.435,13.403L59.08,23.63L51.105,34.459C50.429,35.36 50.226,36.071 50.226,36.964C50.226,38.669 51.625,39.915 53.441,39.915ZM82.304,47.212C83.862,47.212 85.062,46.465 86.38,44.574C90.754,38.736 92.847,31.696 92.847,23.632C92.847,15.598 90.682,8.419 86.38,2.698C85.072,0.797 83.862,0.05 82.304,0.05C80.894,0.05 79.896,0.895 79.896,2.233C79.896,2.84 80.029,3.24 80.452,4.224C83.933,10.39 85.667,16.937 85.667,23.653C85.667,30.56 83.848,36.904 80.452,43.038C80.029,44.033 79.896,44.432 79.896,45.039C79.896,46.377 80.894,47.212 82.304,47.212Z" style="fill-rule:nonzero;"/>
<path d="M5.252,47.17C12.053,47.17 15.203,44.455 16.783,37.027L20.127,21.046L25.664,21.046C27.685,21.046 29.099,19.888 29.099,17.909C29.099,16.108 27.887,15.025 26.141,15.025L21.395,15.025L22.161,11.302C22.935,7.691 24.066,6.295 27.165,6.295C27.697,6.295 28.187,6.263 28.56,6.222C30.37,5.986 31.317,5.049 31.317,3.437C31.317,1.14 29.52,0.071 25.884,0.071C19.19,0.071 15.859,3.04 14.354,10.214L13.352,15.025L9.606,15.025C7.544,15.025 6.149,16.193 6.149,18.163C6.149,19.974 7.342,21.046 9.139,21.046L12.084,21.046L8.945,35.949C8.159,39.645 7.007,40.946 3.961,40.946C3.534,40.946 3.096,40.987 2.775,41.019C0.93,41.266 0,42.325 0,43.856C0,46.1 1.765,47.17 5.252,47.17Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -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(1,0,0,1,14.41405,8.929688)">
<path d="M4.047,46.125L24.406,46.125C26.797,46.125 28.438,44.75 28.438,42.391C28.438,40.094 26.859,38.719 24.422,38.719L19.062,38.719L25.797,7.438L31.156,7.438C33.547,7.438 35.172,6.062 35.172,3.719C35.172,1.406 33.594,0.016 31.172,0.016L10.75,0.016C8.328,0.016 6.734,1.406 6.734,3.719C6.734,6.062 8.375,7.438 10.766,7.438L16.125,7.438L9.391,38.719L4.031,38.719C1.594,38.719 0,40.094 0,42.391C0,44.75 1.641,46.125 4.047,46.125Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 979 B

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(0.905936,0,0,0.905936,8.091803,1)">
<path d="M0,64.406C0,66.625 1.812,68.438 4.031,68.438C6.25,68.438 8.062,66.625 8.062,64.406L8.062,9.844C8.062,8.75 8.75,8.062 9.797,8.062L42.984,8.062C44.031,8.062 44.719,8.75 44.719,9.844L44.719,64.406C44.719,66.625 46.531,68.438 48.766,68.438C50.984,68.438 52.781,66.625 52.781,64.406L52.781,8.828C52.781,3.391 49.391,0 43.875,0L8.922,0C3.406,0 0,3.391 0,8.828L0,64.406Z" style="fill-rule:nonzero;"/>
<path d="M11.891,65.75C11.891,66.547 12.5,66.938 13.359,66.578L22.859,62.547C23.672,62.203 24.031,61.828 24.031,61.062L24.031,17.438C24.031,16.672 23.672,16.281 22.875,15.969L13.359,11.906C12.5,11.562 11.891,11.953 11.891,12.766L11.891,65.75Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,12 +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(1,0,0,1,0,-0.113382)">
<g transform="matrix(0.725404,0,0,0.725404,3.539221,11.473342)">
<path d="M43.789,56.563L11.031,56.562C3.922,56.562 0,52.672 0,45.594L0,11C0,3.922 3.906,0.031 10.516,0.031L67.422,0.031C74.547,0.031 78.469,3.922 78.469,11L78.469,39.642C76.475,37.218 73.921,35.265 71.016,33.991L71.016,11.879L54.327,26.905L60.012,32.59C57.573,32.984 55.276,33.831 53.211,35.039L49.46,31.288L45.719,34.656C43.594,36.578 41.609,37.391 39.219,37.391C36.844,37.391 34.844,36.578 32.734,34.656L28.993,31.29L11.188,49.108L11.328,49.109L43.533,49.109C43.374,50.11 43.292,51.133 43.292,52.174C43.292,53.679 43.464,55.148 43.789,56.563ZM7.453,43.594L24.132,26.915L7.453,11.906L7.453,43.594ZM12.279,7.484L36.734,29.547C37.547,30.266 38.359,30.641 39.219,30.641C40.094,30.641 40.906,30.266 41.719,29.547L66.174,7.484L12.279,7.484Z"/>
</g>
<g transform="matrix(0.692828,0,0,0.692828,-0,0.822733)">
<path d="M87.672,70C87.672,78.984 80.188,86.469 71.219,86.469C62.203,86.469 54.766,79.016 54.766,70C54.766,61 62.203,53.562 71.219,53.562C80.234,53.562 87.672,61 87.672,70ZM75.922,62.812L68.984,72.391L65.766,68.766C65.281,68.219 64.609,67.938 63.797,67.938C62.391,67.938 61.062,68.906 61.062,70.641C61.062,71.328 61.391,72.031 61.891,72.578L67.047,78.156C67.594,78.766 68.453,79 69.188,79C70.078,79 70.938,78.625 71.391,78.016L80.375,65.828C80.719,65.344 80.875,64.781 80.875,64.297C80.875,62.812 79.672,61.625 78.172,61.625C77.266,61.625 76.453,62.078 75.922,62.812Z" style="fill-rule:nonzero;"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -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.725404,0,0,0.725404,3.539221,11.473342)">
<path d="M11.031,56.562C3.922,56.562 0,52.672 0,45.594L0,11C0,3.922 3.906,0.031 10.516,0.031L67.422,0.031C74.547,0.031 78.469,3.922 78.469,11L78.469,45.594C78.469,52.672 74.562,56.562 67.953,56.562L11.031,56.562ZM7.453,43.594L24.132,26.915L7.453,11.906L7.453,43.594ZM67.279,49.107L49.46,31.288L45.719,34.656C43.594,36.578 41.609,37.391 39.219,37.391C36.844,37.391 34.844,36.578 32.734,34.656L28.993,31.29L11.188,49.108C11.234,49.109 11.281,49.109 11.328,49.109L67.109,49.109C67.167,49.109 67.223,49.109 67.279,49.107ZM71.016,11.879L54.327,26.905L71.016,43.594L71.016,11.879ZM12.279,7.484L36.734,29.547C37.547,30.266 38.359,30.641 39.219,30.641C40.094,30.641 40.906,30.266 41.719,29.547L66.174,7.484L12.279,7.484Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" version="1.1" viewBox="0 0 72 69" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
<path d="m20.601 66.402v-19.472h8.932v19.472h28.517c4.163 0 6.597-2.375 6.597-6.48v-23.76c3.033-1.711 5.043-4.945 5.043-8.699v-0.498c0-1.144-0.293-2.17-0.88-3.167l-7.77-13.283v-2.433c0-3.724-2.287-5.952-6.069-5.952h-37.912c-3.783 0-6.099 2.228-6.099 5.952v2.433l-7.77 13.283c-0.587 0.997-0.88 2.023-0.88 3.167v0.498c0 3.754 2.01 6.988 5.043 8.699v23.76c0 4.105 2.434 6.48 6.597 6.48h6.651zm39.325-28.945c-0.098 3e-3 -0.195 5e-3 -0.293 5e-3 -3.255 0-6.099-1.496-7.858-3.871-1.789 2.375-4.604 3.871-7.888 3.871s-6.128-1.496-7.887-3.871c-1.759 2.375-4.603 3.871-7.887 3.871-3.255 0-6.099-1.496-7.858-3.871-1.789 2.375-4.633 3.871-7.888 3.871-0.098 0-0.195-2e-3 -0.293-5e-3v21.262c0 1.906 1.026 2.962 2.903 2.962h1.759v-16.449c0-1.349 0.909-2.229 2.258-2.229h12.432c1.349 0 2.228 0.88 2.228 2.229v16.449h23.399c1.876 0 2.873-1.056 2.873-2.962v-21.262zm-37.443-8.088h11.289c-0.645 2.375-2.639 3.958-5.659 3.958-2.991 0-5.014-1.583-5.63-3.958zm31.52 0h11.289c-0.645 2.375-2.639 3.958-5.659 3.958-2.991 0-5.014-1.583-5.63-3.958zm-47.295 0h11.318c-0.645 2.375-2.639 3.958-5.659 3.958s-5.014-1.583-5.659-3.958zm31.52 0h11.318c-0.645 2.375-2.639 3.958-5.659 3.958s-5.013-1.583-5.659-3.958zm-30.846-4.134 6.363-11.201h44.275l6.539 11.201h-57.177zm8.005-15.629v-0.703c0-1.466 0.851-2.346 2.287-2.346h36.652c1.437 0 2.287 0.88 2.287 2.346v0.703h-41.226z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,12 +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.865066,0,0,0.865066,3,8.230968)">
<path d="M23.234,51.266L63.359,51.266C65.406,51.266 67.047,49.531 67.047,47.516C67.047,45.422 65.422,43.75 63.359,43.75L23.234,43.75C21.172,43.75 19.562,45.469 19.562,47.516C19.562,49.484 21.188,51.266 23.234,51.266Z" style="fill-rule:nonzero;"/>
<path d="M5.922,54.953C10.047,54.953 12.406,53.203 12.406,50.391C12.406,48.562 11.125,47.344 8.859,47.141L8.859,47.031C10.422,46.734 11.797,45.594 11.797,43.688C11.797,41.078 9.203,39.734 5.906,39.734C3.328,39.734 0.391,40.859 0.391,43.172C0.391,44.156 1.094,44.859 2.25,44.859C2.984,44.859 3.344,44.578 3.828,44.125C4.625,43.375 5.203,43.156 5.906,43.156C6.781,43.156 7.484,43.547 7.484,44.422C7.484,45.219 6.812,45.656 5.641,45.656L5.219,45.656C4.297,45.656 3.688,46.172 3.688,47.203C3.688,48.188 4.266,48.734 5.219,48.734L5.672,48.734C6.922,48.734 7.609,49.172 7.625,50.047C7.641,50.812 6.891,51.359 5.922,51.359C4.812,51.359 4,50.672 3.406,50.188C3.016,49.891 2.656,49.609 2,49.609C0.828,49.609 0,50.297 0,51.438C0,53.688 3.094,54.953 5.922,54.953Z" style="fill-rule:nonzero;"/>
<path d="M23.234,31.562L63.359,31.562C65.406,31.562 67.047,29.812 67.047,27.797C67.047,25.719 65.422,24.047 63.359,24.047L23.234,24.047C21.172,24.047 19.562,25.766 19.562,27.797C19.562,29.781 21.188,31.562 23.234,31.562Z" style="fill-rule:nonzero;"/>
<path d="M2.031,34.797L10.5,34.797C11.531,34.797 12.312,34.141 12.312,33.062C12.312,32 11.547,31.312 10.5,31.312L6.219,31.312L6.219,31.203L8.719,29.234C10.859,27.531 11.875,26.531 11.875,24.5C11.875,21.797 9.562,20.031 5.859,20.031C2.641,20.031 0.344,21.609 0.344,23.703C0.344,24.828 1.078,25.453 2.297,25.453C3.062,25.453 3.594,25.203 4.172,24.484C4.688,23.828 5.172,23.5 5.938,23.5C6.75,23.5 7.359,24.016 7.359,24.844C7.359,25.547 6.953,26.125 5.312,27.438L1.109,30.859C0.406,31.422 0.109,32.062 0.109,32.891C0.109,34 0.859,34.797 2.031,34.797Z" style="fill-rule:nonzero;"/>
<path d="M23.234,11.844L63.359,11.844C65.406,11.844 67.047,10.109 67.047,8.094C67.047,6.016 65.422,4.328 63.359,4.328L23.234,4.328C21.172,4.328 19.562,6.047 19.562,8.094C19.562,10.078 21.188,11.844 23.234,11.844Z" style="fill-rule:nonzero;"/>
<path d="M7.312,15.234C8.688,15.234 9.656,14.469 9.656,12.766L9.656,2.469C9.656,0.953 8.562,0 6.984,0C5.766,0 4.969,0.406 4.031,1.016L1.812,2.516C1.094,2.984 0.766,3.438 0.766,4.203C0.766,5.172 1.453,5.828 2.281,5.797C2.703,5.781 2.922,5.734 3.516,5.359L4.828,4.469L4.922,4.469L4.922,12.766C4.922,14.469 5.875,15.234 7.312,15.234Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -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.069972,0,0,0.069972,-2.986098,-11.802594)">
<path d="M242,226C206,232 174.833,248.333 148.5,275C122.167,301.667 106,333 100,369L100,884C106,919.333 122,950 148,976C174,1002 204.333,1018.333 239,1025L243,1026L758,1026L761,1025C796.333,1018.333 826.833,1001.833 852.5,975.5C878.167,949.167 894,918.333 900,883L900,368C894,332.667 877.667,301.667 851,275C824.333,248.333 793,232 757,226L242,226ZM802,325L802,401C731.333,401.667 636.667,401.667 518,401L519,326L802,325ZM481,326C481.667,342.667 482,367.667 482,401L200,401L200,326L481,326ZM518,438L802,438L802,514L518,514L518,438ZM482,438L482,514L200,513L200,439L482,438ZM554,550C590.667,550 645.667,550.333 719,551L801,551L801,627L518,628L518,550L554,550ZM482,550L482,628L200,628L200,551L482,551L482,550ZM518,665L801,665C801.667,677 801.667,695.333 801,720L801,740L519,740L518,665ZM311,700C327.667,700 343.833,703.5 359.5,710.5C375.167,717.5 388.333,727.333 399,740C413.667,756.667 422,776.5 424,799.5C426,822.5 422,844.5 412,865.5C402,886.5 387,902 367,912C348.333,922.667 327.5,927.167 304.5,925.5C281.5,923.833 260.833,916.333 242.5,903C224.167,889.667 212,872.333 206,851C198,831.667 196.333,811.333 201,790C205.667,768.667 215.333,750.167 230,734.5C244.667,718.833 262.333,708.667 283,704C292.333,701.333 301.667,700 311,700Z" style="fill:rgb(255,152,0);fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,11 +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(1,0,0,1,1.056758,1.088008)">
<rect x="0" y="0" width="59.625" height="59.562" style="fill-opacity:0;"/>
</g>
<g transform="matrix(0.918716,0,0,0.918716,5,4.991502)">
<path d="M17.169,58.796L41.597,58.796C47.087,58.796 51.398,57.182 54.261,54.3C57.207,51.396 58.778,47.075 58.778,41.616L58.778,17.19C58.778,11.722 57.207,7.41 54.261,4.496C51.377,1.602 47.087,0 41.597,0L17.169,0C11.7,0 7.358,1.624 4.496,4.496C1.571,7.41 0,11.722 0,17.19L0,41.616C0,47.075 1.55,51.396 4.496,54.3C7.379,57.204 11.7,58.796 17.169,58.796ZM17.487,51.837C13.94,51.837 11.276,50.962 9.566,49.252C7.813,47.53 6.959,44.898 6.959,41.288L6.959,17.508C6.959,13.909 7.813,11.276 9.566,9.545C11.245,7.865 13.94,6.959 17.487,6.959L41.27,6.959C44.848,6.959 47.49,7.844 49.212,9.545C50.965,11.276 51.819,13.909 51.819,17.508L51.819,41.288C51.819,44.898 50.965,47.53 49.212,49.252C47.511,50.941 44.848,51.837 41.27,51.837L17.487,51.837Z" style="fill-rule:nonzero;"/>
<path d="M34.245,20.733L28.455,25.929L17.907,36.476C17.311,37.063 16.929,37.896 16.929,38.71C16.929,40.544 18.242,41.748 19.997,41.748C20.916,41.748 21.667,41.417 22.323,40.771L32.821,30.304L37.996,24.505C40.497,21.719 37.205,18.138 34.245,20.733ZM36.354,30.119L36.354,34.789C36.354,36.636 37.45,37.853 39.143,37.853C40.836,37.853 41.911,36.582 41.911,34.767L41.911,20.374C41.911,18.014 40.563,16.868 38.364,16.868L23.898,16.868C22.062,16.868 20.845,17.943 20.845,19.636C20.845,21.329 22.064,22.404 23.91,22.404L28.872,22.404L37.417,21.188L36.354,30.119Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,12 +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(1,0,0,1,-0.5,-4)">
<path d="M59.063,37.932C60.996,39.115 62.286,41.246 62.286,43.675L62.286,59.025C62.286,62.739 59.271,65.754 55.558,65.754L9.442,65.754C5.729,65.754 2.714,62.739 2.714,59.025L2.714,43.675C2.714,41.692 3.574,39.908 4.941,38.676L4.941,23.857C4.941,21.49 6.144,19.403 8.186,18.218L28.762,6.357C30.813,5.136 33.19,5.136 35.263,6.357L55.817,18.218C57.859,19.403 59.063,21.49 59.063,23.857L59.063,37.932ZM10.076,36.947L27.371,36.947L10.076,27.093L10.076,36.947ZM36.574,36.947L53.923,36.947L53.923,27.065L36.574,36.947ZM31.984,33.577L52.082,22.106C51.964,21.992 51.921,21.927 51.713,21.8L33.492,11.283C32.55,10.719 31.453,10.719 30.511,11.283L12.312,21.8C12.083,21.927 12.03,21.997 11.892,22.125L31.984,33.577ZM58.286,43.675C58.286,42.169 57.064,40.947 55.558,40.947L9.442,40.947C7.936,40.947 6.714,42.169 6.714,43.675L6.714,59.025C6.714,60.531 7.936,61.754 9.442,61.754L55.558,61.754C57.064,61.754 58.286,60.531 58.286,59.025L58.286,43.675Z"/>
<g transform="matrix(1.711502,0,0,1.711502,-43.419476,-29.526577)">
<path d="M34.865,52.259C37.413,52.259 38.935,51.026 38.935,49.082C38.935,47.56 37.996,46.722 35.931,46.326L34.939,46.139C33.887,45.938 33.451,45.656 33.451,45.147C33.451,44.577 33.974,44.174 34.865,44.174C35.576,44.174 36.112,44.409 36.428,45.012C36.716,45.482 37.051,45.676 37.587,45.676C38.204,45.669 38.62,45.287 38.62,44.717C38.62,44.516 38.586,44.355 38.519,44.188C38.05,42.961 36.696,42.25 34.845,42.25C32.62,42.25 31.004,43.45 31.004,45.314C31.004,46.789 32.01,47.734 33.934,48.096L34.933,48.284C36.079,48.505 36.488,48.78 36.488,49.316C36.488,49.906 35.864,50.335 34.906,50.335C34.128,50.335 33.478,50.081 33.149,49.477C32.834,48.995 32.512,48.827 32.036,48.827C31.413,48.827 30.97,49.243 30.97,49.859C30.97,50.061 31.011,50.268 31.098,50.469C31.5,51.468 32.767,52.259 34.865,52.259Z" style="fill-rule:nonzero;"/>
<path d="M41.308,52.239C42.086,52.239 42.535,51.777 42.535,50.959L42.535,49.357L43.433,48.418L45.941,51.549C46.336,52.052 46.691,52.246 47.208,52.246C47.878,52.246 48.388,51.73 48.388,51.059C48.388,50.718 48.22,50.349 47.818,49.853L45.344,46.823L47.657,44.382C47.985,44.027 48.113,43.759 48.113,43.397C48.113,42.767 47.596,42.264 46.953,42.264C46.537,42.264 46.229,42.425 45.88,42.814L42.589,46.46L42.535,46.46L42.535,43.551C42.535,42.733 42.086,42.27 41.308,42.27C40.53,42.27 40.075,42.733 40.075,43.551L40.075,50.959C40.075,51.777 40.53,52.239 41.308,52.239Z" style="fill-rule:nonzero;"/>
<path d="M53.583,52.259C56.097,52.259 57.746,50.832 57.746,48.659L57.746,43.551C57.746,42.733 57.297,42.27 56.513,42.27C55.735,42.27 55.286,42.733 55.286,43.551L55.286,48.398C55.286,49.524 54.676,50.195 53.583,50.195C52.484,50.195 51.874,49.524 51.874,48.398L51.874,43.551C51.874,42.733 51.424,42.27 50.647,42.27C49.869,42.27 49.413,42.733 49.413,43.551L49.413,48.659C49.413,50.832 51.062,52.259 53.583,52.259Z" style="fill-rule:nonzero;"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,12 +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.983114,0,0,0.983114,0.872652,-3.62694)">
<path d="M1.364,59.66L1.364,41.653C1.364,40.114 2.153,38.751 3.47,37.993L15.368,31.125C15.408,31.101 15.448,31.079 15.488,31.057C15.484,30.981 15.482,30.906 15.482,30.83L15.482,17.094C15.482,15.554 16.271,14.192 17.588,13.433L29.487,6.565C30.819,5.777 32.374,5.777 33.714,6.565L45.605,13.433C46.922,14.192 47.71,15.554 47.71,17.094L47.71,30.83C47.71,30.882 47.71,30.934 47.708,30.986C47.794,31.029 47.879,31.075 47.963,31.125L59.854,37.993C61.171,38.751 61.96,40.114 61.96,41.653L61.96,59.66C61.96,63.437 58.893,66.504 55.116,66.504L8.208,66.504C4.431,66.504 1.364,63.437 1.364,59.66ZM50.74,37.202L46.646,34.839C46.14,34.532 45.551,34.532 45.045,34.839L40.954,37.202L50.74,37.202ZM22.294,37.202L17.928,34.679C17.513,34.548 17.07,34.601 16.678,34.839L12.587,37.202L22.294,37.202ZM31.554,22.132L42.251,15.999C42.197,15.953 42.182,15.93 42.067,15.861L32.397,10.279C31.891,9.973 31.302,9.973 30.796,10.279L21.133,15.861C21.011,15.93 20.98,15.96 20.896,16.029L31.554,22.132ZM29.663,36.94L29.663,25.531L19.341,19.62L19.341,30.103C19.341,30.57 19.539,30.987 19.878,31.288L29.663,36.94ZM33.53,36.986C33.721,36.887 33.805,36.833 34.005,36.718L43.047,31.489C43.553,31.19 43.844,30.685 43.844,30.103L43.844,19.574L33.53,25.478L33.53,36.986ZM57.891,44.046C57.891,42.514 56.648,41.271 55.116,41.271L8.208,41.271C6.676,41.271 5.433,42.514 5.433,44.046L5.433,59.66C5.433,61.191 6.676,62.435 8.208,62.435L55.116,62.435C56.648,62.435 57.891,61.191 57.891,59.66L57.891,44.046Z"/>
<g transform="matrix(1.740899,0,0,1.740899,-45.561479,-30.413194)">
<path d="M34.865,52.259C37.413,52.259 38.935,51.026 38.935,49.082C38.935,47.56 37.996,46.722 35.931,46.326L34.939,46.139C33.887,45.938 33.451,45.656 33.451,45.147C33.451,44.577 33.974,44.174 34.865,44.174C35.576,44.174 36.112,44.409 36.428,45.012C36.716,45.482 37.051,45.676 37.587,45.676C38.204,45.669 38.62,45.287 38.62,44.717C38.62,44.516 38.586,44.355 38.519,44.188C38.05,42.961 36.696,42.25 34.845,42.25C32.62,42.25 31.004,43.45 31.004,45.314C31.004,46.789 32.01,47.734 33.934,48.096L34.933,48.284C36.079,48.505 36.488,48.78 36.488,49.316C36.488,49.906 35.864,50.335 34.906,50.335C34.128,50.335 33.478,50.081 33.149,49.477C32.834,48.995 32.512,48.827 32.036,48.827C31.413,48.827 30.97,49.243 30.97,49.859C30.97,50.061 31.011,50.268 31.098,50.469C31.5,51.468 32.767,52.259 34.865,52.259Z" style="fill-rule:nonzero;"/>
<path d="M41.308,52.239C42.086,52.239 42.535,51.777 42.535,50.959L42.535,49.357L43.433,48.418L45.941,51.549C46.336,52.052 46.691,52.246 47.208,52.246C47.878,52.246 48.388,51.73 48.388,51.059C48.388,50.718 48.22,50.349 47.818,49.853L45.344,46.823L47.657,44.382C47.985,44.027 48.113,43.759 48.113,43.397C48.113,42.767 47.596,42.264 46.953,42.264C46.537,42.264 46.229,42.425 45.88,42.814L42.589,46.46L42.535,46.46L42.535,43.551C42.535,42.733 42.086,42.27 41.308,42.27C40.53,42.27 40.075,42.733 40.075,43.551L40.075,50.959C40.075,51.777 40.53,52.239 41.308,52.239Z" style="fill-rule:nonzero;"/>
<path d="M53.583,52.259C56.097,52.259 57.746,50.832 57.746,48.659L57.746,43.551C57.746,42.733 57.297,42.27 56.513,42.27C55.735,42.27 55.286,42.733 55.286,43.551L55.286,48.398C55.286,49.524 54.676,50.195 53.583,50.195C52.484,50.195 51.874,49.524 51.874,48.398L51.874,43.551C51.874,42.733 51.424,42.27 50.647,42.27C49.869,42.27 49.413,42.733 49.413,43.551L49.413,48.659C49.413,50.832 51.062,52.259 53.583,52.259Z" style="fill-rule:nonzero;"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(0.774546,0,0,0.774546,1,13.356447)">
<path d="M42.516,48.141C45.406,48.141 46.984,46.891 47.812,43.844L58.703,9.766L58.906,9.766L69.609,43.844C70.438,46.891 72,48.141 74.891,48.141C78.016,48.141 80.047,46.266 80.047,43.375C80.047,42.234 79.891,41.312 79.484,40.141L67.109,5.75C65.766,1.859 63.125,0 58.781,0C54.688,0 52.062,1.859 50.719,5.734L38.141,40.531C37.766,41.594 37.594,42.594 37.594,43.5C37.594,46.375 39.469,48.141 42.516,48.141ZM47.891,36.469L69.562,36.469C71.672,36.469 73.391,34.75 73.391,32.641C73.391,30.516 71.672,28.812 69.562,28.812L47.891,28.812C45.766,28.812 44.062,30.516 44.062,32.641C44.062,34.75 45.766,36.469 47.891,36.469Z" style="fill-rule:nonzero;"/>
<path d="M4.25,48.141C6.453,48.141 7.766,46.984 8.531,44.219L10.141,39.25L22.188,39.25L23.781,44.312C24.5,47.047 25.812,48.141 28.188,48.141C30.641,48.141 32.391,46.5 32.391,44.109C32.391,43.156 32.188,42.297 31.797,41.203L22.781,16.75C21.531,13.328 19.469,11.766 16.078,11.766C12.875,11.766 10.812,13.344 9.594,16.75L0.609,41.203C0.234,42.203 0,43.281 0,44.109C0,46.625 1.609,48.141 4.25,48.141ZM11.875,32.938L15.797,19.891L16.328,19.891L20.344,32.938L11.875,32.938Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,11 +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.862654,0,0,0.862654,3,7.576119)">
<rect x="0" y="0" width="67.234" height="56.625" style="fill-opacity:0;"/>
<path d="M3.656,56.578L63.359,56.578C65.406,56.578 67.047,54.844 67.047,52.828C67.047,50.75 65.422,49.062 63.359,49.062L3.656,49.062C1.609,49.062 0,50.797 0,52.828C0,54.812 1.625,56.578 3.656,56.578Z" style="fill-rule:nonzero;"/>
<path d="M3.656,40.234L63.359,40.234C65.406,40.234 67.047,38.5 67.047,36.484C67.047,34.391 65.422,32.719 63.359,32.719L3.656,32.719C1.609,32.719 0,34.422 0,36.484C0,38.453 1.625,40.234 3.656,40.234Z" style="fill-rule:nonzero;"/>
<path d="M3.656,23.875L63.359,23.875C65.406,23.875 67.047,22.125 67.047,20.109C67.047,18.047 65.422,16.359 63.359,16.359L3.656,16.359C1.609,16.359 0,18.078 0,20.109C0,22.094 1.625,23.875 3.656,23.875Z" style="fill-rule:nonzero;"/>
<path d="M3.656,7.516L63.359,7.516C65.406,7.516 67.047,5.781 67.047,3.766C67.047,1.688 65.422,0 63.359,0L3.656,0C1.609,0 0,1.734 0,3.766C0,5.75 1.625,7.516 3.656,7.516Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,12.21875,-7.71875)">
<path d="M2.859,68.875L36.688,68.875C38.328,68.875 39.562,67.719 39.562,66.109C39.562,64.516 38.328,63.328 36.688,63.328L2.859,63.328C1.234,63.328 0,64.516 0,66.109C0,67.719 1.234,68.875 2.859,68.875Z" style="fill-rule:nonzero;"/>
<path d="M19.781,58.359C31.812,58.359 39.562,51.5 39.562,41.203L39.562,15.281C39.562,12.328 37.672,10.562 34.703,10.562C31.766,10.562 29.875,12.328 29.875,15.281L29.875,40.234C29.875,46.328 26.203,50.156 19.781,50.156C13.359,50.156 9.703,46.328 9.703,40.234L9.703,15.281C9.703,12.328 7.781,10.562 4.828,10.562C1.906,10.562 0.016,12.328 0.016,15.281L0.016,41.203C0.016,51.5 7.75,58.359 19.781,58.359Z" style="fill-rule:nonzero;"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,7 +1,5 @@
<?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.773655,0,0,0.773655,3,3.979177)">
<path d="M6.922,23.938L68.547,23.938C70.812,23.938 72.156,22.234 72.156,20.547C72.156,19.312 71.453,18.188 69.984,17.328L42.109,1.25C40.734,0.453 39.188,0 37.719,0C36.25,0 34.688,0.453 33.344,1.25L5.469,17.328C3.984,18.188 3.297,19.312 3.297,20.547C3.297,22.234 4.641,23.938 6.922,23.938ZM15.047,19.719L36.859,7.5C37.141,7.344 37.469,7.25 37.719,7.25C37.984,7.25 38.297,7.344 38.594,7.5L60.406,19.719L61.016,17.516L14.438,17.516L15.047,19.719ZM8.516,31.609L17.281,31.609C18.797,31.609 19.734,30.75 19.734,29.234L19.734,28.781C19.734,27.25 18.812,26.312 17.281,26.312L8.516,26.312C7,26.312 6.094,27.25 6.094,28.781L6.094,29.234C6.094,30.75 7.031,31.609 8.516,31.609ZM9.891,59.516L15.938,59.516L15.938,30.047L9.891,30.047L9.891,59.516ZM8.516,63.25L17.281,63.25C18.812,63.25 19.734,62.359 19.734,60.812L19.734,60.359C19.734,58.844 18.797,57.953 17.281,57.953L8.516,57.953C7.031,57.953 6.094,58.844 6.094,60.359L6.094,60.812C6.094,62.359 7,63.25 8.516,63.25ZM25.062,31.609L33.828,31.609C35.344,31.609 36.281,30.75 36.281,29.234L36.281,28.781C36.281,27.25 35.359,26.312 33.828,26.312L25.062,26.312C23.547,26.312 22.641,27.25 22.641,28.781L22.641,29.234C22.641,30.75 23.562,31.609 25.062,31.609ZM26.422,59.516L32.469,59.516L32.469,30.047L26.422,30.047L26.422,59.516ZM25.062,63.25L33.828,63.25C35.359,63.25 36.281,62.359 36.281,60.812L36.281,60.359C36.281,58.844 35.344,57.953 33.828,57.953L25.062,57.953C23.562,57.953 22.641,58.844 22.641,60.359L22.641,60.812C22.641,62.359 23.547,63.25 25.062,63.25ZM41.625,31.609L50.375,31.609C51.875,31.609 52.828,30.75 52.828,29.234L52.828,28.781C52.828,27.25 51.891,26.312 50.375,26.312L41.625,26.312C40.078,26.312 39.188,27.25 39.188,28.781L39.188,29.234C39.188,30.75 40.094,31.609 41.625,31.609ZM42.984,59.516L49.031,59.516L49.031,30.047L42.984,30.047L42.984,59.516ZM41.625,63.25L50.375,63.25C51.891,63.25 52.828,62.359 52.828,60.812L52.828,60.359C52.828,58.844 51.875,57.953 50.375,57.953L41.625,57.953C40.094,57.953 39.188,58.844 39.188,60.359L39.188,60.812C39.188,62.359 40.078,63.25 41.625,63.25ZM58.156,31.609L66.922,31.609C68.406,31.609 69.359,30.75 69.359,29.234L69.359,28.781C69.359,27.25 68.422,26.312 66.922,26.312L58.156,26.312C56.625,26.312 55.719,27.25 55.719,28.781L55.719,29.234C55.719,30.75 56.641,31.609 58.156,31.609ZM59.531,59.516L65.578,59.516L65.578,30.047L59.531,30.047L59.531,59.516ZM58.156,63.25L66.922,63.25C68.422,63.25 69.359,62.359 69.359,60.812L69.359,60.359C69.359,58.844 68.406,57.953 66.922,57.953L58.156,57.953C56.641,57.953 55.719,58.844 55.719,60.359L55.719,60.812C55.719,62.359 56.625,63.25 58.156,63.25ZM3.391,72.438L71.547,72.438C73.375,72.438 74.969,70.922 74.969,69.031C74.969,67.188 73.375,65.641 71.547,65.641L3.391,65.641C1.547,65.641 0,67.188 0,69.031C0,70.906 1.547,72.438 3.391,72.438Z" style="fill-rule:nonzero;"/>
</g>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" version="1.1" viewBox="0 0 72 69" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
<path d="m20.601 66.402v-19.472h8.932v19.472h28.517c4.163 0 6.597-2.375 6.597-6.48v-23.76c3.033-1.711 5.043-4.945 5.043-8.699v-0.498c0-1.144-0.293-2.17-0.88-3.167l-7.77-13.283v-2.433c0-3.724-2.287-5.952-6.069-5.952h-37.912c-3.783 0-6.099 2.228-6.099 5.952v2.433l-7.77 13.283c-0.587 0.997-0.88 2.023-0.88 3.167v0.498c0 3.754 2.01 6.988 5.043 8.699v23.76c0 4.105 2.434 6.48 6.597 6.48h6.651zm39.325-28.945c-0.098 3e-3 -0.195 5e-3 -0.293 5e-3 -3.255 0-6.099-1.496-7.858-3.871-1.789 2.375-4.604 3.871-7.888 3.871s-6.128-1.496-7.887-3.871c-1.759 2.375-4.603 3.871-7.887 3.871-3.255 0-6.099-1.496-7.858-3.871-1.789 2.375-4.633 3.871-7.888 3.871-0.098 0-0.195-2e-3 -0.293-5e-3v21.262c0 1.906 1.026 2.962 2.903 2.962h1.759v-16.449c0-1.349 0.909-2.229 2.258-2.229h12.432c1.349 0 2.228 0.88 2.228 2.229v16.449h23.399c1.876 0 2.873-1.056 2.873-2.962v-21.262zm-37.443-8.088h11.289c-0.645 2.375-2.639 3.958-5.659 3.958-2.991 0-5.014-1.583-5.63-3.958zm31.52 0h11.289c-0.645 2.375-2.639 3.958-5.659 3.958-2.991 0-5.014-1.583-5.63-3.958zm-47.295 0h11.318c-0.645 2.375-2.639 3.958-5.659 3.958s-5.014-1.583-5.659-3.958zm31.52 0h11.318c-0.645 2.375-2.639 3.958-5.659 3.958s-5.013-1.583-5.659-3.958zm-30.846-4.134 6.363-11.201h44.275l6.539 11.201h-57.177zm8.005-15.629v-0.703c0-1.466 0.851-2.346 2.287-2.346h36.652c1.437 0 2.287 0.88 2.287 2.346v0.703h-41.226z"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -35,10 +35,6 @@
.ant-picker-input,
.ant-picker-header-view button,
.ant-badge,
.ant-select-dropdown,
.ant-splitter,
.ant-steps-item-title,
.ant-steps-icon,
[class*=' ant-radio'] {
font-family: 'DM Sans';
}
@ -48,15 +44,6 @@
font-family: 'DM Mono';
}
.ant-steps .ant-steps-item-icon {
line-height: 30px;
font-weight: 600;
}
.ant-steps .ant-steps-item-icon .anticon.anticon-check.ant-steps-finish-icon {
line-height: 32px;
}
.ant-typography .id-text {
font-family: 'DM Mono';
font-size: 11.9px;
@ -181,6 +168,23 @@ code {
}
}
.markdown-display > .ant-space-item *:first-child {
margin-top: 0;
}
.markdown-display > .ant-space-item *:last-child {
margin-bottom: 0;
}
.markdown-display > .ant-space-item h1,
.markdown-display > .ant-space-item h2,
.markdown-display > .ant-space-item h3,
.markdown-display > .ant-space-item h4,
.markdown-display > .ant-space-item h5,
.markdown-display > .ant-space-item h6 {
margin-bottom: 0.15em;
}
.iddisplay .ant-popover-inner {
padding: 0 !important;
}
@ -413,186 +417,3 @@ body {
.ant-badge .ant-badge-count-sm {
font-size: 10px;
}
.input-number-cal {
position: relative;
}
.input-number-cal .ant-input-suffix {
margin-right: 28px;
}
.input-number-cal .ant-input-outlined {
border-color: var(--color-purple);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-purple) 31%, transparent);
}
.input-number-cal .ant-input {
font-family: 'DM Mono';
font-weight: 400;
}
.input-number-cal-icon {
position: absolute;
right: 10px;
top: 0;
bottom: 1px;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
/* --- Start of src/components/Dashboard/common/MarkdownInput.css --- */
.md-editor__content {
padding: 24px;
font-size: 14px;
line-height: 1.6;
color: var(--baseText);
white-space: pre-wrap;
outline: none;
}
.md-editor {
color: var(--baseText);
}
.md-editor .tiptap {
min-height: var(--md-editor-min-height);
}
.md-editor .tiptap p.is-editor-empty:first-child::before {
content: 'Enter text here...';
color: var(--baseBorderHover);
float: left;
height: 0;
pointer-events: none;
}
.markdown pre,
.markdown code,
.markdown blockquote {
background: var(--baseBgSubtle);
color: var(--baseTextContrast);
}
.markdown pre {
border-radius: 8px;
padding: 12px;
overflow-x: auto;
}
.markdown blockquote {
border-left: 3px solid var(--color-primary);
margin: 0;
padding: 8px 12px;
}
.markdown hr {
border: 0;
border-top: 1px solid #8484844d;
margin: 10px 0;
}
.markdown *:first-child {
margin-top: 0;
}
.markdown h1:first-child {
margin-top: -4px;
}
.markdown h2:first-child {
margin-top: -3px;
}
.markdown h3:first-child {
margin-top: -2px;
}
.markdown h4:first-child {
margin-top: -1px;
}
.markdown *:last-child {
margin-bottom: 0;
}
.markdown h1,
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
margin-bottom: 10px;
line-height: 1;
}
.markdown p {
font-size: 14px;
margin: 10px 0;
line-height: 1;
}
.markdown h1 {
font-size: 32px;
}
.markdown h2 {
font-size: 26px;
}
.markdown h3 {
font-size: 20px;
}
.markdown h4 {
font-size: 16px;
}
.markdown {
width: 100%;
}
.markdown ul,
.markdown ol {
padding-left: 20px;
margin: 10px 0;
}
.markdown li {
margin: 10px 0;
line-height: 1;
}
.markdown-code-editor {
padding: 18px;
}
.markdown-splitter > .ant-splitter-bar {
margin: 8px 0;
}
.markdown-code-editor .cm-editor {
background-color: transparent;
}
.markdown-code-editor .cm-editor.cm-focused {
outline: none;
}
.markdown-code-editor .cm-editor .cm-gutters {
background-color: transparent;
}
.h-100 {
height: 100%;
}
.ant-table.ant-table-middle .ant-table-filter-trigger {
height: 20px;
}
.ant-table-wrapper .ant-table-filter-column {
align-items: center;
}

2906
bun.lock Normal file

File diff suppressed because it is too large Load Diff

4805
dist-test/index.js Normal file

File diff suppressed because it is too large Load Diff

31
electrobun.config.ts Normal file
View File

@ -0,0 +1,31 @@
import type { ElectrobunConfig } from 'electrobun'
import { readFileSync } from 'fs'
const packageJson = JSON.parse(readFileSync('./package.json', 'utf8'))
export default {
app: {
name: 'Farm Control',
identifier: 'com.tombutcher.farmcontrol',
version: packageJson.version,
urlSchemes: ['farmcontrol']
},
runtime: {
exitOnLastWindowClosed: true
},
build: {
bun: {
entrypoint: 'src/bun/index.ts'
},
views: {
preload: {
entrypoint: 'src/preload/index.ts'
}
},
copy: {
'build/index.html': 'views/main/index.html',
'build/assets': 'views/main/assets'
},
watch: ['build']
}
} satisfies ElectrobunConfig

View File

@ -29,13 +29,6 @@
"@codemirror/theme-one-dark": "^6.1.3",
"@simplewebauthn/browser": "^13.1.2",
"@tanstack/react-query": "^5.90.10",
"@tiptap/extension-link": "^3.20.1",
"@tiptap/extension-placeholder": "^3.20.1",
"@tiptap/extension-underline": "^3.20.1",
"@tiptap/markdown": "^3.20.1",
"@tiptap/pm": "^3.20.1",
"@tiptap/react": "^3.20.1",
"@tiptap/starter-kit": "^3.20.1",
"@tsparticles/react": "^3.0.0",
"@tsparticles/slim": "^3.9.1",
"@uiw/react-codemirror": "^4.25.1",
@ -48,7 +41,6 @@
"dotenv": "^17.2.1",
"gcode-preview": "^2.18.0",
"keycloak-js": "^26.2.0",
"keytar": "^7.9.0",
"lodash": "^4.17.23",
"loglevel": "^1.9.2",
"online-3d-viewer": "^0.16.0",
@ -70,15 +62,15 @@
"tsparticles": "^3.9.1",
"web-vitals": "^5.1.0"
},
"main": "build/electron.js",
"main": "src/bun/index.ts",
"description": "3D Printer ERP and Control Software.",
"scripts": {
"dev": "cross-env NODE_ENV=development vite",
"electron": "cross-env ELECTRON_START_URL=http://0.0.0.0:5780 && cross-env NODE_ENV=development && electron .",
"electrobun": "cross-env ELECTROBUN_START_URL=http://localhost:5173 electrobun dev",
"start": "serve -s build",
"build": "vite build",
"dev:electron": "concurrently \"cross-env NODE_ENV=development vite --no-open\" \"cross-env ELECTRON_START_URL=http://localhost:5780 cross-env NODE_ENV=development electron public/electron.js\"",
"build:electron": "vite build && electron-builder",
"dev:electrobun": "concurrently \"cross-env NODE_ENV=development vite --no-open\" \"cross-env ELECTROBUN_START_URL=http://localhost:5173 electrobun dev\"",
"build:electrobun": "vite build && electrobun build",
"build:cloudflare": "cross-env VITE_DEPLOY_TARGET=cloudflare vite build",
"deploy": "npm run build:cloudflare && wrangler pages deploy --branch main"
},
@ -104,9 +96,8 @@
"@eslint/js": "^9.39.2",
"@vitejs/plugin-react": "^5.0.2",
"concurrently": "^9.2.1",
"electron": "^38.7.1",
"electron-builder": "^26.0.12",
"electron-packager": "^17.1.2",
"electrobun": "^1.13.1",
"typescript": "^5.0.0",
"eslint": "^9.34.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
@ -144,13 +135,7 @@
{
"target": "dmg",
"arch": [
"arm64"
]
},
{
"target": "dmg",
"arch": [
"x64"
"universal"
]
}
],

662
pnpm-lock.yaml generated
View File

@ -68,27 +68,6 @@ importers:
'@tanstack/react-query':
specifier: ^5.90.10
version: 5.90.20(react@19.2.4)
'@tiptap/extension-link':
specifier: ^3.20.1
version: 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-placeholder':
specifier: ^3.20.1
version: 3.20.1(@tiptap/extensions@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))
'@tiptap/extension-underline':
specifier: ^3.20.1
version: 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/markdown':
specifier: ^3.20.1
version: 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/pm':
specifier: ^3.20.1
version: 3.20.1
'@tiptap/react':
specifier: ^3.20.1
version: 3.20.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tiptap/starter-kit':
specifier: ^3.20.1
version: 3.20.1
'@tsparticles/react':
specifier: ^3.0.0
version: 3.0.0(@tsparticles/engine@3.9.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@ -1130,15 +1109,6 @@ packages:
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
engines: {node: '>=14'}
'@floating-ui/core@1.7.5':
resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
'@floating-ui/dom@1.7.6':
resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
'@floating-ui/utils@0.2.11':
resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
'@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'}
@ -1468,9 +1438,6 @@ packages:
react: '>=18.0.0'
react-dom: '>=18.0.0'
'@remirror/core-constants@3.0.0':
resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
'@rolldown/pluginutils@1.0.0-beta.53':
resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==}
@ -1711,166 +1678,6 @@ packages:
peerDependencies:
react: ^18 || ^19
'@tiptap/core@3.20.1':
resolution: {integrity: sha512-SwkPEWIfaDEZjC8SEIi4kZjqIYUbRgLUHUuQezo5GbphUNC8kM1pi3C3EtoOPtxXrEbY6e4pWEzW54Pcrd+rVA==}
peerDependencies:
'@tiptap/pm': ^3.20.1
'@tiptap/extension-blockquote@3.20.1':
resolution: {integrity: sha512-WzNXk/63PQI2fav4Ta6P0GmYRyu8Gap1pV3VUqaVK829iJ6Zt1T21xayATHEHWMK27VT1GLPJkx9Ycr2jfDyQw==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extension-bold@3.20.1':
resolution: {integrity: sha512-fz++Qv6Rk/Hov0IYG/r7TJ1Y4zWkuGONe0UN5g0KY32NIMg3HeOHicbi4xsNWTm9uAOl3eawWDkezEMrleObMw==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extension-bubble-menu@3.20.1':
resolution: {integrity: sha512-XaPvO6aCoWdFnCBus0s88lnj17NR/OopV79i8Qhgz3WMR0vrsL5zsd45l0lZuu9pSvm5VW47SoxakkJiZC1suw==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/pm': ^3.20.1
'@tiptap/extension-bullet-list@3.20.1':
resolution: {integrity: sha512-mbrlvOZo5OF3vLhp+3fk9KuL/6J/wsN0QxF6ZFRAHzQ9NkJdtdfARcBeBnkWXGN8inB6YxbTGY1/E4lmBkOpOw==}
peerDependencies:
'@tiptap/extension-list': ^3.20.1
'@tiptap/extension-code-block@3.20.1':
resolution: {integrity: sha512-vKejwBq+Nlj4Ybd3qOyDxIQKzYymdNH+8eXkKwGShk2nfLJIxq69DCyGvmuHgipIO1qcYPJ149UNpGN+YGcdmA==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/pm': ^3.20.1
'@tiptap/extension-code@3.20.1':
resolution: {integrity: sha512-509DHINIA/Gg+eTG7TEkfsS8RUiPLH5xZNyLRT0A1oaoaJmECKfrV6aAm05IdfTyqDqz6LW5pbnX6DdUC4keug==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extension-document@3.20.1':
resolution: {integrity: sha512-9vrqdGmRV7bQCSY3NLgu7UhIwgOCDp4sKqMNsoNRX0aZ021QQMTvBQDPkiRkCf7MNsnWrNNnr52PVnULEn3vFQ==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extension-dropcursor@3.20.1':
resolution: {integrity: sha512-K18L9FX4znn+ViPSIbTLOGcIaXMx/gLNwAPE8wPLwswbHhQqdiY1zzdBw6drgOc1Hicvebo2dIoUlSXOZsOEcw==}
peerDependencies:
'@tiptap/extensions': ^3.20.1
'@tiptap/extension-floating-menu@3.20.1':
resolution: {integrity: sha512-BeDC6nfOesIMn5pFuUnkEjOxGv80sOJ8uk1mdt9/3Fkvra8cB9NIYYCVtd6PU8oQFmJ8vFqPrRkUWrG5tbqnOg==}
peerDependencies:
'@floating-ui/dom': ^1.0.0
'@tiptap/core': ^3.20.1
'@tiptap/pm': ^3.20.1
'@tiptap/extension-gapcursor@3.20.1':
resolution: {integrity: sha512-kZOtttV6Ai8VUAgEng3h4WKFbtdSNJ6ps7r0cRPY+FctWhVmgNb/JJwwyC+vSilR7nRENAhrA/Cv/RxVlvLw+g==}
peerDependencies:
'@tiptap/extensions': ^3.20.1
'@tiptap/extension-hard-break@3.20.1':
resolution: {integrity: sha512-9sKpmg/IIdlLXimYWUZ3PplIRcehv4Oc7V1miTqlnAthMzjMqigDkjjgte4JZV67RdnDJTQkRw8TklCAU28Emg==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extension-heading@3.20.1':
resolution: {integrity: sha512-unudyfQP6FxnyWinxvPqe/51DG91J6AaJm666RnAubgYMCgym+33kBftx4j4A6qf+ddWYbD00thMNKOnVLjAEQ==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extension-horizontal-rule@3.20.1':
resolution: {integrity: sha512-rjFKFXNntdl0jay8oIGFvvykHlpyQTLmrH3Ag2fj3i8yh6MVvqhtaDomYQbw5sxECd5hBkL+T4n2d2DRuVw/QQ==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/pm': ^3.20.1
'@tiptap/extension-italic@3.20.1':
resolution: {integrity: sha512-ZYRX13Kt8tR8JOzSXirH3pRpi8x30o7LHxZY58uXBdUvr3tFzOkh03qbN523+diidSVeHP/aMd/+IrplHRkQug==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extension-link@3.20.1':
resolution: {integrity: sha512-oYTTIgsQMqpkSnJAuAc+UtIKMuI4lv9e1y4LfI1iYm6NkEUHhONppU59smhxHLzb3Ww7YpDffbp5IgDTAiJztA==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/pm': ^3.20.1
'@tiptap/extension-list-item@3.20.1':
resolution: {integrity: sha512-tzgnyTW82lYJkUnadYbatwkI9dLz/OWRSWuFpQPRje/ItmFMWuQ9c9NDD8qLbXPdEYnvrgSAA+ipCD/1G0qA0Q==}
peerDependencies:
'@tiptap/extension-list': ^3.20.1
'@tiptap/extension-list-keymap@3.20.1':
resolution: {integrity: sha512-Dr0xsQKx0XPOgDg7xqoWwfv7FFwZ3WeF3eOjqh3rDXlNHMj1v+UW5cj1HLphrsAZHTrVTn2C+VWPJkMZrSbpvQ==}
peerDependencies:
'@tiptap/extension-list': ^3.20.1
'@tiptap/extension-list@3.20.1':
resolution: {integrity: sha512-euBRAn0mkV7R2VEE+AuOt3R0j9RHEMFXamPFmtvTo8IInxDClusrm6mJoDjS8gCGAXsQCRiAe1SCQBPgGbOOwg==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/pm': ^3.20.1
'@tiptap/extension-ordered-list@3.20.1':
resolution: {integrity: sha512-Y+3Ad7OwAdagqdYwCnbqf7/to5ypD4NnUNHA0TXRCs7cAHRA8AdgPoIcGFpaaSpV86oosNU3yfeJouYeroffog==}
peerDependencies:
'@tiptap/extension-list': ^3.20.1
'@tiptap/extension-paragraph@3.20.1':
resolution: {integrity: sha512-QFrAtXNyv7JSnomMQc1nx5AnG9mMznfbYJAbdOQYVdbLtAzTfiTuNPNbQrufy5ZGtGaHxDCoaygu2QEfzaKG+Q==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extension-placeholder@3.20.1':
resolution: {integrity: sha512-k+jfbCugYGuIFBdojukgEopGazIMOgHrw46FnyN2X/6ICOIjQP2rh2ObslrsUOsJYoEevxCsNF9hZl1HvWX66g==}
peerDependencies:
'@tiptap/extensions': ^3.20.1
'@tiptap/extension-strike@3.20.1':
resolution: {integrity: sha512-EYgyma10lpsY+rwbVQL9u+gA7hBlKLSMFH7Zgd37FSxukOjr+HE8iKPQQ+SwbGejyDsPlLT8Z5Jnuxo5Ng90Pg==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extension-text@3.20.1':
resolution: {integrity: sha512-7PlIbYW8UenV6NPOXHmv8IcmPGlGx6HFq66RmkJAOJRPXPkTLAiX0N8rQtzUJ6jDEHqoJpaHFEHJw0xzW1yF+A==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extension-underline@3.20.1':
resolution: {integrity: sha512-fmHvDKzwCgnZUwRreq8tYkb1YyEwgzZ6QQkAQ0CsCRtvRMqzerr3Duz0Als4i8voZTuGDEL3VR6nAJbLAb/wPg==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/extensions@3.20.1':
resolution: {integrity: sha512-JRc/v+OBH0qLTdvQ7HvHWTxGJH73QOf1MC0R8NhOX2QnAbg2mPFv1h+FjGa2gfLGuCXBdWQomjekWkUKbC4e5A==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/pm': ^3.20.1
'@tiptap/markdown@3.20.1':
resolution: {integrity: sha512-dNrtP7kmabDomgjv9G/6+JSFL6WraPaFbmKh1eHSYKdDGvIwBfJnVPTV2VS3bP1OuYJEDJN/2ydtiCHyOTrQsQ==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/pm': ^3.20.1
'@tiptap/pm@3.20.1':
resolution: {integrity: sha512-6kCiGLvpES4AxcEuOhb7HR7/xIeJWMjZlb6J7e8zpiIh5BoQc7NoRdctsnmFEjZvC19bIasccshHQ7H2zchWqw==}
'@tiptap/react@3.20.1':
resolution: {integrity: sha512-UH1NpVpCaZBGB3Yr5N6aTS+rsCMDl9wHfrt/w+6+Gz4KHFZ2OILA82hELxZzhNc1Lmjz8vgCArKcsYql9gbzJA==}
peerDependencies:
'@tiptap/core': ^3.20.1
'@tiptap/pm': ^3.20.1
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
'@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0
react: ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
'@tiptap/starter-kit@3.20.1':
resolution: {integrity: sha512-opqWxL/4OTEiqmVC0wsU4o3JhAf6LycJ2G/gRIZVAIFLljI9uHfpPMTFGxZ5w9IVVJaP5PJysfwW/635kKqkrw==}
'@trysound/sax@0.2.0':
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
engines: {node: '>=10.13.0'}
@ -2134,18 +1941,9 @@ packages:
'@types/keyv@3.1.4':
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
'@types/linkify-it@5.0.0':
resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
'@types/markdown-it@14.1.2':
resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
'@types/mdurl@2.0.0':
resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
@ -2161,11 +1959,6 @@ packages:
'@types/plist@3.0.5':
resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==}
'@types/react-dom@19.2.3':
resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
peerDependencies:
'@types/react': ^19.2.0
'@types/react@19.2.10':
resolution: {integrity: sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==}
@ -2181,9 +1974,6 @@ packages:
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
'@types/use-sync-external-store@0.0.6':
resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==}
'@types/verror@1.10.11':
resolution: {integrity: sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==}
@ -3594,10 +3384,6 @@ packages:
fast-diff@1.3.0:
resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
fast-equals@5.4.0:
resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==}
engines: {node: '>=6.0.0'}
fast-glob@3.3.3:
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
engines: {node: '>=8.6.0'}
@ -4307,12 +4093,6 @@ packages:
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
linkifyjs@4.3.2:
resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==}
load-json-file@2.0.0:
resolution: {integrity: sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==}
engines: {node: '>=4'}
@ -4396,18 +4176,9 @@ packages:
resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==}
engines: {node: ^18.17.0 || >=20.5.0}
markdown-it@14.1.1:
resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==}
hasBin: true
markdown-table@3.0.4:
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
marked@17.0.4:
resolution: {integrity: sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ==}
engines: {node: '>= 20'}
hasBin: true
matcher@3.0.0:
resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
engines: {node: '>=10'}
@ -4473,9 +4244,6 @@ packages:
mdn-data@2.12.2:
resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
media-typer@1.1.0:
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
engines: {node: '>= 0.8'}
@ -4850,9 +4618,6 @@ packages:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
engines: {node: '>=10'}
orderedmap@2.1.1:
resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
own-keys@1.0.1:
resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
engines: {node: '>= 0.4'}
@ -5088,64 +4853,6 @@ packages:
property-information@7.1.0:
resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
prosemirror-changeset@2.4.0:
resolution: {integrity: sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==}
prosemirror-collab@1.3.1:
resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==}
prosemirror-commands@1.7.1:
resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
prosemirror-dropcursor@1.8.2:
resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==}
prosemirror-gapcursor@1.4.0:
resolution: {integrity: sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==}
prosemirror-history@1.5.0:
resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==}
prosemirror-inputrules@1.5.1:
resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==}
prosemirror-keymap@1.2.3:
resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==}
prosemirror-markdown@1.13.4:
resolution: {integrity: sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==}
prosemirror-menu@1.3.0:
resolution: {integrity: sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg==}
prosemirror-model@1.25.4:
resolution: {integrity: sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==}
prosemirror-schema-basic@1.2.4:
resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==}
prosemirror-schema-list@1.5.1:
resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==}
prosemirror-state@1.4.4:
resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==}
prosemirror-tables@1.8.5:
resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==}
prosemirror-trailing-node@3.0.0:
resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==}
peerDependencies:
prosemirror-model: ^1.22.1
prosemirror-state: ^1.4.2
prosemirror-view: ^1.33.8
prosemirror-transform@1.11.0:
resolution: {integrity: sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw==}
prosemirror-view@1.41.6:
resolution: {integrity: sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==}
proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
@ -5156,10 +4863,6 @@ packages:
pump@3.0.3:
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
punycode.js@2.3.1:
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
engines: {node: '>=6'}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@ -5623,9 +5326,6 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
rope-sequence@1.3.4:
resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
router@2.2.0:
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
engines: {node: '>= 18'}
@ -6204,9 +5904,6 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
uc.micro@2.1.0:
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
ufo@1.6.3:
resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}
@ -6282,11 +5979,6 @@ packages:
peerDependencies:
react: '>= 16.x'
use-sync-external-store@1.6.0:
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
utf8-byte-length@1.0.5:
resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==}
@ -7690,20 +7382,6 @@ snapshots:
'@fastify/busboy@2.1.1': {}
'@floating-ui/core@1.7.5':
dependencies:
'@floating-ui/utils': 0.2.11
optional: true
'@floating-ui/dom@1.7.6':
dependencies:
'@floating-ui/core': 1.7.5
'@floating-ui/utils': 0.2.11
optional: true
'@floating-ui/utils@0.2.11':
optional: true
'@humanfs/core@0.19.1': {}
'@humanfs/node@0.16.7':
@ -8067,8 +7745,6 @@ snapshots:
react-dom: 19.2.4(react@19.2.4)
react-is: 18.3.1
'@remirror/core-constants@3.0.0': {}
'@rolldown/pluginutils@1.0.0-beta.53': {}
'@rollup/pluginutils@4.2.1':
@ -8255,193 +7931,6 @@ snapshots:
'@tanstack/query-core': 5.90.20
react: 19.2.4
'@tiptap/core@3.20.1(@tiptap/pm@3.20.1)':
dependencies:
'@tiptap/pm': 3.20.1
'@tiptap/extension-blockquote@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-bold@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-bubble-menu@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)':
dependencies:
'@floating-ui/dom': 1.7.6
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/pm': 3.20.1
optional: true
'@tiptap/extension-bullet-list@3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/extension-list': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-code-block@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/pm': 3.20.1
'@tiptap/extension-code@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-document@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-dropcursor@3.20.1(@tiptap/extensions@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/extensions': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-floating-menu@3.20.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)':
dependencies:
'@floating-ui/dom': 1.7.6
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/pm': 3.20.1
optional: true
'@tiptap/extension-gapcursor@3.20.1(@tiptap/extensions@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/extensions': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-hard-break@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-heading@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-horizontal-rule@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/pm': 3.20.1
'@tiptap/extension-italic@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-link@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/pm': 3.20.1
linkifyjs: 4.3.2
'@tiptap/extension-list-item@3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/extension-list': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-list-keymap@3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/extension-list': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/pm': 3.20.1
'@tiptap/extension-ordered-list@3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/extension-list': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-paragraph@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-placeholder@3.20.1(@tiptap/extensions@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/extensions': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-strike@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-text@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-underline@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extensions@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/pm': 3.20.1
'@tiptap/markdown@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/pm': 3.20.1
marked: 17.0.4
'@tiptap/pm@3.20.1':
dependencies:
prosemirror-changeset: 2.4.0
prosemirror-collab: 1.3.1
prosemirror-commands: 1.7.1
prosemirror-dropcursor: 1.8.2
prosemirror-gapcursor: 1.4.0
prosemirror-history: 1.5.0
prosemirror-inputrules: 1.5.1
prosemirror-keymap: 1.2.3
prosemirror-markdown: 1.13.4
prosemirror-menu: 1.3.0
prosemirror-model: 1.25.4
prosemirror-schema-basic: 1.2.4
prosemirror-schema-list: 1.5.1
prosemirror-state: 1.4.4
prosemirror-tables: 1.8.5
prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6)
prosemirror-transform: 1.11.0
prosemirror-view: 1.41.6
'@tiptap/react@3.20.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/pm': 3.20.1
'@types/react': 19.2.10
'@types/react-dom': 19.2.3(@types/react@19.2.10)
'@types/use-sync-external-store': 0.0.6
fast-equals: 5.4.0
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
use-sync-external-store: 1.6.0(react@19.2.4)
optionalDependencies:
'@tiptap/extension-bubble-menu': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-floating-menu': 3.20.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
transitivePeerDependencies:
- '@floating-ui/dom'
'@tiptap/starter-kit@3.20.1':
dependencies:
'@tiptap/core': 3.20.1(@tiptap/pm@3.20.1)
'@tiptap/extension-blockquote': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extension-bold': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extension-bullet-list': 3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))
'@tiptap/extension-code': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extension-code-block': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-document': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extension-dropcursor': 3.20.1(@tiptap/extensions@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))
'@tiptap/extension-gapcursor': 3.20.1(@tiptap/extensions@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))
'@tiptap/extension-hard-break': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extension-heading': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extension-horizontal-rule': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-italic': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extension-link': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-list': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/extension-list-item': 3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))
'@tiptap/extension-list-keymap': 3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))
'@tiptap/extension-ordered-list': 3.20.1(@tiptap/extension-list@3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1))
'@tiptap/extension-paragraph': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extension-strike': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extension-text': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extension-underline': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))
'@tiptap/extensions': 3.20.1(@tiptap/core@3.20.1(@tiptap/pm@3.20.1))(@tiptap/pm@3.20.1)
'@tiptap/pm': 3.20.1
'@trysound/sax@0.2.0': {}
'@tsparticles/basic@3.9.1':
@ -8793,19 +8282,10 @@ snapshots:
dependencies:
'@types/node': 22.19.7
'@types/linkify-it@5.0.0': {}
'@types/markdown-it@14.1.2':
dependencies:
'@types/linkify-it': 5.0.0
'@types/mdurl': 2.0.0
'@types/mdast@4.0.4':
dependencies:
'@types/unist': 3.0.3
'@types/mdurl@2.0.0': {}
'@types/ms@2.1.0': {}
'@types/node@22.19.7':
@ -8824,10 +8304,6 @@ snapshots:
xmlbuilder: 15.1.1
optional: true
'@types/react-dom@19.2.3(@types/react@19.2.10)':
dependencies:
'@types/react': 19.2.10
'@types/react@19.2.10':
dependencies:
csstype: 3.2.3
@ -8842,8 +8318,6 @@ snapshots:
'@types/unist@3.0.3': {}
'@types/use-sync-external-store@0.0.6': {}
'@types/verror@1.10.11':
optional: true
@ -10720,8 +10194,6 @@ snapshots:
fast-diff@1.3.0: {}
fast-equals@5.4.0: {}
fast-glob@3.3.3:
dependencies:
'@nodelib/fs.stat': 2.0.5
@ -11467,12 +10939,6 @@ snapshots:
lines-and-columns@1.2.4: {}
linkify-it@5.0.0:
dependencies:
uc.micro: 2.1.0
linkifyjs@4.3.2: {}
load-json-file@2.0.0:
dependencies:
graceful-fs: 4.2.11
@ -11572,19 +11038,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
markdown-it@14.1.1:
dependencies:
argparse: 2.0.1
entities: 4.5.0
linkify-it: 5.0.0
mdurl: 2.0.0
punycode.js: 2.3.1
uc.micro: 2.1.0
markdown-table@3.0.4: {}
marked@17.0.4: {}
matcher@3.0.0:
dependencies:
escape-string-regexp: 4.0.0
@ -11755,8 +11210,6 @@ snapshots:
mdn-data@2.12.2: {}
mdurl@2.0.0: {}
media-typer@1.1.0: {}
merge-descriptors@2.0.0: {}
@ -12258,8 +11711,6 @@ snapshots:
strip-ansi: 6.0.1
wcwidth: 1.0.1
orderedmap@2.1.1: {}
own-keys@1.0.1:
dependencies:
get-intrinsic: 1.3.0
@ -12487,109 +11938,6 @@ snapshots:
property-information@7.1.0: {}
prosemirror-changeset@2.4.0:
dependencies:
prosemirror-transform: 1.11.0
prosemirror-collab@1.3.1:
dependencies:
prosemirror-state: 1.4.4
prosemirror-commands@1.7.1:
dependencies:
prosemirror-model: 1.25.4
prosemirror-state: 1.4.4
prosemirror-transform: 1.11.0
prosemirror-dropcursor@1.8.2:
dependencies:
prosemirror-state: 1.4.4
prosemirror-transform: 1.11.0
prosemirror-view: 1.41.6
prosemirror-gapcursor@1.4.0:
dependencies:
prosemirror-keymap: 1.2.3
prosemirror-model: 1.25.4
prosemirror-state: 1.4.4
prosemirror-view: 1.41.6
prosemirror-history@1.5.0:
dependencies:
prosemirror-state: 1.4.4
prosemirror-transform: 1.11.0
prosemirror-view: 1.41.6
rope-sequence: 1.3.4
prosemirror-inputrules@1.5.1:
dependencies:
prosemirror-state: 1.4.4
prosemirror-transform: 1.11.0
prosemirror-keymap@1.2.3:
dependencies:
prosemirror-state: 1.4.4
w3c-keyname: 2.2.8
prosemirror-markdown@1.13.4:
dependencies:
'@types/markdown-it': 14.1.2
markdown-it: 14.1.1
prosemirror-model: 1.25.4
prosemirror-menu@1.3.0:
dependencies:
crelt: 1.0.6
prosemirror-commands: 1.7.1
prosemirror-history: 1.5.0
prosemirror-state: 1.4.4
prosemirror-model@1.25.4:
dependencies:
orderedmap: 2.1.1
prosemirror-schema-basic@1.2.4:
dependencies:
prosemirror-model: 1.25.4
prosemirror-schema-list@1.5.1:
dependencies:
prosemirror-model: 1.25.4
prosemirror-state: 1.4.4
prosemirror-transform: 1.11.0
prosemirror-state@1.4.4:
dependencies:
prosemirror-model: 1.25.4
prosemirror-transform: 1.11.0
prosemirror-view: 1.41.6
prosemirror-tables@1.8.5:
dependencies:
prosemirror-keymap: 1.2.3
prosemirror-model: 1.25.4
prosemirror-state: 1.4.4
prosemirror-transform: 1.11.0
prosemirror-view: 1.41.6
prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6):
dependencies:
'@remirror/core-constants': 3.0.0
escape-string-regexp: 4.0.0
prosemirror-model: 1.25.4
prosemirror-state: 1.4.4
prosemirror-view: 1.41.6
prosemirror-transform@1.11.0:
dependencies:
prosemirror-model: 1.25.4
prosemirror-view@1.41.6:
dependencies:
prosemirror-model: 1.25.4
prosemirror-state: 1.4.4
prosemirror-transform: 1.11.0
proxy-addr@2.0.7:
dependencies:
forwarded: 0.2.0
@ -12602,8 +11950,6 @@ snapshots:
end-of-stream: 1.4.5
once: 1.4.0
punycode.js@2.3.1: {}
punycode@2.3.1: {}
qs@6.14.1:
@ -13221,8 +12567,6 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.57.1
fsevents: 2.3.3
rope-sequence@1.3.4: {}
router@2.2.0:
dependencies:
debug: 4.4.3
@ -13963,8 +13307,6 @@ snapshots:
typescript@5.9.3: {}
uc.micro@2.1.0: {}
ufo@1.6.3: {}
unbox-primitive@1.1.0:
@ -14056,10 +13398,6 @@ snapshots:
dependencies:
react: 19.2.4
use-sync-external-store@1.6.0(react@19.2.4):
dependencies:
react: 19.2.4
utf8-byte-length@1.0.5: {}
util-deprecate@1.0.2: {}

View File

@ -17,7 +17,6 @@ import {
ElectronSpotlightContentPage
} from './components/Dashboard/context/SpotlightContext.jsx'
import { ActionsModalProvider } from './components/Dashboard/context/ActionsModalContext.jsx'
import MissingPlaceholder from './components/Dashboard/common/MissingPlaceholder.jsx'
import {
ThemeProvider,
@ -26,10 +25,9 @@ import {
import AppError from './components/App/AppError'
import { ApiServerProvider } from './components/Dashboard/context/ApiServerContext.jsx'
import { NotificationProvider } from './components/Dashboard/context/NotificationContext.jsx'
import { ElectronProvider } from './components/Dashboard/context/ElectronContext.jsx'
import { ElectrobunProvider } from './components/Dashboard/context/ElectrobunContext.jsx'
import { MessageProvider } from './components/Dashboard/context/MessageContext.jsx'
import AuthCallback from './components/App/AuthCallback.jsx'
import EmailNotificationTemplate from './components/Email/EmailNotificationTemplate.jsx'
import {
ProductionRoutes,
@ -55,17 +53,10 @@ const AppContent = () => {
const Router = getRouter()
return (
<ConfigProvider
theme={themeConfig}
renderEmpty={() => (
<div style={{ margin: '32px' }}>
<MissingPlaceholder message='No data.' hasBackground={false} />
</div>
)}
>
<ConfigProvider theme={themeConfig}>
<App>
<Router>
<ElectronProvider>
<ElectrobunProvider>
<AuthProvider>
<PrintServerProvider>
<ApiServerProvider>
@ -73,60 +64,56 @@ const AppContent = () => {
<NotificationProvider>
<SpotlightProvider>
<ActionsModalProvider>
<Routes>
<Route
path='/dashboard/electron/spotlightcontent'
element={
<PrivateRoute
component={() => (
<ElectronSpotlightContentPage />
)}
/>
}
/>
<Route
path='/'
element={
<PrivateRoute
component={() => (
<Navigate
to='/dashboard/production/overview'
replace
/>
)}
/>
}
/>
<Route
path='/auth/callback'
element={<AuthCallback />}
/>
<Route
path='/email/notification'
element={<EmailNotificationTemplate />}
/>
<Route
path='/dashboard'
element={
<PrivateRoute component={() => <Dashboard />} />
}
>
{ProductionRoutes}
{InventoryRoutes}
{FinanceRoutes}
{SalesRoutes}
{ManagementRoutes}
{DeveloperRoutes}
</Route>
<Route
path='*'
element={
<AppError
message='Error 404. Page not found.'
showRefresh={false}
/>
}
/>
<Routes>
<Route
path='/dashboard/electron/spotlightcontent'
element={
<PrivateRoute
component={() => (
<ElectronSpotlightContentPage />
)}
/>
}
/>
<Route
path='/'
element={
<PrivateRoute
component={() => (
<Navigate
to='/dashboard/production/overview'
replace
/>
)}
/>
}
/>
<Route
path='/auth/callback'
element={<AuthCallback />}
/>
<Route
path='/dashboard'
element={
<PrivateRoute component={() => <Dashboard />} />
}
>
{ProductionRoutes}
{InventoryRoutes}
{FinanceRoutes}
{SalesRoutes}
{ManagementRoutes}
{DeveloperRoutes}
</Route>
<Route
path='*'
element={
<AppError
message='Error 404. Page not found.'
showRefresh={false}
/>
}
/>
</Routes>
</ActionsModalProvider>
</SpotlightProvider>
@ -135,7 +122,7 @@ const AppContent = () => {
</ApiServerProvider>
</PrintServerProvider>
</AuthProvider>
</ElectronProvider>
</ElectrobunProvider>
</Router>
</App>
</ConfigProvider>

157
src/bun/index.ts Normal file
View File

@ -0,0 +1,157 @@
import Electrobun, {
BrowserWindow,
BrowserView,
Utils,
GlobalShortcut,
ApplicationMenu
} from 'electrobun/bun'
import { join } from 'path'
import { type FarmControlRPCType } from '../shared/rpcTypes'
import { createMainWindow, getMainWindow } from './mainWindow'
import {
openSpotlightContentWindow,
registerGlobalShortcuts,
setupSpotlightRPC
} from './spotlightWindow'
// Auth session storage (file-based, keytar alternative for Bun)
const AUTH_FILE = join(Utils.paths.userData, 'auth-session.json')
async function readAuthSession(): Promise<object | null> {
try {
const f = Bun.file(AUTH_FILE)
if (!(await f.exists())) return null
const raw = await f.text()
return JSON.parse(raw) as object
} catch {
return null
}
}
async function writeAuthSession(session: object): Promise<boolean> {
try {
await Bun.write(AUTH_FILE, JSON.stringify(session))
return true
} catch (e) {
console.warn('[auth] Failed to write session:', e)
return false
}
}
async function clearAuthSession(): Promise<boolean> {
try {
const f = Bun.file(AUTH_FILE)
if (await f.exists()) {
await Bun.write(AUTH_FILE, '{}')
}
return true
} catch (e) {
console.warn('[auth] Failed to clear session:', e)
return false
}
}
const farmControlRPC = BrowserView.defineRPC<FarmControlRPCType>({
maxRequestTime: 5000,
handlers: {
requests: {
osInfo: () => ({ platform: process.platform }),
windowState: () => {
const win = getMainWindow()
if (!win || win.isDestroyed?.()) {
return { isFullScreen: false, isMaximized: false }
}
return {
isFullScreen: win.isFullScreen?.(),
isMaximized: win.isMaximized?.()
}
},
openExternalUrl: ({ url }) => {
Utils.openExternal(url)
},
openInternalUrl: ({ url }) => {
const win = getMainWindow()
if (!win || win.isDestroyed?.()) {
createMainWindow(farmControlRPC)
const newWin = getMainWindow()
if (newWin?.webview) {
newWin.webview.on?.('dom-ready', () => {
;(newWin.webview as any).rpc?.navigate?.({ url })
newWin.show?.()
newWin.focus?.()
})
}
} else if (win.webview) {
;(win.webview as any).rpc?.navigate?.({ url })
win.show?.()
win.focus?.()
}
return true
},
windowControl: ({ action }) => {
const win = getMainWindow()
if (!win) return
switch (action) {
case 'minimize':
win.minimize?.()
break
case 'maximize':
if (win.isMaximized?.()) {
win.unmaximize?.()
} else {
win.maximize?.()
}
break
case 'close':
win.close?.()
break
}
},
authSessionGet: () => readAuthSession(),
authSessionSet: ({ session }) => writeAuthSession(session),
authSessionClear: () => clearAuthSession(),
spotlightWindowResize: ({ height }) => setupSpotlightRPC(height)
},
messages: {}
}
})
createMainWindow(farmControlRPC)
registerGlobalShortcuts()
// Application menu
const env = (process.env.NODE_ENV || 'development').trim()
if (env === 'development') {
ApplicationMenu.setApplicationMenu([
{
label: 'Developer',
submenu: [
{
label: 'Toggle Developer Tools',
accelerator:
process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
action: 'toggle-devtools'
}
]
}
])
} else {
ApplicationMenu.setApplicationMenu([])
}
// open-url for farmcontrol:// scheme
Electrobun.events.on('open-url', (e: { data: { url: string } }) => {
const url = e.data.url
if (url.startsWith('farmcontrol://app')) {
const redirectPath = url.replace('farmcontrol://app', '') || '/'
const win = getMainWindow()
if (win?.webview) {
;(win.webview as any).rpc?.navigate?.({ url: redirectPath })
}
}
})
// Unregister shortcuts on quit
Electrobun.events.on('before-quit', () => {
GlobalShortcut.unregisterAll()
})

90
src/bun/mainWindow.ts Normal file
View File

@ -0,0 +1,90 @@
import Electrobun, { BrowserWindow, Utils } from 'electrobun/bun'
import { join } from 'path'
import type { FarmControlRPCType } from '../shared/rpcTypes'
let win: InstanceType<typeof BrowserWindow> | null = null
function getAppUrl(): string {
if (process.env.ELECTROBUN_START_URL || process.env.ELECTRON_START_URL) {
return (
process.env.ELECTROBUN_START_URL ||
process.env.ELECTRON_START_URL ||
'http://localhost:5173'
)
}
return 'views://main/index.html'
}
function setupWindowEvents(
browserWindow: InstanceType<typeof BrowserWindow>,
rpc: any
): void {
if (!browserWindow) return
browserWindow.on?.('resize', () => {
const webview = browserWindow.webview
if (webview?.rpc) {
try {
;(webview.rpc as any).windowState?.({
isMaximized: browserWindow.isMaximized?.(),
isFullScreen: browserWindow.isFullScreen?.()
})
} catch (_) {}
}
})
// Electrobun may use different event names - check docs
const sendWindowState = (state: { isMaximized?: boolean; isFullScreen?: boolean }) => {
const webview = browserWindow.webview
if (webview?.rpc) {
try {
;(webview.rpc as any).windowState?.(state)
} catch (_) {}
}
}
// Listen for maximize/unmaximize/fullscreen - Electrobun BrowserWindow events
browserWindow.on?.('maximize', () => sendWindowState({ isMaximized: true }))
browserWindow.on?.('unmaximize', () => sendWindowState({ isMaximized: false }))
browserWindow.on?.('enter-full-screen', () => sendWindowState({ isFullScreen: true }))
browserWindow.on?.('leave-full-screen', () => sendWindowState({ isFullScreen: false }))
}
export function createMainWindow(rpc: any): void {
const url = getAppUrl()
win = new BrowserWindow({
title: 'Farm Control',
url,
frame: {
width: 1200,
height: 800
},
titleBarStyle: 'hiddenInset',
preload: 'views://preload/index.js',
rpc,
styleMask: {
Titled: true,
Closable: true,
Miniaturizable: true,
Resizable: true,
FullSizeContentView: true
}
})
setupWindowEvents(win, rpc)
// Handle window-all-closed
Electrobun.events.on('close', () => {
const wins = (BrowserWindow as any).getAll?.() ?? []
if (wins.length <= 1) {
if (process.platform !== 'darwin') {
Utils.quit?.()
}
}
})
}
export function getMainWindow(): InstanceType<typeof BrowserWindow> | null {
return win
}

View File

@ -0,0 +1,80 @@
import { BrowserWindow, GlobalShortcut } from 'electrobun/bun'
import { join } from 'path'
let spotlightWin: InstanceType<typeof BrowserWindow> | null = null
function getSpotlightRouteUrl(): string {
const routePath = '/dashboard/electron/spotlightcontent'
if (process.env.ELECTROBUN_START_URL || process.env.ELECTRON_START_URL) {
const base = String(
process.env.ELECTROBUN_START_URL || process.env.ELECTRON_START_URL
).replace(/\/$/, '')
return `${base}${routePath}`
}
return `views://main/index.html#${routePath}`
}
export function openSpotlightContentWindow(): void {
if (spotlightWin && !spotlightWin.isDestroyed?.()) {
spotlightWin.show?.()
spotlightWin.focus?.()
return
}
const target = getSpotlightRouteUrl()
spotlightWin = new BrowserWindow({
title: 'Spotlight',
url: target,
frame: {
width: 700,
height: 40
},
titleBarStyle: 'hidden',
transparent: true,
resizable: false
})
spotlightWin.on?.('close', (e: any) => {
if (e?.preventDefault) e.preventDefault()
if (spotlightWin && !spotlightWin.isDestroyed?.()) {
spotlightWin.hide?.()
}
})
spotlightWin.on?.('blur', () => {
if (spotlightWin && !spotlightWin.isDestroyed?.()) {
spotlightWin.hide?.()
}
})
}
export function registerGlobalShortcuts(): void {
try {
const success = GlobalShortcut.register('Alt+Shift+Q', () => {
openSpotlightContentWindow()
})
if (!success) {
console.warn('[GlobalShortcut] Failed to register Alt+Shift+Q')
}
} catch (e) {
console.warn('[GlobalShortcut] Error:', (e as Error)?.message)
}
}
export function setupSpotlightRPC(height: number): boolean {
if (!spotlightWin || spotlightWin.isDestroyed?.()) return false
try {
const frame = spotlightWin.getFrame?.()
if (frame) {
spotlightWin.setSize?.(frame.width, height)
spotlightWin.center?.()
}
return true
} catch (e) {
console.warn('[spotlight] Failed to resize:', (e as Error)?.message)
return false
}
}

View File

@ -1,10 +1,15 @@
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 FinanceOverview = () => {
const { connected } = useContext(ApiServerContext)
const [collapseState, updateCollapseState] = useCollapseState(
'FinanceOverview',
{
@ -13,6 +18,10 @@ const FinanceOverview = () => {
}
)
if (!connected) {
return null
}
return (
<Flex
gap='large'

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const Invoices = () => {
const [newInvoiceOpen, setNewInvoiceOpen] = useState(false)
@ -21,9 +19,6 @@ const Invoices = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('invoices')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Invoices')
const actionItems = {
items: [
{
@ -49,7 +44,7 @@ const Invoices = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -61,16 +56,13 @@ const Invoices = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='invoice' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -79,7 +71,6 @@ const Invoices = () => {
visibleColumns={columnVisibility}
type='invoice'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const Payments = () => {
const [newPaymentOpen, setNewPaymentOpen] = useState(false)
@ -21,9 +19,6 @@ const Payments = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('payments')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Payments')
const actionItems = {
items: [
{
@ -49,7 +44,7 @@ const Payments = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -61,16 +56,13 @@ const Payments = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='payment' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -79,7 +71,6 @@ const Payments = () => {
visibleColumns={columnVisibility}
type='payment'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -9,12 +9,10 @@ import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTable from '../common/ObjectTable'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import ListIcon from '../../Icons/ListIcon'
import GridIcon from '../../Icons/GridIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const FilamentStocks = () => {
const tableRef = useRef()
@ -26,9 +24,6 @@ const FilamentStocks = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('filamentStocks')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('FilamentStocks')
const actionItems = {
items: [
{
@ -54,7 +49,7 @@ const FilamentStocks = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -66,16 +61,13 @@ const FilamentStocks = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='filamentStock' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -85,7 +77,6 @@ const FilamentStocks = () => {
visibleColumns={columnVisibility}
type='filamentStock'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -1,10 +1,15 @@
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 InventoryOverview = () => {
const { connected } = useContext(ApiServerContext)
const [collapseState, updateCollapseState] = useCollapseState(
'InventoryOverview',
{
@ -13,6 +18,10 @@ const InventoryOverview = () => {
}
)
if (!connected) {
return null
}
return (
<Flex
gap='large'

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const OrderItems = () => {
const [newOrderItemOpen, setNewOrderItemOpen] = useState(false)
@ -21,9 +19,6 @@ const OrderItems = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('orderItems')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('OrderItems')
const actionItems = {
items: [
{
@ -49,7 +44,7 @@ const OrderItems = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -61,16 +56,13 @@ const OrderItems = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='orderItem' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -79,7 +71,6 @@ const OrderItems = () => {
visibleColumns={columnVisibility}
type='orderItem'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -46,34 +46,6 @@ const NewOrderItem = ({ onOk, reset, defaultValues }) => {
/>
)
},
{
title: 'Optional',
key: 'optional',
content: (
<ObjectInfo
type='orderItem'
column={1}
bordered={false}
isEditing={true}
required={false}
objectData={objectData}
visibleProperties={{
sku: true,
shipment: true,
invoicedAmount: false,
invoicedAmountWithTax: false,
invoicedQuantity: false,
invoicedAmountRemaining: false,
invoicedAmountWithTaxRemaining: false,
invoicedQuantityRemaining: false,
orderedAt: false,
receivedAt: false,
syncAmount: false
}}
/>
)
},
{
title: 'Pricing',
key: 'pricing',
@ -95,6 +67,32 @@ const NewOrderItem = ({ onOk, reset, defaultValues }) => {
/>
)
},
{
title: 'Optional',
key: 'optional',
content: (
<ObjectInfo
type='orderItem'
column={1}
bordered={false}
isEditing={true}
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
}}
/>
)
},
{
title: 'Summary',
key: 'summary',

View File

@ -9,12 +9,10 @@ import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTable from '../common/ObjectTable'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import ListIcon from '../../Icons/ListIcon'
import GridIcon from '../../Icons/GridIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const PartStocks = () => {
const tableRef = useRef()
@ -26,9 +24,6 @@ const PartStocks = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('partStocks')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('PartStocks')
const actionItems = {
items: [
{
@ -54,7 +49,7 @@ const PartStocks = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -66,16 +61,13 @@ const PartStocks = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='partStock' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -85,7 +77,6 @@ const PartStocks = () => {
visibleColumns={columnVisibility}
type='partStock'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -1,114 +0,0 @@
// src/components/Dashboard/Inventory/ProductStocks.jsx
// ProductStocks - tracks assembled products consisting of part stocks
import { useState, useRef } from 'react'
import { Button, Flex, Space, Modal, Dropdown } from 'antd'
import NewProductStock from './ProductStocks/NewProductStock'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTable from '../common/ObjectTable'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const ProductStocks = () => {
const tableRef = useRef()
const [newProductStockOpen, setNewProductStockOpen] = useState(false)
const [viewMode, setViewMode] = useViewMode('productStocks')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('productStock')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('ProductStocks')
const actionItems = {
items: [
{
label: 'New Product Stock',
key: 'newProductStock',
icon: <PlusIcon />
},
{ type: 'divider' },
{
label: 'Reload List',
key: 'reloadList',
icon: <ReloadIcon />
}
],
onClick: ({ key }) => {
if (key === 'reloadList') {
tableRef.current?.reload()
} else if (key === 'newProductStock') {
setNewProductStockOpen(true)
}
}
}
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
<Button>Actions</Button>
</Dropdown>
<ColumnViewButton
type='productStock'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='productStock' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
/>
</Space>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='productStock'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal
open={newProductStockOpen}
styles={{ content: { paddingBottom: '24px' } }}
footer={null}
width={800}
onCancel={() => {
setNewProductStockOpen(false)
}}
destroyOnHidden={true}
>
<NewProductStock
onOk={() => {
setNewProductStockOpen(false)
tableRef.current?.reload()
}}
reset={newProductStockOpen}
/>
</Modal>
</>
)
}
export default ProductStocks

View File

@ -1,77 +0,0 @@
import PropTypes from 'prop-types'
import ObjectInfo from '../../common/ObjectInfo'
import NewObjectForm from '../../common/NewObjectForm'
import WizardView from '../../common/WizardView'
const NewProductStock = ({ onOk, reset, defaultValues }) => {
return (
<NewObjectForm
type={'productStock'}
reset={reset}
defaultValues={{ state: { type: 'draft' }, ...defaultValues }}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [
{
title: 'Required',
key: 'required',
content: (
<ObjectInfo
type='productStock'
column={1}
bordered={false}
isEditing={true}
required={true}
objectData={objectData}
visibleProperties={{
partStocks: false
}}
/>
)
},
{
title: 'Summary',
key: 'summary',
content: (
<ObjectInfo
type='productStock'
column={1}
bordered={false}
visibleProperties={{
_id: false,
createdAt: false,
updatedAt: false,
partStocks: false
}}
isEditing={false}
objectData={objectData}
/>
)
}
]
return (
<WizardView
steps={steps}
loading={submitLoading}
formValid={formValid}
title='New Product Stock'
onSubmit={async () => {
const result = await handleSubmit()
if (result) {
onOk()
}
}}
/>
)
}}
</NewObjectForm>
)
}
NewProductStock.propTypes = {
onOk: PropTypes.func.isRequired,
reset: PropTypes.bool,
defaultValues: PropTypes.object
}
export default NewProductStock

View File

@ -1,46 +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 PostProductStock = ({ onOk, objectData }) => {
const [postLoading, setPostLoading] = useState(false)
const { sendObjectFunction } = useContext(ApiServerContext)
const handlePost = async () => {
setPostLoading(true)
try {
const result = await sendObjectFunction(
objectData._id,
'ProductStock',
'post'
)
if (result) {
message.success('Product stock posted successfully')
onOk(result)
}
} catch (error) {
console.error('Error posting product stock:', error)
} finally {
setPostLoading(false)
}
}
return (
<MessageDialogView
title={'Are you sure you want to post this product stock?'}
description={`Posting product stock ${objectData?.name || objectData?._reference || objectData?._id} will finalize it and make it read-only.`}
onOk={handlePost}
okText='Post'
okLoading={postLoading}
/>
)
}
PostProductStock.propTypes = {
onOk: PropTypes.func.isRequired,
objectData: PropTypes.object
}
export default PostProductStock

View File

@ -1,274 +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.jsx'
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 { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
import PostProductStock from './PostProductStock.jsx'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
import NoteIcon from '../../../Icons/NoteIcon.jsx'
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
import PartStockIcon from '../../../Icons/PartStockIcon.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
const log = loglevel.getLogger('ProductStockInfo')
log.setLevel(config.logLevel)
const ProductStockInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const productStockId = new URLSearchParams(location.search).get(
'productStockId'
)
const [postProductStockOpen, setPostProductStockOpen] = useState(false)
const [collapseState, updateCollapseState] = useCollapseState(
'ProductStockInfo',
{
info: true,
partStocks: true,
notes: true,
auditLogs: true
}
)
const [objectFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
locked: false,
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
},
post: () => {
setPostProductStockOpen(true)
return true
}
}
const editDisabled =
getModelByName('productStock')
?.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='productStock'
id={productStockId}
disabled={objectFormState.loading}
objectData={objectFormState.objectData}
/>
<ViewButton
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Product Stock Information' },
{ key: 'partStocks', label: 'Part Stocks' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
<UserNotifierToggle
type='productStock'
objectData={objectFormState.objectData}
disabled={objectFormState.loading}
/>
<DocumentPrintButton
type='productStock'
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={productStockId}
type='productStock'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<Flex vertical gap={'large'}>
<InfoCollapse
title='Product Stock Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='productStock'
objectData={objectData}
labelWidth='175px'
visibleProperties={{ partStocks: false }}
/>
</InfoCollapse>
<InfoCollapse
title='Part Stocks'
icon={<PartStockIcon />}
active={collapseState.partStocks}
onToggle={(expanded) =>
updateCollapseState('partStocks', expanded)
}
collapseKey='partStocks'
>
<ObjectProperty
{...getModelProperty('productStock', 'partStocks')}
isEditing={isEditing}
objectData={objectData}
loading={loading}
size='medium'
/>
</InfoCollapse>
</Flex>
)}
</ObjectForm>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={productStockId} type='productStock' />
</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': productStockId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</ScrollBox>
</Flex>
<Modal
open={postProductStockOpen}
onCancel={() => {
setPostProductStockOpen(false)
}}
width={500}
footer={null}
destroyOnHidden={true}
centered={true}
>
<PostProductStock
onOk={() => {
setPostProductStockOpen(false)
actions.reload()
}}
objectData={objectFormState.objectData}
/>
</Modal>
</>
)
}
export default ProductStockInfo

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const PurchaseOrders = () => {
const [newPurchaseOrderOpen, setNewPurchaseOrderOpen] = useState(false)
@ -21,9 +19,6 @@ const PurchaseOrders = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('purchaseOrders')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('PurchaseOrders')
const actionItems = {
items: [
{
@ -49,7 +44,7 @@ const PurchaseOrders = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -61,16 +56,13 @@ const PurchaseOrders = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='purchaseOrder' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -79,7 +71,6 @@ const PurchaseOrders = () => {
visibleColumns={columnVisibility}
type='purchaseOrder'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const Shipments = () => {
const [newShipmentOpen, setNewShipmentOpen] = useState(false)
@ -21,9 +19,6 @@ const Shipments = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('shipments')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Shipments')
const actionItems = {
items: [
{
@ -49,7 +44,7 @@ const Shipments = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -61,16 +56,13 @@ const Shipments = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='shipment' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -79,7 +71,6 @@ const Shipments = () => {
visibleColumns={columnVisibility}
type='shipment'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -9,12 +9,10 @@ import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTable from '../common/ObjectTable'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import ListIcon from '../../Icons/ListIcon'
import GridIcon from '../../Icons/GridIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const StockAudits = () => {
const tableRef = useRef()
@ -26,9 +24,6 @@ const StockAudits = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('stockAudits')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('StockAudits')
const actionItems = {
items: [
{
@ -54,7 +49,7 @@ const StockAudits = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -66,16 +61,13 @@ const StockAudits = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='stockAudit' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -85,7 +77,6 @@ const StockAudits = () => {
visibleColumns={columnVisibility}
type='stockAudit'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -2,12 +2,10 @@ import { useRef } from 'react'
import { Button, Flex, Space, Dropdown } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTable from '../common/ObjectTable'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import ReloadIcon from '../../Icons/ReloadIcon'
const StockEvents = () => {
@ -15,10 +13,7 @@ const StockEvents = () => {
const [viewMode, setViewMode] = useViewMode('stockEvents')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('stockEvent')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('StockEvents')
useColumnVisibility('stockEvents')
const actionItems = {
items: [
@ -37,7 +32,7 @@ const StockEvents = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -49,16 +44,13 @@ const StockEvents = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='stockEvent' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -68,7 +60,6 @@ const StockEvents = () => {
visibleColumns={columnVisibility}
type='stockEvent'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
</>

View File

@ -1,106 +0,0 @@
import { useRef, useState } from 'react'
import { Button, Flex, Space, Modal, Dropdown } from 'antd'
import NewAppPassword from './AppPasswords/NewAppPassword'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ExportListButton from '../common/ExportListButton'
const AppPasswords = () => {
const [newAppPasswordOpen, setNewAppPasswordOpen] = useState(false)
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('appPassword')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('appPassword')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('AppPasswords')
const actionItems = {
items: [
{
label: 'New App Password',
key: 'newAppPassword',
icon: <PlusIcon />
},
{ type: 'divider' },
{
label: 'Reload List',
key: 'reloadList',
icon: <ReloadIcon />
}
],
onClick: ({ key }) => {
if (key === 'reloadList') {
tableRef.current?.reload()
} else if (key === 'newAppPassword') {
setNewAppPasswordOpen(true)
}
}
}
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'}>
<Space>
<Dropdown menu={actionItems}>
<Button>Actions</Button>
</Dropdown>
<ColumnViewButton
type='appPassword'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='appPassword' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
/>
</Space>
</Flex>
<ObjectTable
ref={tableRef}
type='appPassword'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
/>
<Modal
open={newAppPasswordOpen}
footer={null}
width={700}
onCancel={() => {
setNewAppPasswordOpen(false)
}}
>
<NewAppPassword
onOk={() => {
setNewAppPasswordOpen(false)
tableRef.current?.reload()
}}
reset={newAppPasswordOpen}
/>
</Modal>
</Flex>
</>
)
}
export default AppPasswords

View File

@ -1,212 +0,0 @@
import { useRef, useState, useContext } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Modal } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import useCollapseState from '../../hooks/useCollapseState'
import InfoCollapse from '../../common/InfoCollapse'
import ObjectInfo from '../../common/ObjectInfo'
import ViewButton from '../../common/ViewButton'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
import RegenerateAppPasswordSecret from './RegenerateAppPasswordSecret.jsx'
import { getModelByName } from '../../../../database/ObjectModels.js'
import { AuthContext } from '../../context/AuthContext.jsx'
const AppPasswordInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const { userProfile } = useContext(AuthContext)
const appPasswordId = new URLSearchParams(location.search).get(
'appPasswordId'
)
const [regenerateSecretOpen, setRegenerateSecretOpen] = useState(false)
const [collapseState, updateCollapseState] = useCollapseState(
'AppPasswordInfo',
{
info: 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
},
regenerateSecret: () => {
setRegenerateSecretOpen(true)
return false
},
edit: () => {
objectFormRef?.current?.startEditing?.()
return false
},
cancelEdit: () => {
objectFormRef?.current?.cancelEditing?.()
return true
},
finishEdit: () => {
objectFormRef?.current?.handleUpdate?.()
return true
}
}
const editDisabled = getModelByName('appPassword')
.actions.find((action) => action.name === 'edit')
.disabled({ ...objectFormState.objectData, _user: userProfile })
return (
<>
<Flex
gap='large'
vertical='true'
style={{ maxHeight: '100%', minHeight: 0 }}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='appPassword'
id={appPasswordId}
disabled={objectFormState.loading}
objectData={objectFormState.objectData}
/>
<ViewButton
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'App Password Information' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
<UserNotifierToggle
type='appPassword'
objectData={objectFormState.objectData}
disabled={objectFormState.loading}
/>
<DocumentPrintButton
type='appPassword'
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}
>
<InfoCollapse
title='App Password Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<ObjectForm
id={appPasswordId}
type='appPassword'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='appPassword'
objectData={objectData}
/>
)}
</ObjectForm>
</InfoCollapse>
</ActionHandler>
<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': appPasswordId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</ScrollBox>
</Flex>
<Modal
open={regenerateSecretOpen}
destroyOnClose
width={650}
onCancel={() => {
actionHandlerRef.current?.clearAction?.()
setRegenerateSecretOpen(false)
}}
footer={null}
>
<RegenerateAppPasswordSecret id={appPasswordId} />
</Modal>
</>
)
}
export default AppPasswordInfo

View File

@ -1,91 +0,0 @@
import PropTypes from 'prop-types'
import ObjectInfo from '../../common/ObjectInfo'
import NewObjectForm from '../../common/NewObjectForm'
import WizardView from '../../common/WizardView'
const NewAppPassword = ({ onOk, reset, defaultValues = {} }) => {
return (
<NewObjectForm
type='appPassword'
reset={reset}
defaultValues={{
active: true,
...defaultValues
}}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [
{
title: 'Required',
key: 'required',
content: (
<ObjectInfo
type='appPassword'
column={1}
bordered={false}
isEditing={true}
required={true}
objectData={objectData}
/>
)
},
{
title: 'Optional',
key: 'optional',
content: (
<ObjectInfo
type='appPassword'
column={1}
bordered={false}
isEditing={true}
required={false}
objectData={objectData}
/>
)
},
{
title: 'Summary',
key: 'summary',
content: (
<ObjectInfo
type='appPassword'
column={1}
bordered={false}
visibleProperties={{
_id: false,
createdAt: false,
updatedAt: false,
secret: false
}}
isEditing={false}
objectData={objectData}
/>
)
}
]
return (
<WizardView
steps={steps}
loading={submitLoading}
formValid={formValid}
title='New App Password'
onSubmit={async () => {
const result = await handleSubmit()
if (result) {
onOk()
}
}}
/>
)
}}
</NewObjectForm>
)
}
NewAppPassword.propTypes = {
onOk: PropTypes.func.isRequired,
reset: PropTypes.bool,
defaultValues: PropTypes.object
}
export default NewAppPassword

View File

@ -1,77 +0,0 @@
import PropTypes from 'prop-types'
import { useContext, useState } from 'react'
import { Result, Typography, Flex, Button } from 'antd'
import { ApiServerContext } from '../../context/ApiServerContext'
import CopyButton from '../../common/CopyButton'
import AppPasswordIcon from '../../../Icons/AppPasswordIcon.jsx'
import ReloadIcon from '../../../Icons/ReloadIcon'
const { Text } = Typography
const RegenerateAppPasswordSecret = ({ id }) => {
const { sendObjectFunction } = useContext(ApiServerContext)
const [appPassword, setAppPassword] = useState(null)
const [loading, setLoading] = useState(false)
const [passwordGenerated, setPasswordGenerated] = useState(false)
const handleRegenerate = async () => {
setLoading(true)
setAppPassword(null)
try {
const result = await sendObjectFunction(
id,
'appPassword',
'regenerateSecret',
{}
)
if (result?.appPassword) {
setAppPassword(result.appPassword)
setPasswordGenerated(true)
}
} finally {
setLoading(false)
}
}
return (
<Flex vertical align='center'>
<Result
title={
passwordGenerated ? 'Secret Regenerated' : 'Regenerate Secret'
}
disabled={passwordGenerated}
subTitle={
appPassword ? (
<Text>Copy this secret now. It will not be shown again.</Text>
) : (
<Text>Generate a new secret for this app password.</Text>
)
}
icon={<AppPasswordIcon />}
>
<Flex justify='center' style={{ minWidth: '395px' }}>
<Flex justify='center'>
<Flex gap='small' align='center' justify='center'>
<CopyButton size='default' text={appPassword} />
<Text code style={{ fontSize: '18px' }}>
{appPassword || '••••••••••••••••••••••••••••••••'}
</Text>
<Button
type='text'
loading={loading}
onClick={handleRegenerate}
icon={<ReloadIcon />}
/>
</Flex>
</Flex>
</Flex>
</Result>
</Flex>
)
}
RegenerateAppPasswordSecret.propTypes = {
id: PropTypes.string.isRequired
}
export default RegenerateAppPasswordSecret

View File

@ -3,20 +3,14 @@ import { Button, Flex, Space, Dropdown } from 'antd'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ObjectTable from '../common/ObjectTable'
import FilterSidebarButton from '../common/FilterSidebarButton'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const AuditLogs = () => {
const tableRef = useRef()
const [columnVisibility, updateColumnVisibility] =
useColumnVisibility('auditLog')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('AuditLogs')
useColumnVisibility('auditLogs')
const actionItems = {
items: [
@ -35,7 +29,7 @@ const AuditLogs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -47,13 +41,6 @@ const AuditLogs = () => {
visibleState={columnVisibility}
updateVisibleState={updateColumnVisibility}
/>
<ExportListButton objectType='auditLog' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
</Space>
</Flex>
@ -61,7 +48,6 @@ const AuditLogs = () => {
ref={tableRef}
visibleColumns={columnVisibility}
type='auditLog'
showFilterSidebar={showFilterSidebar}
/>
</Flex>
</>

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const CourierServices = () => {
const [newCourierServiceOpen, setNewCourierServiceOpen] = useState(false)
@ -21,9 +19,6 @@ const CourierServices = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('courierService')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('CourierServices')
const actionItems = {
items: [
{
@ -49,7 +44,7 @@ const CourierServices = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -58,19 +53,16 @@ const CourierServices = () => {
<ColumnViewButton
type='courierService'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='courierService' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -79,7 +71,6 @@ const CourierServices = () => {
visibleColumns={columnVisibility}
type='courierService'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const Couriers = () => {
const [newCourierOpen, setNewCourierOpen] = useState(false)
@ -20,9 +18,6 @@ const Couriers = () => {
const [columnVisibility, setColumnVisibility] = useColumnVisibility('courier')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Couriers')
const actionItems = {
items: [
{
@ -48,7 +43,7 @@ const Couriers = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -57,19 +52,16 @@ const Couriers = () => {
<ColumnViewButton
type='courier'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='courier' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -78,7 +70,6 @@ const Couriers = () => {
visibleColumns={columnVisibility}
type='courier'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const DocumentJobs = () => {
const [newDocumentJobOpen, setNewDocumentJobOpen] = useState(false)
@ -21,9 +19,6 @@ const DocumentJobs = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('documentJob')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('DocumentJobs')
const actionItems = {
items: [
{
@ -49,7 +44,7 @@ const DocumentJobs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -58,19 +53,16 @@ const DocumentJobs = () => {
<ColumnViewButton
type='documentJob'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='documentJob' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -79,7 +71,6 @@ const DocumentJobs = () => {
visibleColumns={columnVisibility}
type='documentJob'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -4,12 +4,10 @@ import PlusIcon from '../../Icons/PlusIcon'
import ObjectTable from '../common/ObjectTable'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import NewDocumentPrinter from './DocumentPrinters/NewDocumentPrinter'
const DocumentPrinters = () => {
@ -20,9 +18,6 @@ const DocumentPrinters = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('documentPrinter')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('DocumentPrinters')
const actionItems = {
items: [
{
@ -48,7 +43,7 @@ const DocumentPrinters = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -57,19 +52,16 @@ const DocumentPrinters = () => {
<ColumnViewButton
type='documentPrinter'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='documentPrinter' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -78,7 +70,6 @@ const DocumentPrinters = () => {
visibleColumns={columnVisibility}
type='documentPrinter'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const DocumentSizes = () => {
const [newDocumentSizeOpen, setNewDocumentSizeOpen] = useState(false)
@ -19,9 +17,6 @@ const DocumentSizes = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('documentSize')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('DocumentSizes')
const actionItems = {
items: [
{
@ -47,7 +42,7 @@ const DocumentSizes = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -59,16 +54,13 @@ const DocumentSizes = () => {
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='documentSize' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -77,7 +69,6 @@ const DocumentSizes = () => {
visibleColumns={columnVisibility}
type='documentSize'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const DocumentTemplates = () => {
const [newDocumentTemplateOpen, setNewDocumentTemplateOpen] = useState(false)
@ -21,9 +19,6 @@ const DocumentTemplates = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('documentTemplate')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('DocumentTemplates')
const actionItems = {
items: [
{
@ -49,7 +44,7 @@ const DocumentTemplates = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -58,19 +53,16 @@ const DocumentTemplates = () => {
<ColumnViewButton
type='documentTemplate'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='documentTemplate' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -79,7 +71,6 @@ const DocumentTemplates = () => {
visibleColumns={columnVisibility}
type='documentTemplate'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -1,111 +0,0 @@
import { useState, useRef } from 'react'
import { Button, Flex, Space, Modal, Dropdown } from 'antd'
import NewFilamentSku from './FilamentSkus/NewFilamentSku'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTable from '../common/ObjectTable'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const FilamentSkus = () => {
const tableRef = useRef()
const [newFilamentSkuOpen, setNewFilamentSkuOpen] = useState(false)
const [viewMode, setViewMode] = useViewMode('filamentSkus')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('filamentSku')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('FilamentSkus')
const actionItems = {
items: [
{
label: 'New Filament SKU',
key: 'newFilamentSku',
icon: <PlusIcon />
},
{ type: 'divider' },
{
label: 'Reload List',
key: 'reloadList',
icon: <ReloadIcon />
}
],
onClick: ({ key }) => {
if (key === 'reloadList') {
tableRef.current?.reload()
} else if (key === 'newFilamentSku') {
setNewFilamentSkuOpen(true)
}
}
}
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
<Button>Actions</Button>
</Dropdown>
<ColumnViewButton
type='filamentSku'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='filamentSku' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
/>
</Space>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='filamentSku'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal
open={newFilamentSkuOpen}
styles={{ content: { paddingBottom: '24px' } }}
footer={null}
width={700}
onCancel={() => {
setNewFilamentSkuOpen(false)
}}
destroyOnHidden={true}
>
<NewFilamentSku
onOk={() => {
setNewFilamentSkuOpen(false)
tableRef.current?.reload()
}}
reset={newFilamentSkuOpen}
/>
</Modal>
</>
)
}
export default FilamentSkus

View File

@ -1,197 +0,0 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
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 ObjectForm from '../../common/ObjectForm'
import EditButtons from '../../common/EditButtons'
import LockIndicator from '../../common/LockIndicator.jsx'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
import NoteIcon from '../../../Icons/NoteIcon.jsx'
import AuditLogIcon from '../../../Icons/AuditLogIcon.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
const FilamentSkuInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const filamentSkuId = new URLSearchParams(location.search).get('filamentSkuId')
const [collapseState, updateCollapseState] = useCollapseState(
'FilamentSkuInfo',
{
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?.fetchObject?.()
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='filamentSku'
id={filamentSkuId}
disabled={objectFormState.loading}
objectData={objectFormState.objectData}
/>
<ViewButton
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Filament SKU Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
<UserNotifierToggle
type='filamentSku'
objectData={objectFormState.objectData}
disabled={objectFormState.loading}
/>
<DocumentPrintButton
type='filamentSku'
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='Filament SKU Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<ObjectForm
id={filamentSkuId}
type='filamentSku'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='filamentSku'
objectData={objectData}
/>
)}
</ObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={filamentSkuId} type='filamentSku' />
</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': filamentSkuId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</ScrollBox>
</Flex>
</>
)
}
export default FilamentSkuInfo

View File

@ -1,123 +0,0 @@
import PropTypes from 'prop-types'
import ObjectInfo from '../../common/ObjectInfo'
import NewObjectForm from '../../common/NewObjectForm'
import WizardView from '../../common/WizardView'
const NewFilamentSku = ({ onOk, reset, defaultValues }) => {
return (
<NewObjectForm
type='filamentSku'
reset={reset}
defaultValues={defaultValues}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [
{
title: 'Required',
key: 'required',
content: (
<ObjectInfo
type='filamentSku'
column={1}
labelWidth={70}
bordered={false}
isEditing={true}
required={true}
objectData={objectData}
visibleProperties={{
description: false,
cost: false,
costWithTax: false,
costTaxRate: false,
vendor: false
}}
/>
)
},
{
title: 'Color & Cost',
key: 'colorCost',
content: (
<ObjectInfo
type='filamentSku'
column={1}
labelWidth={100}
visibleProperties={{
_id: false,
createdAt: false,
updatedAt: false,
barcode: false,
filament: false,
name: false,
description: false
}}
bordered={false}
isEditing={true}
objectData={objectData}
/>
)
},
{
title: 'Optional',
key: 'optional',
content: (
<ObjectInfo
type='filamentSku'
column={1}
labelWidth={100}
visibleProperties={{
barcode: true,
description: true
}}
bordered={false}
isEditing={true}
objectData={objectData}
/>
)
},
{
title: 'Summary',
key: 'summary',
content: (
<ObjectInfo
type='filamentSku'
column={1}
visibleProperties={{
createdAt: false,
updatedAt: false,
_id: false
}}
labelWidth={100}
bordered={false}
isEditing={false}
objectData={objectData}
/>
)
}
]
return (
<WizardView
steps={steps}
loading={submitLoading}
formValid={formValid}
title='New Filament SKU'
onSubmit={async () => {
const result = await handleSubmit()
if (result) {
onOk()
}
}}
/>
)
}}
</NewObjectForm>
)
}
NewFilamentSku.propTypes = {
onOk: PropTypes.func.isRequired,
reset: PropTypes.bool,
defaultValues: PropTypes.object
}
export default NewFilamentSku

View File

@ -10,11 +10,9 @@ import ColumnViewButton from '../common/ColumnViewButton'
import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import ListIcon from '../../Icons/ListIcon'
import GridIcon from '../../Icons/GridIcon'
import useViewMode from '../hooks/useViewMode'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import ExportListButton from '../common/ExportListButton'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
const Filaments = () => {
const [newFilamentOpen, setNewFilamentOpen] = useState(false)
@ -26,9 +24,6 @@ const Filaments = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('filament')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Filaments')
const actionItems = {
items: [
{
@ -54,7 +49,7 @@ const Filaments = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space>
<Dropdown menu={actionItems}>
@ -66,16 +61,13 @@ const Filaments = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='filament' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -85,7 +77,6 @@ const Filaments = () => {
type='filament'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
/>
<Modal

View File

@ -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'
@ -20,11 +20,9 @@ import ObjectActions from '../../common/ObjectActions.jsx'
import ObjectTable from '../../common/ObjectTable.jsx'
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
import FilamentIcon from '../../../Icons/FilamentIcon.jsx'
import FilamentSkuIcon from '../../../Icons/FilamentSkuIcon.jsx'
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import NewFilamentSku from '../FilamentSkus/NewFilamentSku'
const log = loglevel.getLogger('FilamentInfo')
log.setLevel(config.logLevel)
@ -34,13 +32,10 @@ const FilamentInfo = () => {
const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const filamentId = new URLSearchParams(location.search).get('filamentId')
const [newFilamentSkuOpen, setNewFilamentSkuOpen] = useState(false)
const filamentSkusTableRef = useRef()
const [collapseState, updateCollapseState] = useCollapseState(
'FilamentInfo',
{
info: true,
filamentSkus: true,
stocks: true,
notes: true,
auditLogs: true
@ -61,10 +56,6 @@ const FilamentInfo = () => {
objectFormRef?.current?.fetchObject?.()
return true
},
newFilamentSku: () => {
setNewFilamentSkuOpen(true)
return true
},
edit: () => {
objectFormRef?.current?.startEditing?.()
return false
@ -76,10 +67,6 @@ const FilamentInfo = () => {
finishEdit: () => {
objectFormRef?.current?.handleUpdate?.()
return true
},
delete: () => {
objectFormRef?.current?.handleDelete?.()
return true
}
}
@ -106,7 +93,6 @@ const FilamentInfo = () => {
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Filament Information' },
{ key: 'filamentSkus', label: 'Filament SKUs' },
{ key: 'stocks', label: 'Filament Stocks' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
@ -185,27 +171,6 @@ const FilamentInfo = () => {
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Filament SKUs'
icon={<FilamentSkuIcon />}
active={collapseState.filamentSkus}
onToggle={(expanded) =>
updateCollapseState('filamentSkus', expanded)
}
collapseKey='filamentSkus'
>
{objectFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
ref={filamentSkusTableRef}
type='filamentSku'
masterFilter={{ filament: filamentId }}
visibleColumns={{ filament: false }}
/>
)}
</InfoCollapse>
<InfoCollapse
title='Filament Stocks'
icon={<FilamentIcon />}
@ -218,36 +183,16 @@ const FilamentInfo = () => {
) : (
<ObjectTable
type='filamentStock'
masterFilter={{ 'filamentSku.filament._id': filamentId }}
masterFilter={{ 'filament._id': filamentId }}
visibleColumns={{
filamentSku: false,
'filamentSku.filament._id': false,
filament: false,
'filament._id': false,
startingWeight: false
}}
/>
)}
</InfoCollapse>
<Modal
open={newFilamentSkuOpen}
styles={{ content: { paddingBottom: '24px' } }}
footer={null}
width={700}
onCancel={() => setNewFilamentSkuOpen(false)}
destroyOnClose
>
<NewFilamentSku
onOk={() => {
setNewFilamentSkuOpen(false)
filamentSkusTableRef.current?.reload?.()
}}
reset={newFilamentSkuOpen}
defaultValues={{
filament: filamentId ? { _id: filamentId } : undefined
}}
/>
</Modal>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}

View File

@ -3,12 +3,10 @@ import { Button, Flex, Space, Dropdown } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const Files = () => {
const tableRef = useRef()
@ -17,9 +15,6 @@ const Files = () => {
const [columnVisibility, setColumnVisibility] = useColumnVisibility('file')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Files')
const actionItems = {
items: [
{
@ -37,7 +32,7 @@ const Files = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -46,19 +41,16 @@ const Files = () => {
<ColumnViewButton
type='file'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='file' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -67,7 +59,6 @@ const Files = () => {
visibleColumns={columnVisibility}
type='file'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
</>

View File

@ -7,14 +7,12 @@ import NewHost from './Hosts/NewHost'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import ListIcon from '../../Icons/ListIcon'
import GridIcon from '../../Icons/GridIcon'
import useViewMode from '../hooks/useViewMode'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
const Hosts = () => {
const [newHostOpen, setNewHostOpen] = useState(false)
@ -25,9 +23,6 @@ const Hosts = () => {
const [columnVisibility, setColumnVisibility] = useColumnVisibility('host')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Hosts')
const actionItems = {
items: [
{
@ -53,7 +48,7 @@ const Hosts = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space>
<Dropdown menu={actionItems}>
@ -65,16 +60,13 @@ const Hosts = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='host' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -84,14 +76,12 @@ const Hosts = () => {
type='host'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
/>
<Modal
open={newHostOpen}
footer={null}
width={700}
destroyOnHidden={true}
onCancel={() => {
setNewHostOpen(false)
}}

View File

@ -1,14 +1,21 @@
import PropTypes from 'prop-types'
import { useState } from 'react'
import { useMediaQuery } from 'react-responsive'
import { Typography, Flex, Steps, Divider } from 'antd'
import ObjectInfo from '../../common/ObjectInfo'
import NewObjectForm from '../../common/NewObjectForm'
import WizardView from '../../common/WizardView'
import NewObjectButtons from '../../common/NewObjectButtons'
const { Title } = Typography
const NewHost = ({ onOk }) => {
const [currentStep, setCurrentStep] = useState(0)
const isMobile = useMediaQuery({ maxWidth: 768 })
return (
<NewObjectForm
type={'host'}
defaultValues={{ active: true, state: { type: 'offline' } }}
>
<NewObjectForm type={'host'}>
{({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [
{
@ -36,20 +43,6 @@ const NewHost = ({ onOk }) => {
isEditing={true}
required={false}
objectData={objectData}
visibleProperties={{
_id: false,
createdAt: false,
updatedAt: false,
operatingSystem: false,
'deviceInfo.os': false,
'deviceInfo.os.hostname': false,
'deviceInfo.cpu': false,
'deviceInfo.cpu.model': false,
'deviceInfo.user.username': false,
'deviceInfo.user.homedir': false,
'deviceInfo.process.nodeVersion': false,
files: false
}}
/>
)
},
@ -64,18 +57,7 @@ const NewHost = ({ onOk }) => {
visibleProperties={{
_id: false,
createdAt: false,
updatedAt: false,
operatingSystem: false,
'deviceInfo.os': false,
'deviceInfo.os.hostname': false,
'deviceInfo.cpu': false,
'deviceInfo.cpu.model': false,
'deviceInfo.user.username': false,
'deviceInfo.user.homedir': false,
'deviceInfo.process.nodeVersion': false,
files: false,
connectedAt: false,
online: false
updatedAt: false
}}
isEditing={false}
objectData={objectData}
@ -84,18 +66,45 @@ const NewHost = ({ onOk }) => {
}
]
return (
<WizardView
steps={steps}
loading={submitLoading}
formValid={formValid}
title='New Host'
onSubmit={async () => {
const result = await handleSubmit()
if (result) {
onOk()
}
}}
/>
<Flex gap='middle'>
{!isMobile && (
<div style={{ minWidth: '160px' }}>
<Steps
current={currentStep}
items={steps}
direction='vertical'
style={{ width: 'fit-content' }}
/>
</div>
)}
{!isMobile && (
<Divider type='vertical' style={{ height: 'unset' }} />
)}
<Flex vertical gap='middle' style={{ flexGrow: 1 }}>
<Title level={2} style={{ margin: 0 }}>
New Host
</Title>
<div style={{ minHeight: '260px', marginBottom: 8 }}>
{steps[currentStep].content}
</div>
<NewObjectButtons
currentStep={currentStep}
totalSteps={steps.length}
onPrevious={() => setCurrentStep((prev) => prev - 1)}
onNext={() => setCurrentStep((prev) => prev + 1)}
onSubmit={async () => {
const result = await handleSubmit()
if (result) {
onOk()
}
}}
formValid={formValid}
submitLoading={submitLoading}
/>
</Flex>
</Flex>
)
}}
</NewObjectForm>

View File

@ -1,11 +1,8 @@
import { useLocation } from 'react-router-dom'
import DashboardSidebar from '../common/DashboardSidebar'
import FilamentIcon from '../../Icons/FilamentIcon'
import FilamentSkuIcon from '../../Icons/FilamentSkuIcon'
import PartIcon from '../../Icons/PartIcon'
import PartSkuIcon from '../../Icons/PartSkuIcon'
import ProductIcon from '../../Icons/ProductIcon'
import ProductSkuIcon from '../../Icons/ProductSkuIcon'
import VendorIcon from '../../Icons/VendorIcon'
import MaterialIcon from '../../Icons/MaterialIcon'
import NoteTypeIcon from '../../Icons/NoteTypeIcon'
@ -24,7 +21,6 @@ import CourierIcon from '../../Icons/CourierIcon'
import CourierServiceIcon from '../../Icons/CourierServiceIcon'
import TaxRateIcon from '../../Icons/TaxRateIcon'
import TaxRecordIcon from '../../Icons/TaxRecordIcon'
import AppPasswordIcon from '../../Icons/AppPasswordIcon'
const items = [
{
@ -33,36 +29,18 @@ const items = [
label: 'Filaments',
path: '/dashboard/management/filaments'
},
{
key: 'filamentSkus',
icon: <FilamentSkuIcon />,
label: 'Filament SKUs',
path: '/dashboard/management/filamentskus'
},
{
key: 'parts',
icon: <PartIcon />,
label: 'Parts',
path: '/dashboard/management/parts'
},
{
key: 'partSkus',
icon: <PartSkuIcon />,
label: 'Part SKUs',
path: '/dashboard/management/partskus'
},
{
key: 'products',
icon: <ProductIcon />,
label: 'Products',
path: '/dashboard/management/products'
},
{
key: 'productSkus',
icon: <ProductSkuIcon />,
label: 'Product SKUs',
path: '/dashboard/management/productskus'
},
{
key: 'vendors',
icon: <VendorIcon />,
@ -153,12 +131,6 @@ const items = [
label: 'Users',
path: '/dashboard/management/users'
},
{
key: 'appPasswords',
icon: <AppPasswordIcon />,
label: 'App Passwords',
path: '/dashboard/management/apppasswords'
},
{
key: 'settings',
icon: <SettingsIcon />,
@ -193,13 +165,9 @@ if (import.meta.env.MODE === 'development') {
const routeKeyMap = {
'/dashboard/management/filaments': 'filaments',
'/dashboard/management/filamentskus': 'filamentSkus',
'/dashboard/management/parts': 'parts',
'/dashboard/management/partskus': 'partSkus',
'/dashboard/management/users': 'users',
'/dashboard/management/apppasswords': 'appPasswords',
'/dashboard/management/products': 'products',
'/dashboard/management/productskus': 'productSkus',
'/dashboard/management/vendors': 'vendors',
'/dashboard/management/couriers': 'couriers',
'/dashboard/management/courierservices': 'courierServices',

View File

@ -1,30 +1,218 @@
import { useRef, useState } from 'react'
import { Button, Flex, Space, Modal, Dropdown } from 'antd'
// src/materials.js
import { useEffect, useState, useContext, useCallback } from 'react'
import { useNavigate } from 'react-router-dom'
import axios from 'axios'
import {
Table,
Button,
Flex,
Space,
Modal,
Dropdown,
Spin
} from 'antd'
import { createStyles } from 'antd-style'
import { LoadingOutlined } from '@ant-design/icons'
import { useMessageContext } from '../context/MessageContext'
import { AuthContext } from '../context/AuthContext'
import NewMaterial from './Materials/NewMaterial'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ObjectTable from '../common/ObjectTable'
import IdDisplay from '../common/IdDisplay'
import MaterialIcon from '../../Icons/MaterialIcon'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ExportListButton from '../common/ExportListButton'
import TimeDisplay from '../common/TimeDisplay'
import config from '../../../config'
const useStyle = createStyles(({ css, token }) => {
const { antCls } = token
return {
customTable: css`
${antCls}-table {
${antCls}-table-container {
${antCls}-table-body,
${antCls}-table-content {
scrollbar-width: thin;
scrollbar-color: #eaeaea transparent;
scrollbar-gutter: stable;
}
}
}
`
}
})
const Materials = () => {
const { showError } = useMessageContext()
const navigate = useNavigate()
const { styles } = useStyle()
const [materialsData, setMaterialsData] = useState([])
const [page, setPage] = useState(1)
const [hasMore, setHasMore] = useState(true)
const [loading, setLoading] = useState(true)
const [lazyLoading, setLazyLoading] = useState(false)
const [newMaterialOpen, setNewMaterialOpen] = useState(false)
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('material')
const { authenticated } = useContext(AuthContext)
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('material')
const fetchMaterialsData = useCallback(
async (pageNum = 1, append = false) => {
try {
const response = await axios.get(`${config.backendUrl}/materials`, {
params: {
page: pageNum,
limit: 25
},
headers: {
Accept: 'application/json'
},
withCredentials: true
})
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Materials')
const newData = response.data
setHasMore(newData.length === 25) // If we get less than 25 items, we've reached the end
if (append) {
setMaterialsData((prev) => [...prev, ...newData])
} else {
setMaterialsData(newData)
}
setLoading(false)
setLazyLoading(false)
} catch (error) {
if (error.response) {
showError(
`Error updating material details: ${error.response.status}`
)
} else {
showError(
'An unexpected error occurred. Please try again later.'
)
}
setLoading(false)
setLazyLoading(false)
}
},
[showError]
)
useEffect(() => {
if (authenticated) {
fetchMaterialsData()
}
}, [authenticated, fetchMaterialsData])
const handleScroll = useCallback(
(e) => {
const { target } = e
const scrollHeight = target.scrollHeight
const scrollTop = target.scrollTop
const clientHeight = target.clientHeight
// If we're near the bottom (within 100px) and not currently loading
if (
scrollHeight - scrollTop - clientHeight < 100 &&
!lazyLoading &&
hasMore
) {
setLazyLoading(true)
const nextPage = page + 1
setPage(nextPage)
fetchMaterialsData(nextPage, true)
}
},
[page, lazyLoading, hasMore, fetchMaterialsData]
)
const getMaterialActionItems = (id) => {
return {
items: [
{
label: 'Info',
key: 'info',
icon: <InfoCircleIcon />
}
],
onClick: ({ key }) => {
if (key === 'info') {
navigate(`/dashboard/management/materials/info?materialId=${id}`)
}
}
}
}
const columns = [
{
title: '',
dataIndex: '',
key: 'icon',
width: 40,
fixed: 'left',
render: () => <MaterialIcon></MaterialIcon>
},
{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: 200,
fixed: 'left'
},
{
title: 'ID',
dataIndex: '_id',
key: 'id',
width: 180,
render: (text) => <IdDisplay id={text} type={'material'} longId={false} />
},
{
title: 'Category',
dataIndex: 'category',
key: 'category',
width: 150
},
{
title: 'Created At',
dataIndex: 'createdAt',
key: 'createdAt',
width: 180,
render: (createdAt) => {
if (createdAt) {
return <TimeDisplay dateTime={createdAt} />
} else {
return 'n/a'
}
}
},
{
title: 'Actions',
key: 'actions',
fixed: 'right',
width: 150,
render: (text, record) => {
return (
<Space gap='small'>
<Button
icon={<InfoCircleIcon />}
onClick={() =>
navigate(
`/dashboard/management/materials/info?materialId=${record._id}`
)
}
/>
<Dropdown menu={getMaterialActionItems(record._id)}>
<Button>Actions</Button>
</Dropdown>
</Space>
)
}
}
]
const actionItems = {
items: [
@ -42,7 +230,7 @@ const Materials = () => {
],
onClick: ({ key }) => {
if (key === 'reloadList') {
tableRef.current?.reload()
fetchMaterialsData()
} else if (key === 'newMaterial') {
setNewMaterialOpen(true)
}
@ -51,57 +239,44 @@ const Materials = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'}>
<Space>
<Dropdown menu={actionItems}>
<Button>Actions</Button>
</Dropdown>
<ColumnViewButton
type='material'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='material' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
/>
</Space>
</Flex>
<ObjectTable
ref={tableRef}
type='material'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
<Flex vertical={'true'} gap='large'>
<Space>
<Dropdown menu={actionItems}>
<Button>Actions</Button>
</Dropdown>
</Space>
<Table
dataSource={materialsData}
className={styles.customTable}
columns={columns}
pagination={false}
rowKey='_id'
loading={{ spinning: loading, indicator: <LoadingOutlined spin /> }}
scroll={{ y: 'calc(100vh - 270px)' }}
onScroll={handleScroll}
/>
<Modal
open={newMaterialOpen}
footer={null}
width={700}
onCancel={() => {
setNewMaterialOpen(false)
}}
>
<NewMaterial
onOk={() => {
setNewMaterialOpen(false)
tableRef.current?.reload()
}}
reset={newMaterialOpen}
/>
</Modal>
{lazyLoading && (
<div style={{ textAlign: 'center', padding: '10px' }}>
<Spin indicator={<LoadingOutlined spin />} />
</div>
)}
</Flex>
<Modal
open={newMaterialOpen}
styles={{ content: { paddingBottom: '24px' } }}
footer={null}
width={700}
onCancel={() => {
setNewMaterialOpen(false)
}}
>
<NewMaterial
onSuccess={() => {
setNewMaterialOpen(false)
fetchMaterialsData()
}}
/>
</Modal>
</>
)
}

View File

@ -1,199 +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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
const log = loglevel.getLogger('MaterialInfo')
log.setLevel(config.logLevel)
const MaterialInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const materialId = new URLSearchParams(location.search).get('materialId')
const [collapseState, updateCollapseState] = useCollapseState('MaterialInfo', {
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='material'
id={materialId}
disabled={objectFormState.loading}
objectData={objectFormState.objectData}
/>
<ViewButton
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Material Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
<UserNotifierToggle
type='material'
objectData={objectFormState.objectData}
disabled={objectFormState.loading}
/>
<DocumentPrintButton
type='material'
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='Material Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<ObjectForm
id={materialId}
type='material'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='material'
objectData={objectData}
/>
)}
</ObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={materialId} type='material' />
</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': materialId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</ScrollBox>
</Flex>
</>
)
}
export default MaterialInfo

View File

@ -61,11 +61,9 @@ const NewMaterial = ({ onOk }) => {
loading={submitLoading}
formValid={formValid}
title='New Material'
onSubmit={async () => {
const result = await handleSubmit()
if (result) {
onOk()
}
onSubmit={() => {
handleSubmit()
onOk()
}}
/>
)

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const NoteTypes = () => {
const [newNoteTypeOpen, setNewNoteTypeOpen] = useState(false)
@ -21,9 +19,6 @@ const NoteTypes = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('noteType')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('NoteTypes')
const actionItems = {
items: [
{
@ -49,7 +44,7 @@ const NoteTypes = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -58,19 +53,16 @@ const NoteTypes = () => {
<ColumnViewButton
type='noteType'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='noteType' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -79,7 +71,6 @@ const NoteTypes = () => {
visibleColumns={columnVisibility}
type='noteType'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -19,7 +19,6 @@ const NewNote = ({ onOk, defaultValues = {} }) => {
bordered={false}
isEditing={true}
required={true}
labelWidth='100px'
objectData={objectData}
/>
)

View File

@ -1,21 +1,18 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import loglevel from 'loglevel'
import config from '../../../../config.js'
import useCollapseState from '../../hooks/useCollapseState.jsx'
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 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 TextIcon from '../../../Icons/TextIcon.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 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'
@ -24,7 +21,6 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
import { getModelProperty } from '../../../../database/ObjectModels.js'
const log = loglevel.getLogger('NoteInfo')
log.setLevel(config.logLevel)
@ -36,7 +32,6 @@ const NoteInfo = () => {
const noteId = new URLSearchParams(location.search).get('noteId')
const [collapseState, updateCollapseState] = useCollapseState('NoteInfo', {
info: true,
content: true,
notes: true,
auditLogs: true
})
@ -92,7 +87,6 @@ const NoteInfo = () => {
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Note Information' },
{ key: 'content', label: 'Note Content' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
@ -138,58 +132,32 @@ const NoteInfo = () => {
loading={objectFormState.loading}
ref={actionHandlerRef}
>
<ObjectForm
id={noteId}
type='note'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
<InfoCollapse
title='Note Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
{({ loading, isEditing, objectData }) => (
<Flex vertical gap={'large'}>
<InfoCollapse
title='Note Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='note'
objectData={objectData}
visibleProperties={{ content: false }}
/>
</InfoCollapse>
<InfoCollapse
title='Note Content'
icon={<TextIcon />}
active={collapseState.content}
onToggle={(expanded) =>
updateCollapseState('content', expanded)
}
collapseKey='content'
>
<Card>
<ObjectProperty
{...getModelProperty('note', 'content')}
isEditing={isEditing}
objectData={objectData}
loading={loading}
size='medium'
showCard={false}
/>
</Card>
</InfoCollapse>
</Flex>
)}
</ObjectForm>
<ObjectForm
id={noteId}
type='note'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='note'
objectData={objectData}
/>
)}
</ObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Notes'

View File

@ -1,110 +0,0 @@
import { useState, useRef } from 'react'
import { Button, Flex, Space, Modal, Dropdown } from 'antd'
import NewPartSku from './PartSkus/NewPartSku'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTable from '../common/ObjectTable'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const PartSkus = () => {
const tableRef = useRef()
const [newPartSkuOpen, setNewPartSkuOpen] = useState(false)
const [viewMode, setViewMode] = useViewMode('partSkus')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('partSku')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('PartSkus')
const actionItems = {
items: [
{
label: 'New Part SKU',
key: 'newPartSku',
icon: <PlusIcon />
},
{ type: 'divider' },
{
label: 'Reload List',
key: 'reloadList',
icon: <ReloadIcon />
}
],
onClick: ({ key }) => {
if (key === 'reloadList') {
tableRef.current?.reload()
} else if (key === 'newPartSku') {
setNewPartSkuOpen(true)
}
}
}
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
<Button>Actions</Button>
</Dropdown>
<ColumnViewButton
type='partSku'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='partSku' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
/>
</Space>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='partSku'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal
open={newPartSkuOpen}
styles={{ content: { paddingBottom: '24px' } }}
footer={null}
width={700}
onCancel={() => {
setNewPartSkuOpen(false)
}}
destroyOnHidden={true}
>
<NewPartSku
onOk={() => {
setNewPartSkuOpen(false)
tableRef.current?.reload()
}}
reset={newPartSkuOpen}
/>
</Modal>
</>
)
}
export default PartSkus

View File

@ -1,129 +0,0 @@
import PropTypes from 'prop-types'
import ObjectInfo from '../../common/ObjectInfo'
import NewObjectForm from '../../common/NewObjectForm'
import WizardView from '../../common/WizardView'
const NewPartSku = ({ onOk, reset, defaultValues }) => {
return (
<NewObjectForm
type='partSku'
reset={reset}
defaultValues={defaultValues}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [
{
title: 'Required',
key: 'required',
content: (
<ObjectInfo
type='partSku'
column={1}
labelWidth={70}
bordered={false}
isEditing={true}
required={true}
objectData={objectData}
visibleProperties={{
description: false,
priceMode: false,
cost: false,
costWithTax: false,
costTaxRate: false,
price: false,
priceWithTax: false,
margin: false,
amount: false,
priceTaxRate: false,
vendor: false
}}
/>
)
},
{
title: 'Pricing',
key: 'pricing',
content: (
<ObjectInfo
type='partSku'
column={1}
labelWidth={100}
visibleProperties={{
_id: false,
createdAt: false,
updatedAt: false,
barcode: false,
part: false,
name: false,
description: false
}}
bordered={false}
isEditing={true}
objectData={objectData}
/>
)
},
{
title: 'Optional',
key: 'optional',
content: (
<ObjectInfo
type='partSku'
column={1}
labelWidth={100}
visibleProperties={{
barcode: true,
description: true
}}
bordered={false}
isEditing={true}
objectData={objectData}
/>
)
},
{
title: 'Summary',
key: 'summary',
content: (
<ObjectInfo
type='partSku'
column={1}
visibleProperties={{
createdAt: false,
updatedAt: false,
_id: false
}}
labelWidth={100}
bordered={false}
isEditing={false}
objectData={objectData}
/>
)
}
]
return (
<WizardView
steps={steps}
loading={submitLoading}
formValid={formValid}
title='New Part SKU'
onSubmit={async () => {
const result = await handleSubmit()
if (result) {
onOk()
}
}}
/>
)
}}
</NewObjectForm>
)
}
NewPartSku.propTypes = {
onOk: PropTypes.func.isRequired,
reset: PropTypes.bool,
defaultValues: PropTypes.object
}
export default NewPartSku

View File

@ -1,194 +0,0 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
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 ObjectForm from '../../common/ObjectForm'
import EditButtons from '../../common/EditButtons'
import LockIndicator from '../../common/LockIndicator.jsx'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
import NoteIcon from '../../../Icons/NoteIcon.jsx'
import AuditLogIcon from '../../../Icons/AuditLogIcon.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
const PartSkuInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const partSkuId = new URLSearchParams(location.search).get('partSkuId')
const [collapseState, updateCollapseState] = useCollapseState('PartSkuInfo', {
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?.fetchObject?.()
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='partSku'
id={partSkuId}
disabled={objectFormState.loading}
objectData={objectFormState.objectData}
/>
<ViewButton
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Part SKU Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
<UserNotifierToggle
type='partSku'
objectData={objectFormState.objectData}
disabled={objectFormState.loading}
/>
<DocumentPrintButton
type='partSku'
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='Part SKU Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<ObjectForm
id={partSkuId}
type='partSku'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='partSku'
objectData={objectData}
/>
)}
</ObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={partSkuId} type='partSku' />
</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': partSkuId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</ScrollBox>
</Flex>
</>
)
}
export default PartSkuInfo

View File

@ -11,13 +11,11 @@ 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 ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const Parts = (filter) => {
const [newPartOpen, setNewPartOpen] = useState(false)
@ -25,9 +23,6 @@ const Parts = (filter) => {
const [viewMode, setViewMode] = useViewMode('part')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('part')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Parts')
const actionItems = {
items: [
{
@ -53,7 +48,7 @@ const Parts = (filter) => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -62,19 +57,16 @@ const Parts = (filter) => {
<ColumnViewButton
type='part'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='part' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -84,7 +76,6 @@ const Parts = (filter) => {
type='part'
cards={viewMode === 'cards'}
filter={filter}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -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 useCollapseState from '../../hooks/useCollapseState'
import NotesPanel from '../../common/NotesPanel'
import InfoCollapse from '../../common/InfoCollapse'
@ -19,19 +19,15 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
import PartSkuIcon from '../../../Icons/PartSkuIcon.jsx'
import NewPartSku from '../PartSkus/NewPartSku'
const PartInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const partId = new URLSearchParams(location.search).get('partId')
const [newPartSkuOpen, setNewPartSkuOpen] = useState(false)
const partSkusTableRef = useRef()
const [collapseState, updateCollapseState] = useCollapseState('PartInfo', {
info: true,
partSkus: true,
parts: true,
notes: true,
auditLogs: true
})
@ -49,10 +45,6 @@ const PartInfo = () => {
objectFormRef?.current?.fetchObject?.()
return true
},
newPartSku: () => {
setNewPartSkuOpen(true)
return true
},
edit: () => {
objectFormRef?.current?.startEditing?.()
return false
@ -87,7 +79,6 @@ const PartInfo = () => {
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Part Information' },
{ key: 'partSkus', label: 'Part SKUs' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
@ -133,74 +124,33 @@ const PartInfo = () => {
loading={objectFormState.loading}
ref={actionHandlerRef}
>
<ObjectForm
id={partId}
type='part'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
<InfoCollapse
title='Part Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
{({ loading, isEditing, objectData }) => (
<InfoCollapse
title='Part Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectForm
id={partId}
type='part'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='part'
objectData={objectData}
/>
</InfoCollapse>
)}
</ObjectForm>
<InfoCollapse
title='Part SKUs'
icon={<PartSkuIcon />}
active={collapseState.partSkus}
onToggle={(expanded) =>
updateCollapseState('partSkus', expanded)
}
collapseKey='partSkus'
>
{objectFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
ref={partSkusTableRef}
type='partSku'
masterFilter={{ part: partId }}
visibleColumns={{ part: false }}
/>
)}
)}
</ObjectForm>
</InfoCollapse>
</ActionHandler>
<Modal
open={newPartSkuOpen}
styles={{ content: { paddingBottom: '24px' } }}
footer={null}
width={700}
onCancel={() => setNewPartSkuOpen(false)}
destroyOnClose
>
<NewPartSku
onOk={() => {
setNewPartSkuOpen(false)
partSkusTableRef.current?.reload?.()
}}
reset={newPartSkuOpen}
defaultValues={{
part: partId ? { _id: partId } : undefined
}}
/>
</Modal>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}

View File

@ -1,111 +0,0 @@
import { useState, useRef } from 'react'
import { Button, Flex, Space, Modal, Dropdown } from 'antd'
import NewProductSku from './ProductSkus/NewProductSku'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTable from '../common/ObjectTable'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const ProductSkus = () => {
const tableRef = useRef()
const [newProductSkuOpen, setNewProductSkuOpen] = useState(false)
const [viewMode, setViewMode] = useViewMode('productSkus')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('productSku')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('ProductSkus')
const actionItems = {
items: [
{
label: 'New Product SKU',
key: 'newProductSku',
icon: <PlusIcon />
},
{ type: 'divider' },
{
label: 'Reload List',
key: 'reloadList',
icon: <ReloadIcon />
}
],
onClick: ({ key }) => {
if (key === 'reloadList') {
tableRef.current?.reload()
} else if (key === 'newProductSku') {
setNewProductSkuOpen(true)
}
}
}
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
<Button>Actions</Button>
</Dropdown>
<ColumnViewButton
type='productSku'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='productSku' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
/>
</Space>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='productSku'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal
open={newProductSkuOpen}
styles={{ content: { paddingBottom: '24px' } }}
footer={null}
width={700}
onCancel={() => {
setNewProductSkuOpen(false)
}}
destroyOnHidden={true}
>
<NewProductSku
onOk={() => {
setNewProductSkuOpen(false)
tableRef.current?.reload()
}}
reset={newProductSkuOpen}
/>
</Modal>
</>
)
}
export default ProductSkus

View File

@ -1,164 +0,0 @@
import PropTypes from 'prop-types'
import ObjectInfo from '../../common/ObjectInfo'
import NewObjectForm from '../../common/NewObjectForm'
import WizardView from '../../common/WizardView'
const NewProductSku = ({ onOk, reset, defaultValues }) => {
return (
<NewObjectForm
type='productSku'
reset={reset}
defaultValues={defaultValues}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [
{
title: 'Required',
key: 'required',
content: (
<ObjectInfo
type='productSku'
column={1}
labelWidth={70}
bordered={false}
isEditing={true}
required={true}
objectData={objectData}
visibleProperties={{
description: false,
priceMode: false,
cost: false,
costWithTax: false,
costTaxRate: false,
price: false,
priceWithTax: false,
margin: false,
amount: false,
priceTaxRate: false,
vendor: false,
parts: false
}}
/>
)
},
{
title: 'Pricing',
key: 'pricing',
content: (
<ObjectInfo
type='productSku'
column={1}
labelWidth={100}
visibleProperties={{
_id: false,
createdAt: false,
updatedAt: false,
barcode: false,
product: false,
name: false,
description: false,
parts: false
}}
bordered={false}
isEditing={true}
objectData={objectData}
/>
)
},
{
title: 'Parts',
key: 'parts',
content: (
<ObjectInfo
type='productSku'
column={1}
labelWidth={100}
visibleProperties={{
_id: false,
createdAt: false,
updatedAt: false,
barcode: false,
product: false,
name: false,
description: false,
priceMode: false,
cost: false,
costWithTax: false,
costTaxRate: false,
price: false,
priceWithTax: false,
margin: false,
amount: false,
priceTaxRate: false,
vendor: false
}}
bordered={false}
isEditing={true}
objectData={objectData}
/>
)
},
{
title: 'Optional',
key: 'optional',
content: (
<ObjectInfo
type='productSku'
column={1}
labelWidth={100}
visibleProperties={{
barcode: true,
description: true
}}
bordered={false}
isEditing={true}
objectData={objectData}
/>
)
},
{
title: 'Summary',
key: 'summary',
content: (
<ObjectInfo
type='productSku'
column={1}
visibleProperties={{
createdAt: false,
updatedAt: false,
_id: false
}}
labelWidth={100}
bordered={false}
isEditing={false}
objectData={objectData}
/>
)
}
]
return (
<WizardView
steps={steps}
loading={submitLoading}
formValid={formValid}
title='New Product SKU'
onSubmit={async () => {
const result = await handleSubmit()
if (result) {
onOk()
}
}}
/>
)
}}
</NewObjectForm>
)
}
NewProductSku.propTypes = {
onOk: PropTypes.func.isRequired,
reset: PropTypes.bool,
defaultValues: PropTypes.object
}
export default NewProductSku

View File

@ -1,222 +0,0 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
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 ObjectForm from '../../common/ObjectForm'
import EditButtons from '../../common/EditButtons'
import LockIndicator from '../../common/LockIndicator.jsx'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
import NoteIcon from '../../../Icons/NoteIcon.jsx'
import AuditLogIcon from '../../../Icons/AuditLogIcon.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 UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
import ObjectProperty from '../../common/ObjectProperty.jsx'
import { getModelProperty } from '../../../../database/ObjectModels.js'
import PartIcon from '../../../Icons/PartIcon.jsx'
const ProductSkuInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const productSkuId = new URLSearchParams(location.search).get('productSkuId')
const [collapseState, updateCollapseState] = useCollapseState(
'ProductSkuInfo',
{
info: true,
parts: 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?.fetchObject?.()
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='productSku'
id={productSkuId}
disabled={objectFormState.loading}
objectData={objectFormState.objectData}
/>
<ViewButton
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Product SKU Information' },
{ key: 'parts', label: 'SKU Parts' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
<UserNotifierToggle
type='productSku'
objectData={objectFormState.objectData}
disabled={objectFormState.loading}
/>
<DocumentPrintButton
type='productSku'
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='Product SKU Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<ObjectForm
id={productSkuId}
type='productSku'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<>
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='productSku'
objectData={objectData}
visibleProperties={{
parts: false
}}
/>
</>
)}
</ObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='SKU Parts'
icon={<PartIcon />}
active={collapseState.parts}
onToggle={(expanded) => updateCollapseState('parts', expanded)}
collapseKey='parts'
>
<ObjectProperty
{...getModelProperty('productSku', 'parts')}
isEditing={objectFormState.isEditing}
objectData={objectFormState.objectData}
loading={objectFormState.loading}
size='medium'
/>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={productSkuId} type='productSku' />
</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': productSkuId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</ScrollBox>
</Flex>
</>
)
}
export default ProductSkuInfo

View File

@ -25,11 +25,9 @@ import ReloadIcon from '../../Icons/ReloadIcon'
import XMarkIcon from '../../Icons/XMarkIcon'
import CheckIcon from '../../Icons/CheckIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ExportListButton from '../common/ExportListButton'
const Products = () => {
const navigate = useNavigate()
@ -232,13 +230,10 @@ const Products = () => {
]
const [columnVisibility, updateColumnVisibility] = useColumnVisibility(
'product',
'Products',
columns
)
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Products')
const actionItems = {
items: [
{
@ -324,13 +319,12 @@ const Products = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
<Button>Actions</Button>
</Dropdown>
<ExportListButton objectType='product' />
<Popover
content={getViewDropdownItems()}
placement='bottomLeft'
@ -340,13 +334,11 @@ const Products = () => {
</Popover>
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -354,7 +346,6 @@ const Products = () => {
ref={tableRef}
type={'product'}
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -1,5 +1,4 @@
import { useRef, useState } from 'react'
import { Modal } from 'antd'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
import useCollapseState from '../../hooks/useCollapseState'
@ -20,8 +19,9 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
import ProductSkuIcon from '../../../Icons/ProductSkuIcon.jsx'
import NewProductSku from '../ProductSkus/NewProductSku'
import ObjectProperty from '../../common/ObjectProperty.jsx'
import { getModelProperty } from '../../../../database/ObjectModels.js'
import PartIcon from '../../../Icons/PartIcon.jsx'
const ProductInfo = () => {
const location = useLocation()
@ -30,12 +30,10 @@ const ProductInfo = () => {
const productId = new URLSearchParams(location.search).get('productId')
const [collapseState, updateCollapseState] = useCollapseState('ProductInfo', {
info: true,
productSkus: true,
parts: true,
notes: true,
auditLogs: true
})
const [newProductSkuOpen, setNewProductSkuOpen] = useState(false)
const productSkusTableRef = useRef()
const [objectFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
@ -50,10 +48,6 @@ const ProductInfo = () => {
objectFormRef?.current?.fetchObject?.()
return true
},
newProductSku: () => {
setNewProductSkuOpen(true)
return true
},
edit: () => {
objectFormRef?.current?.startEditing?.()
return false
@ -88,7 +82,7 @@ const ProductInfo = () => {
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Product Information' },
{ key: 'productSkus', label: 'Product SKUs' },
{ key: 'parts', label: 'Product Parts' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
@ -159,53 +153,32 @@ const ProductInfo = () => {
isEditing={isEditing}
type='product'
objectData={objectData}
visibleProperties={{
parts: false
}}
/>
</InfoCollapse>
<InfoCollapse
title='Product SKUs'
icon={<ProductSkuIcon />}
active={collapseState.productSkus}
title='Product Parts'
icon={<PartIcon />}
active={collapseState.parts}
onToggle={(expanded) =>
updateCollapseState('productSkus', expanded)
updateCollapseState('parts', expanded)
}
collapseKey='productSkus'
collapseKey='parts'
>
{objectFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
ref={productSkusTableRef}
type='productSku'
masterFilter={{ product: productId }}
visibleColumns={{ product: false }}
/>
)}
<ObjectProperty
{...getModelProperty('product', 'parts')}
isEditing={isEditing}
objectData={objectData}
loading={loading}
/>
</InfoCollapse>
</Flex>
)}
</ObjectForm>
</ActionHandler>
<Modal
open={newProductSkuOpen}
styles={{ content: { paddingBottom: '24px' } }}
footer={null}
width={700}
onCancel={() => setNewProductSkuOpen(false)}
destroyOnClose
>
<NewProductSku
onOk={() => {
setNewProductSkuOpen(false)
productSkusTableRef.current?.reload?.()
}}
reset={newProductSkuOpen}
defaultValues={{
product: productId ? { _id: productId } : undefined
}}
/>
</Modal>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const TaxRates = () => {
const [newTaxRateOpen, setNewTaxRateOpen] = useState(false)
@ -20,9 +18,6 @@ const TaxRates = () => {
const [columnVisibility, setColumnVisibility] = useColumnVisibility('taxRate')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('TaxRates')
const actionItems = {
items: [
{
@ -48,7 +43,7 @@ const TaxRates = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -57,19 +52,16 @@ const TaxRates = () => {
<ColumnViewButton
type='taxRate'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='taxRate' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -78,7 +70,6 @@ const TaxRates = () => {
visibleColumns={columnVisibility}
type='taxRate'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const TaxRecords = () => {
const [newTaxRecordOpen, setNewTaxRecordOpen] = useState(false)
@ -21,9 +19,6 @@ const TaxRecords = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('taxRecord')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('TaxRecords')
const actionItems = {
items: [
{
@ -49,7 +44,7 @@ const TaxRecords = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -61,16 +56,13 @@ const TaxRecords = () => {
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='taxRecord' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -79,7 +71,6 @@ const TaxRecords = () => {
visibleColumns={columnVisibility}
type='taxRecord'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -3,12 +3,10 @@ import { Button, Flex, Space, Dropdown } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const Users = () => {
const tableRef = useRef()
@ -17,9 +15,6 @@ const Users = () => {
const [columnVisibility, setColumnVisibility] = useColumnVisibility('user')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Users')
const actionItems = {
items: [
{
@ -36,7 +31,7 @@ const Users = () => {
}
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -48,16 +43,11 @@ const Users = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='user' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() => setViewMode(viewMode === 'cards' ? 'list' : 'cards')}
/>
</Space>
</Flex>
@ -66,7 +56,6 @@ const Users = () => {
type={'user'}
visibleColumns={columnVisibility}
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
)

View File

@ -1,73 +0,0 @@
import PropTypes from 'prop-types'
import { useContext, useState } from 'react'
import { Result, Typography, Flex, Button } from 'antd'
import { ApiServerContext } from '../../context/ApiServerContext'
import CopyButton from '../../common/CopyButton'
import LockIcon from '../../../Icons/LockIcon'
import ReloadIcon from '../../../Icons/ReloadIcon'
const { Text } = Typography
const SetAppPassword = ({ id }) => {
const { sendObjectFunction } = useContext(ApiServerContext)
const [appPassword, setAppPassword] = useState(null)
const [loading, setLoading] = useState(false)
const [passwordGenerated, setPasswordGenerated] = useState(false)
const handleSet = async () => {
setLoading(true)
setAppPassword(null)
try {
const result = await sendObjectFunction(id, 'user', 'setAppPassword', {})
if (result?.appPassword) {
setAppPassword(result.appPassword)
setPasswordGenerated(true)
}
} finally {
setLoading(false)
}
}
return (
<Flex vertical align='center'>
<Result
title={
passwordGenerated ? 'App Password Generated' : 'Roll New Password'
}
disabled={passwordGenerated}
subTitle={
appPassword ? (
<Text>Copy this password now. It will not be shown again.</Text>
) : (
<Text>Generate a new app password for API access.</Text>
)
}
icon={<LockIcon />}
>
<Flex justify='center' style={{ minWidth: '395px' }}>
<Flex justify='center'>
<Flex gap='small' align='center' justify='center'>
<CopyButton size='default' text={appPassword} />
<Text code style={{ fontSize: '18px' }}>
{appPassword || '••••••••••••••••••••••••••••••••'}
</Text>
<Button
type='texts'
loading={loading}
onClick={handleSet}
icon={<ReloadIcon />}
/>
</Flex>
</Flex>
</Flex>
</Result>
</Flex>
)
}
SetAppPassword.propTypes = {
id: PropTypes.string.isRequired
}
export default SetAppPassword

View File

@ -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 useCollapseState from '../../hooks/useCollapseState'
import NotesPanel from '../../common/NotesPanel'
@ -10,7 +10,6 @@ import ViewButton from '../../common/ViewButton'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
import NoteIcon from '../../../Icons/NoteIcon.jsx'
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
import AppPasswordIcon from '../../../Icons/AppPasswordIcon.jsx'
import ObjectForm from '../../common/ObjectForm'
import EditButtons from '../../common/EditButtons'
import LockIndicator from '../../common/LockIndicator.jsx'
@ -21,18 +20,14 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
import NewAppPassword from '../AppPasswords/NewAppPassword.jsx'
const UserInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
const appPasswordsTableRef = useRef(null)
const actionHandlerRef = useRef(null)
const userId = new URLSearchParams(location.search).get('userId')
const [newAppPasswordOpen, setNewAppPasswordOpen] = useState(false)
const [collapseState, updateCollapseState] = useCollapseState('UserInfo', {
info: true,
appPasswords: true,
notes: true,
auditLogs: true
})
@ -47,13 +42,9 @@ const UserInfo = () => {
const actions = {
reload: () => {
objectFormRef?.current?.handleFetchObject?.()
objectFormRef?.current?.fetchObject?.()
return true
},
newAppPassword: () => {
setNewAppPasswordOpen(true)
return false
},
edit: () => {
objectFormRef?.current?.startEditing?.()
return false
@ -88,7 +79,6 @@ const UserInfo = () => {
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'User Information' },
{ key: 'appPasswords', label: 'App Passwords' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
@ -162,26 +152,6 @@ const UserInfo = () => {
</ObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='App Passwords'
icon={<AppPasswordIcon />}
active={collapseState.appPasswords}
onToggle={(expanded) =>
updateCollapseState('appPasswords', expanded)
}
collapseKey='appPasswords'
>
{!userId ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='appPassword'
masterFilter={{ user: userId }}
visibleColumns={{ user: false }}
ref={appPasswordsTableRef}
/>
)}
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
@ -215,26 +185,6 @@ const UserInfo = () => {
</Flex>
</ScrollBox>
</Flex>
<Modal
open={newAppPasswordOpen}
destroyOnClose
width={700}
onCancel={() => {
actionHandlerRef.current?.clearAction?.()
setNewAppPasswordOpen(false)
}}
footer={null}
>
<NewAppPassword
onOk={() => {
setNewAppPasswordOpen(false)
appPasswordsTableRef.current?.reload?.()
}}
reset={newAppPasswordOpen}
defaultValues={{ user: { ...objectFormState.objectData } }}
/>
</Modal>
</>
)
}

View File

@ -5,12 +5,10 @@ import ObjectTable from '../common/ObjectTable'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const Vendors = () => {
const [newVendorOpen, setNewVendorOpen] = useState(false)
@ -20,9 +18,6 @@ const Vendors = () => {
const [columnVisibility, setColumnVisibility] = useColumnVisibility('vendor')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Vendors')
const actionItems = {
items: [
{
@ -48,7 +43,7 @@ const Vendors = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -57,19 +52,16 @@ const Vendors = () => {
<ColumnViewButton
type='vendor'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
collapseState={columnVisibility}
updateCollapseState={setColumnVisibility}
/>
<ExportListButton objectType='vendor' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -78,7 +70,6 @@ const Vendors = () => {
visibleColumns={columnVisibility}
type='vendor'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -7,13 +7,11 @@ import useColumnVisibility from '../hooks/useColumnVisibility'
import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import ObjectTable from '../common/ObjectTable'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import ListIcon from '../../Icons/ListIcon'
import GridIcon from '../../Icons/GridIcon'
import useViewMode from '../hooks/useViewMode'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
const GCodeFiles = () => {
const [newGCodeFileOpen, setNewGCodeFileOpen] = useState(false)
@ -23,9 +21,6 @@ const GCodeFiles = () => {
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('gcodeFile')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('GCodeFiles')
const actionItems = {
items: [
{
@ -51,7 +46,7 @@ const GCodeFiles = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space>
<Dropdown menu={actionItems}>
@ -63,16 +58,13 @@ const GCodeFiles = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='gcodeFile' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -81,7 +73,6 @@ const GCodeFiles = () => {
type='gcodeFile'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -7,12 +7,10 @@ import useColumnVisibility from '../hooks/useColumnVisibility.jsx'
import PlusIcon from '../../Icons/PlusIcon.jsx'
import ReloadIcon from '../../Icons/ReloadIcon.jsx'
import ObjectTable from '../common/ObjectTable.jsx'
import ListIcon from '../../Icons/ListIcon.jsx'
import GridIcon from '../../Icons/GridIcon.jsx'
import useViewMode from '../hooks/useViewMode.jsx'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import ColumnViewButton from '../common/ColumnViewButton.jsx'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility.jsx'
import ExportListButton from '../common/ExportListButton.jsx'
const Jobs = () => {
const [newJobOpen, setNewJobOpen] = useState(false)
@ -21,9 +19,6 @@ const Jobs = () => {
const [columnVisibility, setColumnVisibility] = useColumnVisibility('job')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('Jobs')
const actionItems = {
items: [
{
@ -53,7 +48,7 @@ const Jobs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large' style={{ height: '100%' }}>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -65,17 +60,14 @@ const Jobs = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='job' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -85,7 +77,6 @@ const Jobs = () => {
type={'job'}
visibleColumns={columnVisibility}
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
<Modal

View File

@ -7,13 +7,11 @@ import PlusIcon from '../../Icons/PlusIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import ObjectTable from '../common/ObjectTable'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import useViewMode from '../hooks/useViewMode'
import useColumnVisibility from '../hooks/useColumnVisibility'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
const Printers = () => {
const [newPrinterOpen, setNewPrinterOpen] = useState(false)
@ -25,9 +23,6 @@ const Printers = () => {
// Column visibility state, persisted in sessionStorage via custom hook
const [columnVisibility, setColumnVisibility] = useColumnVisibility('printer')
// Filter sidebar visibility, persisted in sessionStorage via custom hook
const [showFilterSidebar, setShowFilterSidebar] = useFilterSidebarVisibility('Printers')
const actionItems = {
items: [
{
@ -53,7 +48,7 @@ const Printers = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large'>
<Flex justify={'space-between'}>
<Space>
<Dropdown menu={actionItems}>
@ -65,16 +60,13 @@ const Printers = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='printer' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -82,7 +74,6 @@ const Printers = () => {
<ObjectTable
ref={tableRef}
type='printer'
showFilterSidebar={showFilterSidebar}
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
/>

View File

@ -1,12 +1,17 @@
import { useContext } from 'react'
import { Flex } from 'antd'
import useCollapseState from '../hooks/useCollapseState.jsx'
import StatsDisplay from '../common/StatsDisplay'
import HistoryDisplay from '../common/HistoryDisplay'
import InfoCollapse from '../common/InfoCollapse'
import ScrollBox from '../common/ScrollBox'
import { ApiServerContext } from '../context/ApiServerContext'
import ObjectTable from '../common/ObjectTable'
const ProductionOverview = () => {
const { connected } = useContext(ApiServerContext)
const [collapseState, updateCollapseState] = useCollapseState(
'ProductionOverview',
{
@ -19,6 +24,10 @@ const ProductionOverview = () => {
}
)
if (!connected) {
return null
}
return (
<Flex
gap='large'

View File

@ -5,12 +5,10 @@ import { Button, Flex, Space, Dropdown } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility.jsx'
import ReloadIcon from '../../Icons/ReloadIcon.jsx'
import ObjectTable from '../common/ObjectTable.jsx'
import ListIcon from '../../Icons/ListIcon.jsx'
import GridIcon from '../../Icons/GridIcon.jsx'
import useViewMode from '../hooks/useViewMode.jsx'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import ColumnViewButton from '../common/ColumnViewButton.jsx'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility.jsx'
import ExportListButton from '../common/ExportListButton.jsx'
const SubJobs = () => {
const tableRef = useRef()
@ -18,9 +16,6 @@ const SubJobs = () => {
const [columnVisibility, setColumnVisibility] = useColumnVisibility('subJob')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('SubJobs')
const actionItems = {
items: [
{
@ -38,7 +33,7 @@ const SubJobs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex vertical={'true'} gap='large' style={{ height: '100%' }}>
<Flex justify={'space-between'}>
<Space size='small'>
<Dropdown menu={actionItems}>
@ -50,17 +45,14 @@ const SubJobs = () => {
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='subJob' />
</Space>
<Space>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() =>
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
}
/>
</Space>
</Flex>
@ -70,7 +62,6 @@ const SubJobs = () => {
type={'subJob'}
visibleColumns={columnVisibility}
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
/>
</Flex>
</>

Some files were not shown because too many files have changed in this diff Show More