Compare commits
3 Commits
7cf39ff112
...
13bbcbe50e
| Author | SHA1 | Date | |
|---|---|---|---|
| 13bbcbe50e | |||
| d0911c1166 | |||
| 6f46587f5e |
13
assets/icons/taxrateicon.svg
Normal file
13
assets/icons/taxrateicon.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?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.887143,0,0,0.887143,3,3.211722)">
|
||||||
|
<path d="M37.605,64.798C35.97,65.05 34.296,65.181 32.59,65.181C14.599,65.181 0,50.582 0,32.59C0,14.599 14.599,0 32.59,0C50.592,0 65.191,14.599 65.191,32.59C65.191,32.92 65.186,33.248 65.176,33.575L64.728,33.251L64.711,33.241L63.625,32.757L62.474,32.491L61.304,32.455L60.16,32.642L59.079,33.04L58.094,33.629L58.006,33.706C58.022,33.336 58.03,32.964 58.03,32.59C58.03,18.529 46.652,7.161 32.59,7.161C18.529,7.161 7.161,18.529 7.161,32.59C7.161,46.652 18.529,58.02 32.59,58.02C36.258,58.02 39.743,57.247 42.892,55.854L38.593,62.35C38.366,62.692 38.175,63.045 38.019,63.4L37.639,64.54L37.605,64.798Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.887143,0,0,0.887143,3,3.211722)">
|
||||||
|
<path d="M33.627,46.224L22.957,46.224C21.791,46.224 20.916,45.429 20.916,44.203C20.916,43.087 21.687,42.386 22.622,41.935C24.986,40.884 26.28,38.794 26.28,36.193C26.28,35.474 26.152,34.744 25.961,33.93L22.25,33.93C21.359,33.93 20.726,33.348 20.726,32.529C20.726,31.742 21.359,31.16 22.25,31.16L25.284,31.16C24.896,29.781 24.7,28.625 24.7,27.447C24.7,21.845 29.228,18.881 35.049,18.881C37.066,18.881 38.335,19.006 39.843,19.534C41.021,19.845 41.964,20.474 41.964,21.695C41.964,22.719 41.283,23.327 40.22,23.327C39.785,23.327 39.274,23.232 38.771,23.127C38.008,22.948 36.92,22.811 35.732,22.811C32.123,22.811 29.593,24.555 29.593,27.791C29.593,28.786 29.707,29.656 30.074,31.16L38.186,31.16C39.066,31.16 39.71,31.753 39.71,32.529C39.71,33.338 39.066,33.93 38.186,33.93L30.783,33.93C30.911,34.534 30.975,35.213 30.975,36.008C30.975,38.529 29.988,41.074 27.758,42.081L27.758,42.226L33.619,42.226C33.535,42.851 33.491,43.509 33.491,44.2C33.491,44.907 33.538,45.582 33.627,46.224Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.48434,0,0,0.48434,31.429473,34.991997)">
|
||||||
|
<path d="M20.943,29.037C28.325,29.037 33.046,23.873 33.046,15.317C33.046,6.658 28.272,1.806 20.943,1.806C13.64,1.806 8.841,6.658 8.841,15.343C8.841,23.925 13.588,29.037 20.943,29.037ZM20.943,22.073C18.883,22.073 17.918,19.908 17.918,15.343C17.918,10.831 18.883,8.744 20.943,8.744C23.004,8.744 23.969,10.831 23.969,15.343C23.969,19.908 23.004,22.073 20.943,22.073ZM55.145,57.616C62.526,57.616 67.247,52.478 67.247,43.923C67.247,35.263 62.474,30.386 55.145,30.386C47.868,30.386 43.042,35.263 43.042,43.949C43.042,52.53 47.789,57.616 55.145,57.616ZM55.145,50.678C53.084,50.678 52.119,48.513 52.119,43.949C52.119,39.436 53.084,37.35 55.145,37.35C57.205,37.35 58.17,39.436 58.17,43.949C58.17,48.513 57.205,50.678 55.145,50.678ZM18.469,59.162C20.812,60.537 23.938,59.943 25.469,57.599L58.25,7.881C59.938,5.318 59.344,2.131 56.781,0.662C54.406,-0.682 51.562,0.068 49.938,2.475L17.156,52.006C15.438,54.599 15.906,57.662 18.469,59.162Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
14
assets/icons/taxrecordicon.svg
Normal file
14
assets/icons/taxrecordicon.svg
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.887143,0,0,0.887143,3,3.211722)">
|
||||||
|
<path d="M29.615,65.047C13.017,63.543 0,49.579 0,32.59C0,14.599 14.599,0 32.59,0C50.592,0 65.191,14.599 65.191,32.59C65.191,36.04 64.654,39.365 63.66,42.486C63.098,42.436 62.495,42.411 61.843,42.411L56.068,42.411C57.332,39.391 58.03,36.073 58.03,32.59C58.03,18.529 46.652,7.161 32.59,7.161C18.529,7.161 7.161,18.529 7.161,32.59C7.161,45.539 16.801,56.204 29.308,57.811L29.308,61.604C29.308,62.941 29.41,64.07 29.615,65.047Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.887143,0,0,0.887143,3,3.211722)">
|
||||||
|
<path d="M30.87,46.224L22.957,46.224C21.791,46.224 20.916,45.429 20.916,44.203C20.916,43.087 21.687,42.386 22.622,41.935C24.986,40.884 26.28,38.794 26.28,36.193C26.28,35.474 26.152,34.744 25.961,33.93L22.25,33.93C21.359,33.93 20.726,33.348 20.726,32.529C20.726,31.742 21.359,31.16 22.25,31.16L25.284,31.16C24.896,29.781 24.7,28.625 24.7,27.447C24.7,21.845 29.228,18.881 35.049,18.881C37.066,18.881 38.335,19.006 39.843,19.534C41.021,19.845 41.964,20.474 41.964,21.695C41.964,22.719 41.283,23.327 40.22,23.327C39.785,23.327 39.274,23.232 38.771,23.127C38.008,22.948 36.92,22.811 35.732,22.811C32.123,22.811 29.593,24.555 29.593,27.791C29.593,28.786 29.707,29.656 30.074,31.16L38.186,31.16C39.066,31.16 39.71,31.753 39.71,32.529C39.71,33.338 39.066,33.93 38.186,33.93L30.783,33.93C30.911,34.534 30.975,35.213 30.975,36.008C30.975,38.529 29.988,41.074 27.758,42.081L27.758,42.226L35.078,42.226C35.08,42.226 35.082,42.226 35.084,42.226C35.222,42.228 35.338,42.331 35.355,42.468C35.373,42.605 35.287,42.734 35.154,42.771C34.259,43.164 32.987,43.909 31.885,45.011C31.503,45.392 31.165,45.792 30.87,46.224Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.317224,0,0,0.317224,32,43.836431)">
|
||||||
|
<path d="M19.344,63.562L81.531,63.562C88.781,63.562 93.094,62.406 96.438,59.062C99.75,55.75 100.875,51.562 100.875,44.219L100.875,19.344C100.875,12 99.75,7.781 96.438,4.5C93.062,1.156 88.781,0 81.531,0L19.344,0C12.094,0 7.781,1.156 4.438,4.5C1.125,7.812 0,12 0,19.344L0,44.219C0,51.562 1.125,55.781 4.438,59.062C7.812,62.406 12.094,63.562 19.344,63.562ZM18.469,52.469C15.125,52.469 13.656,51.969 12.594,50.938C11.562,49.844 11.094,48.469 11.094,45.094L11.094,18.469C11.094,15.094 11.562,13.688 12.594,12.625C13.656,11.594 15.125,11.094 18.469,11.094L82.406,11.094C85.75,11.094 87.219,11.594 88.281,12.625C89.312,13.688 89.781,15.094 89.781,18.469L89.781,45.094C89.781,48.469 89.312,49.844 88.281,50.938C87.219,51.969 85.75,52.469 82.406,52.469L18.469,52.469Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M20.531,30.188L28.031,30.188C30.531,30.188 32.25,28.469 32.25,25.969L32.25,20.469C32.25,17.969 30.531,16.25 28.031,16.25L20.531,16.25C18.031,16.25 16.312,17.969 16.312,20.469L16.312,25.969C16.312,28.469 18.031,30.188 20.531,30.188ZM18.75,47.281L82.125,47.281C83.469,47.281 84.531,46.156 84.531,44.844C84.531,43.5 83.469,42.469 82.125,42.469L18.75,42.469C17.406,42.469 16.344,43.5 16.344,44.844C16.344,46.156 17.406,47.281 18.75,47.281Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
13
assets/icons/taxruleicon.svg
Normal file
13
assets/icons/taxruleicon.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?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.887143,0,0,0.887143,3,3.211722)">
|
||||||
|
<path d="M40.292,64.266C37.823,64.864 35.243,65.181 32.59,65.181C14.599,65.181 0,50.582 0,32.59C0,14.599 14.599,0 32.59,0C48.085,0 61.059,10.816 64.372,25.302C62.551,23.444 59.987,22.309 57.084,22.306L57.08,22.306C56.676,22.306 56.281,22.325 55.894,22.364C51.96,13.406 43.012,7.161 32.59,7.161C18.529,7.161 7.161,18.529 7.161,32.59C7.161,46.652 18.529,58.02 32.59,58.02C35.805,58.02 38.88,57.426 41.71,56.341C40.562,58.022 40.04,59.878 40.04,61.96C40.04,62.761 40.127,63.532 40.292,64.266Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.887143,0,0,0.887143,3,3.211722)">
|
||||||
|
<path d="M35.033,46.224L22.957,46.224C21.791,46.224 20.916,45.429 20.916,44.203C20.916,43.087 21.687,42.386 22.622,41.935C24.986,40.884 26.28,38.794 26.28,36.193C26.28,35.474 26.152,34.744 25.961,33.93L22.25,33.93C21.359,33.93 20.726,33.348 20.726,32.529C20.726,31.742 21.359,31.16 22.25,31.16L25.284,31.16C24.896,29.781 24.7,28.625 24.7,27.447C24.7,21.845 29.228,18.881 35.049,18.881C37.066,18.881 38.335,19.006 39.843,19.534C41.021,19.845 41.964,20.474 41.964,21.695C41.964,22.719 41.283,23.327 40.22,23.327C39.785,23.327 39.274,23.232 38.771,23.127C38.008,22.948 36.92,22.811 35.732,22.811C32.123,22.811 29.593,24.555 29.593,27.791C29.593,28.786 29.707,29.656 30.074,31.16L38.186,31.16C39.066,31.16 39.71,31.753 39.71,32.529C39.71,33.338 39.066,33.93 38.186,33.93L30.783,33.93C30.911,34.534 30.975,35.213 30.975,36.008C30.975,38.529 29.988,41.074 27.758,42.081L27.758,42.226L35.079,42.226C34.947,42.877 34.876,43.548 34.87,44.233C34.863,44.909 34.917,45.574 35.033,46.224Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.465962,0,0,0.465962,36.933857,26)">
|
||||||
|
<path d="M0.001,35.371C-0.038,39.202 1.473,42.727 4.441,45.671L16.349,57.61C11.913,60.839 9.844,64.493 9.844,69.059C9.844,76.236 15.431,81.432 22.42,81.549C26.022,81.612 29.343,80.305 31.751,77.938L53.883,55.695C59.804,49.751 59.453,42.361 52.939,35.83L41.879,24.794L44.775,21.89C47.234,19.407 48.675,16.156 48.692,12.936C48.785,5.665 43.16,0.007 35.85,0C31.88,0 28.667,1.489 25.293,4.83L3.649,26.387C1.324,28.753 0.032,31.925 0.001,35.371ZM18.613,24.255L31.665,11.203C33.369,9.474 34.298,9.018 35.85,9.018C37.991,9.018 39.664,10.692 39.664,12.825C39.664,13.85 39.304,14.61 38.379,15.518L24.077,29.836C23.14,27.191 21.326,25.315 18.613,24.255ZM9.135,35.594C9.135,33.425 10.709,31.799 12.756,31.799C14.857,31.799 16.468,33.464 16.468,35.669C16.468,36.68 16.444,37.157 16.216,37.941C15.03,43.335 21.775,44.881 24.421,42.268L35.475,31.174L46.542,42.227C49.615,45.227 49.817,47.01 47.511,49.299L33.566,63.268C32.715,61.558 31.327,59.892 29.126,57.643L11.994,40.417C9.701,38.148 9.135,37.157 9.135,35.594ZM18.887,69.149C18.887,67.295 20.04,65.793 22.846,64.06L23.303,64.516C25.504,66.773 26.164,67.845 26.172,69.173C26.22,71.078 24.636,72.476 22.444,72.476C20.445,72.476 18.887,71.13 18.887,69.149Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
@ -347,6 +347,26 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-select-selection-item .ant-tag,
|
.ant-select-selection-item .ant-tag,
|
||||||
.ant-select-tree-title .ant-tag {
|
.ant-select-tree-title .object-display-tag {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
|
padding: 0;
|
||||||
|
margin-right: 1px !important;
|
||||||
|
padding-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select-outlined.ant-select-multiple
|
||||||
|
.ant-select-selection-item
|
||||||
|
.object-display-tag {
|
||||||
|
padding-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select-outlined.ant-select-multiple
|
||||||
|
.ant-select-selection-item
|
||||||
|
.object-display-tag
|
||||||
|
.ant-flex {
|
||||||
|
margin-top: -0.75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.object-info-descriptions table {
|
||||||
|
table-layout: fixed !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,8 @@ import DocumentJobIcon from '../../Icons/DocumentJobIcon'
|
|||||||
import FileIcon from '../../Icons/FileIcon'
|
import FileIcon from '../../Icons/FileIcon'
|
||||||
import CourierIcon from '../../Icons/CourierIcon'
|
import CourierIcon from '../../Icons/CourierIcon'
|
||||||
import CourierServiceIcon from '../../Icons/CourierServiceIcon'
|
import CourierServiceIcon from '../../Icons/CourierServiceIcon'
|
||||||
|
import TaxRateIcon from '../../Icons/TaxRateIcon'
|
||||||
|
import TaxRecordIcon from '../../Icons/TaxRecordIcon'
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
@ -65,6 +67,19 @@ const items = [
|
|||||||
path: '/dashboard/management/courierservices'
|
path: '/dashboard/management/courierservices'
|
||||||
},
|
},
|
||||||
{ type: 'divider' },
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
key: 'taxRates',
|
||||||
|
icon: <TaxRateIcon />,
|
||||||
|
label: 'Tax Rates',
|
||||||
|
path: '/dashboard/management/taxrates'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'taxRecords',
|
||||||
|
icon: <TaxRecordIcon />,
|
||||||
|
label: 'Tax Records',
|
||||||
|
path: '/dashboard/management/taxrecords'
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
{
|
{
|
||||||
key: 'noteTypes',
|
key: 'noteTypes',
|
||||||
icon: <NoteTypeIcon />,
|
icon: <NoteTypeIcon />,
|
||||||
@ -156,6 +171,8 @@ const routeKeyMap = {
|
|||||||
'/dashboard/management/vendors': 'vendors',
|
'/dashboard/management/vendors': 'vendors',
|
||||||
'/dashboard/management/couriers': 'couriers',
|
'/dashboard/management/couriers': 'couriers',
|
||||||
'/dashboard/management/courierservices': 'courierServices',
|
'/dashboard/management/courierservices': 'courierServices',
|
||||||
|
'/dashboard/management/taxrates': 'taxRates',
|
||||||
|
'/dashboard/management/taxrecords': 'taxRecords',
|
||||||
'/dashboard/management/materials': 'materials',
|
'/dashboard/management/materials': 'materials',
|
||||||
'/dashboard/management/notetypes': 'noteTypes',
|
'/dashboard/management/notetypes': 'noteTypes',
|
||||||
'/dashboard/management/settings': 'settings',
|
'/dashboard/management/settings': 'settings',
|
||||||
|
|||||||
@ -25,8 +25,12 @@ const NewPart = ({ onOk }) => {
|
|||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
file: false,
|
file: false,
|
||||||
priceMode: false,
|
priceMode: false,
|
||||||
globalPricing: false,
|
cost: false,
|
||||||
amount: false,
|
costTaxRate: false,
|
||||||
|
costWithTax: false,
|
||||||
|
price: false,
|
||||||
|
priceTaxRate: false,
|
||||||
|
priceWithTax: false,
|
||||||
margin: false
|
margin: false
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -45,9 +49,13 @@ const NewPart = ({ onOk }) => {
|
|||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
priceMode: true,
|
priceMode: true,
|
||||||
globalPricing: true,
|
margin: true,
|
||||||
amount: true,
|
cost: true,
|
||||||
margin: true
|
costTaxRate: true,
|
||||||
|
costWithTax: true,
|
||||||
|
price: true,
|
||||||
|
priceTaxRate: true,
|
||||||
|
priceWithTax: true
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
97
src/components/Dashboard/Management/TaxRates.jsx
Normal file
97
src/components/Dashboard/Management/TaxRates.jsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { useState, useRef } from 'react'
|
||||||
|
import { Button, Flex, Space, Modal, Dropdown, message } from 'antd'
|
||||||
|
import NewTaxRate from './TaxRates/NewTaxRate'
|
||||||
|
import ObjectTable from '../common/ObjectTable'
|
||||||
|
import PlusIcon from '../../Icons/PlusIcon'
|
||||||
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
|
import GridIcon from '../../Icons/GridIcon'
|
||||||
|
import ListIcon from '../../Icons/ListIcon'
|
||||||
|
import useViewMode from '../hooks/useViewMode'
|
||||||
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
|
|
||||||
|
const TaxRates = () => {
|
||||||
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
const [newTaxRateOpen, setNewTaxRateOpen] = useState(false)
|
||||||
|
const tableRef = useRef()
|
||||||
|
|
||||||
|
const [viewMode, setViewMode] = useViewMode('taxRate')
|
||||||
|
|
||||||
|
const [columnVisibility, setColumnVisibility] = useColumnVisibility('taxRate')
|
||||||
|
|
||||||
|
const actionItems = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'New Tax Rate',
|
||||||
|
key: 'newTaxRate',
|
||||||
|
icon: <PlusIcon />
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
label: 'Reload List',
|
||||||
|
key: 'reloadList',
|
||||||
|
icon: <ReloadIcon />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onClick: ({ key }) => {
|
||||||
|
if (key === 'reloadList') {
|
||||||
|
tableRef.current?.reload()
|
||||||
|
} else if (key === 'newTaxRate') {
|
||||||
|
setNewTaxRateOpen(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex vertical={'true'} gap='large'>
|
||||||
|
{contextHolder}
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='small'>
|
||||||
|
<Dropdown menu={actionItems}>
|
||||||
|
<Button>Actions</Button>
|
||||||
|
</Dropdown>
|
||||||
|
<ColumnViewButton
|
||||||
|
type='taxRate'
|
||||||
|
loading={false}
|
||||||
|
collapseState={columnVisibility}
|
||||||
|
updateCollapseState={setColumnVisibility}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||||
|
onClick={() =>
|
||||||
|
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ObjectTable
|
||||||
|
ref={tableRef}
|
||||||
|
visibleColumns={columnVisibility}
|
||||||
|
type='taxRate'
|
||||||
|
cards={viewMode === 'cards'}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Modal
|
||||||
|
open={newTaxRateOpen}
|
||||||
|
onCancel={() => setNewTaxRateOpen(false)}
|
||||||
|
footer={null}
|
||||||
|
destroyOnHidden={true}
|
||||||
|
width={700}
|
||||||
|
>
|
||||||
|
<NewTaxRate
|
||||||
|
onOk={() => {
|
||||||
|
setNewTaxRateOpen(false)
|
||||||
|
messageApi.success('New tax rate created successfully.')
|
||||||
|
tableRef.current?.reload()
|
||||||
|
}}
|
||||||
|
reset={!newTaxRateOpen}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TaxRates
|
||||||
80
src/components/Dashboard/Management/TaxRates/NewTaxRate.jsx
Normal file
80
src/components/Dashboard/Management/TaxRates/NewTaxRate.jsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
|
import WizardView from '../../common/WizardView'
|
||||||
|
|
||||||
|
const NewTaxRate = ({ onOk }) => {
|
||||||
|
return (
|
||||||
|
<NewObjectForm type={'taxRate'}>
|
||||||
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
title: 'Required',
|
||||||
|
key: 'required',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='taxRate'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={true}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Optional',
|
||||||
|
key: 'optional',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='taxRate'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Summary',
|
||||||
|
key: 'summary',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='taxRate'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
visibleProperties={{
|
||||||
|
_id: false,
|
||||||
|
createdAt: false,
|
||||||
|
updatedAt: false
|
||||||
|
}}
|
||||||
|
isEditing={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<WizardView
|
||||||
|
steps={steps}
|
||||||
|
loading={submitLoading}
|
||||||
|
formValid={formValid}
|
||||||
|
title='New Tax Rate'
|
||||||
|
onSubmit={() => {
|
||||||
|
handleSubmit()
|
||||||
|
onOk()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NewObjectForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
NewTaxRate.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
reset: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewTaxRate
|
||||||
193
src/components/Dashboard/Management/TaxRates/TaxRateInfo.jsx
Normal file
193
src/components/Dashboard/Management/TaxRates/TaxRateInfo.jsx
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
import { useRef, useState } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Space, Flex, Card } from 'antd'
|
||||||
|
import loglevel from 'loglevel'
|
||||||
|
import config from '../../../../config'
|
||||||
|
import useCollapseState from '../../hooks/useCollapseState'
|
||||||
|
import NotesPanel from '../../common/NotesPanel'
|
||||||
|
import InfoCollapse from '../../common/InfoCollapse'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import ViewButton from '../../common/ViewButton'
|
||||||
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import ObjectForm from '../../common/ObjectForm'
|
||||||
|
import EditButtons from '../../common/EditButtons'
|
||||||
|
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||||
|
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||||
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
|
||||||
|
const log = loglevel.getLogger('TaxRateInfo')
|
||||||
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
|
const TaxRateInfo = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const objectFormRef = useRef(null)
|
||||||
|
const actionHandlerRef = useRef(null)
|
||||||
|
const taxRateId = new URLSearchParams(location.search).get('taxRateId')
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState('TaxRateInfo', {
|
||||||
|
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='taxRate'
|
||||||
|
id={taxRateId}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
items={[
|
||||||
|
{ key: 'info', label: 'Tax Rate Information' },
|
||||||
|
{ key: 'notes', label: 'Notes' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
<DocumentPrintButton
|
||||||
|
type='taxRate'
|
||||||
|
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='Tax Rate Information'
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
active={collapseState.info}
|
||||||
|
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||||
|
collapseKey='info'
|
||||||
|
>
|
||||||
|
<ObjectForm
|
||||||
|
id={taxRateId}
|
||||||
|
type='taxRate'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
ref={objectFormRef}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading, isEditing, objectData }) => (
|
||||||
|
<ObjectInfo
|
||||||
|
loading={loading}
|
||||||
|
isEditing={isEditing}
|
||||||
|
type='taxRate'
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ObjectForm>
|
||||||
|
</InfoCollapse>
|
||||||
|
</ActionHandler>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Notes'
|
||||||
|
icon={<NoteIcon />}
|
||||||
|
active={collapseState.notes}
|
||||||
|
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||||
|
collapseKey='notes'
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<NotesPanel _id={taxRateId} type='taxRate' />
|
||||||
|
</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': taxRateId }}
|
||||||
|
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</ScrollBox>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TaxRateInfo
|
||||||
98
src/components/Dashboard/Management/TaxRecords.jsx
Normal file
98
src/components/Dashboard/Management/TaxRecords.jsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { useState, useRef } from 'react'
|
||||||
|
import { Button, Flex, Space, Modal, Dropdown, message } from 'antd'
|
||||||
|
import NewTaxRecord from './TaxRecords/NewTaxRecord'
|
||||||
|
import ObjectTable from '../common/ObjectTable'
|
||||||
|
import PlusIcon from '../../Icons/PlusIcon'
|
||||||
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
|
import GridIcon from '../../Icons/GridIcon'
|
||||||
|
import ListIcon from '../../Icons/ListIcon'
|
||||||
|
import useViewMode from '../hooks/useViewMode'
|
||||||
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
|
|
||||||
|
const TaxRecords = () => {
|
||||||
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
const [newTaxRecordOpen, setNewTaxRecordOpen] = useState(false)
|
||||||
|
const tableRef = useRef()
|
||||||
|
|
||||||
|
const [viewMode, setViewMode] = useViewMode('taxRecord')
|
||||||
|
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
useColumnVisibility('taxRecord')
|
||||||
|
|
||||||
|
const actionItems = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'New Tax Record',
|
||||||
|
key: 'newTaxRecord',
|
||||||
|
icon: <PlusIcon />
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
label: 'Reload List',
|
||||||
|
key: 'reloadList',
|
||||||
|
icon: <ReloadIcon />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onClick: ({ key }) => {
|
||||||
|
if (key === 'reloadList') {
|
||||||
|
tableRef.current?.reload()
|
||||||
|
} else if (key === 'newTaxRecord') {
|
||||||
|
setNewTaxRecordOpen(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex vertical={'true'} gap='large'>
|
||||||
|
{contextHolder}
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='small'>
|
||||||
|
<Dropdown menu={actionItems}>
|
||||||
|
<Button>Actions</Button>
|
||||||
|
</Dropdown>
|
||||||
|
<ColumnViewButton
|
||||||
|
type='taxRecord'
|
||||||
|
loading={false}
|
||||||
|
collapseState={columnVisibility}
|
||||||
|
updateCollapseState={setColumnVisibility}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||||
|
onClick={() =>
|
||||||
|
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ObjectTable
|
||||||
|
ref={tableRef}
|
||||||
|
visibleColumns={columnVisibility}
|
||||||
|
type='taxRecord'
|
||||||
|
cards={viewMode === 'cards'}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Modal
|
||||||
|
open={newTaxRecordOpen}
|
||||||
|
onCancel={() => setNewTaxRecordOpen(false)}
|
||||||
|
footer={null}
|
||||||
|
destroyOnHidden={true}
|
||||||
|
width={700}
|
||||||
|
>
|
||||||
|
<NewTaxRecord
|
||||||
|
onOk={() => {
|
||||||
|
setNewTaxRecordOpen(false)
|
||||||
|
messageApi.success('New tax record created successfully.')
|
||||||
|
tableRef.current?.reload()
|
||||||
|
}}
|
||||||
|
reset={!newTaxRecordOpen}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TaxRecords
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
|
import WizardView from '../../common/WizardView'
|
||||||
|
|
||||||
|
const NewTaxRecord = ({ onOk }) => {
|
||||||
|
return (
|
||||||
|
<NewObjectForm type={'taxRecord'}>
|
||||||
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
title: 'Required',
|
||||||
|
key: 'required',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='taxRecord'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={true}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Optional',
|
||||||
|
key: 'optional',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='taxRecord'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Summary',
|
||||||
|
key: 'summary',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='taxRecord'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
visibleProperties={{
|
||||||
|
_id: false,
|
||||||
|
createdAt: false,
|
||||||
|
updatedAt: false
|
||||||
|
}}
|
||||||
|
isEditing={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<WizardView
|
||||||
|
steps={steps}
|
||||||
|
loading={submitLoading}
|
||||||
|
formValid={formValid}
|
||||||
|
title='New Tax Record'
|
||||||
|
onSubmit={() => {
|
||||||
|
handleSubmit()
|
||||||
|
onOk()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NewObjectForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
NewTaxRecord.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
reset: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewTaxRecord
|
||||||
196
src/components/Dashboard/Management/TaxRecords/TaxRecordInfo.jsx
Normal file
196
src/components/Dashboard/Management/TaxRecords/TaxRecordInfo.jsx
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
import { useRef, useState } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Space, Flex, Card } from 'antd'
|
||||||
|
import loglevel from 'loglevel'
|
||||||
|
import config from '../../../../config'
|
||||||
|
import useCollapseState from '../../hooks/useCollapseState'
|
||||||
|
import NotesPanel from '../../common/NotesPanel'
|
||||||
|
import InfoCollapse from '../../common/InfoCollapse'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import ViewButton from '../../common/ViewButton'
|
||||||
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import ObjectForm from '../../common/ObjectForm'
|
||||||
|
import EditButtons from '../../common/EditButtons'
|
||||||
|
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||||
|
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||||
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
|
||||||
|
const log = loglevel.getLogger('TaxRecordInfo')
|
||||||
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
|
const TaxRecordInfo = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const objectFormRef = useRef(null)
|
||||||
|
const actionHandlerRef = useRef(null)
|
||||||
|
const taxRecordId = new URLSearchParams(location.search).get('taxRecordId')
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState(
|
||||||
|
'TaxRecordInfo',
|
||||||
|
{
|
||||||
|
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='taxRecord'
|
||||||
|
id={taxRecordId}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
items={[
|
||||||
|
{ key: 'info', label: 'Tax Record Information' },
|
||||||
|
{ key: 'notes', label: 'Notes' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
<DocumentPrintButton
|
||||||
|
type='taxRecord'
|
||||||
|
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='Tax Record Information'
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
active={collapseState.info}
|
||||||
|
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||||
|
collapseKey='info'
|
||||||
|
>
|
||||||
|
<ObjectForm
|
||||||
|
id={taxRecordId}
|
||||||
|
type='taxRecord'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
ref={objectFormRef}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading, isEditing, objectData }) => (
|
||||||
|
<ObjectInfo
|
||||||
|
loading={loading}
|
||||||
|
isEditing={isEditing}
|
||||||
|
type='taxRecord'
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ObjectForm>
|
||||||
|
</InfoCollapse>
|
||||||
|
</ActionHandler>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Notes'
|
||||||
|
icon={<NoteIcon />}
|
||||||
|
active={collapseState.notes}
|
||||||
|
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||||
|
collapseKey='notes'
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<NotesPanel _id={taxRecordId} type='taxRecord' />
|
||||||
|
</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': taxRecordId }}
|
||||||
|
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</ScrollBox>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TaxRecordInfo
|
||||||
@ -172,6 +172,7 @@ const GCodeFileInfo = () => {
|
|||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
parts: false
|
parts: false
|
||||||
}}
|
}}
|
||||||
|
labelWidth='200px'
|
||||||
/>
|
/>
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import PrinterIcon from '../../../Icons/PrinterIcon.jsx'
|
|||||||
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel.jsx'
|
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel.jsx'
|
||||||
import PrinterPositionPanel from '../../common/PrinterPositionPanel.jsx'
|
import PrinterPositionPanel from '../../common/PrinterPositionPanel.jsx'
|
||||||
import PrinterMovementPanel from '../../common/PrinterMovementPanel.jsx'
|
import PrinterMovementPanel from '../../common/PrinterMovementPanel.jsx'
|
||||||
|
import PrinterMiscPanel from '../../common/PrinterMiscPanel.jsx'
|
||||||
import JobIcon from '../../../Icons/JobIcon.jsx'
|
import JobIcon from '../../../Icons/JobIcon.jsx'
|
||||||
import SubJobIcon from '../../../Icons/SubJobIcon.jsx'
|
import SubJobIcon from '../../../Icons/SubJobIcon.jsx'
|
||||||
import FilamentStockIcon from '../../../Icons/FilamentStockIcon.jsx'
|
import FilamentStockIcon from '../../../Icons/FilamentStockIcon.jsx'
|
||||||
@ -50,7 +50,8 @@ const ControlPrinter = () => {
|
|||||||
filamentStock: true,
|
filamentStock: true,
|
||||||
temperature: true,
|
temperature: true,
|
||||||
position: true,
|
position: true,
|
||||||
movement: true
|
movement: true,
|
||||||
|
misc: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,7 +59,8 @@ const ControlPrinter = () => {
|
|||||||
const [sideBarVisible, setSideBarVisible] = useState(
|
const [sideBarVisible, setSideBarVisible] = useState(
|
||||||
collapseState.temperature ||
|
collapseState.temperature ||
|
||||||
collapseState.position ||
|
collapseState.position ||
|
||||||
collapseState.movement
|
collapseState.movement ||
|
||||||
|
collapseState.misc
|
||||||
)
|
)
|
||||||
|
|
||||||
const [loadFilamentStockOpen, setLoadFilamentStockOpen] = useState(false)
|
const [loadFilamentStockOpen, setLoadFilamentStockOpen] = useState(false)
|
||||||
@ -68,7 +70,8 @@ const ControlPrinter = () => {
|
|||||||
setSideBarVisible(
|
setSideBarVisible(
|
||||||
collapseState.temperature ||
|
collapseState.temperature ||
|
||||||
collapseState.position ||
|
collapseState.position ||
|
||||||
collapseState.movement
|
collapseState.movement ||
|
||||||
|
collapseState.misc
|
||||||
)
|
)
|
||||||
}, [collapseState])
|
}, [collapseState])
|
||||||
|
|
||||||
@ -168,18 +171,43 @@ const ControlPrinter = () => {
|
|||||||
const sideBarItems = (
|
const sideBarItems = (
|
||||||
<Flex gap={isMobile ? 'large' : 'middle'} vertical>
|
<Flex gap={isMobile ? 'large' : 'middle'} vertical>
|
||||||
{collapseState.temperature && (
|
{collapseState.temperature && (
|
||||||
<Card style={{ width: '100%' }}>
|
<Card
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
title='Temperature'
|
||||||
|
size='small'
|
||||||
|
styles={{ body: { padding: '25px' } }}
|
||||||
|
>
|
||||||
<PrinterTemperaturePanel id={printerId} />
|
<PrinterTemperaturePanel id={printerId} />
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
{collapseState.position && (
|
{collapseState.position && (
|
||||||
<Card style={{ width: '100%' }}>
|
<Card
|
||||||
<PrinterPositionPanel />
|
style={{ width: '100%' }}
|
||||||
|
title='Position'
|
||||||
|
size='small'
|
||||||
|
styles={{ body: { padding: '25px' } }}
|
||||||
|
>
|
||||||
|
<PrinterPositionPanel id={printerId} />
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
{collapseState.movement && (
|
{collapseState.movement && (
|
||||||
<Card style={{ width: '100%' }}>
|
<Card
|
||||||
<PrinterMovementPanel />
|
style={{ width: '100%' }}
|
||||||
|
title='Movement'
|
||||||
|
size='small'
|
||||||
|
styles={{ body: { padding: '25px' } }}
|
||||||
|
>
|
||||||
|
<PrinterMovementPanel id={printerId} />
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
{collapseState.misc && (
|
||||||
|
<Card
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
title='Misc'
|
||||||
|
size='small'
|
||||||
|
styles={{ body: { padding: '25px' } }}
|
||||||
|
>
|
||||||
|
<PrinterMiscPanel id={printerId} />
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -236,6 +264,10 @@ const ControlPrinter = () => {
|
|||||||
key: 'movement',
|
key: 'movement',
|
||||||
label: 'Movement'
|
label: 'Movement'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'misc',
|
||||||
|
label: 'Misc'
|
||||||
|
},
|
||||||
{ key: 'notes', label: 'Notes' }
|
{ key: 'notes', label: 'Notes' }
|
||||||
]}
|
]}
|
||||||
visibleState={collapseState}
|
visibleState={collapseState}
|
||||||
@ -330,6 +362,7 @@ const ControlPrinter = () => {
|
|||||||
}}
|
}}
|
||||||
objectData={printerObjectData}
|
objectData={printerObjectData}
|
||||||
type='printer'
|
type='printer'
|
||||||
|
labelWidth='100px'
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@ -361,7 +394,8 @@ const ControlPrinter = () => {
|
|||||||
showHyperlink={true}
|
showHyperlink={true}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
printers: false,
|
printers: false,
|
||||||
createdAt: false
|
createdAt: false,
|
||||||
|
finishedAt: false
|
||||||
}}
|
}}
|
||||||
objectData={jobObjectData}
|
objectData={jobObjectData}
|
||||||
type='job'
|
type='job'
|
||||||
@ -402,7 +436,8 @@ const ControlPrinter = () => {
|
|||||||
showHyperlink={true}
|
showHyperlink={true}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
printers: false,
|
printers: false,
|
||||||
createdAt: false
|
createdAt: false,
|
||||||
|
finishedAt: false
|
||||||
}}
|
}}
|
||||||
objectData={subJobObjectData}
|
objectData={subJobObjectData}
|
||||||
type='subJob'
|
type='subJob'
|
||||||
|
|||||||
@ -142,6 +142,7 @@ const SubJobInfo = () => {
|
|||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
type='subJob'
|
type='subJob'
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth='175px'
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ObjectForm>
|
</ObjectForm>
|
||||||
|
|||||||
@ -62,14 +62,17 @@ const FileList = ({
|
|||||||
const filesToRender = multiple ? files : [files]
|
const filesToRender = multiple ? files : [files]
|
||||||
|
|
||||||
const renderFileContent = (file) => (
|
const renderFileContent = (file) => (
|
||||||
<Flex vertical gap='10px'>
|
<Flex vertical gap='10px' style={{ maxWidth: '100%', minWidth: 0 }}>
|
||||||
<Flex justify={card ? 'space-between' : 'start'}>
|
<Flex
|
||||||
<Flex gap={'small'} align='center'>
|
justify={card ? 'space-between' : 'start'}
|
||||||
|
style={{ maxWidth: '100%', minWidth: 0 }}
|
||||||
|
>
|
||||||
|
<Flex gap={'small'} align='center' style={{ minWidth: 0 }}>
|
||||||
<FileIcon
|
<FileIcon
|
||||||
style={{ margin: 0, fontSize: card == true ? '24px' : '16px' }}
|
style={{ margin: 0, fontSize: card == true ? '24px' : '16px' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text style={{ marginTop: '1px' }}>
|
<Text style={{ marginTop: '1px', minWidth: 0 }} ellipsis>
|
||||||
{file.name || file.filename || 'Unknown file'}
|
{file.name || file.filename || 'Unknown file'}
|
||||||
</Text>
|
</Text>
|
||||||
<Tag>{file.extension}</Tag>
|
<Tag>{file.extension}</Tag>
|
||||||
|
|||||||
@ -90,7 +90,6 @@ const NotesPanel = ({ _id, type }) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const handleReloadData = useCallback(async () => {
|
const handleReloadData = useCallback(async () => {
|
||||||
console.log('GOT RELOAD DATA')
|
|
||||||
setNotes(await generateNotes(_id))
|
setNotes(await generateNotes(_id))
|
||||||
}, [_id, generateNotes])
|
}, [_id, generateNotes])
|
||||||
|
|
||||||
@ -100,7 +99,6 @@ const NotesPanel = ({ _id, type }) => {
|
|||||||
'note',
|
'note',
|
||||||
(noteData) => {
|
(noteData) => {
|
||||||
if (noteData.parent._id == _id) {
|
if (noteData.parent._id == _id) {
|
||||||
console.log('Note note added to parent:', _id)
|
|
||||||
handleReloadData()
|
handleReloadData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,11 +113,11 @@ const NotesPanel = ({ _id, type }) => {
|
|||||||
}, [_id, subscribeToObjectTypeUpdates, connected, handleReloadData])
|
}, [_id, subscribeToObjectTypeUpdates, connected, handleReloadData])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (token != null && !initialized) {
|
if (connected == true && token != null && !initialized) {
|
||||||
handleReloadData()
|
handleReloadData()
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
}
|
}
|
||||||
}, [token, handleReloadData, initialized])
|
}, [token, handleReloadData, initialized, connected])
|
||||||
|
|
||||||
const actionItems = {
|
const actionItems = {
|
||||||
items: [
|
items: [
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Table, Skeleton, Card, Button, Flex, Form, Typography } from 'antd'
|
|||||||
import PlusIcon from '../../Icons/PlusIcon'
|
import PlusIcon from '../../Icons/PlusIcon'
|
||||||
import ObjectProperty from './ObjectProperty'
|
import ObjectProperty from './ObjectProperty'
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
import BinIcon from '../../Icons/BinIcon'
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
const DEFAULT_COLUMN_WIDTHS = {
|
const DEFAULT_COLUMN_WIDTHS = {
|
||||||
@ -135,8 +136,70 @@ const ObjectChildTable = ({
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return [...propertyColumns, ...additionalColumns]
|
const deleteColumn = isEditing
|
||||||
}, [resolvedProperties, additionalColumns, isEditing])
|
? {
|
||||||
|
title: '',
|
||||||
|
key: 'delete',
|
||||||
|
width: 10,
|
||||||
|
fixed: 'right',
|
||||||
|
render: (_text, record, index) => {
|
||||||
|
if (record?.isSkeleton) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
type='text'
|
||||||
|
danger
|
||||||
|
size='small'
|
||||||
|
icon={<BinIcon />}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
const currentItems = Array.isArray(itemsSource)
|
||||||
|
? itemsSource
|
||||||
|
: []
|
||||||
|
|
||||||
|
// Use record's unique identifier if available, otherwise use index
|
||||||
|
let newItems
|
||||||
|
if (typeof rowKey === 'string' && record[rowKey] != null) {
|
||||||
|
// Use the unique key to find and remove the item
|
||||||
|
newItems = currentItems.filter(
|
||||||
|
(item) => item[rowKey] !== record[rowKey]
|
||||||
|
)
|
||||||
|
} else if (typeof rowKey === 'function') {
|
||||||
|
// If rowKey is a function, find the item by comparing the resolved keys
|
||||||
|
const recordKey = rowKey(record, index)
|
||||||
|
newItems = currentItems.filter((item, i) => {
|
||||||
|
const itemKey = rowKey(item, i)
|
||||||
|
return itemKey !== recordKey
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Fallback to index-based removal
|
||||||
|
newItems = currentItems.filter((_, i) => i !== index)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof onChange === 'function') {
|
||||||
|
onChange(newItems)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
|
||||||
|
return [
|
||||||
|
...propertyColumns,
|
||||||
|
...additionalColumns,
|
||||||
|
...(deleteColumn ? [deleteColumn] : [])
|
||||||
|
]
|
||||||
|
}, [
|
||||||
|
resolvedProperties,
|
||||||
|
additionalColumns,
|
||||||
|
isEditing,
|
||||||
|
itemsSource,
|
||||||
|
onChange,
|
||||||
|
rowKey
|
||||||
|
])
|
||||||
|
|
||||||
const skeletonData = useMemo(() => {
|
const skeletonData = useMemo(() => {
|
||||||
return createSkeletonRows(
|
return createSkeletonRows(
|
||||||
@ -213,7 +276,7 @@ const ObjectChildTable = ({
|
|||||||
}, [properties, rollups, objectData])
|
}, [properties, rollups, objectData])
|
||||||
|
|
||||||
const rollupColumns = useMemo(() => {
|
const rollupColumns = useMemo(() => {
|
||||||
return properties.map((property, index) => {
|
const propertyColumns = properties.map((property, index) => {
|
||||||
const nextProperty = properties[index + 1]
|
const nextProperty = properties[index + 1]
|
||||||
var nextRollup = null
|
var nextRollup = null
|
||||||
if (nextProperty) {
|
if (nextProperty) {
|
||||||
@ -231,9 +294,14 @@ const ObjectChildTable = ({
|
|||||||
return (
|
return (
|
||||||
<Flex justify={'space-between'}>
|
<Flex justify={'space-between'}>
|
||||||
<Text>
|
<Text>
|
||||||
{property?.prefix}
|
{record[property.name] !== undefined &&
|
||||||
{record[property.name]}
|
record[property.name] !== null && (
|
||||||
{property?.suffix}
|
<>
|
||||||
|
{property?.prefix}
|
||||||
|
{record[property.name]}
|
||||||
|
{property?.suffix}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
{rollupLabel && <Text type='secondary'>{rollupLabel}:</Text>}
|
{rollupLabel && <Text type='secondary'>{rollupLabel}:</Text>}
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -241,7 +309,22 @@ const ObjectChildTable = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [properties, rollups])
|
const blankDeleteColumn = isEditing
|
||||||
|
? {
|
||||||
|
title: '',
|
||||||
|
key: 'delete',
|
||||||
|
width: 40,
|
||||||
|
fixed: 'right',
|
||||||
|
render: () => {
|
||||||
|
return <Flex></Flex>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
return [
|
||||||
|
...propertyColumns,
|
||||||
|
...(blankDeleteColumn ? [blankDeleteColumn] : [])
|
||||||
|
]
|
||||||
|
}, [properties, rollups, isEditing])
|
||||||
|
|
||||||
const hasRollups = useMemo(
|
const hasRollups = useMemo(
|
||||||
() => Array.isArray(rollups) && rollups.length > 0,
|
() => Array.isArray(rollups) && rollups.length > 0,
|
||||||
@ -339,7 +422,7 @@ const ObjectChildTable = ({
|
|||||||
if (isEditing === true && formListName) {
|
if (isEditing === true && formListName) {
|
||||||
return (
|
return (
|
||||||
<Form.List name={formListName}>
|
<Form.List name={formListName}>
|
||||||
{(fields, { add }) => {
|
{(fields, { add, remove }) => {
|
||||||
const listDataSource = fields.map((field, index) => ({
|
const listDataSource = fields.map((field, index) => ({
|
||||||
_field: field,
|
_field: field,
|
||||||
_index: index,
|
_index: index,
|
||||||
@ -373,12 +456,38 @@ const ObjectChildTable = ({
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const deleteColumn = {
|
||||||
|
title: '',
|
||||||
|
key: 'delete',
|
||||||
|
width: 10,
|
||||||
|
fixed: 'right',
|
||||||
|
render: (_text, record) => {
|
||||||
|
const field = record?._field
|
||||||
|
const index = record?._index
|
||||||
|
if (!field || index == null) return null
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
type='text'
|
||||||
|
danger
|
||||||
|
size='small'
|
||||||
|
icon={<BinIcon />}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
if (typeof remove === 'function') {
|
||||||
|
remove(index)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const listTable = (
|
const listTable = (
|
||||||
<Flex vertical>
|
<Flex vertical>
|
||||||
<div ref={mainTableWrapperRef}>
|
<div ref={mainTableWrapperRef}>
|
||||||
<Table
|
<Table
|
||||||
dataSource={listDataSource}
|
dataSource={listDataSource}
|
||||||
columns={[...listColumns, ...additionalColumns]}
|
columns={[...listColumns, ...additionalColumns, deleteColumn]}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
size={size}
|
size={size}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
|||||||
@ -53,13 +53,30 @@ const ObjectDisplay = ({ object, objectType }) => {
|
|||||||
const model = getModelByName(objectType)
|
const model = getModelByName(objectType)
|
||||||
const Icon = model.icon
|
const Icon = model.icon
|
||||||
return (
|
return (
|
||||||
<Tag style={{ margin: 0, border: 'none' }}>
|
<Tag
|
||||||
<Flex gap={objectData?.color ? 'small' : '3px'} align='center'>
|
style={{
|
||||||
<Icon />
|
margin: 0,
|
||||||
<Flex gap={'small'} align='center'>
|
border: 'none',
|
||||||
|
minWidth: 0,
|
||||||
|
paddingBottom: '2.5px',
|
||||||
|
maxWidth: '100%'
|
||||||
|
}}
|
||||||
|
className='object-display-tag'
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
gap={objectData?.color ? 'small' : '4px'}
|
||||||
|
align='center'
|
||||||
|
style={{ minWidth: 0 }}
|
||||||
|
>
|
||||||
|
<Icon style={{ marginBottom: '-3px' }} />
|
||||||
|
<Flex gap={'small'} align='center' style={{ minWidth: 0 }}>
|
||||||
{objectData?.color ? <Badge color={objectData?.color} /> : null}
|
{objectData?.color ? <Badge color={objectData?.color} /> : null}
|
||||||
<div style={{ paddingTop: '1.5px' }}>
|
<div style={{ paddingTop: '1.5px', minWidth: 0 }}>
|
||||||
{objectData?.name ? <Text ellipsis>{objectData.name}</Text> : null}
|
{objectData?.name ? (
|
||||||
|
<Text ellipsis style={{ lineHeight: '1' }}>
|
||||||
|
{objectData.name}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
{objectData?._id && !objectData?.name ? (
|
{objectData?._id && !objectData?.name ? (
|
||||||
<IdDisplay
|
<IdDisplay
|
||||||
id={objectData?._id}
|
id={objectData?._id}
|
||||||
|
|||||||
@ -310,11 +310,11 @@ const ObjectForm = forwardRef(
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialized == false && id && token != null) {
|
if (connected == true && initialized == false && id && token != null) {
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
handleFetchObject()
|
handleFetchObject()
|
||||||
}
|
}
|
||||||
}, [id, initialized, handleFetchObject, token])
|
}, [id, initialized, handleFetchObject, token, connected])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id && connected) {
|
if (id && connected) {
|
||||||
@ -391,15 +391,19 @@ const ObjectForm = forwardRef(
|
|||||||
try {
|
try {
|
||||||
const value = await form.validateFields()
|
const value = await form.validateFields()
|
||||||
setEditLoading(true)
|
setEditLoading(true)
|
||||||
onStateChangeRef.current({ editLoading: true })
|
|
||||||
await updateObject(id, type, value)
|
|
||||||
|
|
||||||
|
const currentFormData = {
|
||||||
|
...value,
|
||||||
|
...objectData
|
||||||
|
}
|
||||||
|
onStateChangeRef.current({ editLoading: true })
|
||||||
|
await updateObject(id, type, currentFormData)
|
||||||
setIsEditing(false)
|
setIsEditing(false)
|
||||||
isEditingRef.current = false
|
isEditingRef.current = false
|
||||||
onStateChangeRef.current({ isEditing: isEditingRef.current })
|
onStateChangeRef.current({ isEditing: isEditingRef.current })
|
||||||
setObjectData({
|
setObjectData({
|
||||||
...objectData,
|
...objectData,
|
||||||
...value,
|
...currentFormData,
|
||||||
_isEditing: isEditingRef.current
|
_isEditing: isEditingRef.current
|
||||||
})
|
})
|
||||||
messageApi.success('Information updated successfully')
|
messageApi.success('Information updated successfully')
|
||||||
|
|||||||
@ -17,6 +17,7 @@ const ObjectInfo = ({
|
|||||||
required = undefined,
|
required = undefined,
|
||||||
visibleProperties = {},
|
visibleProperties = {},
|
||||||
objectPropertyProps = {},
|
objectPropertyProps = {},
|
||||||
|
labelWidth = '150px',
|
||||||
column = {
|
column = {
|
||||||
xs: 1,
|
xs: 1,
|
||||||
sm: 1,
|
sm: 1,
|
||||||
@ -86,13 +87,15 @@ const ObjectInfo = ({
|
|||||||
</Flex>
|
</Flex>
|
||||||
) : null,
|
) : null,
|
||||||
children: (
|
children: (
|
||||||
<ObjectProperty
|
<div style={{ maxWidth: '100%', minWidth: 0, width: '100%' }}>
|
||||||
{...item}
|
<ObjectProperty
|
||||||
{...objectPropertyProps}
|
{...item}
|
||||||
isEditing={isEditing}
|
{...objectPropertyProps}
|
||||||
objectData={combinedObjectData}
|
isEditing={isEditing}
|
||||||
showSince={true}
|
objectData={combinedObjectData}
|
||||||
/>
|
showSince={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
),
|
),
|
||||||
span: item?.span || undefined
|
span: item?.span || undefined
|
||||||
}
|
}
|
||||||
@ -105,6 +108,8 @@ const ObjectInfo = ({
|
|||||||
bordered={true}
|
bordered={true}
|
||||||
{...rest}
|
{...rest}
|
||||||
items={descriptionItems}
|
items={descriptionItems}
|
||||||
|
styles={{ label: { width: labelWidth } }}
|
||||||
|
className='object-info-descriptions'
|
||||||
/>
|
/>
|
||||||
</Spin>
|
</Spin>
|
||||||
)
|
)
|
||||||
@ -124,7 +129,8 @@ ObjectInfo.propTypes = {
|
|||||||
objectData: PropTypes.object,
|
objectData: PropTypes.object,
|
||||||
required: PropTypes.bool,
|
required: PropTypes.bool,
|
||||||
visibleProperties: PropTypes.object,
|
visibleProperties: PropTypes.object,
|
||||||
objectPropertyProps: PropTypes.object
|
objectPropertyProps: PropTypes.object,
|
||||||
|
labelWidth: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectInfo
|
export default ObjectInfo
|
||||||
|
|||||||
@ -45,6 +45,7 @@ import DataTree from './DataTree'
|
|||||||
import FileList from './FileList'
|
import FileList from './FileList'
|
||||||
import ObjectChildTable from './ObjectChildTable'
|
import ObjectChildTable from './ObjectChildTable'
|
||||||
import MiscId from './MiscId'
|
import MiscId from './MiscId'
|
||||||
|
import { round } from '../utils/Utils'
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
@ -265,7 +266,7 @@ const ObjectProperty = ({
|
|||||||
} else {
|
} else {
|
||||||
var roundedValue = value
|
var roundedValue = value
|
||||||
if (roundNumber != false && typeof value === 'number') {
|
if (roundNumber != false && typeof value === 'number') {
|
||||||
roundedValue = value.toFixed(roundNumber)
|
roundedValue = round(value, roundNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -35,7 +35,8 @@ const ObjectSelect = ({
|
|||||||
disabled = false,
|
disabled = false,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const { fetchObjectsByProperty, fetchObject } = useContext(ApiServerContext)
|
const { fetchObjectsByProperty, fetchObject, connected } =
|
||||||
|
useContext(ApiServerContext)
|
||||||
const { token } = useContext(AuthContext)
|
const { token } = useContext(AuthContext)
|
||||||
// --- State ---
|
// --- State ---
|
||||||
const [treeData, setTreeData] = useState([])
|
const [treeData, setTreeData] = useState([])
|
||||||
@ -373,11 +374,22 @@ const ObjectSelect = ({
|
|||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!initialized && token != null && type != 'unknown') {
|
if (
|
||||||
|
!initialized &&
|
||||||
|
token != null &&
|
||||||
|
type != 'unknown' &&
|
||||||
|
type != undefined &&
|
||||||
|
connected == true
|
||||||
|
) {
|
||||||
handleFetchObjectsProperties()
|
handleFetchObjectsProperties()
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
}
|
}
|
||||||
if (value == null || type == 'unknown') {
|
if (
|
||||||
|
value == null ||
|
||||||
|
type == 'unknown' ||
|
||||||
|
type == undefined ||
|
||||||
|
connected == false
|
||||||
|
) {
|
||||||
setTreeSelectValue(null)
|
setTreeSelectValue(null)
|
||||||
setInitialLoading(false)
|
setInitialLoading(false)
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
@ -392,7 +404,8 @@ const ObjectSelect = ({
|
|||||||
initialized,
|
initialized,
|
||||||
token,
|
token,
|
||||||
fetchFullObjectIfNeeded,
|
fetchFullObjectIfNeeded,
|
||||||
type
|
type,
|
||||||
|
connected
|
||||||
])
|
])
|
||||||
|
|
||||||
const prevValuesRef = useRef({ type, masterFilter })
|
const prevValuesRef = useRef({ type, masterFilter })
|
||||||
@ -443,7 +456,7 @@ const ObjectSelect = ({
|
|||||||
|
|
||||||
const placeholder = useMemo(
|
const placeholder = useMemo(
|
||||||
() =>
|
() =>
|
||||||
type == 'unknown'
|
type == 'unknown' || type == undefined
|
||||||
? 'n/a'
|
? 'n/a'
|
||||||
: `Select a ${getModelByName(type).label.toLowerCase()}...`,
|
: `Select a ${getModelByName(type).label.toLowerCase()}...`,
|
||||||
[type]
|
[type]
|
||||||
@ -486,7 +499,7 @@ const ObjectSelect = ({
|
|||||||
{...rest}
|
{...rest}
|
||||||
value={treeSelectValue}
|
value={treeSelectValue}
|
||||||
onChange={onTreeSelectChange}
|
onChange={onTreeSelectChange}
|
||||||
disabled={disabled || type == 'unknown'}
|
disabled={disabled || type == 'unknown' || type == undefined}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -458,11 +458,16 @@ const ObjectTable = forwardRef(
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (token != null && !pages.includes(initialPage) && !initialized) {
|
if (
|
||||||
|
connected == true &&
|
||||||
|
token != null &&
|
||||||
|
!pages.includes(initialPage) &&
|
||||||
|
!initialized
|
||||||
|
) {
|
||||||
loadInitialPage()
|
loadInitialPage()
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
}
|
}
|
||||||
}, [token, loadInitialPage, initialPage, pages, initialized])
|
}, [token, loadInitialPage, initialPage, pages, initialized, connected])
|
||||||
|
|
||||||
// Watch for changes in type and masterFilter, reset component state when they change
|
// Watch for changes in type and masterFilter, reset component state when they change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -1,22 +1,21 @@
|
|||||||
import { useContext, useState, useEffect } from 'react'
|
import { useContext, useState, useEffect } from 'react'
|
||||||
import { Typography, Spin, Flex, Space, Slider, Descriptions, Tag } from 'antd'
|
import { Typography, Spin, Flex, Space, Slider, Descriptions, Tag } from 'antd'
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import { PrintServerContext } from '../context/PrintServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import merge from 'lodash/merge'
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
const PrinterMiscPanel = ({
|
const PrinterMiscPanel = ({ id, showControls = true }) => {
|
||||||
printerId,
|
const { subscribeToObjectEvent, connected, sendObjectAction } =
|
||||||
showControls = true,
|
useContext(ApiServerContext)
|
||||||
shouldUnsubscribe = true
|
|
||||||
}) => {
|
|
||||||
const [miscData, setMiscData] = useState({
|
const [miscData, setMiscData] = useState({
|
||||||
fan: {
|
fan: {
|
||||||
speed: 0,
|
speed: 0,
|
||||||
target: 0
|
target: 0
|
||||||
},
|
},
|
||||||
ledBacklight: {
|
lcdBacklight: {
|
||||||
// eslint-disable-line camelcase
|
// eslint-disable-line camelcase
|
||||||
brightness: 0
|
brightness: 0
|
||||||
},
|
},
|
||||||
@ -27,112 +26,81 @@ const PrinterMiscPanel = ({
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
filamentDetected: false
|
filamentDetected: false
|
||||||
},
|
},
|
||||||
heaterFan: {
|
coolingFan: {
|
||||||
speed: 0
|
speed: 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const [initialized, setInitialized] = useState(false)
|
|
||||||
const { printServer } = useContext(PrintServerContext)
|
|
||||||
const [fanSpeed, setFanSpeed] = useState(0)
|
const [fanSpeed, setFanSpeed] = useState(0)
|
||||||
const [ledBrightness, setLedBrightness] = useState(0)
|
const [lcdBrightness, setLcdBrightness] = useState(0)
|
||||||
const [beeperValue, setBeeperValue] = useState(0)
|
const [beeperValue, setBeeperValue] = useState(0)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const params = {
|
if (id && connected == true) {
|
||||||
printerId,
|
console.log('subscribing to misc event')
|
||||||
objects: {
|
const miscEventUnsubscribe = subscribeToObjectEvent(
|
||||||
fan: null,
|
id,
|
||||||
'filament_switch_sensor fsensor': null,
|
'printer',
|
||||||
'output_pin BEEPER_pin': null,
|
'misc',
|
||||||
'output_pin LCD_backlight_pin': null,
|
(event) => {
|
||||||
'heater_fan nozzle_cooling_fan': null
|
console.log('misc event', event)
|
||||||
|
setMiscData((prev) => {
|
||||||
|
const merged = merge({}, prev, event.data)
|
||||||
|
return merged
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return () => {
|
||||||
|
if (miscEventUnsubscribe) miscEventUnsubscribe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, [id, connected, subscribeToObjectEvent])
|
||||||
|
|
||||||
const notifyMiscStatusUpdate = (statusUpdate) => {
|
useEffect(() => {
|
||||||
if (statusUpdate?.fan) {
|
if (miscData.fan?.speed !== undefined) {
|
||||||
setMiscData((prevData) => ({
|
setFanSpeed(miscData.fan.speed)
|
||||||
...prevData,
|
|
||||||
fan: statusUpdate.fan
|
|
||||||
}))
|
|
||||||
setFanSpeed(statusUpdate.fan.speed)
|
|
||||||
}
|
|
||||||
if (statusUpdate?.['heater_fan nozzle_cooling_fan']) {
|
|
||||||
setMiscData((prevData) => ({
|
|
||||||
...prevData,
|
|
||||||
heaterFan: statusUpdate['heater_fan nozzle_cooling_fan']
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
if (statusUpdate?.['output_pin LCD_backlight_pin']) {
|
|
||||||
setMiscData((prevData) => ({
|
|
||||||
...prevData,
|
|
||||||
ledBacklight: statusUpdate['output_pin LCD_backlight_pin'].value
|
|
||||||
}))
|
|
||||||
setLedBrightness(statusUpdate['output_pin LCD_backlight_pin'].value)
|
|
||||||
}
|
|
||||||
if (statusUpdate?.['output_pin BEEPER_pin']) {
|
|
||||||
setMiscData((prevData) => ({
|
|
||||||
...prevData,
|
|
||||||
beeper: statusUpdate['output_pin BEEPER_pin']
|
|
||||||
}))
|
|
||||||
setBeeperValue(statusUpdate['output_pin BEEPER_pin'].value)
|
|
||||||
}
|
|
||||||
if (statusUpdate?.['filament_switch_sensor fsensor']) {
|
|
||||||
setMiscData((prevData) => ({
|
|
||||||
...prevData,
|
|
||||||
filamentSensor: {
|
|
||||||
enabled: statusUpdate['filament_switch_sensor fsensor'].enabled,
|
|
||||||
filamentDetected:
|
|
||||||
statusUpdate['filament_switch_sensor fsensor'].filament_detected
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (miscData.lcdBacklight?.brightness !== undefined) {
|
||||||
if (!initialized && printServer.connected) {
|
setLcdBrightness(miscData.lcdBacklight.brightness)
|
||||||
setInitialized(true)
|
|
||||||
|
|
||||||
printServer.on('connect', () => {
|
|
||||||
printServer.emit('printer.objects.subscribe', params)
|
|
||||||
printServer.emit('printer.objects.query', params)
|
|
||||||
})
|
|
||||||
|
|
||||||
printServer.emit('printer.objects.subscribe', params)
|
|
||||||
printServer.emit('printer.objects.query', params)
|
|
||||||
printServer.on('notify_status_update', notifyMiscStatusUpdate)
|
|
||||||
}
|
}
|
||||||
|
if (miscData.beeper?.value !== undefined) {
|
||||||
return () => {
|
setBeeperValue(miscData.beeper.value)
|
||||||
if (printServer.connected && initialized && shouldUnsubscribe) {
|
|
||||||
printServer.off('notify_status_update', notifyMiscStatusUpdate)
|
|
||||||
printServer.emit('printer.objects.unsubscribe', params)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [printServer, initialized, printerId, shouldUnsubscribe])
|
}, [
|
||||||
|
miscData.fan?.speed,
|
||||||
|
miscData.lcdBacklight?.brightness,
|
||||||
|
miscData.beeper?.value
|
||||||
|
])
|
||||||
|
|
||||||
const handleSetFanSpeed = (value) => {
|
const handleSetFanSpeed = (value) => {
|
||||||
if (printServer) {
|
if (id && connected == true) {
|
||||||
printServer.emit('printer.gcode.script', {
|
sendObjectAction(id, 'printer', {
|
||||||
printerId,
|
type: 'gcodeScript',
|
||||||
script: `M106 S${Math.round(value * 255)}`
|
data: {
|
||||||
|
script: `M106 S${Math.round(value * 255)}`
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSetLedBrightness = (value) => {
|
const handleSetLcdBrightness = (value) => {
|
||||||
if (printServer) {
|
if (id && connected == true) {
|
||||||
printServer.emit('printer.gcode.script', {
|
sendObjectAction(id, 'printer', {
|
||||||
printerId,
|
type: 'gcodeScript',
|
||||||
script: `SET_LED LED=led_backlight BRIGHTNESS=${value}`
|
data: {
|
||||||
|
script: `SET_LCD LCD=lcd_backlight BRIGHTNESS=${value}`
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSetBeeperValue = (value) => {
|
const handleSetBeeperValue = (value) => {
|
||||||
if (printServer) {
|
if (id && connected == true) {
|
||||||
printServer.emit('printer.gcode.script', {
|
sendObjectAction(id, 'printer', {
|
||||||
printerId,
|
type: 'gcodeScript',
|
||||||
script: `M300 S440 P200 V${Math.round(value * 100)}`
|
data: {
|
||||||
|
script: `M300 S440 P200 V${Math.round(value * 100)}`
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,14 +124,14 @@ const PrinterMiscPanel = ({
|
|||||||
tooltip={{ open: false }}
|
tooltip={{ open: false }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Text>LED Backlight: {Math.round(ledBrightness * 100)}%</Text>
|
<Text>LCD Backlight: {Math.round(lcdBrightness * 100)}%</Text>
|
||||||
<Slider
|
<Slider
|
||||||
value={ledBrightness}
|
value={lcdBrightness}
|
||||||
min={0}
|
min={0}
|
||||||
max={1}
|
max={1}
|
||||||
step={0.01}
|
step={0.01}
|
||||||
onChange={setLedBrightness}
|
onChange={setLcdBrightness}
|
||||||
onAfterChange={handleSetLedBrightness}
|
onAfterChange={handleSetLcdBrightness}
|
||||||
tooltip={{ open: false }}
|
tooltip={{ open: false }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -201,9 +169,9 @@ const PrinterMiscPanel = ({
|
|||||||
)}
|
)}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label='Nozzle Cooling Fan'>
|
<Descriptions.Item label='Nozzle Cooling Fan'>
|
||||||
{miscData.heaterFan.speed > 0 ? (
|
{miscData.coolingFan.speed > 0 ? (
|
||||||
<Text>
|
<Text>
|
||||||
Running ({Math.round(miscData.heaterFan.speed * 100)}%)
|
Running ({Math.round(miscData.coolingFan.speed * 100)}%)
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<Text>Off</Text>
|
<Text>Off</Text>
|
||||||
@ -222,9 +190,8 @@ const PrinterMiscPanel = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
PrinterMiscPanel.propTypes = {
|
PrinterMiscPanel.propTypes = {
|
||||||
printerId: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
showControls: PropTypes.bool,
|
showControls: PropTypes.bool
|
||||||
shouldUnsubscribe: PropTypes.bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PrinterMiscPanel
|
export default PrinterMiscPanel
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import {
|
|||||||
Card,
|
Card,
|
||||||
message // eslint-disable-line
|
message // eslint-disable-line
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import { PrintServerContext } from '../context/PrintServerContext'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import LevelBedIcon from '../../Icons/LevelBedIcon'
|
import LevelBedIcon from '../../Icons/LevelBedIcon'
|
||||||
import ArrowLeftIcon from '../../Icons/ArrowLeftIcon'
|
import ArrowLeftIcon from '../../Icons/ArrowLeftIcon'
|
||||||
@ -18,14 +17,12 @@ import ArrowRightIcon from '../../Icons/ArrowRightIcon'
|
|||||||
import ArrowUpIcon from '../../Icons/ArrowUpIcon'
|
import ArrowUpIcon from '../../Icons/ArrowUpIcon'
|
||||||
import ArrowDownIcon from '../../Icons/ArrowDownIcon'
|
import ArrowDownIcon from '../../Icons/ArrowDownIcon'
|
||||||
import HomeIcon from '../../Icons/HomeIcon'
|
import HomeIcon from '../../Icons/HomeIcon'
|
||||||
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
|
|
||||||
const PrinterMovementPanel = ({ printerId }) => {
|
const PrinterMovementPanel = ({ id }) => {
|
||||||
|
const { connected, sendObjectAction } = useContext(ApiServerContext)
|
||||||
const [posValue, setPosValue] = useState(10)
|
const [posValue, setPosValue] = useState(10)
|
||||||
const [rateValue, setRateValue] = useState(1000)
|
const [rateValue, setRateValue] = useState(1000)
|
||||||
const { printServer } = useContext(PrintServerContext)
|
|
||||||
|
|
||||||
//const messageApi = message.useMessage()
|
|
||||||
|
|
||||||
const handlePosRadioChange = (e) => {
|
const handlePosRadioChange = (e) => {
|
||||||
const value = e.target.value
|
const value = e.target.value
|
||||||
setPosValue(value) // Update posValue state when radio button changes
|
setPosValue(value) // Update posValue state when radio button changes
|
||||||
@ -40,20 +37,26 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleHomeAxisClick = (axis) => {
|
const handleHomeAxisClick = (axis) => {
|
||||||
if (printServer) {
|
if (id && connected == true) {
|
||||||
printServer.emit('printer.gcode.script', {
|
sendObjectAction(id, 'printer', {
|
||||||
printerId,
|
type: 'homeAxis',
|
||||||
script: `G28 ${axis}`
|
data: {
|
||||||
|
axis: axis
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMoveAxisClick = (axis, minus) => {
|
const handleMoveAxisClick = (axis, minus) => {
|
||||||
const distanceValue = !minus ? posValue * -1 : posValue
|
const distanceValue = !minus ? posValue * -1 : posValue
|
||||||
if (printServer) {
|
if (id && connected == true) {
|
||||||
printServer.emit('printer.gcode.script', {
|
sendObjectAction(id, 'printer', {
|
||||||
printerId,
|
type: 'moveAxis',
|
||||||
script: `_CLIENT_LINEAR_MOVE ${axis}=${distanceValue} F=${rateValue}`
|
data: {
|
||||||
|
axis: axis,
|
||||||
|
distance: distanceValue,
|
||||||
|
rate: rateValue
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
//sendCommand('moveAxis', { axis, pos, rate })
|
//sendCommand('moveAxis', { axis, pos, rate })
|
||||||
@ -93,7 +96,7 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ minWidth: 190 }}>
|
<div style={{ minWidth: 190 }}>
|
||||||
<Flex vertical gap={'large'}>
|
<Flex vertical gap={'large'} align='center'>
|
||||||
<Flex horizontal='true' gap='small'>
|
<Flex horizontal='true' gap='small'>
|
||||||
<Card size='small' title='XY'>
|
<Card size='small' title='XY'>
|
||||||
<Flex
|
<Flex
|
||||||
@ -211,23 +214,21 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
min={0.1}
|
min={0.1}
|
||||||
max={100}
|
max={100}
|
||||||
value={posValue}
|
value={posValue}
|
||||||
formatter={(value) => `${value} mm`}
|
suffix='mm'
|
||||||
parser={(value) => value?.replace(' mm', '')}
|
|
||||||
onChange={handlePosInputChange}
|
onChange={handlePosInputChange}
|
||||||
placeholder='10 mm'
|
placeholder='10 mm'
|
||||||
name='posInput'
|
name='posInput'
|
||||||
style={{ flexGrow: 1 }}
|
style={{ flexGrow: 1, minWidth: '125px' }}
|
||||||
/>
|
/>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
min={1}
|
min={1}
|
||||||
max={5000}
|
max={5000}
|
||||||
value={rateValue}
|
value={rateValue}
|
||||||
formatter={(value) => `${value} mm/s`}
|
suffix='mm/s'
|
||||||
parser={(value) => value?.replace(' mm/s', '')}
|
|
||||||
onChange={handleRateInputChange}
|
onChange={handleRateInputChange}
|
||||||
placeholder='100 mm/s'
|
placeholder='100 mm/s'
|
||||||
name='rateInput'
|
name='rateInput'
|
||||||
style={{ flexGrow: 1 }}
|
style={{ flexGrow: 1, minWidth: '125px' }}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -237,7 +238,7 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PrinterMovementPanel.propTypes = {
|
PrinterMovementPanel.propTypes = {
|
||||||
printerId: PropTypes.string.isRequired
|
id: PropTypes.string.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PrinterMovementPanel
|
export default PrinterMovementPanel
|
||||||
|
|||||||
@ -7,14 +7,16 @@ import {
|
|||||||
Collapse,
|
Collapse,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
Descriptions,
|
Descriptions,
|
||||||
Button
|
Button,
|
||||||
|
Divider
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import { LoadingOutlined, CaretLeftOutlined } from '@ant-design/icons'
|
import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons'
|
||||||
import { PrintServerContext } from '../context/PrintServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import BoolDisplay from './BoolDisplay'
|
import BoolDisplay from './BoolDisplay'
|
||||||
|
import merge from 'lodash/merge'
|
||||||
|
import { round } from '../utils/Utils'
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
const CustomCollapse = styled(Collapse)`
|
const CustomCollapse = styled(Collapse)`
|
||||||
@ -29,91 +31,151 @@ const CustomCollapse = styled(Collapse)`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const PrinterPositionPanel = ({
|
const PrinterPositionPanel = ({
|
||||||
printerId,
|
id,
|
||||||
showControls = true,
|
showControls = true,
|
||||||
showMoreInfo = true,
|
showMoreInfo = true
|
||||||
shouldUnsubscribe = true
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const { subscribeToObjectEvent, connected, sendObjectAction } =
|
||||||
|
useContext(ApiServerContext)
|
||||||
const [positionData, setPositionData] = useState({
|
const [positionData, setPositionData] = useState({
|
||||||
speed_factor: 1.0, // eslint-disable-line
|
speedFactor: 1.0, // eslint-disable-line
|
||||||
speed: 100,
|
speed: 100,
|
||||||
extrude_factor: 1.0, // eslint-disable-line
|
extrudeFactor: 1.0, // eslint-disable-line
|
||||||
absolute_foordinates: true, // eslint-disable-line
|
absoluteCoordinates: true, // eslint-disable-line
|
||||||
absolute_extrude: false, // eslint-disable-line
|
absoluteExtrude: false, // eslint-disable-line
|
||||||
homing_origin: [0.0, 0.0, 0.0, 0.0], // eslint-disable-line
|
homingOrigin: [0.0, 0.0, 0.0, 0.0], // eslint-disable-line
|
||||||
position: [0.0, 0.0, 0.0, 0.0],
|
toolheadPosition: [0.0, 0.0, 0.0, 0.0],
|
||||||
gcode_position: [0.0, 0.0, 0.0, 0.0] // eslint-disable-line
|
gcodePosition: [0.0, 0.0, 0.0, 0.0], // eslint-disable-line
|
||||||
|
livePosition: [0.0, 0.0, 0.0, 0.0],
|
||||||
|
maxVelocity: 1000,
|
||||||
|
maxAcceleration: 1000,
|
||||||
|
squareCornerVelocity: 100,
|
||||||
|
minCruiseRatio: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const [initialized, setInitialized] = useState(false)
|
useEffect(() => {
|
||||||
const { printServer } = useContext(PrintServerContext)
|
if (id && connected == true) {
|
||||||
const [speedFactor, setSpeedFactor] = useState(positionData.speed_factor)
|
console.log('subscribing to motion event')
|
||||||
const [extrudeFactor, setExtrudeFactor] = useState(
|
const motionEventUnsubscribe = subscribeToObjectEvent(
|
||||||
positionData.extrude_factor
|
id,
|
||||||
|
'printer',
|
||||||
|
'motion',
|
||||||
|
(event) => {
|
||||||
|
console.log('motion event', event)
|
||||||
|
setPositionData((prev) => {
|
||||||
|
const merged = merge({}, prev, event.data)
|
||||||
|
return merged
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return () => {
|
||||||
|
if (motionEventUnsubscribe) motionEventUnsubscribe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [id, connected])
|
||||||
|
const [speedFactor, setSpeedFactor] = useState(positionData.speedFactor)
|
||||||
|
const [extrudeFactor, setExtrudeFactor] = useState(positionData.extrudeFactor)
|
||||||
|
const [maxVelocity, setMaxVelocity] = useState(positionData.maxVelocity)
|
||||||
|
const [maxAcceleration, setMaxAcceleration] = useState(
|
||||||
|
positionData.maxAcceleration
|
||||||
|
)
|
||||||
|
const [squareCornerVelocity, setSquareCornerVelocity] = useState(
|
||||||
|
positionData.squareCornerVelocity
|
||||||
|
)
|
||||||
|
const [minCruiseRatio, setMinCruiseRatio] = useState(
|
||||||
|
positionData.minCruiseRatio
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const params = {
|
if (positionData.speedFactor !== undefined) {
|
||||||
printerId,
|
setSpeedFactor(positionData.speedFactor)
|
||||||
objects: {
|
|
||||||
toolhead: null,
|
|
||||||
gcode_move: null // eslint-disable-line
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (positionData.extrudeFactor !== undefined) {
|
||||||
const notifyPositionStatusUpdate = (statusUpdate) => {
|
setExtrudeFactor(positionData.extrudeFactor)
|
||||||
if (statusUpdate?.toolhead) {
|
|
||||||
setPositionData((prevData) => ({
|
|
||||||
...prevData,
|
|
||||||
...statusUpdate.toolhead
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
if (statusUpdate?.gcode_move) {
|
|
||||||
setPositionData((prevData) => ({
|
|
||||||
...prevData,
|
|
||||||
...statusUpdate.gcode_move
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (positionData.maxVelocity !== undefined) {
|
||||||
if (!initialized && printServer?.connected) {
|
setMaxVelocity(positionData.maxVelocity)
|
||||||
setInitialized(true)
|
|
||||||
|
|
||||||
printServer.on('connect', () => {
|
|
||||||
printServer.emit('printer.objects.subscribe', params)
|
|
||||||
printServer.emit('printer.objects.query', params)
|
|
||||||
})
|
|
||||||
|
|
||||||
printServer.emit('printer.objects.subscribe', params)
|
|
||||||
printServer.emit('printer.objects.query', params)
|
|
||||||
printServer.on('notify_status_update', notifyPositionStatusUpdate)
|
|
||||||
}
|
}
|
||||||
|
if (positionData.maxAcceleration !== undefined) {
|
||||||
setSpeedFactor(positionData.speed_factor)
|
setMaxAcceleration(positionData.maxAcceleration)
|
||||||
setExtrudeFactor(positionData.extrude_factor)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (printServer?.connected && initialized && shouldUnsubscribe) {
|
|
||||||
printServer.off('notify_status_update', notifyPositionStatusUpdate)
|
|
||||||
printServer.emit('printer.objects.unsubscribe', params)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [printServer, initialized, printerId, shouldUnsubscribe])
|
if (positionData.squareCornerVelocity !== undefined) {
|
||||||
|
setSquareCornerVelocity(positionData.squareCornerVelocity)
|
||||||
|
}
|
||||||
|
if (positionData.minCruiseRatio !== undefined) {
|
||||||
|
setMinCruiseRatio(positionData.minCruiseRatio)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
positionData.speedFactor,
|
||||||
|
positionData.extrudeFactor,
|
||||||
|
positionData.maxVelocity,
|
||||||
|
positionData.maxAcceleration,
|
||||||
|
positionData.squareCornerVelocity,
|
||||||
|
positionData.minCruiseRatio
|
||||||
|
])
|
||||||
|
|
||||||
const handleSetSpeedFactor = () => {
|
const handleSetSpeedFactor = () => {
|
||||||
if (printServer) {
|
if (id && connected == true) {
|
||||||
printServer.emit('printer.gcode.script', {
|
sendObjectAction(id, 'printer', {
|
||||||
printerId,
|
type: 'setSpeedFactor',
|
||||||
script: `M220 S${speedFactor * 100}`
|
data: {
|
||||||
|
speedFactor: speedFactor * 100
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSetExtrudeFactor = () => {
|
const handleSetExtrudeFactor = () => {
|
||||||
if (printServer) {
|
if (id && connected == true) {
|
||||||
printServer.emit('printer.gcode.script', {
|
sendObjectAction(id, 'printer', {
|
||||||
printerId,
|
type: 'setExtrudeFactor',
|
||||||
script: `M221 S${extrudeFactor * 100}`
|
data: {
|
||||||
|
extrudeFactor: extrudeFactor * 100
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSetMaxVelocity = () => {
|
||||||
|
if (id && connected == true) {
|
||||||
|
sendObjectAction(id, 'printer', {
|
||||||
|
type: 'setMaxVelocity',
|
||||||
|
data: {
|
||||||
|
maxVelocity: maxVelocity
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSetMaxAcceleration = () => {
|
||||||
|
if (id && connected == true) {
|
||||||
|
sendObjectAction(id, 'printer', {
|
||||||
|
type: 'setMaxAcceleration',
|
||||||
|
data: {
|
||||||
|
maxAcceleration: maxAcceleration
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSetSquareCornerVelocity = () => {
|
||||||
|
if (id && connected == true) {
|
||||||
|
sendObjectAction(id, 'printer', {
|
||||||
|
type: 'setSquareCornerVelocity',
|
||||||
|
data: {
|
||||||
|
squareCornerVelocity: squareCornerVelocity
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSetMinCruiseRatio = () => {
|
||||||
|
if (id && connected == true) {
|
||||||
|
sendObjectAction(id, 'printer', {
|
||||||
|
type: 'setMinCruiseRatio',
|
||||||
|
data: {
|
||||||
|
minCruiseRatio: minCruiseRatio
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,40 +186,156 @@ const PrinterPositionPanel = ({
|
|||||||
label: 'More Position Data',
|
label: 'More Position Data',
|
||||||
children: (
|
children: (
|
||||||
<>
|
<>
|
||||||
|
<Divider style={{ margin: '0 0 12px 0' }} />
|
||||||
<Flex vertical gap={'middle'}>
|
<Flex vertical gap={'middle'}>
|
||||||
<Text>GCode Position:</Text>
|
<Text>GCode Position:</Text>
|
||||||
<Descriptions column={1} size='small' bordered>
|
<Descriptions
|
||||||
|
column={1}
|
||||||
|
size='small'
|
||||||
|
bordered
|
||||||
|
styles={{ label: { width: '40px' } }}
|
||||||
|
>
|
||||||
<Descriptions.Item label='X'>
|
<Descriptions.Item label='X'>
|
||||||
{positionData.gcode_position[0].toFixed(2)}mm
|
{' '}
|
||||||
|
{round(positionData.gcodePosition[0], 2)}mm
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
|
||||||
<Descriptions.Item label='Y'>
|
<Descriptions.Item label='Y'>
|
||||||
{positionData.gcode_position[1].toFixed(2)}mm
|
{round(positionData.gcodePosition[1], 2)}mm
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label='Z'>
|
<Descriptions.Item label='Z'>
|
||||||
{positionData.gcode_position[2].toFixed(2)}mm
|
{round(positionData.gcodePosition[2], 2)}mm
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label='E'>
|
<Descriptions.Item label='E'>
|
||||||
{positionData.gcode_position[3].toFixed(2)}mm
|
{round(positionData.gcodePosition[3], 2)}mm
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
|
{showControls && (
|
||||||
|
<>
|
||||||
|
<Space direction='vertical' style={{ width: '100%' }}>
|
||||||
|
<Space direction='horizontal'>
|
||||||
|
<Text>Max Velocity:</Text>
|
||||||
|
<Space.Compact block size='small'>
|
||||||
|
<InputNumber
|
||||||
|
value={round(maxVelocity, 2)}
|
||||||
|
min={1}
|
||||||
|
max={10000}
|
||||||
|
step={5}
|
||||||
|
style={{ width: '125px' }}
|
||||||
|
suffix='mm/s'
|
||||||
|
onChange={(value) => setMaxVelocity(value)}
|
||||||
|
onPressEnter={handleSetMaxVelocity}
|
||||||
|
size='small'
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type='default'
|
||||||
|
style={{ width: 40 }}
|
||||||
|
onClick={handleSetMaxVelocity}
|
||||||
|
>
|
||||||
|
Set
|
||||||
|
</Button>
|
||||||
|
</Space.Compact>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Space direction='vertical' style={{ width: '100%' }}>
|
||||||
|
<Space direction='horizontal'>
|
||||||
|
<Text>Max Acceleration:</Text>
|
||||||
|
<Space.Compact block size='small'>
|
||||||
|
<InputNumber
|
||||||
|
value={round(maxAcceleration, 2)}
|
||||||
|
min={1}
|
||||||
|
max={10000}
|
||||||
|
step={5}
|
||||||
|
style={{ width: '125px' }}
|
||||||
|
suffix='mm/s²'
|
||||||
|
onChange={(value) => setMaxAcceleration(value)}
|
||||||
|
onPressEnter={handleSetMaxAcceleration}
|
||||||
|
size='small'
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type='default'
|
||||||
|
style={{ width: 40 }}
|
||||||
|
onClick={handleSetMaxAcceleration}
|
||||||
|
>
|
||||||
|
Set
|
||||||
|
</Button>
|
||||||
|
</Space.Compact>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Space direction='vertical' style={{ width: '100%' }}>
|
||||||
|
<Space direction='horizontal'>
|
||||||
|
<Text>Sqr Corner Vel:</Text>
|
||||||
|
<Space.Compact block size='small'>
|
||||||
|
<InputNumber
|
||||||
|
value={round(squareCornerVelocity, 2) || 0}
|
||||||
|
min={0.1}
|
||||||
|
max={1000}
|
||||||
|
step={0.1}
|
||||||
|
suffix='mm/s'
|
||||||
|
style={{ width: '125px' }}
|
||||||
|
onChange={(value) => setSquareCornerVelocity(value)}
|
||||||
|
onPressEnter={handleSetSquareCornerVelocity}
|
||||||
|
size='small'
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type='default'
|
||||||
|
style={{ width: 40 }}
|
||||||
|
onClick={handleSetSquareCornerVelocity}
|
||||||
|
>
|
||||||
|
Set
|
||||||
|
</Button>
|
||||||
|
</Space.Compact>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Space direction='vertical' style={{ width: '100%' }}>
|
||||||
|
<Space direction='horizontal'>
|
||||||
|
<Text>Min Cruise Ratio:</Text>
|
||||||
|
<Space.Compact block size='small'>
|
||||||
|
<InputNumber
|
||||||
|
value={round(minCruiseRatio * 100, 2) || 0}
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
step={1}
|
||||||
|
suffix='%'
|
||||||
|
style={{ width: '125px' }}
|
||||||
|
onChange={(value) => setMinCruiseRatio(value / 100)}
|
||||||
|
onPressEnter={handleSetMinCruiseRatio}
|
||||||
|
size='small'
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type='default'
|
||||||
|
style={{ width: 40 }}
|
||||||
|
onClick={handleSetMinCruiseRatio}
|
||||||
|
>
|
||||||
|
Set
|
||||||
|
</Button>
|
||||||
|
</Space.Compact>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Text>Homing Origin:</Text>
|
<Text>Homing Origin:</Text>
|
||||||
<Descriptions
|
<Descriptions
|
||||||
column={1}
|
column={1}
|
||||||
size='small'
|
size='small'
|
||||||
bordered
|
bordered
|
||||||
style={{ flexGrow: 1 }}
|
style={{ flexGrow: 1 }}
|
||||||
|
styles={{ label: { width: '40px' } }}
|
||||||
>
|
>
|
||||||
<Descriptions.Item label='X' span={1}>
|
<Descriptions.Item label='X' span={1}>
|
||||||
{positionData.homing_origin[0].toFixed(2)}mm
|
{round(positionData.homingOrigin[0], 2)}mm
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label='Y' span={1}>
|
<Descriptions.Item label='Y' span={1}>
|
||||||
{positionData.homing_origin[1].toFixed(2)}mm
|
{round(positionData.homingOrigin[1], 2)}mm
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label='Z' span={1}>
|
<Descriptions.Item label='Z' span={1}>
|
||||||
{positionData.homing_origin[2].toFixed(2)}mm
|
{round(positionData.homingOrigin[2], 2)}mm
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label='E'>
|
<Descriptions.Item label='E'>
|
||||||
{positionData.homing_origin[3].toFixed(2)}mm
|
{round(positionData.homingOrigin[3], 2)}mm
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -171,18 +349,43 @@ const PrinterPositionPanel = ({
|
|||||||
{positionData ? (
|
{positionData ? (
|
||||||
<Flex vertical gap='middle'>
|
<Flex vertical gap='middle'>
|
||||||
<Flex vertical gap={'middle'}>
|
<Flex vertical gap={'middle'}>
|
||||||
<Descriptions column={1} size='small' bordered>
|
<Descriptions
|
||||||
|
column={1}
|
||||||
|
size='small'
|
||||||
|
bordered
|
||||||
|
styles={{ label: { width: '40px' } }}
|
||||||
|
>
|
||||||
<Descriptions.Item label='X'>
|
<Descriptions.Item label='X'>
|
||||||
{positionData.position[0].toFixed(2)}mm
|
<Space>
|
||||||
|
<Text>{round(positionData.livePosition[0], 2)}mm</Text>
|
||||||
|
<Text type='secondary'>
|
||||||
|
{round(positionData.toolheadPosition[0], 2)}mm
|
||||||
|
</Text>
|
||||||
|
</Space>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label='Y'>
|
<Descriptions.Item label='Y'>
|
||||||
{positionData.position[1].toFixed(2)}mm
|
<Space>
|
||||||
|
<Text>{round(positionData.livePosition[1], 2)}mm</Text>
|
||||||
|
<Text type='secondary'>
|
||||||
|
{round(positionData.toolheadPosition[1], 2)}mm
|
||||||
|
</Text>
|
||||||
|
</Space>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label='Z'>
|
<Descriptions.Item label='Z'>
|
||||||
{positionData.position[2].toFixed(2)}mm
|
<Space>
|
||||||
|
<Text>{round(positionData.livePosition[2], 2)}mm</Text>
|
||||||
|
<Text type='secondary'>
|
||||||
|
{round(positionData.toolheadPosition[2], 2)}mm
|
||||||
|
</Text>
|
||||||
|
</Space>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label='E'>
|
<Descriptions.Item label='E'>
|
||||||
{positionData.position[3].toFixed(2)}mm
|
<Space>
|
||||||
|
<Text>{round(positionData.livePosition[3], 2)}mm</Text>
|
||||||
|
<Text type='secondary'>
|
||||||
|
{round(positionData.toolheadPosition[3], 2)}mm
|
||||||
|
</Text>
|
||||||
|
</Space>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
{showControls && (
|
{showControls && (
|
||||||
@ -192,11 +395,12 @@ const PrinterPositionPanel = ({
|
|||||||
<Text>Speed Factor:</Text>
|
<Text>Speed Factor:</Text>
|
||||||
<Space.Compact block size='small'>
|
<Space.Compact block size='small'>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
value={speedFactor.toFixed(2)}
|
value={round(speedFactor, 2)}
|
||||||
min={0.1}
|
min={0.1}
|
||||||
max={2}
|
max={2}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
style={{ width: '100px' }}
|
style={{ width: '125px' }}
|
||||||
|
suffix='%'
|
||||||
onChange={(value) => setSpeedFactor(value)}
|
onChange={(value) => setSpeedFactor(value)}
|
||||||
onPressEnter={handleSetSpeedFactor}
|
onPressEnter={handleSetSpeedFactor}
|
||||||
size='small'
|
size='small'
|
||||||
@ -215,11 +419,12 @@ const PrinterPositionPanel = ({
|
|||||||
<Text>Extrude Factor:</Text>
|
<Text>Extrude Factor:</Text>
|
||||||
<Space.Compact block size='small'>
|
<Space.Compact block size='small'>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
value={extrudeFactor.toFixed(2)}
|
value={round(extrudeFactor, 2)}
|
||||||
min={0.1}
|
min={0.1}
|
||||||
max={2}
|
max={2}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
style={{ width: '100px' }}
|
style={{ width: '125px' }}
|
||||||
|
suffix='%'
|
||||||
onChange={(value) => setExtrudeFactor(value)}
|
onChange={(value) => setExtrudeFactor(value)}
|
||||||
onPressEnter={handleSetExtrudeFactor}
|
onPressEnter={handleSetExtrudeFactor}
|
||||||
size='small'
|
size='small'
|
||||||
@ -236,14 +441,29 @@ const PrinterPositionPanel = ({
|
|||||||
</Space>
|
</Space>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Descriptions column={1} size='small' bordered>
|
<Descriptions
|
||||||
<Descriptions.Item label='Current Speed'>
|
column={1}
|
||||||
{positionData.speed.toFixed(2)}mm/s
|
size='small'
|
||||||
|
bordered
|
||||||
|
styles={{ label: { width: '125px' } }}
|
||||||
|
>
|
||||||
|
<Descriptions.Item label='Speed'>
|
||||||
|
{round(positionData.speed, 2)}mm/s
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label='Absolute Coordinates'>
|
<Descriptions.Item label='Abs Coor'>
|
||||||
{positionData ? (
|
{positionData ? (
|
||||||
<BoolDisplay
|
<BoolDisplay
|
||||||
value={positionData.absolute_coordinates}
|
value={positionData.absoluteCoordinates}
|
||||||
|
yesNo={true}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Text>n/a</Text>
|
||||||
|
)}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Abs Extrude'>
|
||||||
|
{positionData ? (
|
||||||
|
<BoolDisplay
|
||||||
|
value={positionData.absoluteExtrude}
|
||||||
yesNo={true}
|
yesNo={true}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@ -259,7 +479,7 @@ const PrinterPositionPanel = ({
|
|||||||
size='small'
|
size='small'
|
||||||
items={moreInfoItems}
|
items={moreInfoItems}
|
||||||
expandIcon={({ isActive }) => (
|
expandIcon={({ isActive }) => (
|
||||||
<CaretLeftOutlined rotate={isActive ? 90 : 0} />
|
<CaretRightOutlined rotate={isActive ? 90 : 0} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -274,10 +494,9 @@ const PrinterPositionPanel = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
PrinterPositionPanel.propTypes = {
|
PrinterPositionPanel.propTypes = {
|
||||||
printerId: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
showControls: PropTypes.bool,
|
showControls: PropTypes.bool,
|
||||||
showMoreInfo: PropTypes.bool,
|
showMoreInfo: PropTypes.bool
|
||||||
shouldUnsubscribe: PropTypes.bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PrinterPositionPanel
|
export default PrinterPositionPanel
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Collapse,
|
Collapse,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
Button
|
Button,
|
||||||
|
Divider
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons'
|
import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -113,6 +114,7 @@ const PrinterTemperaturePanel = ({
|
|||||||
label: 'More Temperature Data',
|
label: 'More Temperature Data',
|
||||||
children: (
|
children: (
|
||||||
<>
|
<>
|
||||||
|
<Divider style={{ margin: '0 0 12px 0' }} />
|
||||||
<Flex vertical gap={0}>
|
<Flex vertical gap={0}>
|
||||||
<Text>
|
<Text>
|
||||||
Hot End Power:{' '}
|
Hot End Power:{' '}
|
||||||
|
|||||||
@ -49,7 +49,7 @@ const StateDisplay = ({
|
|||||||
strokeColor={
|
strokeColor={
|
||||||
orangeProgressTypes.includes(currentState.type) ? 'orange' : ''
|
orangeProgressTypes.includes(currentState.type) ? 'orange' : ''
|
||||||
}
|
}
|
||||||
style={{ width: '150px', marginBottom: '2px' }}
|
style={{ maxWidth: '200px', marginBottom: '2px' }}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|||||||
@ -307,7 +307,10 @@ const AuthProvider = ({ children }) => {
|
|||||||
setExpiresAt(null)
|
setExpiresAt(null)
|
||||||
setUserProfile(null)
|
setUserProfile(null)
|
||||||
clearAuthCookies()
|
clearAuthCookies()
|
||||||
setShowUnauthorizedModal(true)
|
setAuthenticated(false)
|
||||||
|
if (showSessionExpiredModal == false) {
|
||||||
|
setShowUnauthorizedModal(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const refreshToken = useCallback(async () => {
|
const refreshToken = useCallback(async () => {
|
||||||
@ -489,7 +492,6 @@ const AuthProvider = ({ children }) => {
|
|||||||
authCode == null
|
authCode == null
|
||||||
) {
|
) {
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
console.log('Showing unauth', token, authCode)
|
|
||||||
setShowUnauthorizedModal(true)
|
setShowUnauthorizedModal(true)
|
||||||
setAuthenticated(false)
|
setAuthenticated(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -384,7 +384,7 @@ const SpotlightProvider = ({ children }) => {
|
|||||||
const getModeDescription = (mode) => {
|
const getModeDescription = (mode) => {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case ':':
|
case ':':
|
||||||
return 'ID lookup'
|
return 'ID/Reference lookup'
|
||||||
case '?':
|
case '?':
|
||||||
return 'Filter'
|
return 'Filter'
|
||||||
case '^':
|
case '^':
|
||||||
|
|||||||
@ -30,5 +30,9 @@ export function timeStringToMinutes(timeString) {
|
|||||||
return Math.floor(totalMinutes)
|
return Math.floor(totalMinutes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function round(num, decimals) {
|
||||||
|
return Math.round(num * 10 ** decimals) / 10 ** decimals
|
||||||
|
}
|
||||||
|
|
||||||
// Re-export the functions for backward compatibility
|
// Re-export the functions for backward compatibility
|
||||||
export {}
|
export {}
|
||||||
|
|||||||
6
src/components/Icons/TaxRateIcon.jsx
Normal file
6
src/components/Icons/TaxRateIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import CustomIconSvg from '../../../assets/icons/taxrateicon.svg?react'
|
||||||
|
|
||||||
|
const TaxRateIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default TaxRateIcon
|
||||||
6
src/components/Icons/TaxRecordIcon.jsx
Normal file
6
src/components/Icons/TaxRecordIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import CustomIconSvg from '../../../assets/icons/taxrecordicon.svg?react'
|
||||||
|
|
||||||
|
const TaxRecordIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default TaxRecordIcon
|
||||||
@ -26,6 +26,8 @@ import { DocumentSize } from './models/DocumentSize.js'
|
|||||||
import { DocumentTemplate } from './models/DocumentTemplate.js'
|
import { DocumentTemplate } from './models/DocumentTemplate.js'
|
||||||
import { DocumentPrinter } from './models/DocumentPrinter.js'
|
import { DocumentPrinter } from './models/DocumentPrinter.js'
|
||||||
import { DocumentJob } from './models/DocumentJob.js'
|
import { DocumentJob } from './models/DocumentJob.js'
|
||||||
|
import { TaxRate } from './models/TaxRate.js'
|
||||||
|
import { TaxRecord } from './models/TaxRecord.js'
|
||||||
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
|
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
|
||||||
|
|
||||||
export const objectModels = [
|
export const objectModels = [
|
||||||
@ -56,7 +58,9 @@ export const objectModels = [
|
|||||||
DocumentSize,
|
DocumentSize,
|
||||||
DocumentTemplate,
|
DocumentTemplate,
|
||||||
DocumentPrinter,
|
DocumentPrinter,
|
||||||
DocumentJob
|
DocumentJob,
|
||||||
|
TaxRate,
|
||||||
|
TaxRecord
|
||||||
]
|
]
|
||||||
|
|
||||||
// Re-export individual models for direct access
|
// Re-export individual models for direct access
|
||||||
@ -88,7 +92,9 @@ export {
|
|||||||
DocumentSize,
|
DocumentSize,
|
||||||
DocumentTemplate,
|
DocumentTemplate,
|
||||||
DocumentPrinter,
|
DocumentPrinter,
|
||||||
DocumentJob
|
DocumentJob,
|
||||||
|
TaxRate,
|
||||||
|
TaxRecord
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getModelByName(name, ignoreCase = false) {
|
export function getModelByName(name, ignoreCase = false) {
|
||||||
|
|||||||
@ -41,8 +41,9 @@ export const Job = {
|
|||||||
'_id',
|
'_id',
|
||||||
'gcodeFile',
|
'gcodeFile',
|
||||||
'gcodeFile._id',
|
'gcodeFile._id',
|
||||||
'state',
|
|
||||||
'quantity',
|
'quantity',
|
||||||
|
'state',
|
||||||
|
|
||||||
'createdAt'
|
'createdAt'
|
||||||
],
|
],
|
||||||
filters: ['state', '_id', 'gcodeFile._id', 'quantity'],
|
filters: ['state', '_id', 'gcodeFile._id', 'quantity'],
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import PartIcon from '../../components/Icons/PartIcon'
|
|||||||
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
import CheckIcon from '../../components/Icons/CheckIcon'
|
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||||
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||||
|
|
||||||
export const Part = {
|
export const Part = {
|
||||||
name: 'part',
|
name: 'part',
|
||||||
label: 'Part',
|
label: 'Part',
|
||||||
@ -132,28 +131,47 @@ export const Part = {
|
|||||||
step: 0.01
|
step: 0.01
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'priceMode',
|
name: 'costWithTax',
|
||||||
label: 'Price Mode',
|
label: 'Cost with Tax',
|
||||||
|
columnWidth: 150,
|
||||||
required: true,
|
required: true,
|
||||||
type: 'priceMode',
|
readOnly: true,
|
||||||
disabled: (objectData) => {
|
type: 'number',
|
||||||
return objectData.globalPricing == true
|
prefix: '£',
|
||||||
|
min: 0,
|
||||||
|
step: 0.01,
|
||||||
|
value: (objectData) => {
|
||||||
|
if (objectData?.costTaxRate?.rateType == 'percentage') {
|
||||||
|
return (
|
||||||
|
(
|
||||||
|
objectData?.cost *
|
||||||
|
(1 + objectData?.costTaxRate?.rate / 100)
|
||||||
|
).toFixed(2) || undefined
|
||||||
|
)
|
||||||
|
} else if (objectData?.costTaxRate?.rateType == 'amount') {
|
||||||
|
return (
|
||||||
|
(objectData?.cost + objectData?.costTaxRate?.rate).toFixed(2) ||
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return objectData?.cost || undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'margin',
|
name: 'costTaxRate',
|
||||||
label: 'Margin',
|
label: 'Cost Tax Rate',
|
||||||
required: true,
|
required: true,
|
||||||
type: 'number',
|
type: 'object',
|
||||||
disabled: (objectData) => {
|
objectType: 'taxRate'
|
||||||
return (
|
},
|
||||||
objectData.globalPricing == true || objectData.priceMode == 'amount'
|
{
|
||||||
)
|
name: 'costTaxRate._id',
|
||||||
},
|
label: 'Cost Tax Rate ID',
|
||||||
suffix: '%',
|
readOnly: true,
|
||||||
min: 0,
|
type: 'id',
|
||||||
max: 100,
|
showHyperlink: true,
|
||||||
step: 0.01
|
objectType: 'taxRate'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'price',
|
name: 'price',
|
||||||
@ -163,15 +181,87 @@ export const Part = {
|
|||||||
prefix: '£',
|
prefix: '£',
|
||||||
min: 0,
|
min: 0,
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
roundNumber: 2,
|
readOnly: (objectData) => {
|
||||||
|
return objectData?.priceMode == 'margin'
|
||||||
|
},
|
||||||
value: (objectData) => {
|
value: (objectData) => {
|
||||||
if (objectData?.priceMode == 'margin') {
|
if (
|
||||||
return objectData?.cost * (1 + objectData?.margin / 100) || undefined
|
objectData?.priceMode == 'margin' &&
|
||||||
|
objectData?.margin !== undefined &&
|
||||||
|
objectData?.margin !== null
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
(objectData?.cost * (1 + objectData?.margin / 100)).toFixed(2) ||
|
||||||
|
undefined
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return objectData?.price || undefined
|
return objectData?.price || undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'priceWithTax',
|
||||||
|
label: 'Price with Tax',
|
||||||
|
columnWidth: 150,
|
||||||
|
required: true,
|
||||||
|
readOnly: true,
|
||||||
|
type: 'number',
|
||||||
|
prefix: '£',
|
||||||
|
min: 0,
|
||||||
|
step: 0.01,
|
||||||
|
value: (objectData) => {
|
||||||
|
if (objectData?.priceTaxRate?.rateType == 'percentage') {
|
||||||
|
return (
|
||||||
|
(
|
||||||
|
objectData?.price *
|
||||||
|
(1 + objectData?.priceTaxRate?.rate / 100)
|
||||||
|
).toFixed(2) || undefined
|
||||||
|
)
|
||||||
|
} else if (objectData?.priceTaxRate?.rateType == 'amount') {
|
||||||
|
return (
|
||||||
|
(objectData?.price + objectData?.priceTaxRate?.rate).toFixed(2) ||
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return objectData?.price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'priceMode',
|
||||||
|
label: 'Price Mode',
|
||||||
|
required: true,
|
||||||
|
type: 'priceMode'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'margin',
|
||||||
|
label: 'Margin',
|
||||||
|
required: true,
|
||||||
|
type: 'number',
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData.priceMode == 'amount'
|
||||||
|
},
|
||||||
|
suffix: '%',
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 0.01
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'priceTaxRate',
|
||||||
|
label: 'Price Tax Rate',
|
||||||
|
required: true,
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'taxRate'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'priceTaxRate._id',
|
||||||
|
label: 'Price Tax Rate ID',
|
||||||
|
readOnly: true,
|
||||||
|
type: 'id',
|
||||||
|
showHyperlink: true,
|
||||||
|
objectType: 'taxRate'
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'file',
|
name: 'file',
|
||||||
label: 'File',
|
label: 'File',
|
||||||
|
|||||||
@ -59,21 +59,6 @@ export const PurchaseOrder = {
|
|||||||
showHyperlink: true,
|
showHyperlink: true,
|
||||||
objectType: 'vendor'
|
objectType: 'vendor'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'courierService',
|
|
||||||
label: 'Courier Service',
|
|
||||||
required: true,
|
|
||||||
type: 'object',
|
|
||||||
objectType: 'courierService'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'courierService._id',
|
|
||||||
label: 'Courier Service ID',
|
|
||||||
readOnly: true,
|
|
||||||
type: 'id',
|
|
||||||
showHyperlink: true,
|
|
||||||
objectType: 'courierService'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'items',
|
name: 'items',
|
||||||
label: 'Order Items',
|
label: 'Order Items',
|
||||||
@ -108,29 +93,97 @@ export const PurchaseOrder = {
|
|||||||
return objectData?.item?._id
|
return objectData?.item?._id
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'quantity',
|
name: 'itemCost',
|
||||||
label: 'Quantity',
|
label: 'Item Cost',
|
||||||
type: 'number',
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'price',
|
|
||||||
label: 'Price',
|
|
||||||
type: 'number',
|
type: 'number',
|
||||||
required: true,
|
required: true,
|
||||||
prefix: '£',
|
prefix: '£',
|
||||||
min: 0,
|
min: 0,
|
||||||
step: 0.01,
|
step: 0.01,
|
||||||
|
columnWidth: 150,
|
||||||
value: (objectData) => {
|
value: (objectData) => {
|
||||||
if (objectData?.price == undefined) {
|
if (objectData?.item) {
|
||||||
console.log('PurchaseOrder.js', objectData)
|
return objectData?.item?.cost || undefined
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'quantity',
|
||||||
|
label: 'Quantity',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
columnWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'totalCost',
|
||||||
|
label: 'Total Cost',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
prefix: '£',
|
||||||
|
min: 0,
|
||||||
|
step: 0.01,
|
||||||
|
columnWidth: 150,
|
||||||
|
value: (objectData) => {
|
||||||
|
return (
|
||||||
|
(objectData?.itemCost || 0) * (objectData?.quantity || 0) ||
|
||||||
|
undefined
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'taxRate',
|
||||||
|
label: 'Tax Rate',
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'taxRate',
|
||||||
|
value: (objectData) => {
|
||||||
|
if (objectData?.item) {
|
||||||
|
console.log(objectData?.item)
|
||||||
|
return objectData?.item?.costTaxRate || undefined
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'taxRate._id',
|
||||||
|
label: 'Tax Rate ID',
|
||||||
|
type: 'id',
|
||||||
|
showHyperlink: true,
|
||||||
|
objectType: 'taxRate',
|
||||||
|
value: (objectData) => {
|
||||||
|
return objectData?.taxRate?._id || undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'totalCostWithTax',
|
||||||
|
label: 'Total Cost With Tax',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
readOnly: true,
|
||||||
|
prefix: '£',
|
||||||
|
min: 0,
|
||||||
|
step: 0.01,
|
||||||
|
columnWidth: 175,
|
||||||
|
value: (objectData) => {
|
||||||
|
if (objectData?.taxRate?.rateType == 'percentage') {
|
||||||
return (
|
return (
|
||||||
(objectData?.item?.cost || 0) * (objectData?.quantity || 0) ||
|
(
|
||||||
undefined
|
(objectData?.totalCost || 0) *
|
||||||
|
(1 + objectData?.taxRate?.rate / 100)
|
||||||
|
).toFixed(2) || undefined
|
||||||
|
)
|
||||||
|
} else if (objectData?.taxRate?.rateType == 'amount') {
|
||||||
|
return (
|
||||||
|
(
|
||||||
|
(objectData?.totalCost || 0) + objectData?.taxRate?.rate
|
||||||
|
).toFixed(2) || undefined
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return objectData?.part?.price || undefined
|
return objectData?.totalCost || undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,13 +202,27 @@ export const PurchaseOrder = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'totalPrice',
|
name: 'totalCost',
|
||||||
label: 'Total',
|
label: 'Total',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
prefix: '£',
|
prefix: '£',
|
||||||
property: 'price',
|
property: 'totalCost',
|
||||||
value: (objectData) => {
|
value: (objectData) => {
|
||||||
return objectData?.items?.reduce((acc, item) => acc + item.price, 0)
|
return objectData?.items
|
||||||
|
?.reduce((acc, item) => acc + (item.totalCost || 0), 0)
|
||||||
|
.toFixed(2)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'totalCostWithTax',
|
||||||
|
label: 'Total',
|
||||||
|
type: 'number',
|
||||||
|
prefix: '£',
|
||||||
|
property: 'totalCostWithTax',
|
||||||
|
value: (objectData) => {
|
||||||
|
return objectData?.items
|
||||||
|
?.reduce((acc, item) => acc + (item.totalCostWithTax || 0), 0)
|
||||||
|
.toFixed(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -68,6 +68,36 @@ export const SubJob = {
|
|||||||
readOnly: true,
|
readOnly: true,
|
||||||
columnWidth: 175
|
columnWidth: 175
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'moonrakerJobId',
|
||||||
|
label: 'Moonraker Job ID',
|
||||||
|
type: 'miscId',
|
||||||
|
columnWidth: 140,
|
||||||
|
showCopy: true
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'startedAt',
|
||||||
|
label: 'Started At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'createdPartStock',
|
||||||
|
label: 'Created Part Stock',
|
||||||
|
type: 'bool',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'finishedAt',
|
||||||
|
label: 'Finished At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'job',
|
name: 'job',
|
||||||
label: 'Job',
|
label: 'Job',
|
||||||
@ -83,27 +113,6 @@ export const SubJob = {
|
|||||||
objectType: 'job'
|
objectType: 'job'
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
name: 'startedAt',
|
|
||||||
label: 'Started At',
|
|
||||||
type: 'dateTime',
|
|
||||||
readOnly: true
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: 'moonrakerJobId',
|
|
||||||
label: 'Moonraker Job ID',
|
|
||||||
type: 'miscId',
|
|
||||||
columnWidth: 140,
|
|
||||||
showCopy: true
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: 'finishedAt',
|
|
||||||
label: 'Finished At',
|
|
||||||
type: 'dateTime',
|
|
||||||
readOnly: true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'printer',
|
name: 'printer',
|
||||||
label: 'Printer',
|
label: 'Printer',
|
||||||
|
|||||||
181
src/database/models/TaxRate.js
Normal file
181
src/database/models/TaxRate.js
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import TaxRateIcon from '../../components/Icons/TaxRateIcon'
|
||||||
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||||
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||||
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
|
import BinIcon from '../../components/Icons/BinIcon'
|
||||||
|
|
||||||
|
export const TaxRate = {
|
||||||
|
name: 'taxRate',
|
||||||
|
label: 'Tax Rate',
|
||||||
|
prefix: 'TXR',
|
||||||
|
icon: TaxRateIcon,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
label: 'Info',
|
||||||
|
default: true,
|
||||||
|
row: true,
|
||||||
|
icon: InfoCircleIcon,
|
||||||
|
url: (_id) => `/dashboard/management/taxrates/info?taxRateId=${_id}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reload',
|
||||||
|
label: 'Reload',
|
||||||
|
icon: ReloadIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/taxrates/info?taxRateId=${_id}&action=reload`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/taxrates/info?taxRateId=${_id}&action=edit`,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'finishEdit',
|
||||||
|
label: 'Save Edits',
|
||||||
|
icon: CheckIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/taxrates/info?taxRateId=${_id}&action=finishEdit`,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cancelEdit',
|
||||||
|
label: 'Cancel Edits',
|
||||||
|
icon: XMarkIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/taxrates/info?taxRateId=${_id}&action=cancelEdit`,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
name: 'delete',
|
||||||
|
label: 'Delete',
|
||||||
|
icon: BinIcon,
|
||||||
|
danger: true,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/taxrates/info?taxRateId=${_id}&action=delete`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
'name',
|
||||||
|
'_id',
|
||||||
|
'rate',
|
||||||
|
'rateType',
|
||||||
|
'active',
|
||||||
|
'country',
|
||||||
|
'createdAt'
|
||||||
|
],
|
||||||
|
filters: ['name', '_id', 'rate', 'rateType', 'active', 'country'],
|
||||||
|
sorters: [
|
||||||
|
'name',
|
||||||
|
'rate',
|
||||||
|
'rateType',
|
||||||
|
'active',
|
||||||
|
'country',
|
||||||
|
'createdAt',
|
||||||
|
'_id'
|
||||||
|
],
|
||||||
|
group: ['country', 'rateType'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: '_id',
|
||||||
|
label: 'ID',
|
||||||
|
columnFixed: 'left',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'taxRate',
|
||||||
|
showCopy: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
label: 'Created At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
columnFixed: 'left',
|
||||||
|
required: true,
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'active',
|
||||||
|
label: 'Active',
|
||||||
|
required: true,
|
||||||
|
type: 'bool',
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'effectiveFrom',
|
||||||
|
label: 'Effective From',
|
||||||
|
type: 'date',
|
||||||
|
readOnly: false,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rate',
|
||||||
|
label: 'Rate',
|
||||||
|
required: true,
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
step: 0.01,
|
||||||
|
prefix: (objectData) => {
|
||||||
|
return objectData?.rateType == 'amount' ? '£' : undefined
|
||||||
|
},
|
||||||
|
suffix: (objectData) => {
|
||||||
|
return objectData?.rateType == 'percentage' ? '%' : undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'effectiveTo',
|
||||||
|
label: 'Effective To',
|
||||||
|
type: 'date',
|
||||||
|
readOnly: false,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'rateType',
|
||||||
|
label: 'Rate Type',
|
||||||
|
required: true,
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: 'Percentage', value: 'percentage' },
|
||||||
|
{ label: 'Amount', value: 'amount' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'country',
|
||||||
|
label: 'Country',
|
||||||
|
type: 'country',
|
||||||
|
readOnly: false,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
label: 'Description',
|
||||||
|
type: 'text',
|
||||||
|
readOnly: false,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
174
src/database/models/TaxRecord.js
Normal file
174
src/database/models/TaxRecord.js
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
import TaxRecordIcon from '../../components/Icons/TaxRecordIcon'
|
||||||
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||||
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||||
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
|
import BinIcon from '../../components/Icons/BinIcon'
|
||||||
|
|
||||||
|
export const TaxRecord = {
|
||||||
|
name: 'taxRecord',
|
||||||
|
label: 'Tax Record',
|
||||||
|
prefix: 'TXR',
|
||||||
|
icon: TaxRecordIcon,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
label: 'Info',
|
||||||
|
default: true,
|
||||||
|
row: true,
|
||||||
|
icon: InfoCircleIcon,
|
||||||
|
url: (_id) => `/dashboard/management/taxrecords/info?taxRecordId=${_id}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reload',
|
||||||
|
label: 'Reload',
|
||||||
|
icon: ReloadIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/taxrecords/info?taxRecordId=${_id}&action=reload`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/taxrecords/info?taxRecordId=${_id}&action=edit`,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'finishEdit',
|
||||||
|
label: 'Save Edits',
|
||||||
|
icon: CheckIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/taxrecords/info?taxRecordId=${_id}&action=finishEdit`,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cancelEdit',
|
||||||
|
label: 'Cancel Edits',
|
||||||
|
icon: XMarkIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/taxrecords/info?taxRecordId=${_id}&action=cancelEdit`,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
name: 'delete',
|
||||||
|
label: 'Delete',
|
||||||
|
icon: BinIcon,
|
||||||
|
danger: true,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/taxrecords/info?taxRecordId=${_id}&action=delete`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
'_id',
|
||||||
|
'taxRate',
|
||||||
|
'transactionType',
|
||||||
|
'transaction',
|
||||||
|
'amount',
|
||||||
|
'taxAmount',
|
||||||
|
'transactionDate',
|
||||||
|
'createdAt'
|
||||||
|
],
|
||||||
|
filters: ['taxRate', 'transactionType', 'transaction', 'transactionDate'],
|
||||||
|
sorters: ['transactionDate', 'amount', 'taxAmount', 'createdAt', '_id'],
|
||||||
|
group: ['transactionType'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: '_id',
|
||||||
|
label: 'ID',
|
||||||
|
columnFixed: 'left',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'taxRecord',
|
||||||
|
showCopy: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
label: 'Created At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'taxRate',
|
||||||
|
label: 'Tax Rate',
|
||||||
|
required: true,
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'taxRate'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'taxRate._id',
|
||||||
|
label: 'Tax Rate ID',
|
||||||
|
readOnly: true,
|
||||||
|
type: 'id',
|
||||||
|
showHyperlink: true,
|
||||||
|
objectType: 'taxRate'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'transactionType',
|
||||||
|
label: 'Transaction Type',
|
||||||
|
required: true,
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: 'Purchase Order', value: 'purchaseOrder' },
|
||||||
|
{ label: 'Sales Order', value: 'salesOrder' },
|
||||||
|
{ label: 'Other', value: 'other' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'transaction',
|
||||||
|
label: 'Transaction',
|
||||||
|
required: true,
|
||||||
|
type: 'object',
|
||||||
|
objectType: (objectData) => {
|
||||||
|
return objectData?.transactionType || 'purchaseOrder'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'transaction._id',
|
||||||
|
label: 'Transaction ID',
|
||||||
|
readOnly: true,
|
||||||
|
type: 'id',
|
||||||
|
showHyperlink: true,
|
||||||
|
objectType: (objectData) => {
|
||||||
|
return objectData?.transactionType || 'purchaseOrder'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'amount',
|
||||||
|
label: 'Amount',
|
||||||
|
required: true,
|
||||||
|
type: 'currency',
|
||||||
|
min: 0,
|
||||||
|
step: 0.01
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'taxAmount',
|
||||||
|
label: 'Tax Amount',
|
||||||
|
required: true,
|
||||||
|
type: 'currency',
|
||||||
|
min: 0,
|
||||||
|
step: 0.01
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'transactionDate',
|
||||||
|
label: 'Transaction Date',
|
||||||
|
required: true,
|
||||||
|
type: 'date',
|
||||||
|
default: () => new Date()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -33,6 +33,10 @@ import DocumentJobInfo from '../components/Dashboard/Management/DocumentJobs/Doc
|
|||||||
import DocumentTemplateDesign from '../components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx'
|
import DocumentTemplateDesign from '../components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx'
|
||||||
import Files from '../components/Dashboard/Management/Files.jsx'
|
import Files from '../components/Dashboard/Management/Files.jsx'
|
||||||
import FileInfo from '../components/Dashboard/Management/Files/FileInfo.jsx'
|
import FileInfo from '../components/Dashboard/Management/Files/FileInfo.jsx'
|
||||||
|
import TaxRates from '../components/Dashboard/Management/TaxRates.jsx'
|
||||||
|
import TaxRateInfo from '../components/Dashboard/Management/TaxRates/TaxRateInfo.jsx'
|
||||||
|
import TaxRecords from '../components/Dashboard/Management/TaxRecords.jsx'
|
||||||
|
import TaxRecordInfo from '../components/Dashboard/Management/TaxRecords/TaxRecordInfo.jsx'
|
||||||
|
|
||||||
const ManagementRoutes = [
|
const ManagementRoutes = [
|
||||||
<Route key='filaments' path='management/filaments' element={<Filaments />} />,
|
<Route key='filaments' path='management/filaments' element={<Filaments />} />,
|
||||||
@ -147,7 +151,23 @@ const ManagementRoutes = [
|
|||||||
/>,
|
/>,
|
||||||
<Route key='users' path='management/users' element={<Users />} />,
|
<Route key='users' path='management/users' element={<Users />} />,
|
||||||
<Route key='settings' path='management/settings' element={<Settings />} />,
|
<Route key='settings' path='management/settings' element={<Settings />} />,
|
||||||
<Route key='auditlogs' path='management/auditlogs' element={<AuditLogs />} />
|
<Route key='auditlogs' path='management/auditlogs' element={<AuditLogs />} />,
|
||||||
|
<Route key='taxrates' path='management/taxrates' element={<TaxRates />} />,
|
||||||
|
<Route
|
||||||
|
key='taxrates-info'
|
||||||
|
path='management/taxrates/info'
|
||||||
|
element={<TaxRateInfo />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='taxrecords'
|
||||||
|
path='management/taxrecords'
|
||||||
|
element={<TaxRecords />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='taxrecords-info'
|
||||||
|
path='management/taxrecords/info'
|
||||||
|
element={<TaxRecordInfo />}
|
||||||
|
/>
|
||||||
]
|
]
|
||||||
|
|
||||||
export default ManagementRoutes
|
export default ManagementRoutes
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user