Add Payment and Fulfillment Policy Components with SVG Icons
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Introduced new components for managing Payment Policies and Fulfillment Policies, enhancing the dashboard's functionality for financial management. - Added corresponding SVG icons for Payment Policy, Fulfillment Policy, and Return Policy to improve visual representation. - Updated App.css with new styles for file gallery previews, ensuring a cohesive layout and user experience across the dashboard. - Implemented new object forms and tables for Payment Policies, allowing for better data handling and user interaction.
This commit is contained in:
parent
6cc6bf9a79
commit
26132d206c
14
assets/icons/fulfillmentpolicyicon.svg
Normal file
14
assets/icons/fulfillmentpolicyicon.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.607884,0,0,0.607884,2,6.470704)">
|
||||||
|
<path d="M50.153,64.047L37.296,64.047C35.825,69.222 31.078,73.031 25.453,73.031C19.816,73.031 15.034,69.222 13.552,64.047L10.547,64.047C3.781,64.047 0,60.266 0,53.516L0,11.688C0,4.938 3.781,1.141 10.547,1.141L57.859,1.141C64.609,1.141 68.391,4.938 68.391,11.688L68.391,18.422L76.766,18.422C79.761,18.422 81.953,19.046 83.846,20.626C83.618,20.616 83.389,20.612 83.159,20.611L83.154,20.611C78.507,20.611 74.687,22.221 70.737,26.132L70.727,26.142L60.359,36.468L60.359,12.328C60.359,10.141 59.281,9.141 57.172,9.141L11.203,9.141C9.078,9.141 8.031,10.141 8.031,12.328L8.031,52.859C8.031,55.062 9.078,56.031 11.203,56.031L13.984,56.031C15.828,51.524 20.28,48.344 25.453,48.344C30.614,48.344 35.037,51.524 36.867,56.031L47.85,56.031C47.915,58.873 48.681,61.577 50.153,64.047ZM25.453,66.422C28.609,66.422 31.156,63.828 31.156,60.656C31.156,57.5 28.609,54.906 25.453,54.906C22.281,54.906 19.688,57.5 19.688,60.656C19.688,63.828 22.281,66.422 25.453,66.422Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.319305,0,0,0.319305,12.718629,15.454198)">
|
||||||
|
<rect x="0" y="0" width="61.719" height="58.219" style="fill-opacity:0;"/>
|
||||||
|
<path d="M24.594,58.219C27.5,58.219 29.781,57.156 31.312,54.875L59.969,12.312C61.062,10.719 61.531,9.062 61.531,7.625C61.531,3.5 58.25,0.375 54,0.375C51.125,0.375 49.281,1.406 47.5,4.156L24.469,40.125L13.062,27C11.562,25.281 9.812,24.469 7.438,24.469C3.156,24.469 0,27.594 0,31.75C0,33.625 0.5,35.125 2.125,36.969L18.188,55.406C19.906,57.344 21.969,58.219 24.594,58.219Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.515011,0,0,0.515011,34.08479,22)">
|
||||||
|
<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 |
10
assets/icons/paymentpolicyicon.svg
Normal file
10
assets/icons/paymentpolicyicon.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.81149,0,0,0.81149,2,10.044134)">
|
||||||
|
<path d="M42.914,54.093L10.251,54.093C3.506,54.093 0,50.638 0,43.969L0,10.169C0,3.501 3.506,0.02 10.251,0.02L63.687,0.02C70.463,0.02 73.938,3.501 73.938,10.169L73.938,20.595C73.272,16.95 70.992,13.955 67.816,12.338L67.816,10.709C67.816,7.667 66.186,6.142 63.332,6.142L10.606,6.142C7.701,6.142 6.122,7.667 6.122,10.709L6.122,12.998L55.627,12.998C54.744,13.579 53.871,14.299 52.989,15.172L52.981,15.179L47.835,20.305L6.122,20.305L6.122,43.429C6.122,46.471 7.701,47.97 10.606,47.97L41.376,47.97L44.616,51.219C43.884,52.13 43.321,53.086 42.914,54.093ZM14.864,42.322C13.02,42.322 11.801,41.078 11.801,39.328L11.801,33.547C11.801,31.766 13.02,30.547 14.864,30.547L22.52,30.547C24.364,30.547 25.582,31.766 25.582,33.547L25.582,39.328C25.582,41.078 24.364,42.322 22.52,42.322L14.864,42.322Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.515011,0,0,0.515011,34.08479,22)">
|
||||||
|
<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: 2.6 KiB |
16
assets/icons/returnpolicyicon.svg
Normal file
16
assets/icons/returnpolicyicon.svg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?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.607884,0,0,0.607884,2,6.470704)">
|
||||||
|
<path d="M50.153,64.047L37.296,64.047C35.825,69.222 31.078,73.031 25.453,73.031C19.816,73.031 15.034,69.222 13.552,64.047L10.547,64.047C3.781,64.047 0,60.266 0,53.516L0,11.688C0,4.938 3.781,1.141 10.547,1.141L57.859,1.141C64.609,1.141 68.391,4.938 68.391,11.688L68.391,18.422L76.766,18.422C79.761,18.422 81.953,19.046 83.846,20.626C83.618,20.616 83.389,20.612 83.159,20.611L83.154,20.611C78.507,20.611 74.687,22.221 70.737,26.132L70.727,26.142L60.359,36.468L60.359,12.328C60.359,10.141 59.281,9.141 57.172,9.141L11.203,9.141C9.078,9.141 8.031,10.141 8.031,12.328L8.031,52.859C8.031,55.062 9.078,56.031 11.203,56.031L13.984,56.031C15.828,51.524 20.28,48.344 25.453,48.344C30.614,48.344 35.037,51.524 36.867,56.031L47.85,56.031C47.915,58.873 48.681,61.577 50.153,64.047ZM25.453,66.422C28.609,66.422 31.156,63.828 31.156,60.656C31.156,57.5 28.609,54.906 25.453,54.906C22.281,54.906 19.688,57.5 19.688,60.656C19.688,63.828 22.281,66.422 25.453,66.422Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.33037,0,0,0.33037,12.639581,15.454198)">
|
||||||
|
<path d="M53.25,58.219C54.656,59.562 56.219,60.219 58.125,60.219C62.062,60.219 65.312,56.969 65.312,53.125C65.312,51.156 64.469,49.312 62.938,47.812L43.562,29.156L43.562,30.906L62.938,12.375C64.5,10.844 65.312,9.031 65.312,7.094C65.312,3.219 62.062,0 58.125,0C56.188,0 54.594,0.625 53.25,2L30.719,24C28.781,25.969 27.969,27.75 27.906,30.094C27.906,32.406 28.75,34.219 30.719,36.188L53.25,58.219Z" style="fill-rule:nonzero;"/>
|
||||||
|
<g transform="matrix(1,0,0,1,-4.437961,0)">
|
||||||
|
<path d="M22.23,55.204L2.781,36.187C0.844,34.219 0,32.406 0,30.094C0.031,27.75 0.875,25.969 2.781,24L25.313,2C26.688,0.625 28.25,0 30.188,0C34.125,0 37.406,3.219 37.406,7.094C37.406,9.031 36.594,10.844 35,12.375L16.537,30.034L35,47.812C36.562,49.313 37.406,51.156 37.406,53.125C37.406,55.669 35.969,57.953 33.855,59.207C30.383,57.109 26.443,55.71 22.23,55.204Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.515011,0,0,0.515011,34.08479,22)">
|
||||||
|
<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.6 KiB |
@ -2143,3 +2143,160 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton {
|
|||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
background: color-mix(in srgb, var(--color-text) 50%, transparent);
|
background: color-mix(in srgb, var(--color-text) 50%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-preview {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 600px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-scroll {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-preview .simplebar-track.simplebar-vertical {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-preview .simplebar-track.simplebar-horizontal {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-preview .simplebar-content-wrapper {
|
||||||
|
scroll-snap-type: x mandatory;
|
||||||
|
scroll-padding-inline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-preview:not(.is-mobile) .simplebar-content-wrapper {
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
transparent 0,
|
||||||
|
#000 var(--file-gallery-side-padding, 66px),
|
||||||
|
#000 calc(100% - var(--file-gallery-side-padding, 66px)),
|
||||||
|
transparent 100%
|
||||||
|
);
|
||||||
|
-webkit-mask-image: linear-gradient(
|
||||||
|
to right,
|
||||||
|
transparent 0,
|
||||||
|
#000 var(--file-gallery-side-padding, 66px),
|
||||||
|
#000 calc(100% - var(--file-gallery-side-padding, 66px)),
|
||||||
|
transparent 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-track {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-slide {
|
||||||
|
box-sizing: border-box;
|
||||||
|
scroll-snap-align: center;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-preview-frame {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 600px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-nav {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
z-index: 2;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-nav-left {
|
||||||
|
left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-nav-right {
|
||||||
|
right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-footer {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-thumb-container {
|
||||||
|
height: 64px;
|
||||||
|
width: 64px;
|
||||||
|
}
|
||||||
|
.file-gallery-list-thumb {
|
||||||
|
position: relative;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-thumb.is-active {
|
||||||
|
outline: 2px solid var(--color-primary);
|
||||||
|
outline-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-thumb-delete.ant-card {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
transform: translate(30%, -60%);
|
||||||
|
z-index: 4;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-thumb-delete.ant-card .ant-card-body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-thumb-delete .ant-btn {
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-upload {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-dropzone.ant-upload.ant-upload-drag {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px dashed color-mix(in srgb, var(--color-primary) 50%, transparent);
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
color-mix(in srgb, var(--color-primary) 12%, transparent),
|
||||||
|
color-mix(in srgb, var(--color-primary) 12%, transparent) 6px,
|
||||||
|
transparent 6px,
|
||||||
|
transparent 12px
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-dropzone.ant-upload.ant-upload-drag
|
||||||
|
.ant-upload-drag-container {
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-dropzone.ant-upload.ant-upload-drag:hover,
|
||||||
|
.file-gallery-list-dropzone.ant-upload.ant-upload-drag.ant-upload-drag-hover {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-gallery-list-dropzone .anticon {
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|||||||
79
src/components/Dashboard/Finance/PaymentPolicies.jsx
Normal file
79
src/components/Dashboard/Finance/PaymentPolicies.jsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { useRef } from 'react'
|
||||||
|
import { Flex, Space } from 'antd'
|
||||||
|
import ObjectTable from '../common/ObjectTable'
|
||||||
|
import ObjectActions from '../common/ObjectActions'
|
||||||
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
|
import ObjectTableViewButton from '../common/ObjectTableViewButton'
|
||||||
|
import FilterSidebarButton from '../common/FilterSidebarButton'
|
||||||
|
import SortSidebarButton from '../common/SortSidebarButton'
|
||||||
|
import useViewMode from '../hooks/useViewMode'
|
||||||
|
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
|
||||||
|
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
|
||||||
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
|
import ExportListButton from '../common/ExportListButton'
|
||||||
|
|
||||||
|
const PaymentPolicies = () => {
|
||||||
|
const tableRef = useRef()
|
||||||
|
const [viewMode, setViewMode] = useViewMode('paymentPolicy')
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
useColumnVisibility('paymentPolicy')
|
||||||
|
const [showFilterSidebar, setShowFilterSidebar] =
|
||||||
|
useFilterSidebarVisibility('PaymentPolicies')
|
||||||
|
const [showSortSidebar, setShowSortSidebar] =
|
||||||
|
useSortSidebarVisibility('PaymentPolicies')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex vertical={'true'} gap='large' className='h-100'>
|
||||||
|
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='paymentPolicy'
|
||||||
|
pageName='list'
|
||||||
|
onReload={() => tableRef.current?.reload()}
|
||||||
|
/>
|
||||||
|
<ColumnViewButton
|
||||||
|
type='paymentPolicy'
|
||||||
|
loading={false}
|
||||||
|
visibleState={columnVisibility}
|
||||||
|
updateVisibleState={setColumnVisibility}
|
||||||
|
/>
|
||||||
|
<ExportListButton objectType='paymentPolicy' />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<SortSidebarButton
|
||||||
|
active={showSortSidebar}
|
||||||
|
onClick={() => setShowSortSidebar(!showSortSidebar)}
|
||||||
|
/>
|
||||||
|
<FilterSidebarButton
|
||||||
|
active={showFilterSidebar}
|
||||||
|
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
|
||||||
|
/>
|
||||||
|
<ObjectTableViewButton
|
||||||
|
viewMode={viewMode}
|
||||||
|
setViewMode={setViewMode}
|
||||||
|
showEndingDivider={true}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ObjectTable
|
||||||
|
ref={tableRef}
|
||||||
|
visibleColumns={columnVisibility}
|
||||||
|
type='paymentPolicy'
|
||||||
|
cards={viewMode === 'cards'}
|
||||||
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
showSortSidebar={showSortSidebar}
|
||||||
|
expandHeight={true}
|
||||||
|
saveFilterInSession={true}
|
||||||
|
saveFilterInUrl={true}
|
||||||
|
useFilterInSession={true}
|
||||||
|
useFilterInUrl={true}
|
||||||
|
saveSortInSession={true}
|
||||||
|
saveSortInUrl={true}
|
||||||
|
useSortInSession={true}
|
||||||
|
useSortInUrl={true}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PaymentPolicies
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
|
import WizardView from '../../common/WizardView'
|
||||||
|
|
||||||
|
const NewPaymentPolicy = ({ onOk, defaultValues }) => {
|
||||||
|
return (
|
||||||
|
<NewObjectForm
|
||||||
|
type={'paymentPolicy'}
|
||||||
|
defaultValues={{ immediatePay: true, ...defaultValues }}
|
||||||
|
>
|
||||||
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
title: 'Required',
|
||||||
|
key: 'required',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='paymentPolicy'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={true}
|
||||||
|
objectData={objectData}
|
||||||
|
labelWidth={140}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Optional',
|
||||||
|
key: 'optional',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='paymentPolicy'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={false}
|
||||||
|
labelWidth={140}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Summary',
|
||||||
|
key: 'summary',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='paymentPolicy'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
visibleProperties={{
|
||||||
|
_id: false,
|
||||||
|
_reference: false,
|
||||||
|
createdAt: false,
|
||||||
|
updatedAt: false
|
||||||
|
}}
|
||||||
|
labelWidth={140}
|
||||||
|
isEditing={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<WizardView
|
||||||
|
steps={steps}
|
||||||
|
loading={submitLoading}
|
||||||
|
formValid={formValid}
|
||||||
|
title='New Payment Policy'
|
||||||
|
onSubmit={async () => {
|
||||||
|
const result = await handleSubmit()
|
||||||
|
if (result) {
|
||||||
|
onOk()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NewObjectForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
NewPaymentPolicy.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
reset: PropTypes.bool,
|
||||||
|
defaultValues: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewPaymentPolicy
|
||||||
@ -0,0 +1,264 @@
|
|||||||
|
import { useRef, useState, useMemo } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Card, Flex, Space, Spin } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
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 ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||||
|
import ViewButton from '../../common/ViewButton'
|
||||||
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import MarketplaceIcon from '../../../Icons/MarketplaceIcon.jsx'
|
||||||
|
import ObjectForm from '../../common/ObjectForm'
|
||||||
|
import EditButtons from '../../common/EditButtons'
|
||||||
|
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||||
|
import ActivityIndicator from '../../common/ActivityIndicator.jsx'
|
||||||
|
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||||
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
|
import {
|
||||||
|
getModelProperty,
|
||||||
|
getModelByName
|
||||||
|
} from '../../../../database/ObjectModels.js'
|
||||||
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
|
||||||
|
const log = loglevel.getLogger('PaymentPolicyInfo')
|
||||||
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
|
const PaymentPolicyInfo = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const objectFormRef = useRef(null)
|
||||||
|
const actionHandlerRef = useRef(null)
|
||||||
|
const paymentPolicyId = new URLSearchParams(location.search).get(
|
||||||
|
'paymentPolicyId'
|
||||||
|
)
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState(
|
||||||
|
'PaymentPolicyInfo',
|
||||||
|
{
|
||||||
|
info: true,
|
||||||
|
marketplaces: true,
|
||||||
|
notes: true,
|
||||||
|
auditLogs: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const [objectFormState, setEditFormState] = useState({
|
||||||
|
isEditing: false,
|
||||||
|
editLoading: false,
|
||||||
|
formValid: false,
|
||||||
|
lock: null,
|
||||||
|
loading: false,
|
||||||
|
editDisabled: false,
|
||||||
|
objectData: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentObjectTools = useMemo(
|
||||||
|
() => (
|
||||||
|
<ObjectTableNavigationButtons
|
||||||
|
disabled={objectFormState.loading || objectFormState.isEditing}
|
||||||
|
_id={paymentPolicyId}
|
||||||
|
objectType='paymentPolicy'
|
||||||
|
showEndingDivider={false}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[objectFormState.loading, objectFormState.isEditing, paymentPolicyId]
|
||||||
|
)
|
||||||
|
useDashboardObjectTools(currentObjectTools)
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
edit: () => {
|
||||||
|
objectFormRef?.current?.startEditing?.()
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
cancelEdit: () => {
|
||||||
|
objectFormRef?.current?.cancelEditing?.()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
finishEdit: () => {
|
||||||
|
objectFormRef?.current?.handleUpdate?.()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex
|
||||||
|
gap='large'
|
||||||
|
vertical='true'
|
||||||
|
style={{ maxHeight: '100%', minHeight: 0 }}
|
||||||
|
>
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='small'>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='paymentPolicy'
|
||||||
|
pageName='info'
|
||||||
|
id={paymentPolicyId}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
items={[
|
||||||
|
{ key: 'info', label: 'Payment Policy Information' },
|
||||||
|
{ key: 'marketplaces', label: 'Marketplaces' },
|
||||||
|
{ key: 'notes', label: 'Notes' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
<UserNotifierToggle
|
||||||
|
type='paymentPolicy'
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
/>
|
||||||
|
<DocumentPrintButton
|
||||||
|
type='paymentPolicy'
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<ActivityIndicator
|
||||||
|
activities={objectFormState.activities}
|
||||||
|
showStartingDivider={true}
|
||||||
|
/>
|
||||||
|
</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.beingEditedByOther ||
|
||||||
|
objectFormState.loading ||
|
||||||
|
objectFormState.editDisabled
|
||||||
|
}
|
||||||
|
loading={objectFormState.editLoading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ScrollBox>
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<ActionHandler
|
||||||
|
actions={actions}
|
||||||
|
loading={objectFormState.loading}
|
||||||
|
ref={actionHandlerRef}
|
||||||
|
>
|
||||||
|
<ObjectForm
|
||||||
|
id={paymentPolicyId}
|
||||||
|
type='paymentPolicy'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
ref={objectFormRef}
|
||||||
|
setCurrentObject={true}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading, isEditing, objectData }) => (
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Payment Policy Information'
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
active={collapseState.info}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('info', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='info'
|
||||||
|
>
|
||||||
|
<ObjectInfo
|
||||||
|
loading={loading}
|
||||||
|
isEditing={isEditing}
|
||||||
|
type='paymentPolicy'
|
||||||
|
objectData={objectData}
|
||||||
|
labelWidth={210}
|
||||||
|
visibleProperties={{
|
||||||
|
marketplaces: false
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Marketplaces'
|
||||||
|
icon={<MarketplaceIcon />}
|
||||||
|
active={collapseState.marketplaces}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('marketplaces', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='marketplaces'
|
||||||
|
>
|
||||||
|
<ObjectProperty
|
||||||
|
{...getModelProperty('paymentPolicy', 'marketplaces')}
|
||||||
|
isEditing={isEditing}
|
||||||
|
objectData={objectData}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</ObjectForm>
|
||||||
|
</ActionHandler>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Notes'
|
||||||
|
icon={<NoteIcon />}
|
||||||
|
active={collapseState.notes}
|
||||||
|
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||||
|
collapseKey='notes'
|
||||||
|
>
|
||||||
|
<Spin
|
||||||
|
spinning={objectFormState.loading}
|
||||||
|
indicator={<LoadingOutlined />}
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<NotesPanel _id={paymentPolicyId} type='paymentPolicy' />
|
||||||
|
</Card>
|
||||||
|
</Spin>
|
||||||
|
</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:
|
||||||
|
getModelByName('paymentPolicy').prefix +
|
||||||
|
':' +
|
||||||
|
paymentPolicyId
|
||||||
|
}}
|
||||||
|
visibleColumns={{ _id: false, parent: false }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</ScrollBox>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PaymentPolicyInfo
|
||||||
@ -8,7 +8,7 @@ const NewPartStock = ({ onOk, reset, defaultValues }) => {
|
|||||||
<NewObjectForm
|
<NewObjectForm
|
||||||
type={'partStock'}
|
type={'partStock'}
|
||||||
reset={reset}
|
reset={reset}
|
||||||
defaultValues={{ state: { type: 'new' }, ...defaultValues }}
|
defaultValues={{ state: { type: 'draft' }, ...defaultValues }}
|
||||||
>
|
>
|
||||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
const steps = [
|
const steps = [
|
||||||
@ -38,7 +38,8 @@ const NewPartStock = ({ onOk, reset, defaultValues }) => {
|
|||||||
_id: false,
|
_id: false,
|
||||||
_reference: false,
|
_reference: false,
|
||||||
createdAt: false,
|
createdAt: false,
|
||||||
updatedAt: false
|
updatedAt: false,
|
||||||
|
postedAt: false
|
||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
import { useState, useContext } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||||
|
import { message } from 'antd'
|
||||||
|
import MessageDialogView from '../../common/MessageDialogView.jsx'
|
||||||
|
|
||||||
|
const PostPartStock = ({ onOk, objectData }) => {
|
||||||
|
const [postLoading, setPostLoading] = useState(false)
|
||||||
|
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||||
|
|
||||||
|
const handlePost = async () => {
|
||||||
|
setPostLoading(true)
|
||||||
|
try {
|
||||||
|
const result = await sendObjectFunction(objectData._id, 'PartStock', 'post')
|
||||||
|
if (result) {
|
||||||
|
message.success('Part stock posted successfully')
|
||||||
|
onOk(result)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error posting part stock:', error)
|
||||||
|
} finally {
|
||||||
|
setPostLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MessageDialogView
|
||||||
|
title={'Are you sure you want to post this part stock?'}
|
||||||
|
description={`Posting part stock ${objectData?.name || objectData?._reference || objectData?._id} will finalize it and make it read-only.`}
|
||||||
|
onOk={handlePost}
|
||||||
|
okText='Post'
|
||||||
|
okLoading={postLoading}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
PostPartStock.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
objectData: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PostPartStock
|
||||||
@ -3,9 +3,9 @@ import ObjectInfo from '../../common/ObjectInfo'
|
|||||||
import NewObjectForm from '../../common/NewObjectForm'
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
import WizardView from '../../common/WizardView'
|
import WizardView from '../../common/WizardView'
|
||||||
|
|
||||||
const NewProductCategory = ({ onOk }) => {
|
const NewProductCategory = ({ onOk, defaultValues }) => {
|
||||||
return (
|
return (
|
||||||
<NewObjectForm type={'productCategory'}>
|
<NewObjectForm type={'productCategory'} defaultValues={defaultValues}>
|
||||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
@ -23,6 +23,21 @@ const NewProductCategory = ({ onOk }) => {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Optional',
|
||||||
|
key: 'optional',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='productCategory'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={false}
|
||||||
|
objectData={objectData}
|
||||||
|
labelWidth={120}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Summary',
|
title: 'Summary',
|
||||||
key: 'summary',
|
key: 'summary',
|
||||||
@ -39,7 +54,7 @@ const NewProductCategory = ({ onOk }) => {
|
|||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
labelWidth={70}
|
labelWidth={120}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -65,7 +80,8 @@ const NewProductCategory = ({ onOk }) => {
|
|||||||
|
|
||||||
NewProductCategory.propTypes = {
|
NewProductCategory.propTypes = {
|
||||||
onOk: PropTypes.func.isRequired,
|
onOk: PropTypes.func.isRequired,
|
||||||
reset: PropTypes.bool
|
reset: PropTypes.bool,
|
||||||
|
defaultValues: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NewProductCategory
|
export default NewProductCategory
|
||||||
|
|||||||
@ -6,10 +6,12 @@ import useCollapseState from '../../hooks/useCollapseState'
|
|||||||
import NotesPanel from '../../common/NotesPanel'
|
import NotesPanel from '../../common/NotesPanel'
|
||||||
import InfoCollapse from '../../common/InfoCollapse'
|
import InfoCollapse from '../../common/InfoCollapse'
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||||
import ViewButton from '../../common/ViewButton'
|
import ViewButton from '../../common/ViewButton'
|
||||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import MarketplaceIcon from '../../../Icons/MarketplaceIcon.jsx'
|
||||||
import ObjectForm from '../../common/ObjectForm'
|
import ObjectForm from '../../common/ObjectForm'
|
||||||
import EditButtons from '../../common/EditButtons'
|
import EditButtons from '../../common/EditButtons'
|
||||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||||
@ -21,7 +23,7 @@ import ObjectTable from '../../common/ObjectTable.jsx'
|
|||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
|
||||||
const ProductCategoryInfo = () => {
|
const ProductCategoryInfo = () => {
|
||||||
@ -35,6 +37,7 @@ const ProductCategoryInfo = () => {
|
|||||||
'ProductCategoryInfo',
|
'ProductCategoryInfo',
|
||||||
{
|
{
|
||||||
info: true,
|
info: true,
|
||||||
|
marketplaces: true,
|
||||||
notes: true,
|
notes: true,
|
||||||
auditLogs: false
|
auditLogs: false
|
||||||
}
|
}
|
||||||
@ -98,6 +101,7 @@ const ProductCategoryInfo = () => {
|
|||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
items={[
|
items={[
|
||||||
{ key: 'info', label: 'Product Category Information' },
|
{ key: 'info', label: 'Product Category Information' },
|
||||||
|
{ key: 'marketplaces', label: 'Marketplaces' },
|
||||||
{ key: 'notes', label: 'Notes' },
|
{ key: 'notes', label: 'Notes' },
|
||||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
]}
|
]}
|
||||||
@ -150,33 +154,56 @@ const ProductCategoryInfo = () => {
|
|||||||
loading={objectFormState.loading}
|
loading={objectFormState.loading}
|
||||||
ref={actionHandlerRef}
|
ref={actionHandlerRef}
|
||||||
>
|
>
|
||||||
<InfoCollapse
|
<ObjectForm
|
||||||
title='Product Category Information'
|
id={productCategoryId}
|
||||||
icon={<InfoCircleIcon />}
|
type='productCategory'
|
||||||
active={collapseState.info}
|
style={{ height: '100%' }}
|
||||||
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
ref={objectFormRef}
|
||||||
collapseKey='info'
|
setCurrentObject={true}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ObjectForm
|
{({ loading, isEditing, objectData }) => (
|
||||||
id={productCategoryId}
|
<Flex vertical gap={'large'}>
|
||||||
type='productCategory'
|
<InfoCollapse
|
||||||
style={{ height: '100%' }}
|
title='Product Category Information'
|
||||||
ref={objectFormRef}
|
icon={<InfoCircleIcon />}
|
||||||
setCurrentObject={true}
|
active={collapseState.info}
|
||||||
onStateChange={(state) => {
|
onToggle={(expanded) =>
|
||||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
updateCollapseState('info', expanded)
|
||||||
}}
|
}
|
||||||
>
|
collapseKey='info'
|
||||||
{({ loading, isEditing, objectData }) => (
|
>
|
||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
loading={loading}
|
loading={loading}
|
||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
type='productCategory'
|
type='productCategory'
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
visibleProperties={{
|
||||||
)}
|
marketplaces: false
|
||||||
</ObjectForm>
|
}}
|
||||||
</InfoCollapse>
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Marketplaces'
|
||||||
|
icon={<MarketplaceIcon />}
|
||||||
|
active={collapseState.marketplaces}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('marketplaces', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='marketplaces'
|
||||||
|
>
|
||||||
|
<ObjectProperty
|
||||||
|
{...getModelProperty('productCategory', 'marketplaces')}
|
||||||
|
isEditing={isEditing}
|
||||||
|
objectData={objectData}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</ObjectForm>
|
||||||
</ActionHandler>
|
</ActionHandler>
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Notes'
|
title='Notes'
|
||||||
|
|||||||
@ -8,10 +8,12 @@ import useCollapseState from '../../hooks/useCollapseState'
|
|||||||
import NotesPanel from '../../common/NotesPanel'
|
import NotesPanel from '../../common/NotesPanel'
|
||||||
import InfoCollapse from '../../common/InfoCollapse'
|
import InfoCollapse from '../../common/InfoCollapse'
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||||
import ViewButton from '../../common/ViewButton'
|
import ViewButton from '../../common/ViewButton'
|
||||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import MarketplaceIcon from '../../../Icons/MarketplaceIcon.jsx'
|
||||||
import ObjectForm from '../../common/ObjectForm'
|
import ObjectForm from '../../common/ObjectForm'
|
||||||
import EditButtons from '../../common/EditButtons'
|
import EditButtons from '../../common/EditButtons'
|
||||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||||
@ -23,7 +25,7 @@ import ObjectTable from '../../common/ObjectTable.jsx'
|
|||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
|
||||||
const log = loglevel.getLogger('TaxRateInfo')
|
const log = loglevel.getLogger('TaxRateInfo')
|
||||||
@ -36,6 +38,7 @@ const TaxRateInfo = () => {
|
|||||||
const taxRateId = new URLSearchParams(location.search).get('taxRateId')
|
const taxRateId = new URLSearchParams(location.search).get('taxRateId')
|
||||||
const [collapseState, updateCollapseState] = useCollapseState('TaxRateInfo', {
|
const [collapseState, updateCollapseState] = useCollapseState('TaxRateInfo', {
|
||||||
info: true,
|
info: true,
|
||||||
|
marketplaces: true,
|
||||||
notes: true,
|
notes: true,
|
||||||
auditLogs: false
|
auditLogs: false
|
||||||
})
|
})
|
||||||
@ -98,6 +101,7 @@ const TaxRateInfo = () => {
|
|||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
items={[
|
items={[
|
||||||
{ key: 'info', label: 'Tax Rate Information' },
|
{ key: 'info', label: 'Tax Rate Information' },
|
||||||
|
{ key: 'marketplaces', label: 'Marketplaces' },
|
||||||
{ key: 'notes', label: 'Notes' },
|
{ key: 'notes', label: 'Notes' },
|
||||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
]}
|
]}
|
||||||
@ -150,33 +154,56 @@ const TaxRateInfo = () => {
|
|||||||
loading={objectFormState.loading}
|
loading={objectFormState.loading}
|
||||||
ref={actionHandlerRef}
|
ref={actionHandlerRef}
|
||||||
>
|
>
|
||||||
<InfoCollapse
|
<ObjectForm
|
||||||
title='Tax Rate Information'
|
id={taxRateId}
|
||||||
icon={<InfoCircleIcon />}
|
type='taxRate'
|
||||||
active={collapseState.info}
|
style={{ height: '100%' }}
|
||||||
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
ref={objectFormRef}
|
||||||
collapseKey='info'
|
setCurrentObject={true}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<ObjectForm
|
{({ loading, isEditing, objectData }) => (
|
||||||
id={taxRateId}
|
<Flex vertical gap={'large'}>
|
||||||
type='taxRate'
|
<InfoCollapse
|
||||||
style={{ height: '100%' }}
|
title='Tax Rate Information'
|
||||||
ref={objectFormRef}
|
icon={<InfoCircleIcon />}
|
||||||
setCurrentObject={true}
|
active={collapseState.info}
|
||||||
onStateChange={(state) => {
|
onToggle={(expanded) =>
|
||||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
updateCollapseState('info', expanded)
|
||||||
}}
|
}
|
||||||
>
|
collapseKey='info'
|
||||||
{({ loading, isEditing, objectData }) => (
|
>
|
||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
loading={loading}
|
loading={loading}
|
||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
type='taxRate'
|
type='taxRate'
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
visibleProperties={{
|
||||||
)}
|
marketplaces: false
|
||||||
</ObjectForm>
|
}}
|
||||||
</InfoCollapse>
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Marketplaces'
|
||||||
|
icon={<MarketplaceIcon />}
|
||||||
|
active={collapseState.marketplaces}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('marketplaces', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='marketplaces'
|
||||||
|
>
|
||||||
|
<ObjectProperty
|
||||||
|
{...getModelProperty('taxRate', 'marketplaces')}
|
||||||
|
isEditing={isEditing}
|
||||||
|
objectData={objectData}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</ObjectForm>
|
||||||
</ActionHandler>
|
</ActionHandler>
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Notes'
|
title='Notes'
|
||||||
|
|||||||
79
src/components/Dashboard/Sales/FulfillmentPolicies.jsx
Normal file
79
src/components/Dashboard/Sales/FulfillmentPolicies.jsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { useRef } from 'react'
|
||||||
|
import { Flex, Space } from 'antd'
|
||||||
|
import ObjectTable from '../common/ObjectTable'
|
||||||
|
import ObjectActions from '../common/ObjectActions'
|
||||||
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
|
import ObjectTableViewButton from '../common/ObjectTableViewButton'
|
||||||
|
import FilterSidebarButton from '../common/FilterSidebarButton'
|
||||||
|
import SortSidebarButton from '../common/SortSidebarButton'
|
||||||
|
import useViewMode from '../hooks/useViewMode'
|
||||||
|
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
|
||||||
|
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
|
||||||
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
|
import ExportListButton from '../common/ExportListButton'
|
||||||
|
|
||||||
|
const FulfillmentPolicies = () => {
|
||||||
|
const tableRef = useRef()
|
||||||
|
const [viewMode, setViewMode] = useViewMode('fulfillmentPolicy')
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
useColumnVisibility('fulfillmentPolicy')
|
||||||
|
const [showFilterSidebar, setShowFilterSidebar] =
|
||||||
|
useFilterSidebarVisibility('FulfillmentPolicies')
|
||||||
|
const [showSortSidebar, setShowSortSidebar] =
|
||||||
|
useSortSidebarVisibility('FulfillmentPolicies')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex vertical={'true'} gap='large' className='h-100'>
|
||||||
|
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
pageName='list'
|
||||||
|
onReload={() => tableRef.current?.reload()}
|
||||||
|
/>
|
||||||
|
<ColumnViewButton
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
loading={false}
|
||||||
|
visibleState={columnVisibility}
|
||||||
|
updateVisibleState={setColumnVisibility}
|
||||||
|
/>
|
||||||
|
<ExportListButton objectType='fulfillmentPolicy' />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<SortSidebarButton
|
||||||
|
active={showSortSidebar}
|
||||||
|
onClick={() => setShowSortSidebar(!showSortSidebar)}
|
||||||
|
/>
|
||||||
|
<FilterSidebarButton
|
||||||
|
active={showFilterSidebar}
|
||||||
|
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
|
||||||
|
/>
|
||||||
|
<ObjectTableViewButton
|
||||||
|
viewMode={viewMode}
|
||||||
|
setViewMode={setViewMode}
|
||||||
|
showEndingDivider={true}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ObjectTable
|
||||||
|
ref={tableRef}
|
||||||
|
visibleColumns={columnVisibility}
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
cards={viewMode === 'cards'}
|
||||||
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
showSortSidebar={showSortSidebar}
|
||||||
|
expandHeight={true}
|
||||||
|
saveFilterInSession={true}
|
||||||
|
saveFilterInUrl={true}
|
||||||
|
useFilterInSession={true}
|
||||||
|
useFilterInUrl={true}
|
||||||
|
saveSortInSession={true}
|
||||||
|
saveSortInUrl={true}
|
||||||
|
useSortInSession={true}
|
||||||
|
useSortInUrl={true}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FulfillmentPolicies
|
||||||
@ -0,0 +1,270 @@
|
|||||||
|
import { useRef, useState, useMemo } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Card, Flex, Space, Spin } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
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 ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||||
|
import ViewButton from '../../common/ViewButton'
|
||||||
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import MarketplaceIcon from '../../../Icons/MarketplaceIcon.jsx'
|
||||||
|
import ObjectForm from '../../common/ObjectForm'
|
||||||
|
import EditButtons from '../../common/EditButtons'
|
||||||
|
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||||
|
import ActivityIndicator from '../../common/ActivityIndicator.jsx'
|
||||||
|
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||||
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
|
import {
|
||||||
|
getModelProperty,
|
||||||
|
getModelByName
|
||||||
|
} from '../../../../database/ObjectModels.js'
|
||||||
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
|
||||||
|
const log = loglevel.getLogger('FulfillmentPolicyInfo')
|
||||||
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
|
const FulfillmentPolicyInfo = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const objectFormRef = useRef(null)
|
||||||
|
const actionHandlerRef = useRef(null)
|
||||||
|
const fulfillmentPolicyId = new URLSearchParams(location.search).get(
|
||||||
|
'fulfillmentPolicyId'
|
||||||
|
)
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState(
|
||||||
|
'FulfillmentPolicyInfo',
|
||||||
|
{
|
||||||
|
info: true,
|
||||||
|
marketplaces: true,
|
||||||
|
notes: true,
|
||||||
|
auditLogs: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const [objectFormState, setEditFormState] = useState({
|
||||||
|
isEditing: false,
|
||||||
|
editLoading: false,
|
||||||
|
formValid: false,
|
||||||
|
lock: null,
|
||||||
|
loading: false,
|
||||||
|
editDisabled: false,
|
||||||
|
objectData: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentObjectTools = useMemo(
|
||||||
|
() => (
|
||||||
|
<ObjectTableNavigationButtons
|
||||||
|
disabled={objectFormState.loading || objectFormState.isEditing}
|
||||||
|
_id={fulfillmentPolicyId}
|
||||||
|
objectType='fulfillmentPolicy'
|
||||||
|
showEndingDivider={false}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[objectFormState.loading, objectFormState.isEditing, fulfillmentPolicyId]
|
||||||
|
)
|
||||||
|
useDashboardObjectTools(currentObjectTools)
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
edit: () => {
|
||||||
|
objectFormRef?.current?.startEditing?.()
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
cancelEdit: () => {
|
||||||
|
objectFormRef?.current?.cancelEditing?.()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
finishEdit: () => {
|
||||||
|
objectFormRef?.current?.handleUpdate?.()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex
|
||||||
|
gap='large'
|
||||||
|
vertical='true'
|
||||||
|
style={{ maxHeight: '100%', minHeight: 0 }}
|
||||||
|
>
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='small'>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
pageName='info'
|
||||||
|
id={fulfillmentPolicyId}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
items={[
|
||||||
|
{ key: 'info', label: 'Fulfillment Policy Information' },
|
||||||
|
{ key: 'marketplaces', label: 'Marketplaces' },
|
||||||
|
{ key: 'notes', label: 'Notes' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
<UserNotifierToggle
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
/>
|
||||||
|
<DocumentPrintButton
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<ActivityIndicator
|
||||||
|
activities={objectFormState.activities}
|
||||||
|
showStartingDivider={true}
|
||||||
|
/>
|
||||||
|
</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.beingEditedByOther ||
|
||||||
|
objectFormState.loading ||
|
||||||
|
objectFormState.editDisabled
|
||||||
|
}
|
||||||
|
loading={objectFormState.editLoading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ScrollBox>
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<ActionHandler
|
||||||
|
actions={actions}
|
||||||
|
loading={objectFormState.loading}
|
||||||
|
ref={actionHandlerRef}
|
||||||
|
>
|
||||||
|
<ObjectForm
|
||||||
|
id={fulfillmentPolicyId}
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
ref={objectFormRef}
|
||||||
|
setCurrentObject={true}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading, isEditing, objectData }) => (
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Fulfillment Policy Information'
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
active={collapseState.info}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('info', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='info'
|
||||||
|
>
|
||||||
|
<ObjectInfo
|
||||||
|
loading={loading}
|
||||||
|
isEditing={isEditing}
|
||||||
|
labelWidth={175}
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
objectData={objectData}
|
||||||
|
visibleProperties={{
|
||||||
|
marketplaces: false
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Marketplaces'
|
||||||
|
icon={<MarketplaceIcon />}
|
||||||
|
active={collapseState.marketplaces}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('marketplaces', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='marketplaces'
|
||||||
|
>
|
||||||
|
<ObjectProperty
|
||||||
|
{...getModelProperty(
|
||||||
|
'fulfillmentPolicy',
|
||||||
|
'marketplaces'
|
||||||
|
)}
|
||||||
|
isEditing={isEditing}
|
||||||
|
objectData={objectData}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</ObjectForm>
|
||||||
|
</ActionHandler>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Notes'
|
||||||
|
icon={<NoteIcon />}
|
||||||
|
active={collapseState.notes}
|
||||||
|
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||||
|
collapseKey='notes'
|
||||||
|
>
|
||||||
|
<Spin
|
||||||
|
spinning={objectFormState.loading}
|
||||||
|
indicator={<LoadingOutlined />}
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<NotesPanel
|
||||||
|
_id={fulfillmentPolicyId}
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</Spin>
|
||||||
|
</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:
|
||||||
|
getModelByName('fulfillmentPolicy').prefix +
|
||||||
|
':' +
|
||||||
|
fulfillmentPolicyId
|
||||||
|
}}
|
||||||
|
visibleColumns={{ _id: false, parent: false }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</ScrollBox>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FulfillmentPolicyInfo
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
|
import WizardView from '../../common/WizardView'
|
||||||
|
|
||||||
|
const NewFulfillmentPolicy = ({ onOk, defaultValues }) => {
|
||||||
|
return (
|
||||||
|
<NewObjectForm
|
||||||
|
type={'fulfillmentPolicy'}
|
||||||
|
defaultValues={{ handlingTime: 1, ...defaultValues }}
|
||||||
|
>
|
||||||
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
title: 'Required',
|
||||||
|
key: 'required',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={true}
|
||||||
|
objectData={objectData}
|
||||||
|
labelWidth={130}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Optional',
|
||||||
|
key: 'optional',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={false}
|
||||||
|
labelWidth={130}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Summary',
|
||||||
|
key: 'summary',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='fulfillmentPolicy'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
visibleProperties={{
|
||||||
|
_id: false,
|
||||||
|
_reference: false,
|
||||||
|
createdAt: false,
|
||||||
|
updatedAt: false
|
||||||
|
}}
|
||||||
|
labelWidth={130}
|
||||||
|
isEditing={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<WizardView
|
||||||
|
steps={steps}
|
||||||
|
loading={submitLoading}
|
||||||
|
formValid={formValid}
|
||||||
|
title='New Fulfillment Policy'
|
||||||
|
onSubmit={async () => {
|
||||||
|
const result = await handleSubmit()
|
||||||
|
if (result) {
|
||||||
|
onOk()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NewObjectForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
NewFulfillmentPolicy.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
reset: PropTypes.bool,
|
||||||
|
defaultValues: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewFulfillmentPolicy
|
||||||
86
src/components/Dashboard/Sales/ListingVarients.jsx
Normal file
86
src/components/Dashboard/Sales/ListingVarients.jsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { useRef } from 'react'
|
||||||
|
import { Flex, Space } from 'antd'
|
||||||
|
import ObjectTable from '../common/ObjectTable'
|
||||||
|
import ObjectActions from '../common/ObjectActions'
|
||||||
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
|
import ObjectTableViewButton from '../common/ObjectTableViewButton'
|
||||||
|
import FilterSidebarButton from '../common/FilterSidebarButton'
|
||||||
|
import SortSidebarButton from '../common/SortSidebarButton'
|
||||||
|
import useViewMode from '../hooks/useViewMode'
|
||||||
|
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
|
||||||
|
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
|
||||||
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
|
import ExportListButton from '../common/ExportListButton'
|
||||||
|
|
||||||
|
const ListingVarients = () => {
|
||||||
|
const tableRef = useRef()
|
||||||
|
|
||||||
|
const [viewMode, setViewMode] = useViewMode('listingVarients')
|
||||||
|
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
useColumnVisibility('listingVarients')
|
||||||
|
|
||||||
|
const [showFilterSidebar, setShowFilterSidebar] =
|
||||||
|
useFilterSidebarVisibility('ListingVarients')
|
||||||
|
|
||||||
|
const [showSortSidebar, setShowSortSidebar] =
|
||||||
|
useSortSidebarVisibility('ListingVarients')
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex vertical={'true'} gap='large' className='h-100'>
|
||||||
|
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='listingVarient'
|
||||||
|
pageName='list'
|
||||||
|
onReload={() => tableRef.current?.reload()}
|
||||||
|
/>
|
||||||
|
<ColumnViewButton
|
||||||
|
type='listingVarient'
|
||||||
|
loading={false}
|
||||||
|
visibleState={columnVisibility}
|
||||||
|
updateVisibleState={setColumnVisibility}
|
||||||
|
/>
|
||||||
|
<ExportListButton objectType='listingVarient' />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<SortSidebarButton
|
||||||
|
active={showSortSidebar}
|
||||||
|
onClick={() => setShowSortSidebar(!showSortSidebar)}
|
||||||
|
/>
|
||||||
|
<FilterSidebarButton
|
||||||
|
active={showFilterSidebar}
|
||||||
|
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
|
||||||
|
/>
|
||||||
|
<ObjectTableViewButton
|
||||||
|
viewMode={viewMode}
|
||||||
|
setViewMode={setViewMode}
|
||||||
|
showEndingDivider={true}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ObjectTable
|
||||||
|
ref={tableRef}
|
||||||
|
visibleColumns={columnVisibility}
|
||||||
|
type='listingVarient'
|
||||||
|
cards={viewMode === 'cards'}
|
||||||
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
showSortSidebar={showSortSidebar}
|
||||||
|
expandHeight={true}
|
||||||
|
saveFilterInSession={true}
|
||||||
|
saveFilterInUrl={true}
|
||||||
|
useFilterInSession={true}
|
||||||
|
useFilterInUrl={true}
|
||||||
|
saveSortInSession={true}
|
||||||
|
saveSortInUrl={true}
|
||||||
|
useSortInSession={true}
|
||||||
|
useSortInUrl={true}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ListingVarients
|
||||||
@ -8,8 +8,10 @@ import useCollapseState from '../../hooks/useCollapseState'
|
|||||||
import NotesPanel from '../../common/NotesPanel'
|
import NotesPanel from '../../common/NotesPanel'
|
||||||
import InfoCollapse from '../../common/InfoCollapse'
|
import InfoCollapse from '../../common/InfoCollapse'
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||||
import ViewButton from '../../common/ViewButton'
|
import ViewButton from '../../common/ViewButton'
|
||||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
|
import PictureIcon from '../../../Icons/PictureIcon.jsx'
|
||||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
import ObjectForm from '../../common/ObjectForm'
|
import ObjectForm from '../../common/ObjectForm'
|
||||||
@ -25,7 +27,10 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
|||||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
import {
|
||||||
|
getModelByName,
|
||||||
|
getModelProperty
|
||||||
|
} from '../../../../database/ObjectModels.js'
|
||||||
|
|
||||||
const log = loglevel.getLogger('ListingVarientInfo')
|
const log = loglevel.getLogger('ListingVarientInfo')
|
||||||
log.setLevel(config.logLevel)
|
log.setLevel(config.logLevel)
|
||||||
@ -49,6 +54,7 @@ const ListingVarientInfo = () => {
|
|||||||
'ListingVarientInfo',
|
'ListingVarientInfo',
|
||||||
{
|
{
|
||||||
info: true,
|
info: true,
|
||||||
|
listingImages: true,
|
||||||
notes: true,
|
notes: true,
|
||||||
auditLogs: false
|
auditLogs: false
|
||||||
}
|
}
|
||||||
@ -111,6 +117,7 @@ const ListingVarientInfo = () => {
|
|||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
items={[
|
items={[
|
||||||
{ key: 'info', label: 'Listing Varient Information' },
|
{ key: 'info', label: 'Listing Varient Information' },
|
||||||
|
{ key: 'listingImages', label: 'Listing Images' },
|
||||||
{ key: 'notes', label: 'Notes' },
|
{ key: 'notes', label: 'Notes' },
|
||||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
]}
|
]}
|
||||||
@ -189,6 +196,25 @@ const ListingVarientInfo = () => {
|
|||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
type='listingVarient'
|
type='listingVarient'
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
visibleProperties={{ listingImages: false }}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Listing Images'
|
||||||
|
icon={<PictureIcon />}
|
||||||
|
active={collapseState.listingImages}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('listingImages', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='listingImages'
|
||||||
|
>
|
||||||
|
<ObjectProperty
|
||||||
|
{...getModelProperty('listingVarient', 'listingImages')}
|
||||||
|
isEditing={isEditing}
|
||||||
|
objectData={objectData}
|
||||||
|
loading={loading}
|
||||||
|
size='medium'
|
||||||
|
showCard={false}
|
||||||
/>
|
/>
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -37,6 +37,7 @@ const NewListingVarient = ({ onOk, defaultValues }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
visibleProperties={{ listingImages: false }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -54,7 +55,8 @@ const NewListingVarient = ({ onOk, defaultValues }) => {
|
|||||||
createdAt: false,
|
createdAt: false,
|
||||||
updatedAt: false,
|
updatedAt: false,
|
||||||
lastSyncedAt: false,
|
lastSyncedAt: false,
|
||||||
stockQuantity: false
|
stockQuantity: false,
|
||||||
|
listingImages: false
|
||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const PublishListingVarient = ({ onOk, objectData }) => {
|
|||||||
'publish'
|
'publish'
|
||||||
)
|
)
|
||||||
if (result) {
|
if (result) {
|
||||||
message.success('Published successfully')
|
message.success('Publishing started...')
|
||||||
onOk(result)
|
onOk(result)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const UnpublishListingVarient = ({ onOk, objectData }) => {
|
|||||||
'unpublish'
|
'unpublish'
|
||||||
)
|
)
|
||||||
if (result) {
|
if (result) {
|
||||||
message.success('Unpublished successfully')
|
message.success('Unpublishing started...')
|
||||||
onOk(result)
|
onOk(result)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -35,7 +35,7 @@ const UnpublishListingVarient = ({ onOk, objectData }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageDialogView
|
<MessageDialogView
|
||||||
title="Withdraw this variant from the marketplace?"
|
title='Withdraw this variant from the marketplace?'
|
||||||
description={`Ends the live marketplace listing for this SKU where applicable (eBay).${
|
description={`Ends the live marketplace listing for this SKU where applicable (eBay).${
|
||||||
ref ? ` (variant: ${ref})` : ''
|
ref ? ` (variant: ${ref})` : ''
|
||||||
}`}
|
}`}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import ObjectProperty from '../../common/ObjectProperty.jsx'
|
|||||||
import ViewButton from '../../common/ViewButton'
|
import ViewButton from '../../common/ViewButton'
|
||||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
import TextIcon from '../../../Icons/TextIcon.jsx'
|
import TextIcon from '../../../Icons/TextIcon.jsx'
|
||||||
|
import PictureIcon from '../../../Icons/PictureIcon.jsx'
|
||||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
import ListingVarientIcon from '../../../Icons/ListingVarientIcon.jsx'
|
import ListingVarientIcon from '../../../Icons/ListingVarientIcon.jsx'
|
||||||
@ -27,7 +28,10 @@ import ObjectTable from '../../common/ObjectTable.jsx'
|
|||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
import { getModelByName, getModelProperty } from '../../../../database/ObjectModels.js'
|
import {
|
||||||
|
getModelByName,
|
||||||
|
getModelProperty
|
||||||
|
} from '../../../../database/ObjectModels.js'
|
||||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
|
||||||
const log = loglevel.getLogger('ListingInfo')
|
const log = loglevel.getLogger('ListingInfo')
|
||||||
@ -49,6 +53,7 @@ const ListingInfo = () => {
|
|||||||
const listingId = new URLSearchParams(location.search).get('listingId')
|
const listingId = new URLSearchParams(location.search).get('listingId')
|
||||||
const [collapseState, updateCollapseState] = useCollapseState('ListingInfo', {
|
const [collapseState, updateCollapseState] = useCollapseState('ListingInfo', {
|
||||||
info: true,
|
info: true,
|
||||||
|
listingImages: true,
|
||||||
description: true,
|
description: true,
|
||||||
listingVarients: true,
|
listingVarients: true,
|
||||||
notes: true,
|
notes: true,
|
||||||
@ -89,7 +94,7 @@ const ListingInfo = () => {
|
|||||||
finishEdit: () => {
|
finishEdit: () => {
|
||||||
objectFormRef?.current?.handleUpdate?.()
|
objectFormRef?.current?.handleUpdate?.()
|
||||||
return true
|
return true
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -112,6 +117,7 @@ const ListingInfo = () => {
|
|||||||
disabled={objectFormState.loading}
|
disabled={objectFormState.loading}
|
||||||
items={[
|
items={[
|
||||||
{ key: 'info', label: 'Listing Information' },
|
{ key: 'info', label: 'Listing Information' },
|
||||||
|
{ key: 'listingImages', label: 'Listing Images' },
|
||||||
{ key: 'description', label: 'Listing Description' },
|
{ key: 'description', label: 'Listing Description' },
|
||||||
{ key: 'listingVarients', label: 'Listing Varients' },
|
{ key: 'listingVarients', label: 'Listing Varients' },
|
||||||
{ key: 'notes', label: 'Notes' },
|
{ key: 'notes', label: 'Notes' },
|
||||||
@ -191,11 +197,35 @@ const ListingInfo = () => {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
type='listing'
|
type='listing'
|
||||||
labelWidth={165}
|
labelWidth={180}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
visibleProperties={{ description: false }}
|
visibleProperties={{
|
||||||
|
description: false,
|
||||||
|
listingImages: false
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Listing Images'
|
||||||
|
icon={<PictureIcon />}
|
||||||
|
active={collapseState.listingImages}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('listingImages', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='listingImages'
|
||||||
|
>
|
||||||
|
<Spin spinning={loading} indicator={<LoadingOutlined />}>
|
||||||
|
<ObjectProperty
|
||||||
|
{...getModelProperty('listing', 'listingImages')}
|
||||||
|
isEditing={isEditing}
|
||||||
|
objectData={objectData}
|
||||||
|
loading={loading}
|
||||||
|
size='medium'
|
||||||
|
showCard={false}
|
||||||
|
/>
|
||||||
|
</Spin>
|
||||||
|
</InfoCollapse>
|
||||||
|
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Listing Description'
|
title='Listing Description'
|
||||||
icon={<TextIcon />}
|
icon={<TextIcon />}
|
||||||
@ -205,16 +235,18 @@ const ListingInfo = () => {
|
|||||||
}
|
}
|
||||||
collapseKey='description'
|
collapseKey='description'
|
||||||
>
|
>
|
||||||
<Card>
|
<Spin spinning={loading} indicator={<LoadingOutlined />}>
|
||||||
<ObjectProperty
|
<Card>
|
||||||
{...getModelProperty('listing', 'description')}
|
<ObjectProperty
|
||||||
isEditing={isEditing}
|
{...getModelProperty('listing', 'description')}
|
||||||
objectData={objectData}
|
isEditing={isEditing}
|
||||||
loading={loading}
|
objectData={objectData}
|
||||||
size='medium'
|
loading={loading}
|
||||||
showCard={false}
|
size='medium'
|
||||||
/>
|
showCard={false}
|
||||||
</Card>
|
/>
|
||||||
|
</Card>
|
||||||
|
</Spin>
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Listing Varients'
|
title='Listing Varients'
|
||||||
@ -228,7 +260,8 @@ const ListingInfo = () => {
|
|||||||
<ObjectTable
|
<ObjectTable
|
||||||
type='listingVarient'
|
type='listingVarient'
|
||||||
masterFilter={{
|
masterFilter={{
|
||||||
listing: getModelByName('listing').prefix + ':' + listingId
|
listing:
|
||||||
|
getModelByName('listing').prefix + ':' + listingId
|
||||||
}}
|
}}
|
||||||
visibleColumns={{ listing: false }}
|
visibleColumns={{ listing: false }}
|
||||||
ref={listingVarientsTableRef}
|
ref={listingVarientsTableRef}
|
||||||
@ -250,7 +283,7 @@ const ListingInfo = () => {
|
|||||||
indicator={<LoadingOutlined />}
|
indicator={<LoadingOutlined />}
|
||||||
>
|
>
|
||||||
<Card>
|
<Card>
|
||||||
<NotesPanel _id={listingId} type='listing' />
|
<NotesPanel _id={listingId} type='listing' />
|
||||||
</Card>
|
</Card>
|
||||||
</Spin>
|
</Spin>
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
@ -267,10 +300,7 @@ const ListingInfo = () => {
|
|||||||
<ObjectTable
|
<ObjectTable
|
||||||
type='auditLog'
|
type='auditLog'
|
||||||
masterFilter={{
|
masterFilter={{
|
||||||
parent:
|
parent: getModelByName('listing').prefix + ':' + listingId
|
||||||
getModelByName('listing').prefix +
|
|
||||||
':' +
|
|
||||||
listingId
|
|
||||||
}}
|
}}
|
||||||
visibleColumns={{ _id: false, parent: false }}
|
visibleColumns={{ _id: false, parent: false }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -37,6 +37,7 @@ const NewListing = ({ onOk, defaultValues }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
visibleProperties={{ listingImages: false }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -54,7 +55,8 @@ const NewListing = ({ onOk, defaultValues }) => {
|
|||||||
createdAt: false,
|
createdAt: false,
|
||||||
updatedAt: false,
|
updatedAt: false,
|
||||||
lastSyncedAt: false,
|
lastSyncedAt: false,
|
||||||
stockQuantity: false
|
stockQuantity: false,
|
||||||
|
listingImages: false
|
||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
|||||||
@ -11,9 +11,13 @@ const PublishListing = ({ onOk, objectData }) => {
|
|||||||
const handlePublish = async () => {
|
const handlePublish = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const result = await sendObjectFunction(objectData._id, 'Listing', 'publish')
|
const result = await sendObjectFunction(
|
||||||
|
objectData._id,
|
||||||
|
'Listing',
|
||||||
|
'publish'
|
||||||
|
)
|
||||||
if (result) {
|
if (result) {
|
||||||
message.success('Published successfully')
|
message.success('Publishing started...')
|
||||||
onOk(result)
|
onOk(result)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -11,9 +11,13 @@ const UnpublishListing = ({ onOk, objectData }) => {
|
|||||||
const handleUnpublish = async () => {
|
const handleUnpublish = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const result = await sendObjectFunction(objectData._id, 'Listing', 'unpublish')
|
const result = await sendObjectFunction(
|
||||||
|
objectData._id,
|
||||||
|
'Listing',
|
||||||
|
'unpublish'
|
||||||
|
)
|
||||||
if (result) {
|
if (result) {
|
||||||
message.success('Unpublished successfully')
|
message.success('Unpublishing started...')
|
||||||
onOk(result)
|
onOk(result)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -253,6 +253,10 @@ const MarketplaceInfo = () => {
|
|||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
type='marketplace'
|
type='marketplace'
|
||||||
labelWidth={215}
|
labelWidth={215}
|
||||||
|
visibleProperties={{
|
||||||
|
'eBay.categoryReferences': false,
|
||||||
|
'eBay.availableShippingServices': false
|
||||||
|
}}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -271,7 +275,7 @@ const MarketplaceInfo = () => {
|
|||||||
indicator={<LoadingOutlined />}
|
indicator={<LoadingOutlined />}
|
||||||
>
|
>
|
||||||
<Card>
|
<Card>
|
||||||
<NotesPanel _id={marketplaceId} type='marketplace' />
|
<NotesPanel _id={marketplaceId} type='marketplace' />
|
||||||
</Card>
|
</Card>
|
||||||
</Spin>
|
</Spin>
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
@ -288,10 +292,8 @@ const MarketplaceInfo = () => {
|
|||||||
<ObjectTable
|
<ObjectTable
|
||||||
type='auditLog'
|
type='auditLog'
|
||||||
masterFilter={{
|
masterFilter={{
|
||||||
parent:
|
parent:
|
||||||
getModelByName('marketplace').prefix +
|
getModelByName('marketplace').prefix + ':' + marketplaceId
|
||||||
':' +
|
|
||||||
marketplaceId
|
|
||||||
}}
|
}}
|
||||||
visibleColumns={{ _id: false, parent: false }}
|
visibleColumns={{ _id: false, parent: false }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -0,0 +1,57 @@
|
|||||||
|
import { useState, useContext } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||||
|
import { message } from 'antd'
|
||||||
|
import MessageDialogView from '../../common/MessageDialogView.jsx'
|
||||||
|
|
||||||
|
const SyncAccountPolicies = ({
|
||||||
|
onOk,
|
||||||
|
objectData,
|
||||||
|
functionName,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
successMessage
|
||||||
|
}) => {
|
||||||
|
const [syncLoading, setSyncLoading] = useState(false)
|
||||||
|
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||||
|
|
||||||
|
const handleSync = async () => {
|
||||||
|
setSyncLoading(true)
|
||||||
|
try {
|
||||||
|
const result = await sendObjectFunction(
|
||||||
|
objectData._id,
|
||||||
|
'Marketplace',
|
||||||
|
functionName
|
||||||
|
)
|
||||||
|
if (result) {
|
||||||
|
message.success(successMessage)
|
||||||
|
onOk(result)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error syncing ${functionName}:`, error)
|
||||||
|
} finally {
|
||||||
|
setSyncLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MessageDialogView
|
||||||
|
title={title}
|
||||||
|
description={description}
|
||||||
|
onOk={handleSync}
|
||||||
|
okText='Sync'
|
||||||
|
okLoading={syncLoading}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncAccountPolicies.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
objectData: PropTypes.object,
|
||||||
|
functionName: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
description: PropTypes.string.isRequired,
|
||||||
|
successMessage: PropTypes.string.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SyncAccountPolicies
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import SyncAccountPolicies from './SyncAccountPolicies.jsx'
|
||||||
|
|
||||||
|
const SyncFulfillmentPolicies = ({ onOk, objectData }) => (
|
||||||
|
<SyncAccountPolicies
|
||||||
|
onOk={onOk}
|
||||||
|
objectData={objectData}
|
||||||
|
functionName='sync/fulfillmentPolicies'
|
||||||
|
title='Sync fulfillment policies from marketplace?'
|
||||||
|
description={`This will import fulfillment policies from ${objectData?.name || objectData?._reference || objectData?._id}.`}
|
||||||
|
successMessage='Fulfillment policies synced successfully'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
SyncFulfillmentPolicies.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
objectData: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SyncFulfillmentPolicies
|
||||||
@ -30,7 +30,7 @@ const SyncMarketplace = ({ onOk, objectData }) => {
|
|||||||
return (
|
return (
|
||||||
<MessageDialogView
|
<MessageDialogView
|
||||||
title='Sync marketplace?'
|
title='Sync marketplace?'
|
||||||
description={`This will refresh marketplace metadata from ${objectData?.name || objectData?._reference || objectData?._id}, including available shipping services.`}
|
description={`This will refresh marketplace metadata from ${objectData?.name || objectData?._reference || objectData?._id}, including shipping services, categories, business policies, and tax tables.`}
|
||||||
onOk={handleSync}
|
onOk={handleSync}
|
||||||
okText='Sync'
|
okText='Sync'
|
||||||
okLoading={syncLoading}
|
okLoading={syncLoading}
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import SyncAccountPolicies from './SyncAccountPolicies.jsx'
|
||||||
|
|
||||||
|
const SyncPaymentPolicies = ({ onOk, objectData }) => (
|
||||||
|
<SyncAccountPolicies
|
||||||
|
onOk={onOk}
|
||||||
|
objectData={objectData}
|
||||||
|
functionName='sync/paymentPolicies'
|
||||||
|
title='Sync payment policies from marketplace?'
|
||||||
|
description={`This will import payment policies from ${objectData?.name || objectData?._reference || objectData?._id}.`}
|
||||||
|
successMessage='Payment policies synced successfully'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
SyncPaymentPolicies.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
objectData: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SyncPaymentPolicies
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import SyncAccountPolicies from './SyncAccountPolicies.jsx'
|
||||||
|
|
||||||
|
const SyncReturnPolicies = ({ onOk, objectData }) => (
|
||||||
|
<SyncAccountPolicies
|
||||||
|
onOk={onOk}
|
||||||
|
objectData={objectData}
|
||||||
|
functionName='sync/returnPolicies'
|
||||||
|
title='Sync return policies from marketplace?'
|
||||||
|
description={`This will import return policies from ${objectData?.name || objectData?._reference || objectData?._id}.`}
|
||||||
|
successMessage='Return policies synced successfully'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
SyncReturnPolicies.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
objectData: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SyncReturnPolicies
|
||||||
20
src/components/Dashboard/Sales/Marketplaces/SyncTaxRates.jsx
Normal file
20
src/components/Dashboard/Sales/Marketplaces/SyncTaxRates.jsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import SyncAccountPolicies from './SyncAccountPolicies.jsx'
|
||||||
|
|
||||||
|
const SyncTaxRates = ({ onOk, objectData }) => (
|
||||||
|
<SyncAccountPolicies
|
||||||
|
onOk={onOk}
|
||||||
|
objectData={objectData}
|
||||||
|
functionName='sync/taxRates'
|
||||||
|
title='Sync tax rates from marketplace?'
|
||||||
|
description={`This will import tax tables from ${objectData?.name || objectData?._reference || objectData?._id} and push matching local tax rates.`}
|
||||||
|
successMessage='Tax rates synced successfully'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
SyncTaxRates.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
objectData: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SyncTaxRates
|
||||||
78
src/components/Dashboard/Sales/ReturnPolicies.jsx
Normal file
78
src/components/Dashboard/Sales/ReturnPolicies.jsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { useRef } from 'react'
|
||||||
|
import { Flex, Space } from 'antd'
|
||||||
|
import ObjectTable from '../common/ObjectTable'
|
||||||
|
import ObjectActions from '../common/ObjectActions'
|
||||||
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
|
import ObjectTableViewButton from '../common/ObjectTableViewButton'
|
||||||
|
import FilterSidebarButton from '../common/FilterSidebarButton'
|
||||||
|
import SortSidebarButton from '../common/SortSidebarButton'
|
||||||
|
import useViewMode from '../hooks/useViewMode'
|
||||||
|
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
|
||||||
|
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
|
||||||
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
|
import ExportListButton from '../common/ExportListButton'
|
||||||
|
|
||||||
|
const ReturnPolicies = () => {
|
||||||
|
const tableRef = useRef()
|
||||||
|
const [viewMode, setViewMode] = useViewMode('returnPolicy')
|
||||||
|
const [columnVisibility, setColumnVisibility] = useColumnVisibility('returnPolicy')
|
||||||
|
const [showFilterSidebar, setShowFilterSidebar] =
|
||||||
|
useFilterSidebarVisibility('ReturnPolicies')
|
||||||
|
const [showSortSidebar, setShowSortSidebar] =
|
||||||
|
useSortSidebarVisibility('ReturnPolicies')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex vertical={'true'} gap='large' className='h-100'>
|
||||||
|
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='returnPolicy'
|
||||||
|
pageName='list'
|
||||||
|
onReload={() => tableRef.current?.reload()}
|
||||||
|
/>
|
||||||
|
<ColumnViewButton
|
||||||
|
type='returnPolicy'
|
||||||
|
loading={false}
|
||||||
|
visibleState={columnVisibility}
|
||||||
|
updateVisibleState={setColumnVisibility}
|
||||||
|
/>
|
||||||
|
<ExportListButton objectType='returnPolicy' />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<SortSidebarButton
|
||||||
|
active={showSortSidebar}
|
||||||
|
onClick={() => setShowSortSidebar(!showSortSidebar)}
|
||||||
|
/>
|
||||||
|
<FilterSidebarButton
|
||||||
|
active={showFilterSidebar}
|
||||||
|
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
|
||||||
|
/>
|
||||||
|
<ObjectTableViewButton
|
||||||
|
viewMode={viewMode}
|
||||||
|
setViewMode={setViewMode}
|
||||||
|
showEndingDivider={true}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ObjectTable
|
||||||
|
ref={tableRef}
|
||||||
|
visibleColumns={columnVisibility}
|
||||||
|
type='returnPolicy'
|
||||||
|
cards={viewMode === 'cards'}
|
||||||
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
showSortSidebar={showSortSidebar}
|
||||||
|
expandHeight={true}
|
||||||
|
saveFilterInSession={true}
|
||||||
|
saveFilterInUrl={true}
|
||||||
|
useFilterInSession={true}
|
||||||
|
useFilterInUrl={true}
|
||||||
|
saveSortInSession={true}
|
||||||
|
saveSortInUrl={true}
|
||||||
|
useSortInSession={true}
|
||||||
|
useSortInUrl={true}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReturnPolicies
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
|
import WizardView from '../../common/WizardView'
|
||||||
|
|
||||||
|
const NewReturnPolicy = ({ onOk, defaultValues }) => {
|
||||||
|
return (
|
||||||
|
<NewObjectForm
|
||||||
|
type={'returnPolicy'}
|
||||||
|
defaultValues={{
|
||||||
|
returnsAccepted: true,
|
||||||
|
returnPeriodDays: 30,
|
||||||
|
returnShippingCostPayer: 'buyer',
|
||||||
|
refundMethod: 'moneyBack',
|
||||||
|
...defaultValues
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
title: 'Required',
|
||||||
|
key: 'required',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='returnPolicy'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={true}
|
||||||
|
objectData={objectData}
|
||||||
|
labelWidth={160}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Optional',
|
||||||
|
key: 'optional',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='returnPolicy'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={false}
|
||||||
|
labelWidth={160}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Summary',
|
||||||
|
key: 'summary',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='returnPolicy'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
visibleProperties={{
|
||||||
|
_id: false,
|
||||||
|
_reference: false,
|
||||||
|
createdAt: false,
|
||||||
|
updatedAt: false
|
||||||
|
}}
|
||||||
|
labelWidth={160}
|
||||||
|
isEditing={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<WizardView
|
||||||
|
steps={steps}
|
||||||
|
loading={submitLoading}
|
||||||
|
formValid={formValid}
|
||||||
|
title='New Return Policy'
|
||||||
|
onSubmit={async () => {
|
||||||
|
const result = await handleSubmit()
|
||||||
|
if (result) {
|
||||||
|
onOk()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NewObjectForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
NewReturnPolicy.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
reset: PropTypes.bool,
|
||||||
|
defaultValues: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewReturnPolicy
|
||||||
@ -0,0 +1,264 @@
|
|||||||
|
import { useRef, useState, useMemo } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Card, Flex, Space, Spin } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
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 ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||||
|
import ViewButton from '../../common/ViewButton'
|
||||||
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import MarketplaceIcon from '../../../Icons/MarketplaceIcon.jsx'
|
||||||
|
import ObjectForm from '../../common/ObjectForm'
|
||||||
|
import EditButtons from '../../common/EditButtons'
|
||||||
|
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||||
|
import ActivityIndicator from '../../common/ActivityIndicator.jsx'
|
||||||
|
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||||
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
|
import { useDashboardObjectTools } from '../../context/DashboardObjectToolsContext'
|
||||||
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
|
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||||
|
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||||
|
import {
|
||||||
|
getModelProperty,
|
||||||
|
getModelByName
|
||||||
|
} from '../../../../database/ObjectModels.js'
|
||||||
|
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||||
|
|
||||||
|
const log = loglevel.getLogger('ReturnPolicyInfo')
|
||||||
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
|
const ReturnPolicyInfo = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const objectFormRef = useRef(null)
|
||||||
|
const actionHandlerRef = useRef(null)
|
||||||
|
const returnPolicyId = new URLSearchParams(location.search).get(
|
||||||
|
'returnPolicyId'
|
||||||
|
)
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState(
|
||||||
|
'ReturnPolicyInfo',
|
||||||
|
{
|
||||||
|
info: true,
|
||||||
|
marketplaces: true,
|
||||||
|
notes: true,
|
||||||
|
auditLogs: false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const [objectFormState, setEditFormState] = useState({
|
||||||
|
isEditing: false,
|
||||||
|
editLoading: false,
|
||||||
|
formValid: false,
|
||||||
|
lock: null,
|
||||||
|
loading: false,
|
||||||
|
editDisabled: false,
|
||||||
|
objectData: {}
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentObjectTools = useMemo(
|
||||||
|
() => (
|
||||||
|
<ObjectTableNavigationButtons
|
||||||
|
disabled={objectFormState.loading || objectFormState.isEditing}
|
||||||
|
_id={returnPolicyId}
|
||||||
|
objectType='returnPolicy'
|
||||||
|
showEndingDivider={false}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[objectFormState.loading, objectFormState.isEditing, returnPolicyId]
|
||||||
|
)
|
||||||
|
useDashboardObjectTools(currentObjectTools)
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
edit: () => {
|
||||||
|
objectFormRef?.current?.startEditing?.()
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
cancelEdit: () => {
|
||||||
|
objectFormRef?.current?.cancelEditing?.()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
finishEdit: () => {
|
||||||
|
objectFormRef?.current?.handleUpdate?.()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex
|
||||||
|
gap='large'
|
||||||
|
vertical='true'
|
||||||
|
style={{ maxHeight: '100%', minHeight: 0 }}
|
||||||
|
>
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='small'>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='returnPolicy'
|
||||||
|
pageName='info'
|
||||||
|
id={returnPolicyId}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
items={[
|
||||||
|
{ key: 'info', label: 'Return Policy Information' },
|
||||||
|
{ key: 'marketplaces', label: 'Marketplaces' },
|
||||||
|
{ key: 'notes', label: 'Notes' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
<UserNotifierToggle
|
||||||
|
type='returnPolicy'
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
/>
|
||||||
|
<DocumentPrintButton
|
||||||
|
type='returnPolicy'
|
||||||
|
objectData={objectFormState.objectData}
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<ActivityIndicator
|
||||||
|
activities={objectFormState.activities}
|
||||||
|
showStartingDivider={true}
|
||||||
|
/>
|
||||||
|
</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.beingEditedByOther ||
|
||||||
|
objectFormState.loading ||
|
||||||
|
objectFormState.editDisabled
|
||||||
|
}
|
||||||
|
loading={objectFormState.editLoading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ScrollBox>
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<ActionHandler
|
||||||
|
actions={actions}
|
||||||
|
loading={objectFormState.loading}
|
||||||
|
ref={actionHandlerRef}
|
||||||
|
>
|
||||||
|
<ObjectForm
|
||||||
|
id={returnPolicyId}
|
||||||
|
type='returnPolicy'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
ref={objectFormRef}
|
||||||
|
setCurrentObject={true}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading, isEditing, objectData }) => (
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Return Policy Information'
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
active={collapseState.info}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('info', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='info'
|
||||||
|
>
|
||||||
|
<ObjectInfo
|
||||||
|
loading={loading}
|
||||||
|
isEditing={isEditing}
|
||||||
|
type='returnPolicy'
|
||||||
|
objectData={objectData}
|
||||||
|
labelWidth={225}
|
||||||
|
visibleProperties={{
|
||||||
|
marketplaces: false
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Marketplaces'
|
||||||
|
icon={<MarketplaceIcon />}
|
||||||
|
active={collapseState.marketplaces}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('marketplaces', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='marketplaces'
|
||||||
|
>
|
||||||
|
<ObjectProperty
|
||||||
|
{...getModelProperty('returnPolicy', 'marketplaces')}
|
||||||
|
isEditing={isEditing}
|
||||||
|
objectData={objectData}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</ObjectForm>
|
||||||
|
</ActionHandler>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Notes'
|
||||||
|
icon={<NoteIcon />}
|
||||||
|
active={collapseState.notes}
|
||||||
|
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||||
|
collapseKey='notes'
|
||||||
|
>
|
||||||
|
<Spin
|
||||||
|
spinning={objectFormState.loading}
|
||||||
|
indicator={<LoadingOutlined />}
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<NotesPanel _id={returnPolicyId} type='returnPolicy' />
|
||||||
|
</Card>
|
||||||
|
</Spin>
|
||||||
|
</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:
|
||||||
|
getModelByName('returnPolicy').prefix +
|
||||||
|
':' +
|
||||||
|
returnPolicyId
|
||||||
|
}}
|
||||||
|
visibleColumns={{ _id: false, parent: false }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</ScrollBox>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReturnPolicyInfo
|
||||||
74
src/components/Dashboard/common/CustomTreeSelect.jsx
Normal file
74
src/components/Dashboard/common/CustomTreeSelect.jsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
import { TreeSelect } from 'antd'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
export const findTreeNode = (nodes, value) => {
|
||||||
|
if (!Array.isArray(nodes) || value == null || value === '') return null
|
||||||
|
const match = String(value)
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (node?.value != null && String(node.value) === match) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
const child = findTreeNode(node?.children, value)
|
||||||
|
if (child) return child
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findTreeLabel = (nodes, value) => {
|
||||||
|
const node = findTreeNode(nodes, value)
|
||||||
|
return node?.title || node?.label || null
|
||||||
|
}
|
||||||
|
|
||||||
|
const CustomTreeSelect = ({
|
||||||
|
placeholder,
|
||||||
|
disabled,
|
||||||
|
options = [],
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
const prevOptionsRef = useRef(options)
|
||||||
|
const treeData = Array.isArray(options) ? options : []
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (prevOptionsRef.current === options) return
|
||||||
|
|
||||||
|
if (value !== undefined && value !== null) {
|
||||||
|
const valueExists = findTreeNode(options, value)
|
||||||
|
if (!valueExists && onChange) {
|
||||||
|
onChange(undefined)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prevOptionsRef.current = options
|
||||||
|
}, [options, value, onChange])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TreeSelect
|
||||||
|
placeholder={placeholder}
|
||||||
|
disabled={disabled}
|
||||||
|
treeData={treeData}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
showSearch
|
||||||
|
allowClear
|
||||||
|
treeNodeFilterProp='title'
|
||||||
|
treeLine
|
||||||
|
listHeight={400}
|
||||||
|
popupMatchSelectWidth={false}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomTreeSelect.propTypes = {
|
||||||
|
placeholder: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
options: PropTypes.array,
|
||||||
|
value: PropTypes.any,
|
||||||
|
onChange: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomTreeSelect
|
||||||
@ -4,7 +4,7 @@ import { Link, useLocation, useNavigate } from 'react-router-dom'
|
|||||||
import ArrowLeftIcon from '../../Icons/ArrowLeftIcon'
|
import ArrowLeftIcon from '../../Icons/ArrowLeftIcon'
|
||||||
import ArrowRightIcon from '../../Icons/ArrowRightIcon'
|
import ArrowRightIcon from '../../Icons/ArrowRightIcon'
|
||||||
import FilterIcon from '../../Icons/FilterIcon'
|
import FilterIcon from '../../Icons/FilterIcon'
|
||||||
import { getModelByName } from '../../../database/ObjectModels'
|
import { getModelByPluralName } from '../../../database/ObjectModels'
|
||||||
import { useTableState } from '../context/TableStateContext'
|
import { useTableState } from '../context/TableStateContext'
|
||||||
|
|
||||||
const breadcrumbNameMap = {
|
const breadcrumbNameMap = {
|
||||||
@ -19,6 +19,7 @@ const breadcrumbNameMap = {
|
|||||||
design: 'Design',
|
design: 'Design',
|
||||||
control: 'Control',
|
control: 'Control',
|
||||||
preview: 'Preview',
|
preview: 'Preview',
|
||||||
|
settings: 'Settings',
|
||||||
about: 'About'
|
about: 'About'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,13 +33,8 @@ const DashboardBreadcrumb = () => {
|
|||||||
|
|
||||||
function segmentToModel(segment) {
|
function segmentToModel(segment) {
|
||||||
if (segment) {
|
if (segment) {
|
||||||
// If segment ends with 's', remove it and get the model
|
|
||||||
if (segment.endsWith('s')) {
|
|
||||||
const singularSegment = segment.slice(0, -1)
|
|
||||||
return getModelByName(singularSegment, true)
|
|
||||||
}
|
|
||||||
// Otherwise, get the model as is
|
// Otherwise, get the model as is
|
||||||
return getModelByName(segment, true)
|
return getModelByPluralName(segment, true)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -48,7 +44,7 @@ const DashboardBreadcrumb = () => {
|
|||||||
if (segment !== 'dashboard') {
|
if (segment !== 'dashboard') {
|
||||||
const isMainSection = mainSections.includes(segment)
|
const isMainSection = mainSections.includes(segment)
|
||||||
const model = segmentToModel(segment)
|
const model = segmentToModel(segment)
|
||||||
const modelLabelPlural = model?.label ? `${model.label}s` : null
|
const modelLabelPlural = model?.labelPlural ? model.labelPlural : null
|
||||||
const name = breadcrumbNameMap[segment] || modelLabelPlural || segment
|
const name = breadcrumbNameMap[segment] || modelLabelPlural || segment
|
||||||
const showFilterIcon =
|
const showFilterIcon =
|
||||||
hasPageFilter(url) ||
|
hasPageFilter(url) ||
|
||||||
|
|||||||
362
src/components/Dashboard/common/FileGalleryList.jsx
Normal file
362
src/components/Dashboard/common/FileGalleryList.jsx
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
import { Button, Card, Flex, Divider } from 'antd'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { useMediaQuery } from 'react-responsive'
|
||||||
|
import FilePreview from './FilePreview'
|
||||||
|
import ScrollBox from './ScrollBox'
|
||||||
|
import Thumbnail from './Thumbnail'
|
||||||
|
import ChevronLeftIcon from '../../Icons/ChevronLeftIcon'
|
||||||
|
import ChevronRightIcon from '../../Icons/ChevronRightIcon'
|
||||||
|
import BinIcon from '../../Icons/BinIcon'
|
||||||
|
|
||||||
|
const GALLERY_PADDING = 24
|
||||||
|
const MIN_SIDE_PADDING = 66
|
||||||
|
const MAX_SLIDE_HEIGHT = 600
|
||||||
|
|
||||||
|
const getFileKey = (file, index) => file?._id || file?.id || index
|
||||||
|
|
||||||
|
const getSlideLayout = (containerWidth, naturalWidth, naturalHeight) => {
|
||||||
|
const availableWidth = Math.max(0, containerWidth - MIN_SIDE_PADDING * 2)
|
||||||
|
if (!containerWidth || availableWidth <= 0) {
|
||||||
|
return {
|
||||||
|
padding: MIN_SIDE_PADDING,
|
||||||
|
width: 0,
|
||||||
|
height: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const aspect =
|
||||||
|
naturalWidth > 0 && naturalHeight > 0 ? naturalWidth / naturalHeight : 1
|
||||||
|
|
||||||
|
let width = availableWidth
|
||||||
|
let height = width / aspect
|
||||||
|
|
||||||
|
if (height > MAX_SLIDE_HEIGHT) {
|
||||||
|
height = MAX_SLIDE_HEIGHT
|
||||||
|
width = height * aspect
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
padding: Math.max(MIN_SIDE_PADDING, (containerWidth - width) / 2),
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FileGalleryList = ({
|
||||||
|
files = [],
|
||||||
|
editing = false,
|
||||||
|
onRemove,
|
||||||
|
uploadSlot = null
|
||||||
|
}) => {
|
||||||
|
const filesToRender = useMemo(
|
||||||
|
() => (Array.isArray(files) ? files : files ? [files] : []),
|
||||||
|
[files]
|
||||||
|
)
|
||||||
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
|
const previewRef = useRef(null)
|
||||||
|
const scrollElRef = useRef(null)
|
||||||
|
const slideRefs = useRef([])
|
||||||
|
const [containerWidth, setContainerWidth] = useState(0)
|
||||||
|
const [imageSizes, setImageSizes] = useState({})
|
||||||
|
const [activeIndex, setActiveIndex] = useState(0)
|
||||||
|
|
||||||
|
const slideLayouts = useMemo(
|
||||||
|
() =>
|
||||||
|
filesToRender.map((file, index) => {
|
||||||
|
const size = imageSizes[getFileKey(file, index)]
|
||||||
|
return getSlideLayout(containerWidth, size?.width, size?.height)
|
||||||
|
}),
|
||||||
|
[containerWidth, filesToRender, imageSizes]
|
||||||
|
)
|
||||||
|
|
||||||
|
const slideHeight = useMemo(() => {
|
||||||
|
const heights = slideLayouts.map((layout) => layout.height)
|
||||||
|
return Math.max(0, ...heights)
|
||||||
|
}, [slideLayouts])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const el = previewRef.current
|
||||||
|
if (!el) return undefined
|
||||||
|
|
||||||
|
const updateWidth = () => {
|
||||||
|
setContainerWidth(el.clientWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateWidth()
|
||||||
|
const observer = new ResizeObserver(updateWidth)
|
||||||
|
observer.observe(el)
|
||||||
|
return () => observer.disconnect()
|
||||||
|
}, [filesToRender.length])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const root = previewRef.current
|
||||||
|
if (!root) return undefined
|
||||||
|
|
||||||
|
const collectSizes = () => {
|
||||||
|
const next = {}
|
||||||
|
filesToRender.forEach((file, index) => {
|
||||||
|
const img = slideRefs.current[index]?.querySelector('img')
|
||||||
|
if (img?.naturalWidth && img.naturalHeight) {
|
||||||
|
next[getFileKey(file, index)] = {
|
||||||
|
width: img.naturalWidth,
|
||||||
|
height: img.naturalHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setImageSizes((prev) => {
|
||||||
|
const nextKeys = Object.keys(next)
|
||||||
|
if (
|
||||||
|
nextKeys.length === Object.keys(prev).length &&
|
||||||
|
nextKeys.every(
|
||||||
|
(key) =>
|
||||||
|
prev[key]?.width === next[key].width &&
|
||||||
|
prev[key]?.height === next[key].height
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onLoadCapture = (event) => {
|
||||||
|
if (event.target?.tagName === 'IMG') {
|
||||||
|
collectSizes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.addEventListener('load', onLoadCapture, true)
|
||||||
|
collectSizes()
|
||||||
|
const mutationObserver = new MutationObserver(collectSizes)
|
||||||
|
mutationObserver.observe(root, { childList: true, subtree: true })
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
root.removeEventListener('load', onLoadCapture, true)
|
||||||
|
mutationObserver.disconnect()
|
||||||
|
}
|
||||||
|
}, [filesToRender])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeIndex >= filesToRender.length) {
|
||||||
|
setActiveIndex(Math.max(0, filesToRender.length - 1))
|
||||||
|
}
|
||||||
|
}, [activeIndex, filesToRender.length])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const root = scrollElRef.current
|
||||||
|
if (!root || filesToRender.length === 0) return undefined
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
const visible = entries
|
||||||
|
.filter((entry) => entry.isIntersecting)
|
||||||
|
.sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0]
|
||||||
|
if (!visible) return
|
||||||
|
const index = slideRefs.current.findIndex(
|
||||||
|
(node) => node === visible.target
|
||||||
|
)
|
||||||
|
if (index >= 0) {
|
||||||
|
setActiveIndex(index)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ root, threshold: 0.6 }
|
||||||
|
)
|
||||||
|
|
||||||
|
slideRefs.current.forEach((node) => {
|
||||||
|
if (node) observer.observe(node)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => observer.disconnect()
|
||||||
|
}, [filesToRender.length, containerWidth, slideHeight])
|
||||||
|
|
||||||
|
const scrollToIndex = useCallback((index) => {
|
||||||
|
const nextIndex = Math.max(0, Math.min(index, slideRefs.current.length - 1))
|
||||||
|
const node = slideRefs.current[nextIndex]
|
||||||
|
if (!node) return
|
||||||
|
node.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
inline: 'center',
|
||||||
|
block: 'nearest'
|
||||||
|
})
|
||||||
|
setActiveIndex(nextIndex)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleRemove = (fileToRemove) => {
|
||||||
|
onRemove?.(fileToRemove)
|
||||||
|
}
|
||||||
|
|
||||||
|
const activePadding = slideLayouts[activeIndex]?.padding || MIN_SIDE_PADDING
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
className='file-gallery-list'
|
||||||
|
styles={{
|
||||||
|
body: {
|
||||||
|
paddingTop: GALLERY_PADDING,
|
||||||
|
paddingBottom: GALLERY_PADDING,
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: 0
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{filesToRender.length > 0 ? (
|
||||||
|
<div
|
||||||
|
className={`file-gallery-list-preview${isMobile ? ' is-mobile' : ''}`}
|
||||||
|
ref={previewRef}
|
||||||
|
style={
|
||||||
|
slideHeight
|
||||||
|
? {
|
||||||
|
height: slideHeight,
|
||||||
|
'--file-gallery-side-padding': `${activePadding}px`
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className='file-gallery-list-nav file-gallery-list-nav-left'
|
||||||
|
icon={<ChevronLeftIcon />}
|
||||||
|
disabled={activeIndex <= 0}
|
||||||
|
onClick={() => scrollToIndex(activeIndex - 1)}
|
||||||
|
aria-label='Previous image'
|
||||||
|
/>
|
||||||
|
<ScrollBox
|
||||||
|
className='file-gallery-list-scroll'
|
||||||
|
horizontalBottomPadding={0}
|
||||||
|
verticalRightPadding={0}
|
||||||
|
scrollableNodeProps={{
|
||||||
|
ref: (node) => {
|
||||||
|
scrollElRef.current = node
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className='file-gallery-list-track'
|
||||||
|
style={slideHeight ? { height: slideHeight } : undefined}
|
||||||
|
>
|
||||||
|
{filesToRender.map((file, index) => {
|
||||||
|
const layout = slideLayouts[index] || {
|
||||||
|
padding: MIN_SIDE_PADDING,
|
||||||
|
width: 0,
|
||||||
|
height: 0
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={file._id || file.id || index}
|
||||||
|
className='file-gallery-list-slide'
|
||||||
|
ref={(node) => {
|
||||||
|
slideRefs.current[index] = node
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
flex: `0 0 ${containerWidth || 0}px`,
|
||||||
|
width: containerWidth || undefined,
|
||||||
|
paddingLeft: layout.padding,
|
||||||
|
paddingRight: layout.padding
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className='file-gallery-list-preview-frame'
|
||||||
|
style={
|
||||||
|
layout.width && layout.height
|
||||||
|
? {
|
||||||
|
width: layout.width,
|
||||||
|
height: layout.height
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FilePreview
|
||||||
|
file={file}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
objectFit: 'contain',
|
||||||
|
borderRadius: 8
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</ScrollBox>
|
||||||
|
<Button
|
||||||
|
className='file-gallery-list-nav file-gallery-list-nav-right'
|
||||||
|
icon={<ChevronRightIcon />}
|
||||||
|
disabled={activeIndex >= filesToRender.length - 1}
|
||||||
|
onClick={() => scrollToIndex(activeIndex + 1)}
|
||||||
|
aria-label='Next image'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<div className='file-gallery-footer'>
|
||||||
|
{filesToRender.length > 0 ? (
|
||||||
|
<Divider style={{ margin: '24px 0' }} />
|
||||||
|
) : null}
|
||||||
|
<Flex
|
||||||
|
className='file-gallery-list-thumbs'
|
||||||
|
gap={GALLERY_PADDING}
|
||||||
|
align='center'
|
||||||
|
justify='center'
|
||||||
|
wrap='nowrap'
|
||||||
|
>
|
||||||
|
{filesToRender.map((file, index) => (
|
||||||
|
<div
|
||||||
|
key={file._id || file.id || index}
|
||||||
|
className={`file-gallery-list-thumb${
|
||||||
|
index === activeIndex ? ' is-active' : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => scrollToIndex(index)}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter' || event.key === ' ') {
|
||||||
|
event.preventDefault()
|
||||||
|
scrollToIndex(index)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
role='button'
|
||||||
|
tabIndex={0}
|
||||||
|
aria-label={`Show image ${index + 1}`}
|
||||||
|
aria-current={index === activeIndex ? 'true' : undefined}
|
||||||
|
>
|
||||||
|
<Thumbnail
|
||||||
|
file={file}
|
||||||
|
size={64}
|
||||||
|
alt={file.name || file.filename || ''}
|
||||||
|
/>
|
||||||
|
{editing ? (
|
||||||
|
<Card
|
||||||
|
size='small'
|
||||||
|
className='file-gallery-list-thumb-delete'
|
||||||
|
styles={{ body: { padding: 2 } }}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type='text'
|
||||||
|
size='small'
|
||||||
|
icon={<BinIcon />}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
handleRemove(file)
|
||||||
|
}}
|
||||||
|
aria-label='Remove image'
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{editing && uploadSlot ? (
|
||||||
|
<div className='file-gallery-list-upload'>{uploadSlot}</div>
|
||||||
|
) : null}
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
FileGalleryList.propTypes = {
|
||||||
|
files: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||||
|
editing: PropTypes.bool,
|
||||||
|
onRemove: PropTypes.func,
|
||||||
|
uploadSlot: PropTypes.node
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileGalleryList
|
||||||
@ -7,11 +7,13 @@ import DownloadIcon from '../../Icons/DownloadIcon'
|
|||||||
import { useContext, useState } from 'react'
|
import { useContext, useState } from 'react'
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
import FilePreview from './FilePreview'
|
import FilePreview from './FilePreview'
|
||||||
|
import FileGalleryList from './FileGalleryList'
|
||||||
import EyeSlashIcon from '../../Icons/EyeSlashIcon'
|
import EyeSlashIcon from '../../Icons/EyeSlashIcon'
|
||||||
import { getModelByName } from '../../../database/ObjectModels'
|
import { getModelByName } from '../../../database/ObjectModels'
|
||||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import ElipsisText from './ElipsisText'
|
import ElipsisText from './ElipsisText'
|
||||||
|
import MissingPlaceholder from './MissingPlaceholder'
|
||||||
|
|
||||||
const FileList = ({
|
const FileList = ({
|
||||||
files,
|
files,
|
||||||
@ -24,6 +26,8 @@ const FileList = ({
|
|||||||
defaultPreviewOpen = false,
|
defaultPreviewOpen = false,
|
||||||
minimal = false,
|
minimal = false,
|
||||||
card = true,
|
card = true,
|
||||||
|
gallery = false,
|
||||||
|
uploadSlot = null,
|
||||||
maxWidth = '100%'
|
maxWidth = '100%'
|
||||||
}) => {
|
}) => {
|
||||||
const { fetchFileContent, flushFile } = useContext(ApiServerContext)
|
const { fetchFileContent, flushFile } = useContext(ApiServerContext)
|
||||||
@ -37,10 +41,6 @@ const FileList = ({
|
|||||||
? !files || !Array.isArray(files) || files.length === 0
|
? !files || !Array.isArray(files) || files.length === 0
|
||||||
: !files
|
: !files
|
||||||
|
|
||||||
if (hasNoItems) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleRemove = (fileToRemove) => {
|
const handleRemove = (fileToRemove) => {
|
||||||
flushFile(fileToRemove._id)
|
flushFile(fileToRemove._id)
|
||||||
if (multiple) {
|
if (multiple) {
|
||||||
@ -60,7 +60,41 @@ const FileList = ({
|
|||||||
await fetchFileContent(file, true)
|
await fetchFileContent(file, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const filesToRender = multiple ? files : [files]
|
const filesToRender = multiple
|
||||||
|
? Array.isArray(files)
|
||||||
|
? files
|
||||||
|
: []
|
||||||
|
: files
|
||||||
|
? [files]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const rootStyle = {
|
||||||
|
minWidth: 0,
|
||||||
|
maxWidth,
|
||||||
|
...(card
|
||||||
|
? { width: '100%' }
|
||||||
|
: { width: 'fit-content', alignSelf: 'flex-start' })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gallery && !minimal) {
|
||||||
|
if (hasNoItems && !(gallery && (editing || uploadSlot))) {
|
||||||
|
return <MissingPlaceholder message='No listing images.' />
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div style={rootStyle}>
|
||||||
|
<FileGalleryList
|
||||||
|
files={filesToRender}
|
||||||
|
editing={editing}
|
||||||
|
uploadSlot={uploadSlot}
|
||||||
|
onRemove={handleRemove}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNoItems && !(gallery && (editing || uploadSlot))) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const renderFileContent = (file) => (
|
const renderFileContent = (file) => (
|
||||||
<Flex
|
<Flex
|
||||||
@ -151,14 +185,6 @@ const FileList = ({
|
|||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
|
|
||||||
const rootStyle = {
|
|
||||||
minWidth: 0,
|
|
||||||
maxWidth,
|
|
||||||
...(card
|
|
||||||
? { width: '100%' }
|
|
||||||
: { width: 'fit-content', alignSelf: 'flex-start' })
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={rootStyle}>
|
<div style={rootStyle}>
|
||||||
{filesToRender.map((file, index) => {
|
{filesToRender.map((file, index) => {
|
||||||
@ -205,6 +231,8 @@ FileList.propTypes = {
|
|||||||
showDownload: PropTypes.bool,
|
showDownload: PropTypes.bool,
|
||||||
defaultPreviewOpen: PropTypes.bool,
|
defaultPreviewOpen: PropTypes.bool,
|
||||||
card: PropTypes.bool,
|
card: PropTypes.bool,
|
||||||
|
gallery: PropTypes.bool,
|
||||||
|
uploadSlot: PropTypes.node,
|
||||||
minimal: PropTypes.bool,
|
minimal: PropTypes.bool,
|
||||||
maxWidth: PropTypes.string
|
maxWidth: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,22 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
import { useCallback, useContext, useEffect, useState, memo } from 'react'
|
import {
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
memo,
|
||||||
|
useRef
|
||||||
|
} from 'react'
|
||||||
import LoadingPlaceholder from './LoadingPlaceholder'
|
import LoadingPlaceholder from './LoadingPlaceholder'
|
||||||
import GCodePreview from './GCodePreview'
|
import GCodePreview from './GCodePreview'
|
||||||
import ThreeDPreview from './ThreeDPreview'
|
import ThreeDPreview from './ThreeDPreview'
|
||||||
import { AuthContext } from '../context/AuthContext'
|
import { AuthContext } from '../context/AuthContext'
|
||||||
|
|
||||||
const FilePreview = ({ file, style = {} }) => {
|
const FilePreview = ({ file, style = {} }) => {
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('FILEPREVIEWFILE', file)
|
||||||
|
}, [file])
|
||||||
const { token } = useContext(AuthContext)
|
const { token } = useContext(AuthContext)
|
||||||
const { fetchFileContent } = useContext(ApiServerContext)
|
const { fetchFileContent } = useContext(ApiServerContext)
|
||||||
|
|
||||||
@ -14,6 +24,8 @@ const FilePreview = ({ file, style = {} }) => {
|
|||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
|
|
||||||
|
const currentId = useRef(null)
|
||||||
|
|
||||||
const fetchPreview = useCallback(async () => {
|
const fetchPreview = useCallback(async () => {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
return
|
return
|
||||||
@ -31,7 +43,8 @@ const FilePreview = ({ file, style = {} }) => {
|
|||||||
}, [file, fetchFileContent, error])
|
}, [file, fetchFileContent, error])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (file?.type && token != null) {
|
if (file?.type && token != null && file._id !== currentId.current) {
|
||||||
|
currentId.current = file._id
|
||||||
fetchPreview()
|
fetchPreview()
|
||||||
}
|
}
|
||||||
}, [file._id, file?.type, fetchPreview, token])
|
}, [file._id, file?.type, fetchPreview, token])
|
||||||
|
|||||||
@ -28,6 +28,62 @@ const getFileIdentity = (value, multiple) => {
|
|||||||
return value?._id || value || ''
|
return value?._id || value || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getMasterFilterAccept = (masterFilter) =>
|
||||||
|
Array.isArray(masterFilter) && masterFilter.length > 0
|
||||||
|
? masterFilter.join(',')
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
const FileUploadDropzone = ({ onUploaded, multiple = true, masterFilter = [] }) => {
|
||||||
|
const { uploadFile } = useContext(ApiServerContext)
|
||||||
|
const [uploading, setUploading] = useState(false)
|
||||||
|
const [uploadProgress, setUploadProgress] = useState(0)
|
||||||
|
|
||||||
|
const handleFileUpload = async (file) => {
|
||||||
|
try {
|
||||||
|
setUploading(true)
|
||||||
|
const uploadedFile = await uploadFile(file, {}, (progress) => {
|
||||||
|
setUploadProgress(progress)
|
||||||
|
})
|
||||||
|
if (uploadedFile) {
|
||||||
|
onUploaded?.(uploadedFile)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('File upload failed:', error)
|
||||||
|
} finally {
|
||||||
|
setUploading(false)
|
||||||
|
setUploadProgress(0)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Upload.Dragger
|
||||||
|
beforeUpload={handleFileUpload}
|
||||||
|
showUploadList={false}
|
||||||
|
multiple={multiple}
|
||||||
|
accept={getMasterFilterAccept(masterFilter)}
|
||||||
|
className='file-gallery-list-dropzone'
|
||||||
|
openFileDialogOnClick
|
||||||
|
>
|
||||||
|
{uploading ? (
|
||||||
|
uploadProgress > 0 && uploadProgress < 100 ? (
|
||||||
|
<Text style={{ fontSize: 11 }}>{uploadProgress}%</Text>
|
||||||
|
) : (
|
||||||
|
<LoadingOutlined />
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<PlusIcon />
|
||||||
|
)}
|
||||||
|
</Upload.Dragger>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
FileUploadDropzone.propTypes = {
|
||||||
|
onUploaded: PropTypes.func,
|
||||||
|
multiple: PropTypes.bool,
|
||||||
|
masterFilter: PropTypes.array
|
||||||
|
}
|
||||||
|
|
||||||
const FileUpload = ({
|
const FileUpload = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
@ -36,6 +92,7 @@ const FileUpload = ({
|
|||||||
defaultPreviewOpen = false,
|
defaultPreviewOpen = false,
|
||||||
showPreview = true,
|
showPreview = true,
|
||||||
showInfo,
|
showInfo,
|
||||||
|
gallery = false,
|
||||||
masterFilter = []
|
masterFilter = []
|
||||||
}) => {
|
}) => {
|
||||||
const { uploadFile } = useContext(ApiServerContext)
|
const { uploadFile } = useContext(ApiServerContext)
|
||||||
@ -135,6 +192,37 @@ const FileUpload = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gallery) {
|
||||||
|
return (
|
||||||
|
<FileList
|
||||||
|
files={currentFiles}
|
||||||
|
multiple={multiple}
|
||||||
|
editing={true}
|
||||||
|
gallery={true}
|
||||||
|
showInfo={showInfo || false}
|
||||||
|
showPreview={showPreview}
|
||||||
|
defaultPreviewOpen={defaultPreviewOpen}
|
||||||
|
uploadSlot={
|
||||||
|
<FileUploadDropzone
|
||||||
|
multiple={multiple}
|
||||||
|
masterFilter={masterFilter}
|
||||||
|
onUploaded={(uploadedFile) => {
|
||||||
|
if (multiple) {
|
||||||
|
const existing = Array.isArray(currentFiles) ? currentFiles : []
|
||||||
|
updateCurrentFiles([...existing, uploadedFile])
|
||||||
|
} else {
|
||||||
|
updateCurrentFiles(uploadedFile)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onChange={(updatedFiles) => {
|
||||||
|
updateCurrentFiles(updatedFiles)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap={'small'} vertical>
|
<Flex gap={'small'} vertical>
|
||||||
{hasNoItems && uploading == false ? (
|
{hasNoItems && uploading == false ? (
|
||||||
@ -220,6 +308,7 @@ FileUpload.propTypes = {
|
|||||||
showInfo: PropTypes.bool,
|
showInfo: PropTypes.bool,
|
||||||
defaultPreviewOpen: PropTypes.bool,
|
defaultPreviewOpen: PropTypes.bool,
|
||||||
minimal: PropTypes.bool,
|
minimal: PropTypes.bool,
|
||||||
|
gallery: PropTypes.bool,
|
||||||
masterFilter: PropTypes.array
|
masterFilter: PropTypes.array
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -783,19 +783,16 @@ const ObjectForm = forwardRef(
|
|||||||
isEditingRef.current = false
|
isEditingRef.current = false
|
||||||
onStateChangeRef.current({ isEditing: isEditingRef.current })
|
onStateChangeRef.current({ isEditing: isEditingRef.current })
|
||||||
|
|
||||||
serverObjectData.current = updatedObject
|
const mergedObject = mergeFormData(updatedObject, currentFormData)
|
||||||
const computedEntries = calculateComputedValues(
|
serverObjectData.current = mergedObject
|
||||||
updatedObject,
|
const computedEntries = calculateComputedValues(mergedObject, model, {
|
||||||
model,
|
skipObjectChildrenValue: true
|
||||||
{
|
})
|
||||||
skipObjectChildrenValue: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const nextObjectData = applyComputedEntries(
|
const nextObjectData = applyComputedEntries(
|
||||||
updatedObject,
|
mergedObject,
|
||||||
computedEntries
|
computedEntries
|
||||||
)
|
)
|
||||||
copyObjectChildren(nextObjectData, updatedObject, model)
|
copyObjectChildren(nextObjectData, mergedObject, model)
|
||||||
setObjectData({
|
setObjectData({
|
||||||
...nextObjectData,
|
...nextObjectData,
|
||||||
_isEditing: false
|
_isEditing: false
|
||||||
|
|||||||
@ -6,7 +6,8 @@ import {
|
|||||||
InputNumber,
|
InputNumber,
|
||||||
Form,
|
Form,
|
||||||
Select,
|
Select,
|
||||||
Switch
|
Switch,
|
||||||
|
Tree
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import IdDisplay from './IdDisplay'
|
import IdDisplay from './IdDisplay'
|
||||||
import TimeDisplay from './TimeDisplay'
|
import TimeDisplay from './TimeDisplay'
|
||||||
@ -41,6 +42,7 @@ import ObjectTypeSelect from './ObjectTypeSelect'
|
|||||||
import ObjectTypeDisplay from './ObjectTypeDisplay'
|
import ObjectTypeDisplay from './ObjectTypeDisplay'
|
||||||
import CodeBlockEditor from './CodeBlockEditor'
|
import CodeBlockEditor from './CodeBlockEditor'
|
||||||
import CustomSelect from './CustomSelect'
|
import CustomSelect from './CustomSelect'
|
||||||
|
import CustomTreeSelect, { findTreeLabel } from './CustomTreeSelect'
|
||||||
import StateDisplay from './StateDisplay'
|
import StateDisplay from './StateDisplay'
|
||||||
import AlertsDisplay from './AlertsDisplay'
|
import AlertsDisplay from './AlertsDisplay'
|
||||||
import FileUpload from './FileUpload'
|
import FileUpload from './FileUpload'
|
||||||
@ -136,6 +138,7 @@ const ObjectProperty = ({
|
|||||||
hiddenPropertyWidth = '100px',
|
hiddenPropertyWidth = '100px',
|
||||||
inherit = true,
|
inherit = true,
|
||||||
scrollHeight,
|
scrollHeight,
|
||||||
|
gallery = false,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
if (typeof value === 'function') {
|
if (typeof value === 'function') {
|
||||||
@ -270,6 +273,28 @@ const ObjectProperty = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 'treeSelect': {
|
||||||
|
if (Array.isArray(value) && value.length) {
|
||||||
|
return (
|
||||||
|
<Tree
|
||||||
|
treeData={value}
|
||||||
|
fieldNames={{ title: 'title', key: 'value', children: 'children' }}
|
||||||
|
height={320}
|
||||||
|
virtual
|
||||||
|
defaultExpandAll={false}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const treeLabel = findTreeLabel(options, value)
|
||||||
|
if (treeLabel || (value != null && value !== '')) {
|
||||||
|
return <Text {...textParams}>{treeLabel || String(value)}</Text>
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Text type='secondary' {...textParams}>
|
||||||
|
n/a
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
case 'priceMode':
|
case 'priceMode':
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 'margin':
|
case 'margin':
|
||||||
@ -655,6 +680,8 @@ const ObjectProperty = ({
|
|||||||
showPreview={showPreview}
|
showPreview={showPreview}
|
||||||
showInfo={showHyperlink}
|
showInfo={showHyperlink}
|
||||||
showDownload={showDownload}
|
showDownload={showDownload}
|
||||||
|
gallery={gallery}
|
||||||
|
editing={false}
|
||||||
masterFilter={masterFilter}
|
masterFilter={masterFilter}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -670,6 +697,8 @@ const ObjectProperty = ({
|
|||||||
defaultPreviewOpen={previewOpen}
|
defaultPreviewOpen={previewOpen}
|
||||||
showPreview={showPreview}
|
showPreview={showPreview}
|
||||||
showInfo={showHyperlink}
|
showInfo={showHyperlink}
|
||||||
|
gallery={gallery}
|
||||||
|
editing={false}
|
||||||
masterFilter={masterFilter}
|
masterFilter={masterFilter}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -838,6 +867,14 @@ const ObjectProperty = ({
|
|||||||
{...inputProps}
|
{...inputProps}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
case 'treeSelect':
|
||||||
|
return (
|
||||||
|
<CustomTreeSelect
|
||||||
|
placeholder={'Select a ' + label.toLowerCase() + '...'}
|
||||||
|
options={Array.isArray(options) ? options : []}
|
||||||
|
{...inputProps}
|
||||||
|
/>
|
||||||
|
)
|
||||||
case 'state':
|
case 'state':
|
||||||
if (options?.length && !readOnly) {
|
if (options?.length && !readOnly) {
|
||||||
return (
|
return (
|
||||||
@ -1002,6 +1039,7 @@ const ObjectProperty = ({
|
|||||||
defaultPreviewOpen={previewOpen}
|
defaultPreviewOpen={previewOpen}
|
||||||
showPreview={showPreview}
|
showPreview={showPreview}
|
||||||
showInfo={showHyperlink}
|
showInfo={showHyperlink}
|
||||||
|
gallery={gallery}
|
||||||
masterFilter={masterFilter}
|
masterFilter={masterFilter}
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
/>
|
/>
|
||||||
@ -1014,6 +1052,7 @@ const ObjectProperty = ({
|
|||||||
defaultPreviewOpen={previewOpen}
|
defaultPreviewOpen={previewOpen}
|
||||||
showPreview={showPreview}
|
showPreview={showPreview}
|
||||||
showInfo={showHyperlink}
|
showInfo={showHyperlink}
|
||||||
|
gallery={gallery}
|
||||||
masterFilter={masterFilter}
|
masterFilter={masterFilter}
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
/>
|
/>
|
||||||
@ -1073,7 +1112,7 @@ const ObjectProperty = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
ObjectProperty.propTypes = {
|
ObjectProperty.propTypes = {
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.oneOfType([PropTypes.string, PropTypes.func]).isRequired,
|
||||||
modelType: PropTypes.string,
|
modelType: PropTypes.string,
|
||||||
value: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
|
value: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
|
||||||
isEditing: PropTypes.bool,
|
isEditing: PropTypes.bool,
|
||||||
@ -1101,7 +1140,7 @@ ObjectProperty.propTypes = {
|
|||||||
showPreview: PropTypes.bool,
|
showPreview: PropTypes.bool,
|
||||||
useFormItem: PropTypes.bool,
|
useFormItem: PropTypes.bool,
|
||||||
showHyperlink: PropTypes.bool,
|
showHyperlink: PropTypes.bool,
|
||||||
options: PropTypes.array,
|
options: PropTypes.oneOfType([PropTypes.array, PropTypes.func]),
|
||||||
showSince: PropTypes.bool,
|
showSince: PropTypes.bool,
|
||||||
inTable: PropTypes.bool,
|
inTable: PropTypes.bool,
|
||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
@ -1112,7 +1151,8 @@ ObjectProperty.propTypes = {
|
|||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
hiddenPropertyWidth: PropTypes.string,
|
hiddenPropertyWidth: PropTypes.string,
|
||||||
inherit: PropTypes.bool,
|
inherit: PropTypes.bool,
|
||||||
scrollHeight: PropTypes.string
|
scrollHeight: PropTypes.string,
|
||||||
|
gallery: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectProperty
|
export default ObjectProperty
|
||||||
|
|||||||
@ -20,10 +20,19 @@ const StateDisplay = ({
|
|||||||
'queued',
|
'queued',
|
||||||
'printing',
|
'printing',
|
||||||
'used',
|
'used',
|
||||||
'deploying'
|
'deploying',
|
||||||
|
'syncing',
|
||||||
|
'publishing',
|
||||||
|
'unpublishing'
|
||||||
]
|
]
|
||||||
const orangeProgressTypes = ['used', 'deploying', 'queued']
|
const orangeProgressTypes = ['used', 'deploying', 'queued']
|
||||||
const activeProgressTypes = ['printing', 'deploying']
|
const activeProgressTypes = [
|
||||||
|
'printing',
|
||||||
|
'deploying',
|
||||||
|
'publishing',
|
||||||
|
'unpublishing',
|
||||||
|
'syncing'
|
||||||
|
]
|
||||||
|
|
||||||
const currentState = state || {
|
const currentState = state || {
|
||||||
type: 'unknown',
|
type: 'unknown',
|
||||||
|
|||||||
@ -100,6 +100,14 @@ const StateTag = ({ state, showBadge = true, showTag = true, style = {} }) => {
|
|||||||
status = 'processing'
|
status = 'processing'
|
||||||
text = 'Syncing'
|
text = 'Syncing'
|
||||||
break
|
break
|
||||||
|
case 'publishing':
|
||||||
|
status = 'processing'
|
||||||
|
text = 'Publishing'
|
||||||
|
break
|
||||||
|
case 'unpublishing':
|
||||||
|
status = 'processing'
|
||||||
|
text = 'Unpublishing'
|
||||||
|
break
|
||||||
case 'disconnected':
|
case 'disconnected':
|
||||||
status = 'default'
|
status = 'default'
|
||||||
text = 'Disconnected'
|
text = 'Disconnected'
|
||||||
@ -112,6 +120,10 @@ const StateTag = ({ state, showBadge = true, showTag = true, style = {} }) => {
|
|||||||
status = 'warning'
|
status = 'warning'
|
||||||
text = 'Queued'
|
text = 'Queued'
|
||||||
break
|
break
|
||||||
|
case 'pending':
|
||||||
|
status = 'default'
|
||||||
|
text = 'Pending'
|
||||||
|
break
|
||||||
case 'used':
|
case 'used':
|
||||||
status = 'warning'
|
status = 'warning'
|
||||||
text = 'Used'
|
text = 'Used'
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
|
import get from 'lodash/get'
|
||||||
import mergeWith from 'lodash/mergeWith'
|
import mergeWith from 'lodash/mergeWith'
|
||||||
|
import set from 'lodash/set'
|
||||||
|
|
||||||
|
const NESTED_OBJECT_KEYS = ['_id', '_reference', 'name', 'state']
|
||||||
|
|
||||||
export function capitalizeFirstLetter(string) {
|
export function capitalizeFirstLetter(string) {
|
||||||
try {
|
try {
|
||||||
@ -71,3 +75,72 @@ export function mergeFormData(...sources) {
|
|||||||
mergeFormCustomizer
|
mergeFormCustomizer
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pickNestedObjectFields = (value) => {
|
||||||
|
if (value == null || typeof value !== 'object') {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map(pickNestedObjectFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
const picked = {}
|
||||||
|
NESTED_OBJECT_KEYS.forEach((key) => {
|
||||||
|
if (value[key] !== undefined) {
|
||||||
|
picked[key] = value[key]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return picked
|
||||||
|
}
|
||||||
|
|
||||||
|
const stripProperties = (data, properties) => {
|
||||||
|
if (data == null || typeof data !== 'object' || !Array.isArray(properties)) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
const next = Array.isArray(data) ? [...data] : { ...data }
|
||||||
|
|
||||||
|
properties.forEach((property) => {
|
||||||
|
if (!property?.name) return
|
||||||
|
|
||||||
|
const current = get(next, property.name)
|
||||||
|
|
||||||
|
if (property.type === 'object') {
|
||||||
|
if (current === undefined) return
|
||||||
|
set(next, property.name, pickNestedObjectFields(current))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property.type === 'objectList') {
|
||||||
|
if (!Array.isArray(current)) return
|
||||||
|
set(next, property.name, current.map(pickNestedObjectFields))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(property.properties) || property.properties.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property.type === 'objectChildren' || Array.isArray(current)) {
|
||||||
|
if (!Array.isArray(current)) return
|
||||||
|
set(
|
||||||
|
next,
|
||||||
|
property.name,
|
||||||
|
current.map((child) => stripProperties(child, property.properties))
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current != null && typeof current === 'object') {
|
||||||
|
set(next, property.name, stripProperties(current, property.properties))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before create/update, keep nested object / objectList refs as stubs so we
|
||||||
|
// do not post populated fields (e.g. marketplace category trees).
|
||||||
|
export function stripNestedObjectProperties(data, modelDefinition) {
|
||||||
|
return stripProperties(data, modelDefinition?.properties)
|
||||||
|
}
|
||||||
|
|||||||
6
src/components/Icons/FulfillmentPolicyIcon.jsx
Normal file
6
src/components/Icons/FulfillmentPolicyIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import CustomIconSvg from '../../../assets/icons/fulfillmentpolicyicon.svg?react'
|
||||||
|
|
||||||
|
const FulfillmentPolicyIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default FulfillmentPolicyIcon
|
||||||
6
src/components/Icons/PaymentPolicyIcon.jsx
Normal file
6
src/components/Icons/PaymentPolicyIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import CustomIconSvg from '../../../assets/icons/paymentpolicyicon.svg?react'
|
||||||
|
|
||||||
|
const PaymentPolicyIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default PaymentPolicyIcon
|
||||||
6
src/components/Icons/ReturnPolicyIcon.jsx
Normal file
6
src/components/Icons/ReturnPolicyIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import CustomIconSvg from '../../../assets/icons/returnpolicyicon.svg?react'
|
||||||
|
|
||||||
|
const ReturnPolicyIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default ReturnPolicyIcon
|
||||||
@ -21,6 +21,7 @@ import ClientIcon from './ClientIcon'
|
|||||||
import SalesOrderIcon from './SalesOrderIcon'
|
import SalesOrderIcon from './SalesOrderIcon'
|
||||||
import MarketplaceIcon from './MarketplaceIcon'
|
import MarketplaceIcon from './MarketplaceIcon'
|
||||||
import ListingIcon from './ListingIcon'
|
import ListingIcon from './ListingIcon'
|
||||||
|
import ListingVarientIcon from './ListingVarientIcon'
|
||||||
import FinanceIcon from './FinanceIcon'
|
import FinanceIcon from './FinanceIcon'
|
||||||
import InvoiceIcon from './InvoiceIcon'
|
import InvoiceIcon from './InvoiceIcon'
|
||||||
import PaymentIcon from './PaymentIcon'
|
import PaymentIcon from './PaymentIcon'
|
||||||
@ -51,6 +52,9 @@ import CourierIcon from './CourierIcon'
|
|||||||
import CourierServiceIcon from './CourierServiceIcon'
|
import CourierServiceIcon from './CourierServiceIcon'
|
||||||
import TaxRateIcon from './TaxRateIcon'
|
import TaxRateIcon from './TaxRateIcon'
|
||||||
import TaxRecordIcon from './TaxRecordIcon'
|
import TaxRecordIcon from './TaxRecordIcon'
|
||||||
|
import FulfillmentPolicyIcon from './FulfillmentPolicyIcon'
|
||||||
|
import ReturnPolicyIcon from './ReturnPolicyIcon'
|
||||||
|
import PaymentPolicyIcon from './PaymentPolicyIcon'
|
||||||
import AppPasswordIcon from './AppPasswordIcon'
|
import AppPasswordIcon from './AppPasswordIcon'
|
||||||
import InfoCircleIcon from './InfoCircleIcon'
|
import InfoCircleIcon from './InfoCircleIcon'
|
||||||
|
|
||||||
@ -80,6 +84,10 @@ const sidebarIconMap = {
|
|||||||
salesOrder: <SalesOrderIcon />,
|
salesOrder: <SalesOrderIcon />,
|
||||||
marketplace: <MarketplaceIcon />,
|
marketplace: <MarketplaceIcon />,
|
||||||
listing: <ListingIcon />,
|
listing: <ListingIcon />,
|
||||||
|
listingVarient: <ListingVarientIcon />,
|
||||||
|
fulfillmentPolicy: <FulfillmentPolicyIcon />,
|
||||||
|
returnPolicy: <ReturnPolicyIcon />,
|
||||||
|
paymentPolicy: <PaymentPolicyIcon />,
|
||||||
finance: <FinanceIcon />,
|
finance: <FinanceIcon />,
|
||||||
invoice: <InvoiceIcon />,
|
invoice: <InvoiceIcon />,
|
||||||
payment: <PaymentIcon />,
|
payment: <PaymentIcon />,
|
||||||
|
|||||||
@ -49,6 +49,9 @@ import { SalesOrder } from './models/SalesOrder.js'
|
|||||||
import { Marketplace } from './models/Marketplace.js'
|
import { Marketplace } from './models/Marketplace.js'
|
||||||
import { Listing } from './models/Listing.js'
|
import { Listing } from './models/Listing.js'
|
||||||
import { ListingVarient } from './models/ListingVarient.js'
|
import { ListingVarient } from './models/ListingVarient.js'
|
||||||
|
import { FulfillmentPolicy } from './models/FulfillmentPolicy.js'
|
||||||
|
import { ReturnPolicy } from './models/ReturnPolicy.js'
|
||||||
|
import { PaymentPolicy } from './models/PaymentPolicy.js'
|
||||||
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
|
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
|
||||||
|
|
||||||
export const objectModels = [
|
export const objectModels = [
|
||||||
@ -102,7 +105,10 @@ export const objectModels = [
|
|||||||
SalesOrder,
|
SalesOrder,
|
||||||
Marketplace,
|
Marketplace,
|
||||||
Listing,
|
Listing,
|
||||||
ListingVarient
|
ListingVarient,
|
||||||
|
FulfillmentPolicy,
|
||||||
|
ReturnPolicy,
|
||||||
|
PaymentPolicy
|
||||||
]
|
]
|
||||||
|
|
||||||
// Re-export individual models for direct access
|
// Re-export individual models for direct access
|
||||||
@ -157,7 +163,10 @@ export {
|
|||||||
SalesOrder,
|
SalesOrder,
|
||||||
Marketplace,
|
Marketplace,
|
||||||
Listing,
|
Listing,
|
||||||
ListingVarient
|
ListingVarient,
|
||||||
|
FulfillmentPolicy,
|
||||||
|
ReturnPolicy,
|
||||||
|
PaymentPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getModelByName(name, ignoreCase = false) {
|
export function getModelByName(name, ignoreCase = false) {
|
||||||
@ -179,6 +188,27 @@ export function getModelByName(name, ignoreCase = false) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getModelByPluralName(pluralName, ignoreCase = false) {
|
||||||
|
function formatName(formattedName) {
|
||||||
|
if (ignoreCase == true) {
|
||||||
|
formattedName = formattedName.toUpperCase().replace(' ', '')
|
||||||
|
}
|
||||||
|
return formattedName
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
objectModels.find(
|
||||||
|
(meta) => formatName(meta.labelPlural) === formatName(pluralName)
|
||||||
|
) || {
|
||||||
|
name: 'unknown',
|
||||||
|
label: 'Unknown',
|
||||||
|
prefix: 'UNK',
|
||||||
|
icon: QuestionCircleIcon,
|
||||||
|
url: () => '#',
|
||||||
|
properties: {}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function getModelProperty(name, property) {
|
export function getModelProperty(name, property) {
|
||||||
const model = getModelByName(name)
|
const model = getModelByName(name)
|
||||||
|
|
||||||
|
|||||||
@ -24,10 +24,7 @@ const SIDEBARS = {
|
|||||||
key: 'sales',
|
key: 'sales',
|
||||||
label: 'Sales',
|
label: 'Sales',
|
||||||
iconKey: 'sales',
|
iconKey: 'sales',
|
||||||
items: salesSidebarItems,
|
items: salesSidebarItems
|
||||||
routeAliases: {
|
|
||||||
'/dashboard/sales/listingvarients': 'listings'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
finance: {
|
finance: {
|
||||||
key: 'finance',
|
key: 'finance',
|
||||||
|
|||||||
@ -30,6 +30,15 @@ export const File = {
|
|||||||
label: 'List',
|
label: 'List',
|
||||||
icon: ListIcon
|
icon: ListIcon
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'new',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
label: 'New File',
|
||||||
|
visible: () => {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'info',
|
name: 'info',
|
||||||
type: 'page',
|
type: 'page',
|
||||||
@ -117,14 +126,7 @@ export const File = {
|
|||||||
'updatedAt',
|
'updatedAt',
|
||||||
'_reference'
|
'_reference'
|
||||||
],
|
],
|
||||||
sorters: [
|
sorters: ['name', 'type', 'size', 'createdAt', 'temp', 'updatedAt'],
|
||||||
'name',
|
|
||||||
'type',
|
|
||||||
'size',
|
|
||||||
'createdAt',
|
|
||||||
'temp',
|
|
||||||
'updatedAt'
|
|
||||||
],
|
|
||||||
group: ['type'],
|
group: ['type'],
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
|
|||||||
231
src/database/models/FulfillmentPolicy.js
Normal file
231
src/database/models/FulfillmentPolicy.js
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
import { createElement, lazy } from 'react'
|
||||||
|
|
||||||
|
const FulfillmentPolicyInfo = lazy(
|
||||||
|
() =>
|
||||||
|
import('../../components/Dashboard/Sales/FulfillmentPolicies/FulfillmentPolicyInfo')
|
||||||
|
)
|
||||||
|
const NewFulfillmentPolicy = lazy(
|
||||||
|
() =>
|
||||||
|
import('../../components/Dashboard/Sales/FulfillmentPolicies/NewFulfillmentPolicy')
|
||||||
|
)
|
||||||
|
const DeleteObject = lazy(
|
||||||
|
() => import('../../components/Dashboard/common/DeleteObject')
|
||||||
|
)
|
||||||
|
import FulfillmentPolicyIcon from '../../components/Icons/FulfillmentPolicyIcon'
|
||||||
|
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||||
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||||
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||||
|
import BinIcon from '../../components/Icons/BinIcon'
|
||||||
|
import ListIcon from '../../components/Icons/ListIcon'
|
||||||
|
import { marketplaceSyncMappingsProperty } from './marketplaceMappings'
|
||||||
|
|
||||||
|
export const FulfillmentPolicy = {
|
||||||
|
name: 'fulfillmentPolicy',
|
||||||
|
label: 'Fulfillment Policy',
|
||||||
|
labelPlural: 'Fulfillment Policies',
|
||||||
|
url: '/dashboard/sales/fulfillmentpolicies',
|
||||||
|
prefix: 'FPL',
|
||||||
|
endpoint: 'fulfillmentpolicies',
|
||||||
|
icon: FulfillmentPolicyIcon,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
name: 'new',
|
||||||
|
type: 'modal',
|
||||||
|
pageName: 'list',
|
||||||
|
modalWidth: 700,
|
||||||
|
label: 'New Fulfillment Policy',
|
||||||
|
icon: PlusIcon,
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(NewFulfillmentPolicy, {
|
||||||
|
defaultValues: objectData,
|
||||||
|
onOk,
|
||||||
|
reset: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'list',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'list',
|
||||||
|
label: 'List',
|
||||||
|
icon: ListIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
label: 'Info',
|
||||||
|
default: true,
|
||||||
|
row: true,
|
||||||
|
icon: InfoCircleIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cancelEdit',
|
||||||
|
label: 'Cancel Edits',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
icon: XMarkIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'finishEdit',
|
||||||
|
label: 'Save Edits',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
icon: CheckIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
name: 'delete',
|
||||||
|
type: 'modal',
|
||||||
|
modalWidth: 520,
|
||||||
|
modalCentered: true,
|
||||||
|
label: 'Delete',
|
||||||
|
icon: BinIcon,
|
||||||
|
danger: true,
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(DeleteObject, { objectData, onOk })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
content: () => createElement(FulfillmentPolicyInfo)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
'_reference',
|
||||||
|
'name',
|
||||||
|
'handlingTime',
|
||||||
|
'courierServices',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt'
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
'name',
|
||||||
|
'handlingTime',
|
||||||
|
'courierServices',
|
||||||
|
'localPickup',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
'_reference'
|
||||||
|
],
|
||||||
|
sorters: ['name', 'handlingTime', 'createdAt', '_id', 'updatedAt'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: '_id',
|
||||||
|
label: 'ID',
|
||||||
|
columnFixed: 'left',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'fulfillmentPolicy',
|
||||||
|
showCopy: true,
|
||||||
|
columnWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
label: 'Created At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '_reference',
|
||||||
|
label: 'Reference',
|
||||||
|
type: 'reference',
|
||||||
|
columnFixed: 'left',
|
||||||
|
objectType: 'fulfillmentPolicy',
|
||||||
|
showCopy: true,
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
columnFixed: 'left',
|
||||||
|
required: true,
|
||||||
|
type: 'text',
|
||||||
|
columnWidth: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
label: 'Description',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
span: 2,
|
||||||
|
columnWidth: 280
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'handlingTime',
|
||||||
|
label: 'Handling Time',
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
required: false,
|
||||||
|
suffix: 'days',
|
||||||
|
columnWidth: 175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'courierServices',
|
||||||
|
label: 'Courier Services',
|
||||||
|
type: 'objectList',
|
||||||
|
objectType: 'courierService',
|
||||||
|
showHyperlink: true,
|
||||||
|
required: false,
|
||||||
|
columnWidth: 250
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'localPickup',
|
||||||
|
label: 'Local Pickup',
|
||||||
|
type: 'bool',
|
||||||
|
required: false,
|
||||||
|
columnWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'globalShipping',
|
||||||
|
label: 'Global Shipping',
|
||||||
|
type: 'bool',
|
||||||
|
required: false,
|
||||||
|
columnWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'freightShipping',
|
||||||
|
label: 'Freight Shipping',
|
||||||
|
type: 'bool',
|
||||||
|
required: false,
|
||||||
|
columnWidth: 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pickupDropOff',
|
||||||
|
label: 'Pickup Drop Off',
|
||||||
|
type: 'bool',
|
||||||
|
required: false,
|
||||||
|
columnWidth: 160
|
||||||
|
},
|
||||||
|
marketplaceSyncMappingsProperty('fulfillmentPolicy')
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -112,7 +112,10 @@ export const Listing = {
|
|||||||
visible: (objectData) =>
|
visible: (objectData) =>
|
||||||
objectData?.state?.type === 'draft' ||
|
objectData?.state?.type === 'draft' ||
|
||||||
objectData?.state?.type === 'inactive',
|
objectData?.state?.type === 'inactive',
|
||||||
disabled: (objectData) => objectData?.state?.type === 'syncing',
|
disabled: (objectData) =>
|
||||||
|
['syncing', 'publishing', 'unpublishing'].includes(
|
||||||
|
objectData?.state?.type
|
||||||
|
),
|
||||||
content: (objectData, { onOk } = {}) => {
|
content: (objectData, { onOk } = {}) => {
|
||||||
return createElement(PublishListing, { objectData, onOk })
|
return createElement(PublishListing, { objectData, onOk })
|
||||||
}
|
}
|
||||||
@ -125,7 +128,10 @@ export const Listing = {
|
|||||||
icon: XMarkIcon,
|
icon: XMarkIcon,
|
||||||
danger: true,
|
danger: true,
|
||||||
visible: (objectData) => objectData?.state?.type === 'active',
|
visible: (objectData) => objectData?.state?.type === 'active',
|
||||||
disabled: (objectData) => objectData?.state?.type === 'syncing',
|
disabled: (objectData) =>
|
||||||
|
['syncing', 'publishing', 'unpublishing'].includes(
|
||||||
|
objectData?.state?.type
|
||||||
|
),
|
||||||
content: (objectData, { onOk } = {}) => {
|
content: (objectData, { onOk } = {}) => {
|
||||||
return createElement(UnpublishListing, { objectData, onOk })
|
return createElement(UnpublishListing, { objectData, onOk })
|
||||||
}
|
}
|
||||||
@ -158,6 +164,9 @@ export const Listing = {
|
|||||||
'stockLocation',
|
'stockLocation',
|
||||||
'stockQuantity',
|
'stockQuantity',
|
||||||
'marketplace',
|
'marketplace',
|
||||||
|
'fulfillmentPolicy',
|
||||||
|
'paymentPolicy',
|
||||||
|
'returnPolicy',
|
||||||
'courierServices',
|
'courierServices',
|
||||||
'state',
|
'state',
|
||||||
'condition',
|
'condition',
|
||||||
@ -173,6 +182,9 @@ export const Listing = {
|
|||||||
'stockLocation',
|
'stockLocation',
|
||||||
'stockQuantity',
|
'stockQuantity',
|
||||||
'marketplace',
|
'marketplace',
|
||||||
|
'fulfillmentPolicy',
|
||||||
|
'paymentPolicy',
|
||||||
|
'returnPolicy',
|
||||||
'courierServices',
|
'courierServices',
|
||||||
'state',
|
'state',
|
||||||
'state.type',
|
'state.type',
|
||||||
@ -252,6 +264,14 @@ export const Listing = {
|
|||||||
extra:
|
extra:
|
||||||
'Used as the eBay listing description. If empty, the title is sent instead.'
|
'Used as the eBay listing description. If empty, the title is sent instead.'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'listingImages',
|
||||||
|
label: 'Listing Images',
|
||||||
|
type: 'fileList',
|
||||||
|
gallery: true,
|
||||||
|
required: false,
|
||||||
|
masterFilter: ['.jpg', '.jpeg', '.png', '.gif', '.webp']
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'lastSyncedAt',
|
name: 'lastSyncedAt',
|
||||||
label: 'Last Synced',
|
label: 'Last Synced',
|
||||||
@ -314,6 +334,58 @@ export const Listing = {
|
|||||||
required: true,
|
required: true,
|
||||||
columnWidth: 200
|
columnWidth: 200
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'fulfillmentPolicy',
|
||||||
|
label: 'Fulfillment Policy',
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'fulfillmentPolicy',
|
||||||
|
showHyperlink: true,
|
||||||
|
readOnly: false,
|
||||||
|
required: false,
|
||||||
|
extra:
|
||||||
|
'Defaults from the marketplace. Courier services are used as a fallback when this is empty.',
|
||||||
|
value: (objectData) => {
|
||||||
|
if (objectData?.fulfillmentPolicy == undefined) {
|
||||||
|
return objectData?.marketplace?.defaultFulfillmentPolicy
|
||||||
|
}
|
||||||
|
return objectData?.fulfillmentPolicy
|
||||||
|
},
|
||||||
|
columnWidth: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'paymentPolicy',
|
||||||
|
label: 'Payment Policy',
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'paymentPolicy',
|
||||||
|
showHyperlink: true,
|
||||||
|
readOnly: false,
|
||||||
|
required: false,
|
||||||
|
extra: 'Defaults from the marketplace. Required when publishing to eBay.',
|
||||||
|
value: (objectData) => {
|
||||||
|
if (objectData?.paymentPolicy == undefined) {
|
||||||
|
return objectData?.marketplace?.defaultPaymentPolicy
|
||||||
|
}
|
||||||
|
return objectData?.paymentPolicy
|
||||||
|
},
|
||||||
|
columnWidth: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'returnPolicy',
|
||||||
|
label: 'Return Policy',
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'returnPolicy',
|
||||||
|
showHyperlink: true,
|
||||||
|
readOnly: false,
|
||||||
|
required: false,
|
||||||
|
extra: 'Defaults from the marketplace. Required when publishing to eBay.',
|
||||||
|
value: (objectData) => {
|
||||||
|
if (objectData?.returnPolicy == undefined) {
|
||||||
|
return objectData?.marketplace?.defaultReturnPolicy
|
||||||
|
}
|
||||||
|
return objectData?.returnPolicy
|
||||||
|
},
|
||||||
|
columnWidth: 200
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'state',
|
name: 'state',
|
||||||
label: 'State',
|
label: 'State',
|
||||||
@ -378,6 +450,17 @@ export const Listing = {
|
|||||||
required: false,
|
required: false,
|
||||||
columnWidth: 250
|
columnWidth: 250
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'externalReference',
|
||||||
|
label: 'External Reference',
|
||||||
|
type: 'miscId',
|
||||||
|
readOnly: true,
|
||||||
|
required: false,
|
||||||
|
extra:
|
||||||
|
'eBay item ID for this listing. Publish and unpublish reuse this ID when set.',
|
||||||
|
showCopy: true,
|
||||||
|
columnWidth: 180
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'courierServices',
|
name: 'courierServices',
|
||||||
label: 'Courier Services',
|
label: 'Courier Services',
|
||||||
@ -386,6 +469,8 @@ export const Listing = {
|
|||||||
objectType: 'courierService',
|
objectType: 'courierService',
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
required: true,
|
required: true,
|
||||||
|
extra:
|
||||||
|
'Used for order shipping and as the eBay fulfillment fallback when no fulfillment policy is set.',
|
||||||
columnWidth: 250
|
columnWidth: 250
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -414,6 +499,18 @@ export const Listing = {
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
color: 'processing'
|
color: 'processing'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'publishing.count',
|
||||||
|
label: 'Publishing',
|
||||||
|
type: 'number',
|
||||||
|
color: 'processing'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'unpublishing.count',
|
||||||
|
label: 'Unpublishing',
|
||||||
|
type: 'number',
|
||||||
|
color: 'processing'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'suspended.count',
|
name: 'suspended.count',
|
||||||
label: 'Suspended',
|
label: 'Suspended',
|
||||||
|
|||||||
@ -108,7 +108,10 @@ export const ListingVarient = {
|
|||||||
visible: (objectData) =>
|
visible: (objectData) =>
|
||||||
objectData?.state?.type === 'draft' ||
|
objectData?.state?.type === 'draft' ||
|
||||||
objectData?.state?.type === 'inactive',
|
objectData?.state?.type === 'inactive',
|
||||||
disabled: (objectData) => objectData?.state?.type === 'syncing',
|
disabled: (objectData) =>
|
||||||
|
['syncing', 'publishing', 'unpublishing'].includes(
|
||||||
|
objectData?.state?.type
|
||||||
|
),
|
||||||
content: (objectData, { onOk } = {}) => {
|
content: (objectData, { onOk } = {}) => {
|
||||||
return createElement(PublishListingVarient, { objectData, onOk })
|
return createElement(PublishListingVarient, { objectData, onOk })
|
||||||
}
|
}
|
||||||
@ -121,7 +124,10 @@ export const ListingVarient = {
|
|||||||
icon: XMarkIcon,
|
icon: XMarkIcon,
|
||||||
danger: true,
|
danger: true,
|
||||||
visible: (objectData) => objectData?.state?.type === 'active',
|
visible: (objectData) => objectData?.state?.type === 'active',
|
||||||
disabled: (objectData) => objectData?.state?.type === 'syncing',
|
disabled: (objectData) =>
|
||||||
|
['syncing', 'publishing', 'unpublishing'].includes(
|
||||||
|
objectData?.state?.type
|
||||||
|
),
|
||||||
content: (objectData, { onOk } = {}) => {
|
content: (objectData, { onOk } = {}) => {
|
||||||
return createElement(UnpublishListingVarient, { objectData, onOk })
|
return createElement(UnpublishListingVarient, { objectData, onOk })
|
||||||
}
|
}
|
||||||
@ -312,6 +318,42 @@ export const ListingVarient = {
|
|||||||
type: 'dateTime',
|
type: 'dateTime',
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
columnWidth: 175
|
columnWidth: 175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'listingImages',
|
||||||
|
label: 'Listing Images',
|
||||||
|
type: 'fileList',
|
||||||
|
gallery: true,
|
||||||
|
required: false,
|
||||||
|
masterFilter: ['.jpg', '.jpeg', '.png', '.gif', '.webp']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'aspects',
|
||||||
|
label: 'Aspects',
|
||||||
|
type: 'objectChildren',
|
||||||
|
required: false,
|
||||||
|
canAddRemove: true,
|
||||||
|
size: 'medium',
|
||||||
|
span: 2,
|
||||||
|
extra:
|
||||||
|
'Required for multi-variation eBay listings. Use the same aspect names on every varient in a listing (e.g. Color, Size).',
|
||||||
|
columns: ['name', 'value'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
columnWidth: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'value',
|
||||||
|
label: 'Value',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
columnWidth: 180
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,21 @@ const SyncListings = lazy(
|
|||||||
const SyncOrders = lazy(
|
const SyncOrders = lazy(
|
||||||
() => import('../../components/Dashboard/Sales/Marketplaces/SyncOrders')
|
() => import('../../components/Dashboard/Sales/Marketplaces/SyncOrders')
|
||||||
)
|
)
|
||||||
|
const SyncFulfillmentPolicies = lazy(
|
||||||
|
() =>
|
||||||
|
import('../../components/Dashboard/Sales/Marketplaces/SyncFulfillmentPolicies')
|
||||||
|
)
|
||||||
|
const SyncPaymentPolicies = lazy(
|
||||||
|
() =>
|
||||||
|
import('../../components/Dashboard/Sales/Marketplaces/SyncPaymentPolicies')
|
||||||
|
)
|
||||||
|
const SyncReturnPolicies = lazy(
|
||||||
|
() =>
|
||||||
|
import('../../components/Dashboard/Sales/Marketplaces/SyncReturnPolicies')
|
||||||
|
)
|
||||||
|
const SyncTaxRates = lazy(
|
||||||
|
() => import('../../components/Dashboard/Sales/Marketplaces/SyncTaxRates')
|
||||||
|
)
|
||||||
const DeleteObject = lazy(
|
const DeleteObject = lazy(
|
||||||
() => import('../../components/Dashboard/common/DeleteObject')
|
() => import('../../components/Dashboard/common/DeleteObject')
|
||||||
)
|
)
|
||||||
@ -134,19 +149,6 @@ export const Marketplace = {
|
|||||||
return createElement(SyncListings, { objectData, onOk })
|
return createElement(SyncListings, { objectData, onOk })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'syncMarketplace',
|
|
||||||
type: 'modal',
|
|
||||||
modalWidth: 520,
|
|
||||||
label: 'Sync Marketplace',
|
|
||||||
icon: ReloadIcon,
|
|
||||||
disabled: (objectData) => {
|
|
||||||
return objectData?.state?.type != 'ready'
|
|
||||||
},
|
|
||||||
content: (objectData, { onOk } = {}) => {
|
|
||||||
return createElement(SyncMarketplace, { objectData, onOk })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'syncOrders',
|
name: 'syncOrders',
|
||||||
type: 'modal',
|
type: 'modal',
|
||||||
@ -159,6 +161,58 @@ export const Marketplace = {
|
|||||||
content: (objectData, { onOk } = {}) => {
|
content: (objectData, { onOk } = {}) => {
|
||||||
return createElement(SyncOrders, { objectData, onOk })
|
return createElement(SyncOrders, { objectData, onOk })
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'syncFulfillmentPolicies',
|
||||||
|
type: 'modal',
|
||||||
|
modalWidth: 520,
|
||||||
|
label: 'Sync Fulfillment Policies',
|
||||||
|
icon: ReloadIcon,
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData?.state?.type != 'ready'
|
||||||
|
},
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(SyncFulfillmentPolicies, { objectData, onOk })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'syncReturnPolicies',
|
||||||
|
type: 'modal',
|
||||||
|
modalWidth: 520,
|
||||||
|
label: 'Sync Return Policies',
|
||||||
|
icon: ReloadIcon,
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData?.state?.type != 'ready'
|
||||||
|
},
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(SyncReturnPolicies, { objectData, onOk })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'syncPaymentPolicies',
|
||||||
|
type: 'modal',
|
||||||
|
modalWidth: 520,
|
||||||
|
label: 'Sync Payment Policies',
|
||||||
|
icon: ReloadIcon,
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData?.state?.type != 'ready'
|
||||||
|
},
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(SyncPaymentPolicies, { objectData, onOk })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'syncTaxRates',
|
||||||
|
type: 'modal',
|
||||||
|
modalWidth: 520,
|
||||||
|
label: 'Sync Tax Rates',
|
||||||
|
icon: ReloadIcon,
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData?.state?.type != 'ready'
|
||||||
|
},
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(SyncTaxRates, { objectData, onOk })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -313,17 +367,7 @@ export const Marketplace = {
|
|||||||
],
|
],
|
||||||
columnWidth: 150
|
columnWidth: 150
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'eBay.availableShippingServices',
|
|
||||||
label: 'Available Shipping Services',
|
|
||||||
type: 'stringList',
|
|
||||||
readOnly: true,
|
|
||||||
required: false,
|
|
||||||
visible: (objectData) => objectData?.provider === 'ebay',
|
|
||||||
extra: 'Loaded from eBay GeteBayDetails when this marketplace is synced.',
|
|
||||||
span: 2,
|
|
||||||
columnWidth: 280
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'config.accessTokenExpiresAt',
|
name: 'config.accessTokenExpiresAt',
|
||||||
label: 'Access Token Expires At',
|
label: 'Access Token Expires At',
|
||||||
@ -331,7 +375,6 @@ export const Marketplace = {
|
|||||||
readOnly: true,
|
readOnly: true,
|
||||||
columnWidth: 215
|
columnWidth: 215
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'state',
|
name: 'state',
|
||||||
label: 'State',
|
label: 'State',
|
||||||
@ -340,21 +383,7 @@ export const Marketplace = {
|
|||||||
readOnly: true,
|
readOnly: true,
|
||||||
columnWidth: 260
|
columnWidth: 260
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'active',
|
|
||||||
label: 'Active',
|
|
||||||
type: 'bool',
|
|
||||||
readOnly: false,
|
|
||||||
required: true,
|
|
||||||
columnWidth: 125
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'connected',
|
|
||||||
label: 'Connected',
|
|
||||||
type: 'bool',
|
|
||||||
readOnly: true,
|
|
||||||
columnWidth: 125
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'config.refreshTokenExpiresAt',
|
name: 'config.refreshTokenExpiresAt',
|
||||||
label: 'Refresh Token Expires At',
|
label: 'Refresh Token Expires At',
|
||||||
@ -364,15 +393,40 @@ export const Marketplace = {
|
|||||||
columnWidth: 200
|
columnWidth: 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'config.clientId',
|
name: 'eBay.availableShippingServices',
|
||||||
label: 'Client ID',
|
label: 'Available Shipping Services',
|
||||||
type: 'secret',
|
type: 'number',
|
||||||
readOnly: false,
|
readOnly: true,
|
||||||
required: false,
|
required: false,
|
||||||
columnWidth: 200,
|
visible: (objectData) => objectData?.provider === 'ebay',
|
||||||
visible: (objectData) => objectData?.provider === 'ebay'
|
extra: 'Loaded from eBay GeteBayDetails when this marketplace is synced.',
|
||||||
|
span: 2,
|
||||||
|
columnWidth: 280,
|
||||||
|
value: (objectData) => {
|
||||||
|
return objectData?.eBay?.availableShippingServices?.length
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'eBay.categoryReferences',
|
||||||
|
label: 'Category References',
|
||||||
|
type: 'treeSelect',
|
||||||
|
readOnly: true,
|
||||||
|
required: false,
|
||||||
|
visible: false,
|
||||||
|
extra:
|
||||||
|
'Loaded from the eBay taxonomy category tree when this marketplace is synced.',
|
||||||
|
span: 2,
|
||||||
|
columnWidth: 280
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'active',
|
||||||
|
label: 'Active',
|
||||||
|
type: 'bool',
|
||||||
|
readOnly: false,
|
||||||
|
required: true,
|
||||||
|
columnWidth: 125
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'config.lastTokenRefreshAt',
|
name: 'config.lastTokenRefreshAt',
|
||||||
label: 'Last Token Refresh At',
|
label: 'Last Token Refresh At',
|
||||||
@ -381,6 +435,23 @@ export const Marketplace = {
|
|||||||
showSince: true,
|
showSince: true,
|
||||||
columnWidth: 200
|
columnWidth: 200
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'connected',
|
||||||
|
label: 'Connected',
|
||||||
|
type: 'bool',
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 125
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'config.clientId',
|
||||||
|
label: 'Client ID',
|
||||||
|
type: 'secret',
|
||||||
|
readOnly: false,
|
||||||
|
required: false,
|
||||||
|
columnWidth: 200,
|
||||||
|
visible: (objectData) => objectData?.provider === 'ebay'
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'config.clientSecret',
|
name: 'config.clientSecret',
|
||||||
@ -555,6 +626,37 @@ export const Marketplace = {
|
|||||||
}
|
}
|
||||||
return `${window.location.origin}/auth/marketplace/callback`
|
return `${window.location.origin}/auth/marketplace/callback`
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'defaultFulfillmentPolicy',
|
||||||
|
label: 'Default Fulfillment Policy',
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'fulfillmentPolicy',
|
||||||
|
showHyperlink: true,
|
||||||
|
required: false,
|
||||||
|
extra:
|
||||||
|
'Used on new listings and when a listing has no fulfillment policy.',
|
||||||
|
columnWidth: 240
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'defaultPaymentPolicy',
|
||||||
|
label: 'Default Payment Policy',
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'paymentPolicy',
|
||||||
|
showHyperlink: true,
|
||||||
|
required: false,
|
||||||
|
extra: 'Used on new listings and when a listing has no payment policy.',
|
||||||
|
columnWidth: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'defaultReturnPolicy',
|
||||||
|
label: 'Default Return Policy',
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'returnPolicy',
|
||||||
|
showHyperlink: true,
|
||||||
|
required: false,
|
||||||
|
extra: 'Used on new listings and when a listing has no return policy.',
|
||||||
|
columnWidth: 220
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
stats: [
|
stats: [
|
||||||
|
|||||||
@ -6,9 +6,20 @@ const PartStockInfo = lazy(
|
|||||||
const NewPartStock = lazy(
|
const NewPartStock = lazy(
|
||||||
() => import('../../components/Dashboard/Inventory/PartStocks/NewPartStock')
|
() => import('../../components/Dashboard/Inventory/PartStocks/NewPartStock')
|
||||||
)
|
)
|
||||||
|
const PostPartStock = lazy(
|
||||||
|
() =>
|
||||||
|
import('../../components/Dashboard/Inventory/PartStocks/PostPartStock.jsx')
|
||||||
|
)
|
||||||
|
const DeleteObject = lazy(
|
||||||
|
() => import('../../components/Dashboard/common/DeleteObject')
|
||||||
|
)
|
||||||
import PartStockIcon from '../../components/Icons/PartStockIcon'
|
import PartStockIcon from '../../components/Icons/PartStockIcon'
|
||||||
import PlusIcon from '../../components/Icons/PlusIcon'
|
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||||
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||||
|
import BinIcon from '../../components/Icons/BinIcon'
|
||||||
import ListIcon from '../../components/Icons/ListIcon'
|
import ListIcon from '../../components/Icons/ListIcon'
|
||||||
|
|
||||||
export const PartStock = {
|
export const PartStock = {
|
||||||
@ -17,7 +28,6 @@ export const PartStock = {
|
|||||||
labelPlural: 'Part Stocks',
|
labelPlural: 'Part Stocks',
|
||||||
url: '/dashboard/inventory/partstocks',
|
url: '/dashboard/inventory/partstocks',
|
||||||
prefix: 'PTS',
|
prefix: 'PTS',
|
||||||
readOnly: true,
|
|
||||||
icon: PartStockIcon,
|
icon: PartStockIcon,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
@ -50,6 +60,75 @@ export const PartStock = {
|
|||||||
default: true,
|
default: true,
|
||||||
row: true,
|
row: true,
|
||||||
icon: InfoCircleIcon
|
icon: InfoCircleIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||||
|
},
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData?.state?.type != 'draft'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cancelEdit',
|
||||||
|
label: 'Cancel Edits',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
icon: XMarkIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'finishEdit',
|
||||||
|
label: 'Save Edits',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
icon: CheckIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'delete',
|
||||||
|
type: 'modal',
|
||||||
|
modalWidth: 520,
|
||||||
|
modalCentered: true,
|
||||||
|
label: 'Delete',
|
||||||
|
icon: BinIcon,
|
||||||
|
danger: true,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||||
|
},
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData?.state?.type != 'draft'
|
||||||
|
},
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(DeleteObject, { objectData, onOk })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
name: 'post',
|
||||||
|
type: 'modal',
|
||||||
|
modalWidth: 520,
|
||||||
|
label: 'Post',
|
||||||
|
icon: CheckIcon,
|
||||||
|
disabled: (objectData) => {
|
||||||
|
return objectData?._isEditing == true
|
||||||
|
},
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?.state?.type == 'draft'
|
||||||
|
},
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(PostPartStock, { objectData, onOk })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
pages: [
|
pages: [
|
||||||
@ -62,7 +141,6 @@ export const PartStock = {
|
|||||||
'part',
|
'part',
|
||||||
'partSku',
|
'partSku',
|
||||||
'state',
|
'state',
|
||||||
'startingQuantity',
|
|
||||||
'currentQuantity',
|
'currentQuantity',
|
||||||
'stockLocation',
|
'stockLocation',
|
||||||
'createdAt',
|
'createdAt',
|
||||||
@ -72,7 +150,6 @@ export const PartStock = {
|
|||||||
sorters: [
|
sorters: [
|
||||||
'part',
|
'part',
|
||||||
'partSku',
|
'partSku',
|
||||||
'startingQuantity',
|
|
||||||
'currentQuantity',
|
'currentQuantity',
|
||||||
'state',
|
'state',
|
||||||
'createdAt',
|
'createdAt',
|
||||||
@ -81,7 +158,6 @@ export const PartStock = {
|
|||||||
columns: [
|
columns: [
|
||||||
'_reference',
|
'_reference',
|
||||||
'state',
|
'state',
|
||||||
'startingQuantity',
|
|
||||||
'currentQuantity',
|
'currentQuantity',
|
||||||
'part',
|
'part',
|
||||||
'partSku',
|
'partSku',
|
||||||
@ -116,13 +192,6 @@ export const PartStock = {
|
|||||||
readOnly: true,
|
readOnly: true,
|
||||||
columnWidth: 180
|
columnWidth: 180
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'state',
|
|
||||||
label: 'State',
|
|
||||||
type: 'state',
|
|
||||||
readOnly: true,
|
|
||||||
columnWidth: 260
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'updatedAt',
|
name: 'updatedAt',
|
||||||
label: 'Updated At',
|
label: 'Updated At',
|
||||||
@ -131,13 +200,19 @@ export const PartStock = {
|
|||||||
columnWidth: 175
|
columnWidth: 175
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'sourceType',
|
name: 'state',
|
||||||
label: 'Source Type',
|
label: 'State',
|
||||||
type: 'objectType',
|
type: 'state',
|
||||||
readOnly: false,
|
readOnly: true,
|
||||||
columnWidth: 200,
|
columnWidth: 260
|
||||||
required: true,
|
},
|
||||||
masterFilter: ['subJob', 'stockTransfer']
|
|
||||||
|
{
|
||||||
|
name: 'postedAt',
|
||||||
|
label: 'Posted At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 175
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'part',
|
name: 'part',
|
||||||
@ -169,36 +244,22 @@ export const PartStock = {
|
|||||||
showHyperlink: true,
|
showHyperlink: true,
|
||||||
columnWidth: 200
|
columnWidth: 200
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
name: 'source',
|
|
||||||
label: 'Source',
|
|
||||||
type: 'object',
|
|
||||||
readOnly: false,
|
|
||||||
required: true,
|
|
||||||
columnWidth: 200,
|
|
||||||
objectType: (objectData) => {
|
|
||||||
return objectData?.sourceType
|
|
||||||
},
|
|
||||||
showHyperlink: true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'currentQuantity',
|
name: 'currentQuantity',
|
||||||
label: 'Current Quantity',
|
label: 'Current Quantity',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
readOnly: true,
|
|
||||||
columnWidth: 200,
|
columnWidth: 200,
|
||||||
required: true,
|
required: true
|
||||||
value: (objectData) => {
|
|
||||||
if (objectData?.state?.type === 'new') {
|
|
||||||
return objectData?.startingQuantity
|
|
||||||
} else {
|
|
||||||
return objectData.currentQuantity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
stats: [
|
stats: [
|
||||||
|
{
|
||||||
|
name: 'draft.count',
|
||||||
|
label: 'Draft',
|
||||||
|
type: 'number',
|
||||||
|
color: 'default',
|
||||||
|
history: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'new.count',
|
name: 'new.count',
|
||||||
label: 'New',
|
label: 'New',
|
||||||
|
|||||||
187
src/database/models/PaymentPolicy.js
Normal file
187
src/database/models/PaymentPolicy.js
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import { createElement, lazy } from 'react'
|
||||||
|
|
||||||
|
const PaymentPolicyInfo = lazy(
|
||||||
|
() =>
|
||||||
|
import('../../components/Dashboard/Finance/PaymentPolicies/PaymentPolicyInfo')
|
||||||
|
)
|
||||||
|
const NewPaymentPolicy = lazy(
|
||||||
|
() =>
|
||||||
|
import('../../components/Dashboard/Finance/PaymentPolicies/NewPaymentPolicy')
|
||||||
|
)
|
||||||
|
const DeleteObject = lazy(
|
||||||
|
() => import('../../components/Dashboard/common/DeleteObject')
|
||||||
|
)
|
||||||
|
import PaymentPolicyIcon from '../../components/Icons/PaymentPolicyIcon'
|
||||||
|
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||||
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||||
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||||
|
import BinIcon from '../../components/Icons/BinIcon'
|
||||||
|
import ListIcon from '../../components/Icons/ListIcon'
|
||||||
|
import { marketplaceSyncMappingsProperty } from './marketplaceMappings'
|
||||||
|
|
||||||
|
export const PaymentPolicy = {
|
||||||
|
name: 'paymentPolicy',
|
||||||
|
label: 'Payment Policy',
|
||||||
|
labelPlural: 'Payment Policies',
|
||||||
|
url: '/dashboard/finance/paymentpolicies',
|
||||||
|
prefix: 'PPL',
|
||||||
|
endpoint: 'paymentpolicies',
|
||||||
|
icon: PaymentPolicyIcon,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
name: 'new',
|
||||||
|
type: 'modal',
|
||||||
|
pageName: 'list',
|
||||||
|
modalWidth: 700,
|
||||||
|
label: 'New Payment Policy',
|
||||||
|
icon: PlusIcon,
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(NewPaymentPolicy, {
|
||||||
|
defaultValues: objectData,
|
||||||
|
onOk,
|
||||||
|
reset: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'list',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'list',
|
||||||
|
label: 'List',
|
||||||
|
icon: ListIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
label: 'Info',
|
||||||
|
default: true,
|
||||||
|
row: true,
|
||||||
|
icon: InfoCircleIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cancelEdit',
|
||||||
|
label: 'Cancel Edits',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
icon: XMarkIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'finishEdit',
|
||||||
|
label: 'Save Edits',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
icon: CheckIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
name: 'delete',
|
||||||
|
type: 'modal',
|
||||||
|
modalWidth: 520,
|
||||||
|
modalCentered: true,
|
||||||
|
label: 'Delete',
|
||||||
|
icon: BinIcon,
|
||||||
|
danger: true,
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(DeleteObject, { objectData, onOk })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
content: () => createElement(PaymentPolicyInfo)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: ['_reference', 'name', 'immediatePay', 'createdAt', 'updatedAt'],
|
||||||
|
filters: ['name', 'immediatePay', 'createdAt', 'updatedAt', '_reference'],
|
||||||
|
sorters: ['name', 'immediatePay', 'createdAt', '_id', 'updatedAt'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: '_id',
|
||||||
|
label: 'ID',
|
||||||
|
columnFixed: 'left',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'paymentPolicy',
|
||||||
|
showCopy: true,
|
||||||
|
columnWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
label: 'Created At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '_reference',
|
||||||
|
label: 'Reference',
|
||||||
|
type: 'reference',
|
||||||
|
columnFixed: 'left',
|
||||||
|
objectType: 'paymentPolicy',
|
||||||
|
showCopy: true,
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
columnFixed: 'left',
|
||||||
|
required: true,
|
||||||
|
type: 'text',
|
||||||
|
columnWidth: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
label: 'Description',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
span: 2,
|
||||||
|
columnWidth: 280
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'immediatePay',
|
||||||
|
label: 'Immediate Pay',
|
||||||
|
type: 'bool',
|
||||||
|
required: false,
|
||||||
|
default: true,
|
||||||
|
extra: 'Required for eBay managed payments.',
|
||||||
|
columnWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'paymentInstructions',
|
||||||
|
label: 'Payment Instructions',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
span: 2,
|
||||||
|
columnWidth: 280
|
||||||
|
},
|
||||||
|
marketplaceSyncMappingsProperty('paymentPolicy')
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -1,14 +1,17 @@
|
|||||||
import { createElement, lazy } from 'react'
|
import { createElement, lazy } from 'react'
|
||||||
|
|
||||||
const ProductCategoryInfo = lazy(
|
const ProductCategoryInfo = lazy(
|
||||||
() => import('../../components/Dashboard/Management/ProductCategories/ProductCategoryInfo')
|
() =>
|
||||||
|
import('../../components/Dashboard/Management/ProductCategories/ProductCategoryInfo')
|
||||||
)
|
)
|
||||||
const NewProductCategory = lazy(
|
const NewProductCategory = lazy(
|
||||||
() => import('../../components/Dashboard/Management/ProductCategories/NewProductCategory')
|
() =>
|
||||||
|
import('../../components/Dashboard/Management/ProductCategories/NewProductCategory')
|
||||||
)
|
)
|
||||||
const DeleteObject = lazy(
|
const DeleteObject = lazy(
|
||||||
() => import('../../components/Dashboard/common/DeleteObject')
|
() => import('../../components/Dashboard/common/DeleteObject')
|
||||||
)
|
)
|
||||||
|
import { findTreeNode } from '../../components/Dashboard/common/CustomTreeSelect'
|
||||||
import ProductCategoryIcon from '../../components/Icons/ProductCategoryIcon'
|
import ProductCategoryIcon from '../../components/Icons/ProductCategoryIcon'
|
||||||
import PlusIcon from '../../components/Icons/PlusIcon'
|
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
@ -35,7 +38,11 @@ export const ProductCategory = {
|
|||||||
label: 'New Product Category',
|
label: 'New Product Category',
|
||||||
icon: PlusIcon,
|
icon: PlusIcon,
|
||||||
content: (objectData, { onOk } = {}) => {
|
content: (objectData, { onOk } = {}) => {
|
||||||
return createElement(NewProductCategory, { defaultValues: objectData, onOk, reset: true })
|
return createElement(NewProductCategory, {
|
||||||
|
defaultValues: objectData,
|
||||||
|
onOk,
|
||||||
|
reset: true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -106,7 +113,14 @@ export const ProductCategory = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
columns: ['_reference', 'name', 'createdAt', 'updatedAt'],
|
columns: ['_reference', 'name', 'createdAt', 'updatedAt'],
|
||||||
filters: ['_id','name','createdAt','updatedAt','_reference'],
|
filters: [
|
||||||
|
'_id',
|
||||||
|
'name',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
'_reference',
|
||||||
|
'marketplaces.marketplace'
|
||||||
|
],
|
||||||
sorters: ['name', 'createdAt', 'updatedAt', '_id'],
|
sorters: ['name', 'createdAt', 'updatedAt', '_id'],
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
@ -136,6 +150,14 @@ export const ProductCategory = {
|
|||||||
readOnly: true,
|
readOnly: true,
|
||||||
columnWidth: 180
|
columnWidth: 180
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 175
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: 'Name',
|
label: 'Name',
|
||||||
@ -145,11 +167,47 @@ export const ProductCategory = {
|
|||||||
columnWidth: 200
|
columnWidth: 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'updatedAt',
|
name: 'marketplaces',
|
||||||
label: 'Updated At',
|
label: 'Marketplaces',
|
||||||
type: 'dateTime',
|
type: 'objectChildren',
|
||||||
readOnly: true,
|
required: false,
|
||||||
columnWidth: 175
|
canAddRemove: true,
|
||||||
|
size: 'medium',
|
||||||
|
span: 2,
|
||||||
|
columns: ['marketplace', 'externalReference'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: 'marketplace',
|
||||||
|
label: 'Marketplace',
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'marketplace',
|
||||||
|
required: true,
|
||||||
|
showHyperlink: true,
|
||||||
|
columnWidth: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'externalReference',
|
||||||
|
label: 'External Reference',
|
||||||
|
type: (objectData) =>
|
||||||
|
objectData?.marketplace?.provider === 'ebay'
|
||||||
|
? 'treeSelect'
|
||||||
|
: 'text',
|
||||||
|
options: (objectData) => {
|
||||||
|
const tree = objectData?.marketplace?.eBay?.categoryReferences || []
|
||||||
|
const current = objectData?.externalReference
|
||||||
|
if (!current || findTreeNode(tree, current)) return tree
|
||||||
|
return [
|
||||||
|
{ title: current, value: current, selectable: true },
|
||||||
|
...tree
|
||||||
|
]
|
||||||
|
},
|
||||||
|
extra:
|
||||||
|
'For eBay listings, choose a category from the selected marketplace. Sync the marketplace if the tree is empty.',
|
||||||
|
showCopy: true,
|
||||||
|
required: true,
|
||||||
|
columnWidth: 260
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -371,10 +371,11 @@ export const ProductStock = {
|
|||||||
required: false,
|
required: false,
|
||||||
showHyperlink: true,
|
showHyperlink: true,
|
||||||
columnWidth: 260,
|
columnWidth: 260,
|
||||||
masterFilter: (objectData) => {
|
masterFilter: (objectData, parentData) => {
|
||||||
return {
|
return {
|
||||||
part: objectData?.part?._id,
|
part: objectData?.part?._id,
|
||||||
partSku: objectData?.partSku?._id,
|
partSku: objectData?.partSku?._id,
|
||||||
|
stockLocation: parentData?.stockLocation?._id,
|
||||||
$or: [{ 'state.type': 'new' }, { 'state.type': 'used' }]
|
$or: [{ 'state.type': 'new' }, { 'state.type': 'used' }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
294
src/database/models/ReturnPolicy.js
Normal file
294
src/database/models/ReturnPolicy.js
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
import { createElement, lazy } from 'react'
|
||||||
|
|
||||||
|
const ReturnPolicyInfo = lazy(
|
||||||
|
() =>
|
||||||
|
import('../../components/Dashboard/Sales/ReturnPolicies/ReturnPolicyInfo')
|
||||||
|
)
|
||||||
|
const NewReturnPolicy = lazy(
|
||||||
|
() =>
|
||||||
|
import('../../components/Dashboard/Sales/ReturnPolicies/NewReturnPolicy')
|
||||||
|
)
|
||||||
|
const DeleteObject = lazy(
|
||||||
|
() => import('../../components/Dashboard/common/DeleteObject')
|
||||||
|
)
|
||||||
|
import ReturnPolicyIcon from '../../components/Icons/ReturnPolicyIcon'
|
||||||
|
import PlusIcon from '../../components/Icons/PlusIcon'
|
||||||
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||||
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||||
|
import BinIcon from '../../components/Icons/BinIcon'
|
||||||
|
import ListIcon from '../../components/Icons/ListIcon'
|
||||||
|
import { marketplaceSyncMappingsProperty } from './marketplaceMappings'
|
||||||
|
|
||||||
|
export const ReturnPolicy = {
|
||||||
|
name: 'returnPolicy',
|
||||||
|
label: 'Return Policy',
|
||||||
|
labelPlural: 'Return Policies',
|
||||||
|
url: '/dashboard/sales/returnpolicies',
|
||||||
|
prefix: 'RPL',
|
||||||
|
endpoint: 'returnpolicies',
|
||||||
|
icon: ReturnPolicyIcon,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
name: 'new',
|
||||||
|
type: 'modal',
|
||||||
|
pageName: 'list',
|
||||||
|
modalWidth: 700,
|
||||||
|
label: 'New Return Policy',
|
||||||
|
icon: PlusIcon,
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(NewReturnPolicy, {
|
||||||
|
defaultValues: objectData,
|
||||||
|
onOk,
|
||||||
|
reset: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'list',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'list',
|
||||||
|
label: 'List',
|
||||||
|
icon: ListIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
label: 'Info',
|
||||||
|
default: true,
|
||||||
|
row: true,
|
||||||
|
icon: InfoCircleIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cancelEdit',
|
||||||
|
label: 'Cancel Edits',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
icon: XMarkIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'finishEdit',
|
||||||
|
label: 'Save Edits',
|
||||||
|
type: 'page',
|
||||||
|
pageName: 'info',
|
||||||
|
icon: CheckIcon,
|
||||||
|
visible: (objectData) => {
|
||||||
|
return objectData?._isEditing && objectData?._isEditing == true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
name: 'delete',
|
||||||
|
type: 'modal',
|
||||||
|
modalWidth: 520,
|
||||||
|
modalCentered: true,
|
||||||
|
label: 'Delete',
|
||||||
|
icon: BinIcon,
|
||||||
|
danger: true,
|
||||||
|
content: (objectData, { onOk } = {}) => {
|
||||||
|
return createElement(DeleteObject, { objectData, onOk })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
content: () => createElement(ReturnPolicyInfo)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
'_reference',
|
||||||
|
'name',
|
||||||
|
'returnsAccepted',
|
||||||
|
'returnPeriodDays',
|
||||||
|
'returnShippingCostPayer',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt'
|
||||||
|
],
|
||||||
|
filters: [
|
||||||
|
'name',
|
||||||
|
'returnsAccepted',
|
||||||
|
'returnPeriodDays',
|
||||||
|
'returnShippingCostPayer',
|
||||||
|
'refundMethod',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
'_reference'
|
||||||
|
],
|
||||||
|
sorters: [
|
||||||
|
'name',
|
||||||
|
'returnsAccepted',
|
||||||
|
'returnPeriodDays',
|
||||||
|
'createdAt',
|
||||||
|
'_id',
|
||||||
|
'updatedAt'
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: '_id',
|
||||||
|
label: 'ID',
|
||||||
|
columnFixed: 'left',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'returnPolicy',
|
||||||
|
showCopy: true,
|
||||||
|
columnWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
label: 'Created At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '_reference',
|
||||||
|
label: 'Reference',
|
||||||
|
type: 'reference',
|
||||||
|
columnFixed: 'left',
|
||||||
|
objectType: 'returnPolicy',
|
||||||
|
showCopy: true,
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
columnFixed: 'left',
|
||||||
|
required: true,
|
||||||
|
type: 'text',
|
||||||
|
columnWidth: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
label: 'Description',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
span: 2,
|
||||||
|
columnWidth: 280
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'returnsAccepted',
|
||||||
|
label: 'Returns Accepted',
|
||||||
|
type: 'bool',
|
||||||
|
required: true,
|
||||||
|
default: true,
|
||||||
|
columnWidth: 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'returnPeriodDays',
|
||||||
|
label: 'Return Period',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '14 days', value: 14 },
|
||||||
|
{ label: '30 days', value: 30 },
|
||||||
|
{ label: '60 days', value: 60 }
|
||||||
|
],
|
||||||
|
default: 30,
|
||||||
|
required: false,
|
||||||
|
extra: 'eBay only accepts 14, 30, or 60 days.',
|
||||||
|
visible: (objectData) => objectData?.returnsAccepted !== false,
|
||||||
|
columnWidth: 170
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'returnShippingCostPayer',
|
||||||
|
label: 'Return Shipping Paid By',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: 'Buyer', value: 'buyer' },
|
||||||
|
{ label: 'Seller', value: 'seller' }
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
visible: (objectData) => objectData?.returnsAccepted !== false,
|
||||||
|
columnWidth: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'refundMethod',
|
||||||
|
label: 'Refund Method',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: 'Money Back', value: 'moneyBack' },
|
||||||
|
{ label: 'Merchandise Credit', value: 'merchandiseCredit' }
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
visible: (objectData) => objectData?.returnsAccepted !== false,
|
||||||
|
columnWidth: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'restockingFeePercentage',
|
||||||
|
label: 'Restocking Fee',
|
||||||
|
type: 'number',
|
||||||
|
min: 0,
|
||||||
|
suffix: '%',
|
||||||
|
required: false,
|
||||||
|
extra: 'Stored locally. eBay no longer accepts restocking fees.',
|
||||||
|
columnWidth: 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'returnInstructions',
|
||||||
|
label: 'Return Instructions',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
span: 2,
|
||||||
|
columnWidth: 280
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'internationalReturnsAccepted',
|
||||||
|
label: 'International Returns',
|
||||||
|
type: 'bool',
|
||||||
|
required: false,
|
||||||
|
columnWidth: 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'internationalReturnPeriodDays',
|
||||||
|
label: 'International Return Period',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '14 days', value: 14 },
|
||||||
|
{ label: '30 days', value: 30 },
|
||||||
|
{ label: '60 days', value: 60 }
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
extra: 'eBay only accepts 14, 30, or 60 days.',
|
||||||
|
visible: (objectData) =>
|
||||||
|
objectData?.internationalReturnsAccepted === true,
|
||||||
|
columnWidth: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'internationalReturnShippingCostPayer',
|
||||||
|
label: 'International Return Shipping Paid By',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: 'Buyer', value: 'buyer' },
|
||||||
|
{ label: 'Seller', value: 'seller' }
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
visible: (objectData) =>
|
||||||
|
objectData?.internationalReturnsAccepted === true,
|
||||||
|
columnWidth: 260
|
||||||
|
},
|
||||||
|
marketplaceSyncMappingsProperty('returnPolicy')
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -17,6 +17,7 @@ import CheckIcon from '../../components/Icons/CheckIcon'
|
|||||||
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||||
import BinIcon from '../../components/Icons/BinIcon'
|
import BinIcon from '../../components/Icons/BinIcon'
|
||||||
import ListIcon from '../../components/Icons/ListIcon'
|
import ListIcon from '../../components/Icons/ListIcon'
|
||||||
|
import { marketplaceSyncMappingsProperty } from './marketplaceMappings'
|
||||||
|
|
||||||
export const TaxRate = {
|
export const TaxRate = {
|
||||||
name: 'taxRate',
|
name: 'taxRate',
|
||||||
@ -111,6 +112,7 @@ export const TaxRate = {
|
|||||||
'rateType',
|
'rateType',
|
||||||
'active',
|
'active',
|
||||||
'country',
|
'country',
|
||||||
|
'jurisdiction',
|
||||||
'createdAt',
|
'createdAt',
|
||||||
'updatedAt'
|
'updatedAt'
|
||||||
],
|
],
|
||||||
@ -120,6 +122,7 @@ export const TaxRate = {
|
|||||||
'rateType',
|
'rateType',
|
||||||
'active',
|
'active',
|
||||||
'country',
|
'country',
|
||||||
|
'jurisdiction',
|
||||||
'createdAt',
|
'createdAt',
|
||||||
'updatedAt',
|
'updatedAt',
|
||||||
'_reference'
|
'_reference'
|
||||||
@ -130,6 +133,7 @@ export const TaxRate = {
|
|||||||
'rateType',
|
'rateType',
|
||||||
'active',
|
'active',
|
||||||
'country',
|
'country',
|
||||||
|
'jurisdiction',
|
||||||
'createdAt',
|
'createdAt',
|
||||||
'_id',
|
'_id',
|
||||||
'updatedAt'
|
'updatedAt'
|
||||||
@ -237,6 +241,23 @@ export const TaxRate = {
|
|||||||
required: false,
|
required: false,
|
||||||
columnWidth: 150
|
columnWidth: 150
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'jurisdiction',
|
||||||
|
label: 'Jurisdiction',
|
||||||
|
type: 'text',
|
||||||
|
readOnly: false,
|
||||||
|
required: false,
|
||||||
|
extra:
|
||||||
|
'Marketplace tax table region, for example GU or ON. eBay only uses US territories and Canada.',
|
||||||
|
columnWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'shippingAndHandlingTaxed',
|
||||||
|
label: 'Tax Shipping',
|
||||||
|
type: 'bool',
|
||||||
|
required: false,
|
||||||
|
columnWidth: 140
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'description',
|
name: 'description',
|
||||||
label: 'Description',
|
label: 'Description',
|
||||||
@ -244,6 +265,10 @@ export const TaxRate = {
|
|||||||
readOnly: false,
|
readOnly: false,
|
||||||
required: false,
|
required: false,
|
||||||
columnWidth: 200
|
columnWidth: 200
|
||||||
}
|
},
|
||||||
|
marketplaceSyncMappingsProperty(
|
||||||
|
'taxRate',
|
||||||
|
'eBay tax table key as COUNTRY:JURISDICTION, for example US:GU. Filled when synced.'
|
||||||
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
49
src/database/models/marketplaceMappings.js
Normal file
49
src/database/models/marketplaceMappings.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
export function marketplaceSyncMappingsProperty(objectType, extra) {
|
||||||
|
return {
|
||||||
|
name: 'marketplaces',
|
||||||
|
label: 'Marketplaces',
|
||||||
|
type: 'objectChildren',
|
||||||
|
required: false,
|
||||||
|
canAddRemove: true,
|
||||||
|
size: 'medium',
|
||||||
|
span: 2,
|
||||||
|
columns: ['marketplace', 'state', 'externalReference'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: 'marketplace',
|
||||||
|
label: 'Marketplace',
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'marketplace',
|
||||||
|
required: true,
|
||||||
|
showHyperlink: true,
|
||||||
|
columnWidth: 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'state',
|
||||||
|
label: 'State',
|
||||||
|
type: 'state',
|
||||||
|
objectType,
|
||||||
|
readOnly: true,
|
||||||
|
columnWidth: 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'externalReference',
|
||||||
|
label: 'External Reference',
|
||||||
|
type: (objectData) => {
|
||||||
|
return objectData?.marketplace?.provider === 'ebay'
|
||||||
|
? 'miscId'
|
||||||
|
: 'text'
|
||||||
|
},
|
||||||
|
readOnly: (objectData) => {
|
||||||
|
return objectData?.marketplace?.provider === 'ebay'
|
||||||
|
},
|
||||||
|
extra:
|
||||||
|
extra ||
|
||||||
|
'Marketplace policy ID. Filled when the policy is synced; you can paste an existing ID.',
|
||||||
|
showCopy: true,
|
||||||
|
required: false,
|
||||||
|
columnWidth: 240
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,12 @@ const financeSidebarItems = [
|
|||||||
iconKey: 'payment',
|
iconKey: 'payment',
|
||||||
path: '/dashboard/finance/payments'
|
path: '/dashboard/finance/payments'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'paymentpolicies',
|
||||||
|
label: 'Payment Policies',
|
||||||
|
iconKey: 'paymentPolicy',
|
||||||
|
path: '/dashboard/finance/paymentpolicies'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'taxRecords',
|
key: 'taxRecords',
|
||||||
iconKey: 'taxRecord',
|
iconKey: 'taxRecord',
|
||||||
|
|||||||
@ -18,6 +18,7 @@ const salesSidebarItems = [
|
|||||||
iconKey: 'salesOrder',
|
iconKey: 'salesOrder',
|
||||||
path: '/dashboard/sales/salesorders'
|
path: '/dashboard/sales/salesorders'
|
||||||
},
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
{
|
{
|
||||||
key: 'marketplaces',
|
key: 'marketplaces',
|
||||||
label: 'Marketplaces',
|
label: 'Marketplaces',
|
||||||
@ -29,6 +30,24 @@ const salesSidebarItems = [
|
|||||||
label: 'Listings',
|
label: 'Listings',
|
||||||
iconKey: 'listing',
|
iconKey: 'listing',
|
||||||
path: '/dashboard/sales/listings'
|
path: '/dashboard/sales/listings'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'listingvarients',
|
||||||
|
label: 'Listing Varients',
|
||||||
|
iconKey: 'listingVarient',
|
||||||
|
path: '/dashboard/sales/listingvarients'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'fulfillmentpolicies',
|
||||||
|
label: 'Fulfillment Policies',
|
||||||
|
iconKey: 'fulfillmentPolicy',
|
||||||
|
path: '/dashboard/sales/fulfillmentpolicies'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'returnpolicies',
|
||||||
|
label: 'Return Policies',
|
||||||
|
iconKey: 'returnPolicy',
|
||||||
|
path: '/dashboard/sales/returnpolicies'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,9 @@ const FinanceOverview = lazy(
|
|||||||
const TaxRecords = lazy(
|
const TaxRecords = lazy(
|
||||||
() => import('../components/Dashboard/Finance/TaxRecords.jsx')
|
() => import('../components/Dashboard/Finance/TaxRecords.jsx')
|
||||||
)
|
)
|
||||||
|
const PaymentPolicies = lazy(
|
||||||
|
() => import('../components/Dashboard/Finance/PaymentPolicies.jsx')
|
||||||
|
)
|
||||||
|
|
||||||
const FinanceRoutes = [
|
const FinanceRoutes = [
|
||||||
<Route
|
<Route
|
||||||
@ -33,6 +36,11 @@ const FinanceRoutes = [
|
|||||||
<Route key='invoices' path='finance/invoices' element={<Invoices />} />,
|
<Route key='invoices' path='finance/invoices' element={<Invoices />} />,
|
||||||
<Route key='payments' path='finance/payments' element={<Payments />} />,
|
<Route key='payments' path='finance/payments' element={<Payments />} />,
|
||||||
<Route key='taxrecords' path='finance/taxrecords' element={<TaxRecords />} />,
|
<Route key='taxrecords' path='finance/taxrecords' element={<TaxRecords />} />,
|
||||||
|
<Route
|
||||||
|
key='paymentpolicies'
|
||||||
|
path='finance/paymentpolicies'
|
||||||
|
element={<PaymentPolicies />}
|
||||||
|
/>,
|
||||||
<Route
|
<Route
|
||||||
key='taxrecords-redirect'
|
key='taxrecords-redirect'
|
||||||
path='management/taxrecords'
|
path='management/taxrecords'
|
||||||
|
|||||||
@ -14,6 +14,15 @@ const Marketplaces = lazy(
|
|||||||
const Listings = lazy(
|
const Listings = lazy(
|
||||||
() => import('../components/Dashboard/Sales/Listings.jsx')
|
() => import('../components/Dashboard/Sales/Listings.jsx')
|
||||||
)
|
)
|
||||||
|
const ListingVarients = lazy(
|
||||||
|
() => import('../components/Dashboard/Sales/ListingVarients.jsx')
|
||||||
|
)
|
||||||
|
const FulfillmentPolicies = lazy(
|
||||||
|
() => import('../components/Dashboard/Sales/FulfillmentPolicies.jsx')
|
||||||
|
)
|
||||||
|
const ReturnPolicies = lazy(
|
||||||
|
() => import('../components/Dashboard/Sales/ReturnPolicies.jsx')
|
||||||
|
)
|
||||||
|
|
||||||
const SalesRoutes = [
|
const SalesRoutes = [
|
||||||
<Route key='overview' path='sales/overview' element={<SalesOverview />} />,
|
<Route key='overview' path='sales/overview' element={<SalesOverview />} />,
|
||||||
@ -28,7 +37,22 @@ const SalesRoutes = [
|
|||||||
path='sales/marketplaces'
|
path='sales/marketplaces'
|
||||||
element={<Marketplaces />}
|
element={<Marketplaces />}
|
||||||
/>,
|
/>,
|
||||||
<Route key='listings' path='sales/listings' element={<Listings />} />
|
<Route key='listings' path='sales/listings' element={<Listings />} />,
|
||||||
|
<Route
|
||||||
|
key='listingvarients'
|
||||||
|
path='sales/listingvarients'
|
||||||
|
element={<ListingVarients />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='fulfillmentpolicies'
|
||||||
|
path='sales/fulfillmentpolicies'
|
||||||
|
element={<FulfillmentPolicies />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='returnpolicies'
|
||||||
|
path='sales/returnpolicies'
|
||||||
|
element={<ReturnPolicies />}
|
||||||
|
/>
|
||||||
]
|
]
|
||||||
|
|
||||||
export default SalesRoutes
|
export default SalesRoutes
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user