Compare commits
2 Commits
49a8bb3a6d
...
1e2adb2b28
| Author | SHA1 | Date | |
|---|---|---|---|
| 1e2adb2b28 | |||
| 7ea5eaf1f5 |
8
assets/icons/apppasswordicon.svg
Normal file
8
assets/icons/apppasswordicon.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(0.913777,0,0,0.913777,-11.957695,-8.458487)">
|
||||
<path d="M56.866,48.187C56.775,48.817 56.734,49.463 56.734,50.118C56.734,52.243 57.143,54.279 57.906,56.141C54.515,53.972 49.815,52.352 44,52.352C31.125,52.352 23.734,60.274 23.734,64.477C23.734,64.93 23.953,65.165 24.547,65.165L63.031,65.165L63.031,71.774L24.766,71.774C19.047,71.774 16.219,69.852 16.219,65.774C16.219,56.587 27.547,45.727 44,45.727C48.778,45.727 53.123,46.643 56.866,48.187ZM57.984,27.368C57.984,35.743 51.766,42.477 44,42.477C36.25,42.477 30.031,35.758 30.031,27.399C30.031,19.227 36.328,12.54 44,12.54C51.719,12.54 57.984,19.165 57.984,27.368ZM36.812,27.384C36.812,32.352 40.109,35.868 44,35.868C47.938,35.868 51.203,32.321 51.203,27.368C51.203,22.587 47.922,19.149 44,19.149C40.125,19.149 36.812,22.634 36.812,27.384Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M72.469,39.508C66.531,39.508 61.812,44.29 61.812,50.118C61.812,54.462 64.266,58.212 68.109,59.899L68.109,74.743C68.109,75.29 68.359,75.821 68.766,76.243L71.609,78.915C72.141,79.399 72.969,79.462 73.562,78.868L78.5,73.946C79.125,73.305 79.062,72.383 78.484,71.774L76.031,69.258L79.641,65.649C80.234,65.055 80.25,64.118 79.609,63.43L76.234,60.087C80.641,57.962 83.125,54.399 83.125,50.118C83.125,44.29 78.375,39.508 72.469,39.508ZM72.453,43.696C74.094,43.696 75.422,45.055 75.422,46.696C75.422,48.321 74.094,49.696 72.453,49.696C70.844,49.696 69.469,48.321 69.469,46.696C69.469,45.055 70.797,43.696 72.453,43.696Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
12
assets/icons/csvicon.svg
Normal file
12
assets/icons/csvicon.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(0.156042,0,0,0.156042,1.80453,0.721021)">
|
||||
<rect x="0" y="0" width="53.774" height="67.661" style="fill-opacity:0;"/>
|
||||
<path d="M10.149,67.641L43.438,67.641C50.137,67.641 53.586,64.135 53.586,57.41L53.586,29.058C53.586,24.717 53.028,22.725 50.308,19.954L33.894,3.284C31.282,0.616 29.111,0 25.212,0L10.149,0C3.481,0 0,3.532 0,10.257L0,57.41C0,64.161 3.455,67.641 10.149,67.641ZM10.637,61.519C7.621,61.519 6.122,59.941 6.122,57.029L6.122,10.637C6.122,7.752 7.621,6.122 10.663,6.122L23.984,6.122L23.984,23.261C23.984,27.733 26.167,29.895 30.619,29.895L47.464,29.895L47.464,57.029C47.464,59.941 45.965,61.519 42.929,61.519L10.637,61.519ZM31.198,24.496C29.903,24.496 29.384,23.945 29.384,22.656L29.384,6.973L46.613,24.496L31.198,24.496Z" style="fill-rule:nonzero;"/>
|
||||
<g transform="matrix(12.808657,0,0,12.808657,-40.118025,-49.663642)">
|
||||
<path d="M4.662,8.034C4.545,8.034 4.448,8.015 4.37,7.978C4.293,7.94 4.236,7.89 4.2,7.825C4.163,7.761 4.145,7.69 4.145,7.612C4.145,7.528 4.166,7.454 4.208,7.389C4.25,7.325 4.316,7.274 4.405,7.236C4.495,7.198 4.607,7.178 4.743,7.178L5.084,7.178C5.084,7.115 5.076,7.063 5.06,7.022C5.045,6.981 5.019,6.95 4.984,6.93C4.948,6.909 4.9,6.899 4.838,6.899C4.773,6.899 4.718,6.912 4.673,6.938C4.629,6.964 4.601,7.005 4.59,7.061L4.187,7.061C4.197,6.961 4.23,6.873 4.286,6.798C4.343,6.724 4.419,6.665 4.514,6.622C4.609,6.58 4.718,6.558 4.841,6.558C4.975,6.558 5.092,6.58 5.19,6.624C5.289,6.668 5.366,6.731 5.421,6.815C5.476,6.899 5.503,7.003 5.503,7.128L5.503,8L5.154,8L5.104,7.796C5.083,7.831 5.059,7.864 5.031,7.892C5.003,7.921 4.971,7.946 4.933,7.968C4.896,7.989 4.855,8.006 4.81,8.017C4.766,8.028 4.716,8.034 4.662,8.034ZM4.766,7.715C4.81,7.715 4.849,7.708 4.883,7.693C4.917,7.678 4.945,7.657 4.97,7.631C4.994,7.605 5.014,7.575 5.03,7.54C5.046,7.506 5.057,7.468 5.065,7.427L5.065,7.424L4.794,7.424C4.747,7.424 4.708,7.43 4.678,7.443C4.647,7.455 4.624,7.472 4.609,7.494C4.594,7.517 4.587,7.543 4.587,7.572C4.587,7.604 4.595,7.631 4.611,7.652C4.626,7.674 4.648,7.689 4.675,7.7C4.702,7.71 4.732,7.715 4.766,7.715Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M5.746,8.344L5.906,7.609L6.302,7.609L6.017,8.344L5.746,8.344Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
14
assets/icons/excelicon.svg
Normal file
14
assets/icons/excelicon.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.124,0,0,0.124,1.867256,1)">
|
||||
<path d="M69.43,159.719C69.43,125.199 97.41,97.219 131.922,97.219L486.012,97.219L486.012,458.328C486.012,481.34 467.359,500 444.352,500L152.738,500C106.73,500 69.43,462.691 69.43,416.672L69.43,159.719Z" style="fill:rgb(38,120,43);fill-rule:nonzero;"/>
|
||||
<path d="M69.43,229.172C69.43,194.648 97.41,166.672 131.922,166.672L319.379,166.672C296.371,166.672 277.719,185.328 277.719,208.34L277.719,291.672C277.719,314.68 259.07,333.34 236.059,333.34L152.75,333.34C106.738,333.34 69.441,370.648 69.441,416.672L69.441,229.172L69.43,229.172Z" style="fill:rgb(74,165,70);fill-rule:nonzero;"/>
|
||||
<path d="M69.43,83.328C69.43,37.309 106.73,0 152.738,0L319.371,0L319.371,166.672L152.738,166.672C106.73,166.672 69.43,203.98 69.43,250L69.43,83.328Z" style="fill:rgb(115,218,95);fill-rule:nonzero;"/>
|
||||
<path d="M319.371,0L444.34,0C467.348,0 486,18.652 486,41.66L486,125.012C486,148.02 467.348,166.672 444.34,166.672L319.371,166.672C296.363,166.672 277.711,148.02 277.711,125.012L277.711,41.66C277.711,18.652 296.363,0 319.371,0Z" style="fill:rgb(143,233,111);fill-rule:nonzero;"/>
|
||||
<g transform="matrix(1.250027,0,0,1.250027,0,-114.594378)">
|
||||
<path d="M45.129,236.109L177.039,236.109C201.965,236.109 222.172,256.316 222.172,281.238L222.172,413.199C222.172,438.125 201.965,458.328 177.039,458.328L45.129,458.328C20.207,458.328 0,438.125 0,413.199L0,281.238C0,256.316 20.207,236.109 45.129,236.109Z" style="fill:rgb(11,78,46);fill-rule:nonzero;"/>
|
||||
<path d="M169.48,410.711L135.23,410.711L113.73,370.238C112.961,368.82 112.371,367.699 111.961,366.871C111.609,365.988 111.219,364.98 110.809,363.859L110.461,363.859C109.93,365.281 109.43,366.43 108.961,367.309C108.488,368.199 107.93,369.289 107.281,370.59L84.98,410.699L52.68,410.699L91.441,347.121L55.34,283.719L89.141,283.719L108.25,319.852C109.02,321.328 109.672,322.629 110.199,323.75C110.789,324.809 111.379,326.078 111.969,327.559L112.32,327.559C113.141,325.852 113.789,324.488 114.27,323.488C114.801,322.488 115.512,321.16 116.391,319.512L136.211,283.738L168.422,283.738L131.789,346.172L169.488,410.719L169.48,410.711Z" style="fill:rgb(254,254,254);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
9
assets/icons/exporticon.svg
Normal file
9
assets/icons/exporticon.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(0.760492,0,0,0.760492,9.844746,-1.023725)">
|
||||
<path d="M19.578,35.125L14.438,35.125C10.359,35.125 8.062,37.422 8.062,41.5L8.062,64.734C8.062,68.828 10.359,71.125 14.438,71.125L43.828,71.125C47.906,71.125 50.203,68.828 50.203,64.734L50.203,41.5C50.203,37.422 47.906,35.125 43.828,35.125L38.672,35.125L38.672,27.062L44.266,27.062C53.234,27.062 58.266,32.062 58.266,41.047L58.266,65.188C58.266,74.156 53.234,79.188 44.266,79.188L14,79.188C5.016,79.188 0,74.156 0,65.188L0,41.047C0,32.062 5.016,27.062 14,27.062L19.578,27.062L19.578,35.125Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M32.423,14.141L32.75,18.578L32.75,49.219C32.75,51.125 31.141,52.766 29.125,52.766C27.109,52.766 25.516,51.125 25.516,49.219L25.516,18.578L25.851,14.122L29.125,9.438L32.423,14.141Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M18.172,21.328C19.078,21.328 19.938,20.953 20.547,20.297L24.375,16.234L29.125,9.438L33.891,16.234L37.703,20.297C38.312,20.953 39.156,21.328 40.062,21.328C41.703,21.328 43.188,20.094 43.188,18.328C43.188,17.406 42.859,16.734 42.234,16.109L31.984,6.281C31.047,5.359 30.109,5.031 29.125,5.031C28.156,5.031 27.234,5.359 26.266,6.281L16.031,16.109C15.422,16.734 15.062,17.406 15.062,18.328C15.062,20.094 16.531,21.328 18.172,21.328Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
7
assets/icons/odataicon.svg
Normal file
7
assets/icons/odataicon.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(0.069972,0,0,0.069972,-2.986098,-11.802594)">
|
||||
<path d="M242,226C206,232 174.833,248.333 148.5,275C122.167,301.667 106,333 100,369L100,884C106,919.333 122,950 148,976C174,1002 204.333,1018.333 239,1025L243,1026L758,1026L761,1025C796.333,1018.333 826.833,1001.833 852.5,975.5C878.167,949.167 894,918.333 900,883L900,368C894,332.667 877.667,301.667 851,275C824.333,248.333 793,232 757,226L242,226ZM802,325L802,401C731.333,401.667 636.667,401.667 518,401L519,326L802,325ZM481,326C481.667,342.667 482,367.667 482,401L200,401L200,326L481,326ZM518,438L802,438L802,514L518,514L518,438ZM482,438L482,514L200,513L200,439L482,438ZM554,550C590.667,550 645.667,550.333 719,551L801,551L801,627L518,628L518,550L554,550ZM482,550L482,628L200,628L200,551L482,551L482,550ZM518,665L801,665C801.667,677 801.667,695.333 801,720L801,740L519,740L518,665ZM311,700C327.667,700 343.833,703.5 359.5,710.5C375.167,717.5 388.333,727.333 399,740C413.667,756.667 422,776.5 424,799.5C426,822.5 422,844.5 412,865.5C402,886.5 387,902 367,912C348.333,922.667 327.5,927.167 304.5,925.5C281.5,923.833 260.833,916.333 242.5,903C224.167,889.667 212,872.333 206,851C198,831.667 196.333,811.333 201,790C205.667,768.667 215.333,750.167 230,734.5C244.667,718.833 262.333,708.667 283,704C292.333,701.333 301.667,700 311,700Z" style="fill:rgb(255,152,0);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Invoices = () => {
|
||||
const [newInvoiceOpen, setNewInvoiceOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const Invoices = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='invoice' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Payments = () => {
|
||||
const [newPaymentOpen, setNewPaymentOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const Payments = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='payment' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -13,6 +13,7 @@ import ListIcon from '../../Icons/ListIcon'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const FilamentStocks = () => {
|
||||
const tableRef = useRef()
|
||||
@ -61,6 +62,7 @@ const FilamentStocks = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='filamentStock' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const OrderItems = () => {
|
||||
const [newOrderItemOpen, setNewOrderItemOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const OrderItems = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='orderItem' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -13,6 +13,7 @@ import ListIcon from '../../Icons/ListIcon'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const PartStocks = () => {
|
||||
const tableRef = useRef()
|
||||
@ -61,6 +62,7 @@ const PartStocks = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='partStock' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const PurchaseOrders = () => {
|
||||
const [newPurchaseOrderOpen, setNewPurchaseOrderOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const PurchaseOrders = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='purchaseOrder' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Shipments = () => {
|
||||
const [newShipmentOpen, setNewShipmentOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const Shipments = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='shipment' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -13,6 +13,7 @@ import ListIcon from '../../Icons/ListIcon'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const StockAudits = () => {
|
||||
const tableRef = useRef()
|
||||
@ -61,6 +62,7 @@ const StockAudits = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='stockAudit' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -6,6 +6,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
|
||||
const StockEvents = () => {
|
||||
@ -44,6 +45,7 @@ const StockEvents = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='stockEvent' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
99
src/components/Dashboard/Management/AppPasswords.jsx
Normal file
99
src/components/Dashboard/Management/AppPasswords.jsx
Normal file
@ -0,0 +1,99 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { Button, Flex, Space, Modal, Dropdown } from 'antd'
|
||||
import NewAppPassword from './AppPasswords/NewAppPassword'
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const AppPasswords = () => {
|
||||
const [newAppPasswordOpen, setNewAppPasswordOpen] = useState(false)
|
||||
const tableRef = useRef()
|
||||
|
||||
const [viewMode, setViewMode] = useViewMode('appPassword')
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
useColumnVisibility('appPassword')
|
||||
|
||||
const actionItems = {
|
||||
items: [
|
||||
{
|
||||
label: 'New App Password',
|
||||
key: 'newAppPassword',
|
||||
icon: <PlusIcon />
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
label: 'Reload List',
|
||||
key: 'reloadList',
|
||||
icon: <ReloadIcon />
|
||||
}
|
||||
],
|
||||
onClick: ({ key }) => {
|
||||
if (key === 'reloadList') {
|
||||
tableRef.current?.reload()
|
||||
} else if (key === 'newAppPassword') {
|
||||
setNewAppPasswordOpen(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex vertical={'true'} gap='large'>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space>
|
||||
<Dropdown menu={actionItems}>
|
||||
<Button>Actions</Button>
|
||||
</Dropdown>
|
||||
<ColumnViewButton
|
||||
type='appPassword'
|
||||
loading={false}
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='appPassword' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||
onClick={() =>
|
||||
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
<ObjectTable
|
||||
ref={tableRef}
|
||||
type='appPassword'
|
||||
cards={viewMode === 'cards'}
|
||||
visibleColumns={columnVisibility}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
open={newAppPasswordOpen}
|
||||
footer={null}
|
||||
width={700}
|
||||
onCancel={() => {
|
||||
setNewAppPasswordOpen(false)
|
||||
}}
|
||||
>
|
||||
<NewAppPassword
|
||||
onOk={() => {
|
||||
setNewAppPasswordOpen(false)
|
||||
tableRef.current?.reload()
|
||||
}}
|
||||
reset={newAppPasswordOpen}
|
||||
/>
|
||||
</Modal>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppPasswords
|
||||
@ -0,0 +1,212 @@
|
||||
import { useRef, useState, useContext } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Space, Flex, Modal } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import useCollapseState from '../../hooks/useCollapseState'
|
||||
import InfoCollapse from '../../common/InfoCollapse'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import ViewButton from '../../common/ViewButton'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm'
|
||||
import EditButtons from '../../common/EditButtons'
|
||||
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import RegenerateAppPasswordSecret from './RegenerateAppPasswordSecret.jsx'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import { AuthContext } from '../../context/AuthContext.jsx'
|
||||
|
||||
const AppPasswordInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const { userProfile } = useContext(AuthContext)
|
||||
const appPasswordId = new URLSearchParams(location.search).get(
|
||||
'appPasswordId'
|
||||
)
|
||||
const [regenerateSecretOpen, setRegenerateSecretOpen] = useState(false)
|
||||
const [collapseState, updateCollapseState] = useCollapseState(
|
||||
'AppPasswordInfo',
|
||||
{
|
||||
info: true,
|
||||
auditLogs: true
|
||||
}
|
||||
)
|
||||
const [objectFormState, setEditFormState] = useState({
|
||||
isEditing: false,
|
||||
editLoading: false,
|
||||
formValid: false,
|
||||
lock: null,
|
||||
loading: false,
|
||||
objectData: {}
|
||||
})
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.handleFetchObject?.()
|
||||
return true
|
||||
},
|
||||
regenerateSecret: () => {
|
||||
setRegenerateSecretOpen(true)
|
||||
return false
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
},
|
||||
cancelEdit: () => {
|
||||
objectFormRef?.current?.cancelEditing?.()
|
||||
return true
|
||||
},
|
||||
finishEdit: () => {
|
||||
objectFormRef?.current?.handleUpdate?.()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const editDisabled = getModelByName('appPassword')
|
||||
.actions.find((action) => action.name === 'edit')
|
||||
.disabled({ ...objectFormState.objectData, _user: userProfile })
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
gap='large'
|
||||
vertical='true'
|
||||
style={{ maxHeight: '100%', minHeight: 0 }}
|
||||
>
|
||||
<Flex justify={'space-between'}>
|
||||
<Space size='middle'>
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='appPassword'
|
||||
id={appPasswordId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
/>
|
||||
<ViewButton
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'App Password Information' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
visibleState={collapseState}
|
||||
updateVisibleState={updateCollapseState}
|
||||
/>
|
||||
<UserNotifierToggle
|
||||
type='appPassword'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
<DocumentPrintButton
|
||||
type='appPassword'
|
||||
objectData={objectFormState.objectData}
|
||||
disabled={objectFormState.loading}
|
||||
/>
|
||||
</Space>
|
||||
<LockIndicator lock={objectFormState.lock} />
|
||||
</Space>
|
||||
<Space>
|
||||
<EditButtons
|
||||
isEditing={objectFormState.isEditing}
|
||||
handleUpdate={() => {
|
||||
actionHandlerRef.current.callAction('finishEdit')
|
||||
}}
|
||||
cancelEditing={() => {
|
||||
actionHandlerRef.current.callAction('cancelEdit')
|
||||
}}
|
||||
startEditing={() => {
|
||||
actionHandlerRef.current.callAction('edit')
|
||||
}}
|
||||
editLoading={objectFormState.editLoading}
|
||||
formValid={objectFormState.formValid}
|
||||
disabled={
|
||||
objectFormState.lock?.locked ||
|
||||
objectFormState.loading ||
|
||||
editDisabled
|
||||
}
|
||||
loading={objectFormState.editLoading}
|
||||
/>
|
||||
</Space>
|
||||
</Flex>
|
||||
<ScrollBox>
|
||||
<Flex vertical gap={'large'}>
|
||||
<ActionHandler
|
||||
actions={actions}
|
||||
loading={objectFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<InfoCollapse
|
||||
title='App Password Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectForm
|
||||
id={appPasswordId}
|
||||
type='appPassword'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
isEditing={isEditing}
|
||||
type='appPassword'
|
||||
objectData={objectData}
|
||||
/>
|
||||
)}
|
||||
</ObjectForm>
|
||||
</InfoCollapse>
|
||||
</ActionHandler>
|
||||
<InfoCollapse
|
||||
title='Audit Logs'
|
||||
icon={<AuditLogIcon />}
|
||||
active={collapseState.auditLogs}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('auditLogs', expanded)
|
||||
}
|
||||
collapseKey='auditLogs'
|
||||
>
|
||||
{objectFormState.loading ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='auditLog'
|
||||
masterFilter={{ 'parent._id': appPasswordId }}
|
||||
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
|
||||
<Modal
|
||||
open={regenerateSecretOpen}
|
||||
destroyOnClose
|
||||
width={650}
|
||||
onCancel={() => {
|
||||
actionHandlerRef.current?.clearAction?.()
|
||||
setRegenerateSecretOpen(false)
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<RegenerateAppPasswordSecret id={appPasswordId} />
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppPasswordInfo
|
||||
@ -0,0 +1,91 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import ObjectInfo from '../../common/ObjectInfo'
|
||||
import NewObjectForm from '../../common/NewObjectForm'
|
||||
import WizardView from '../../common/WizardView'
|
||||
|
||||
const NewAppPassword = ({ onOk, reset, defaultValues = {} }) => {
|
||||
return (
|
||||
<NewObjectForm
|
||||
type='appPassword'
|
||||
reset={reset}
|
||||
defaultValues={{
|
||||
active: true,
|
||||
...defaultValues
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||
const steps = [
|
||||
{
|
||||
title: 'Required',
|
||||
key: 'required',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='appPassword'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={true}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Optional',
|
||||
key: 'optional',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='appPassword'
|
||||
column={1}
|
||||
bordered={false}
|
||||
isEditing={true}
|
||||
required={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Summary',
|
||||
key: 'summary',
|
||||
content: (
|
||||
<ObjectInfo
|
||||
type='appPassword'
|
||||
column={1}
|
||||
bordered={false}
|
||||
visibleProperties={{
|
||||
_id: false,
|
||||
createdAt: false,
|
||||
updatedAt: false,
|
||||
secret: false
|
||||
}}
|
||||
isEditing={false}
|
||||
objectData={objectData}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
return (
|
||||
<WizardView
|
||||
steps={steps}
|
||||
loading={submitLoading}
|
||||
formValid={formValid}
|
||||
title='New App Password'
|
||||
onSubmit={async () => {
|
||||
const result = await handleSubmit()
|
||||
if (result) {
|
||||
onOk()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</NewObjectForm>
|
||||
)
|
||||
}
|
||||
|
||||
NewAppPassword.propTypes = {
|
||||
onOk: PropTypes.func.isRequired,
|
||||
reset: PropTypes.bool,
|
||||
defaultValues: PropTypes.object
|
||||
}
|
||||
|
||||
export default NewAppPassword
|
||||
@ -0,0 +1,77 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useContext, useState } from 'react'
|
||||
import { Result, Typography, Flex, Button } from 'antd'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
import CopyButton from '../../common/CopyButton'
|
||||
import AppPasswordIcon from '../../../Icons/AppPasswordIcon.jsx'
|
||||
import ReloadIcon from '../../../Icons/ReloadIcon'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
const RegenerateAppPasswordSecret = ({ id }) => {
|
||||
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||
const [appPassword, setAppPassword] = useState(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [passwordGenerated, setPasswordGenerated] = useState(false)
|
||||
|
||||
const handleRegenerate = async () => {
|
||||
setLoading(true)
|
||||
setAppPassword(null)
|
||||
try {
|
||||
const result = await sendObjectFunction(
|
||||
id,
|
||||
'appPassword',
|
||||
'regenerateSecret',
|
||||
{}
|
||||
)
|
||||
if (result?.appPassword) {
|
||||
setAppPassword(result.appPassword)
|
||||
setPasswordGenerated(true)
|
||||
}
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex vertical align='center'>
|
||||
<Result
|
||||
title={
|
||||
passwordGenerated ? 'Secret Regenerated' : 'Regenerate Secret'
|
||||
}
|
||||
disabled={passwordGenerated}
|
||||
subTitle={
|
||||
appPassword ? (
|
||||
<Text>Copy this secret now. It will not be shown again.</Text>
|
||||
) : (
|
||||
<Text>Generate a new secret for this app password.</Text>
|
||||
)
|
||||
}
|
||||
icon={<AppPasswordIcon />}
|
||||
>
|
||||
<Flex justify='center' style={{ minWidth: '395px' }}>
|
||||
<Flex justify='center'>
|
||||
<Flex gap='small' align='center' justify='center'>
|
||||
<CopyButton size='default' text={appPassword} />
|
||||
<Text code style={{ fontSize: '18px' }}>
|
||||
{appPassword || '••••••••••••••••••••••••••••••••'}
|
||||
</Text>
|
||||
<Button
|
||||
type='text'
|
||||
loading={loading}
|
||||
onClick={handleRegenerate}
|
||||
icon={<ReloadIcon />}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Result>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
RegenerateAppPasswordSecret.propTypes = {
|
||||
id: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default RegenerateAppPasswordSecret
|
||||
@ -5,6 +5,7 @@ import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const AuditLogs = () => {
|
||||
const tableRef = useRef()
|
||||
@ -41,6 +42,7 @@ const AuditLogs = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={updateColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='auditLog' />
|
||||
</Space>
|
||||
</Flex>
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const CourierServices = () => {
|
||||
const [newCourierServiceOpen, setNewCourierServiceOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const CourierServices = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='courierService' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Couriers = () => {
|
||||
const [newCourierOpen, setNewCourierOpen] = useState(false)
|
||||
@ -55,6 +56,7 @@ const Couriers = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='courier' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const DocumentJobs = () => {
|
||||
const [newDocumentJobOpen, setNewDocumentJobOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const DocumentJobs = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='documentJob' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -8,6 +8,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
import NewDocumentPrinter from './DocumentPrinters/NewDocumentPrinter'
|
||||
|
||||
const DocumentPrinters = () => {
|
||||
@ -55,6 +56,7 @@ const DocumentPrinters = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='documentPrinter' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const DocumentSizes = () => {
|
||||
const [newDocumentSizeOpen, setNewDocumentSizeOpen] = useState(false)
|
||||
@ -54,6 +55,7 @@ const DocumentSizes = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='documentSize' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const DocumentTemplates = () => {
|
||||
const [newDocumentTemplateOpen, setNewDocumentTemplateOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const DocumentTemplates = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='documentTemplate' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -13,6 +13,7 @@ import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Filaments = () => {
|
||||
const [newFilamentOpen, setNewFilamentOpen] = useState(false)
|
||||
@ -61,6 +62,7 @@ const Filaments = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='filament' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -7,6 +7,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Files = () => {
|
||||
const tableRef = useRef()
|
||||
@ -44,6 +45,7 @@ const Files = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='file' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -7,6 +7,7 @@ import NewHost from './Hosts/NewHost'
|
||||
|
||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import PlusIcon from '../../Icons/PlusIcon'
|
||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
@ -60,6 +61,7 @@ const Hosts = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='host' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -21,6 +21,7 @@ import CourierIcon from '../../Icons/CourierIcon'
|
||||
import CourierServiceIcon from '../../Icons/CourierServiceIcon'
|
||||
import TaxRateIcon from '../../Icons/TaxRateIcon'
|
||||
import TaxRecordIcon from '../../Icons/TaxRecordIcon'
|
||||
import AppPasswordIcon from '../../Icons/AppPasswordIcon'
|
||||
|
||||
const items = [
|
||||
{
|
||||
@ -131,6 +132,12 @@ const items = [
|
||||
label: 'Users',
|
||||
path: '/dashboard/management/users'
|
||||
},
|
||||
{
|
||||
key: 'appPasswords',
|
||||
icon: <AppPasswordIcon />,
|
||||
label: 'App Passwords',
|
||||
path: '/dashboard/management/apppasswords'
|
||||
},
|
||||
{
|
||||
key: 'settings',
|
||||
icon: <SettingsIcon />,
|
||||
@ -167,6 +174,7 @@ const routeKeyMap = {
|
||||
'/dashboard/management/filaments': 'filaments',
|
||||
'/dashboard/management/parts': 'parts',
|
||||
'/dashboard/management/users': 'users',
|
||||
'/dashboard/management/apppasswords': 'appPasswords',
|
||||
'/dashboard/management/products': 'products',
|
||||
'/dashboard/management/vendors': 'vendors',
|
||||
'/dashboard/management/couriers': 'couriers',
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const NoteTypes = () => {
|
||||
const [newNoteTypeOpen, setNewNoteTypeOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const NoteTypes = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='noteType' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -16,6 +16,7 @@ import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Parts = (filter) => {
|
||||
const [newPartOpen, setNewPartOpen] = useState(false)
|
||||
@ -60,6 +61,7 @@ const Parts = (filter) => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='part' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -28,6 +28,7 @@ import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Products = () => {
|
||||
const navigate = useNavigate()
|
||||
@ -325,6 +326,7 @@ const Products = () => {
|
||||
<Dropdown menu={actionItems}>
|
||||
<Button>Actions</Button>
|
||||
</Dropdown>
|
||||
<ExportListButton objectType='product' />
|
||||
<Popover
|
||||
content={getViewDropdownItems()}
|
||||
placement='bottomLeft'
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const TaxRates = () => {
|
||||
const [newTaxRateOpen, setNewTaxRateOpen] = useState(false)
|
||||
@ -55,6 +56,7 @@ const TaxRates = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='taxRate' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const TaxRecords = () => {
|
||||
const [newTaxRecordOpen, setNewTaxRecordOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const TaxRecords = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='taxRecord' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -7,6 +7,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Users = () => {
|
||||
const tableRef = useRef()
|
||||
@ -43,6 +44,7 @@ const Users = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='user' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
73
src/components/Dashboard/Management/Users/SetAppPassword.jsx
Normal file
73
src/components/Dashboard/Management/Users/SetAppPassword.jsx
Normal file
@ -0,0 +1,73 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useContext, useState } from 'react'
|
||||
import { Result, Typography, Flex, Button } from 'antd'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
import CopyButton from '../../common/CopyButton'
|
||||
import LockIcon from '../../../Icons/LockIcon'
|
||||
import ReloadIcon from '../../../Icons/ReloadIcon'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
const SetAppPassword = ({ id }) => {
|
||||
const { sendObjectFunction } = useContext(ApiServerContext)
|
||||
const [appPassword, setAppPassword] = useState(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [passwordGenerated, setPasswordGenerated] = useState(false)
|
||||
|
||||
const handleSet = async () => {
|
||||
setLoading(true)
|
||||
setAppPassword(null)
|
||||
try {
|
||||
const result = await sendObjectFunction(id, 'user', 'setAppPassword', {})
|
||||
if (result?.appPassword) {
|
||||
setAppPassword(result.appPassword)
|
||||
setPasswordGenerated(true)
|
||||
}
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex vertical align='center'>
|
||||
<Result
|
||||
title={
|
||||
passwordGenerated ? 'App Password Generated' : 'Roll New Password'
|
||||
}
|
||||
disabled={passwordGenerated}
|
||||
subTitle={
|
||||
appPassword ? (
|
||||
<Text>Copy this password now. It will not be shown again.</Text>
|
||||
) : (
|
||||
<Text>Generate a new app password for API access.</Text>
|
||||
)
|
||||
}
|
||||
icon={<LockIcon />}
|
||||
>
|
||||
<Flex justify='center' style={{ minWidth: '395px' }}>
|
||||
<Flex justify='center'>
|
||||
<Flex gap='small' align='center' justify='center'>
|
||||
<CopyButton size='default' text={appPassword} />
|
||||
|
||||
<Text code style={{ fontSize: '18px' }}>
|
||||
{appPassword || '••••••••••••••••••••••••••••••••'}
|
||||
</Text>
|
||||
<Button
|
||||
type='texts'
|
||||
loading={loading}
|
||||
onClick={handleSet}
|
||||
icon={<ReloadIcon />}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Result>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
SetAppPassword.propTypes = {
|
||||
id: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default SetAppPassword
|
||||
@ -1,6 +1,6 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Space, Flex, Card } from 'antd'
|
||||
import { Space, Flex, Card, Modal } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import useCollapseState from '../../hooks/useCollapseState'
|
||||
import NotesPanel from '../../common/NotesPanel'
|
||||
@ -10,6 +10,7 @@ import ViewButton from '../../common/ViewButton'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import AppPasswordIcon from '../../../Icons/AppPasswordIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm'
|
||||
import EditButtons from '../../common/EditButtons'
|
||||
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||
@ -20,14 +21,18 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import NewAppPassword from '../AppPasswords/NewAppPassword.jsx'
|
||||
|
||||
const UserInfo = () => {
|
||||
const location = useLocation()
|
||||
const objectFormRef = useRef(null)
|
||||
const appPasswordsTableRef = useRef(null)
|
||||
const actionHandlerRef = useRef(null)
|
||||
const userId = new URLSearchParams(location.search).get('userId')
|
||||
const [newAppPasswordOpen, setNewAppPasswordOpen] = useState(false)
|
||||
const [collapseState, updateCollapseState] = useCollapseState('UserInfo', {
|
||||
info: true,
|
||||
appPasswords: true,
|
||||
notes: true,
|
||||
auditLogs: true
|
||||
})
|
||||
@ -42,9 +47,13 @@ const UserInfo = () => {
|
||||
|
||||
const actions = {
|
||||
reload: () => {
|
||||
objectFormRef?.current?.fetchObject?.()
|
||||
objectFormRef?.current?.handleFetchObject?.()
|
||||
return true
|
||||
},
|
||||
newAppPassword: () => {
|
||||
setNewAppPasswordOpen(true)
|
||||
return false
|
||||
},
|
||||
edit: () => {
|
||||
objectFormRef?.current?.startEditing?.()
|
||||
return false
|
||||
@ -79,6 +88,7 @@ const UserInfo = () => {
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'User Information' },
|
||||
{ key: 'appPasswords', label: 'App Passwords' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
@ -152,6 +162,26 @@ const UserInfo = () => {
|
||||
</ObjectForm>
|
||||
</InfoCollapse>
|
||||
</ActionHandler>
|
||||
<InfoCollapse
|
||||
title='App Passwords'
|
||||
icon={<AppPasswordIcon />}
|
||||
active={collapseState.appPasswords}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('appPasswords', expanded)
|
||||
}
|
||||
collapseKey='appPasswords'
|
||||
>
|
||||
{!userId ? (
|
||||
<InfoCollapsePlaceholder />
|
||||
) : (
|
||||
<ObjectTable
|
||||
type='appPassword'
|
||||
masterFilter={{ user: userId }}
|
||||
visibleColumns={{ user: false }}
|
||||
ref={appPasswordsTableRef}
|
||||
/>
|
||||
)}
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Notes'
|
||||
icon={<NoteIcon />}
|
||||
@ -185,6 +215,26 @@ const UserInfo = () => {
|
||||
</Flex>
|
||||
</ScrollBox>
|
||||
</Flex>
|
||||
|
||||
<Modal
|
||||
open={newAppPasswordOpen}
|
||||
destroyOnClose
|
||||
width={700}
|
||||
onCancel={() => {
|
||||
actionHandlerRef.current?.clearAction?.()
|
||||
setNewAppPasswordOpen(false)
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<NewAppPassword
|
||||
onOk={() => {
|
||||
setNewAppPasswordOpen(false)
|
||||
appPasswordsTableRef.current?.reload?.()
|
||||
}}
|
||||
reset={newAppPasswordOpen}
|
||||
defaultValues={{ user: { ...objectFormState.objectData } }}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Vendors = () => {
|
||||
const [newVendorOpen, setNewVendorOpen] = useState(false)
|
||||
@ -55,6 +56,7 @@ const Vendors = () => {
|
||||
collapseState={columnVisibility}
|
||||
updateCollapseState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='vendor' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -12,6 +12,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const GCodeFiles = () => {
|
||||
const [newGCodeFileOpen, setNewGCodeFileOpen] = useState(false)
|
||||
@ -58,6 +59,7 @@ const GCodeFiles = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='gcodeFile' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -11,6 +11,7 @@ import ListIcon from '../../Icons/ListIcon.jsx'
|
||||
import GridIcon from '../../Icons/GridIcon.jsx'
|
||||
import useViewMode from '../hooks/useViewMode.jsx'
|
||||
import ColumnViewButton from '../common/ColumnViewButton.jsx'
|
||||
import ExportListButton from '../common/ExportListButton.jsx'
|
||||
|
||||
const Jobs = () => {
|
||||
const [newJobOpen, setNewJobOpen] = useState(false)
|
||||
@ -60,6 +61,7 @@ const Jobs = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='job' />
|
||||
</Space>
|
||||
|
||||
<Space>
|
||||
|
||||
@ -7,6 +7,7 @@ import PlusIcon from '../../Icons/PlusIcon'
|
||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||
import ObjectTable from '../common/ObjectTable'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
@ -60,6 +61,7 @@ const Printers = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='printer' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import ListIcon from '../../Icons/ListIcon.jsx'
|
||||
import GridIcon from '../../Icons/GridIcon.jsx'
|
||||
import useViewMode from '../hooks/useViewMode.jsx'
|
||||
import ColumnViewButton from '../common/ColumnViewButton.jsx'
|
||||
import ExportListButton from '../common/ExportListButton.jsx'
|
||||
|
||||
const SubJobs = () => {
|
||||
const tableRef = useRef()
|
||||
@ -45,6 +46,7 @@ const SubJobs = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='subJob' />
|
||||
</Space>
|
||||
|
||||
<Space>
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const Clients = () => {
|
||||
const [newClientOpen, setNewClientOpen] = useState(false)
|
||||
@ -55,6 +56,7 @@ const Clients = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='client' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ import GridIcon from '../../Icons/GridIcon'
|
||||
import ListIcon from '../../Icons/ListIcon'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import ColumnViewButton from '../common/ColumnViewButton'
|
||||
import ExportListButton from '../common/ExportListButton'
|
||||
|
||||
const SalesOrders = () => {
|
||||
const [newSalesOrderOpen, setNewSalesOrderOpen] = useState(false)
|
||||
@ -56,6 +57,7 @@ const SalesOrders = () => {
|
||||
visibleState={columnVisibility}
|
||||
updateVisibleState={setColumnVisibility}
|
||||
/>
|
||||
<ExportListButton objectType='salesOrder' />
|
||||
</Space>
|
||||
<Space>
|
||||
<Button
|
||||
|
||||
78
src/components/Dashboard/common/ExportListButton.jsx
Normal file
78
src/components/Dashboard/common/ExportListButton.jsx
Normal file
@ -0,0 +1,78 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useState } from 'react'
|
||||
import { Button, Dropdown, Modal } from 'antd'
|
||||
import ExcelIcon from '../../Icons/ExcelIcon'
|
||||
import ODataIcon from '../../Icons/ODataIcon'
|
||||
import CsvIcon from '../../Icons/CsvIcon'
|
||||
import ExportIcon from '../../Icons/ExportIcon'
|
||||
import ODataURL from './ODataURL'
|
||||
|
||||
const ExportListButton = ({
|
||||
objectType,
|
||||
disabled = false,
|
||||
size = 'middle',
|
||||
...buttonProps
|
||||
}) => {
|
||||
const [odataModalOpen, setOdataModalOpen] = useState(false)
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
key: 'excel',
|
||||
label: 'Excel',
|
||||
icon: <ExcelIcon />,
|
||||
disabled: true,
|
||||
onClick: () => {
|
||||
// TODO: implement Excel export
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'odata',
|
||||
label: 'OData',
|
||||
icon: <ODataIcon />,
|
||||
onClick: () => setOdataModalOpen(true)
|
||||
},
|
||||
{
|
||||
key: 'csv',
|
||||
label: 'CSV',
|
||||
icon: <CsvIcon />,
|
||||
disabled: true,
|
||||
onClick: () => {
|
||||
// TODO: implement CSV export
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dropdown
|
||||
menu={{ items: menuItems }}
|
||||
trigger={['hover']}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Button
|
||||
icon={<ExportIcon />}
|
||||
disabled={disabled}
|
||||
size={size}
|
||||
{...buttonProps}
|
||||
/>
|
||||
</Dropdown>
|
||||
<Modal
|
||||
open={odataModalOpen}
|
||||
destroyOnClose
|
||||
width={650}
|
||||
onCancel={() => setOdataModalOpen(false)}
|
||||
footer={null}
|
||||
>
|
||||
<ODataURL objectType={objectType} />
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
ExportListButton.propTypes = {
|
||||
objectType: PropTypes.string.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
size: PropTypes.oneOf(['large', 'middle', 'small'])
|
||||
}
|
||||
|
||||
export default ExportListButton
|
||||
45
src/components/Dashboard/common/ODataURL.jsx
Normal file
45
src/components/Dashboard/common/ODataURL.jsx
Normal file
@ -0,0 +1,45 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { Result, Typography, Flex } from 'antd'
|
||||
import CopyButton from './CopyButton'
|
||||
import ODataIcon from '../../Icons/ODataIcon'
|
||||
import config from '../../../config'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
const ODataURL = ({ objectType }) => {
|
||||
const baseUrl = config.backendUrl?.replace(/\/$/, '') || ''
|
||||
const odataUrl = `${baseUrl}/odata/${objectType}`
|
||||
|
||||
return (
|
||||
<Flex vertical align='center'>
|
||||
<Result
|
||||
title='OData URL'
|
||||
subTitle={
|
||||
<Text>
|
||||
Use this URL to connect Power BI, Excel, or other OData clients. An
|
||||
app password is required and can be configured in your user
|
||||
settings.
|
||||
</Text>
|
||||
}
|
||||
icon={<ODataIcon />}
|
||||
>
|
||||
<Flex justify='center' style={{ minWidth: '395px' }}>
|
||||
<Flex justify='center'>
|
||||
<Flex gap='small' align='center' justify='center'>
|
||||
<CopyButton size='default' text={odataUrl} />
|
||||
<Text code style={{ fontSize: '14px', wordBreak: 'break-all' }}>
|
||||
{odataUrl}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Result>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
ODataURL.propTypes = {
|
||||
objectType: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default ODataURL
|
||||
@ -1,10 +1,11 @@
|
||||
import { createElement } from 'react'
|
||||
import { createElement, useContext } from 'react'
|
||||
import { Dropdown, Button } from 'antd'
|
||||
import { getModelByName } from '../../../database/ObjectModels'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
import { useActionsModal } from '../context/ActionsModalContext'
|
||||
import KeyboardShortcut from './KeyboardShortcut'
|
||||
import { AuthContext } from '../context/AuthContext'
|
||||
|
||||
// Recursively filter actions based on visibleActions
|
||||
function filterActionsByVisibility(actions, visibleActions) {
|
||||
@ -47,9 +48,11 @@ function mapActionsToMenuItems(actions, currentUrlWithActions, id, objectData) {
|
||||
var disabled = actionUrl && actionUrl === currentUrlWithActions
|
||||
var visible = true
|
||||
|
||||
const { userProfile } = useContext(AuthContext)
|
||||
|
||||
if (action.disabled) {
|
||||
if (typeof action.disabled === 'function') {
|
||||
disabled = action.disabled(objectData)
|
||||
disabled = action.disabled({ ...objectData, _user: userProfile })
|
||||
} else {
|
||||
disabled = action.disabled
|
||||
}
|
||||
@ -105,7 +108,6 @@ const ObjectActions = ({
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const { showActionsModal } = useActionsModal()
|
||||
|
||||
// Get current url without 'action' param
|
||||
const currentUrlWithoutActions = stripActionParam(
|
||||
location.pathname,
|
||||
|
||||
@ -105,7 +105,7 @@ const ObjectTable = forwardRef(
|
||||
const { token } = useContext(AuthContext)
|
||||
const { isElectron } = useContext(ElectronContext)
|
||||
const onStateChangeRef = useRef(onStateChange)
|
||||
|
||||
const { userProfile } = useContext(AuthContext)
|
||||
useEffect(() => {
|
||||
onStateChangeRef.current = onStateChange
|
||||
}, [onStateChange])
|
||||
@ -191,7 +191,10 @@ const ObjectTable = forwardRef(
|
||||
var disabled = false
|
||||
if (action.disabled) {
|
||||
if (typeof action.disabled === 'function') {
|
||||
disabled = action.disabled(objectData)
|
||||
disabled = action.disabled({
|
||||
...objectData,
|
||||
_user: userProfile
|
||||
})
|
||||
} else {
|
||||
disabled = action.disabled
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import PropTypes from 'prop-types'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
||||
import { getModelByName } from '../../../database/ObjectModels'
|
||||
import { AuthContext } from './AuthContext'
|
||||
|
||||
const ActionsModalContext = createContext()
|
||||
|
||||
@ -63,6 +64,7 @@ const ActionsModalProvider = ({ children }) => {
|
||||
const { Text } = Typography
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const { userProfile } = useContext(AuthContext)
|
||||
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [query, setQuery] = useState('')
|
||||
@ -129,7 +131,7 @@ const ActionsModalProvider = ({ children }) => {
|
||||
|
||||
if (typeof action.disabled !== 'undefined') {
|
||||
if (typeof action.disabled === 'function') {
|
||||
disabled = action.disabled(objectData)
|
||||
disabled = action.disabled({ ...objectData, _user: userProfile })
|
||||
} else {
|
||||
disabled = action.disabled
|
||||
}
|
||||
|
||||
6
src/components/Icons/AppPasswordIcon.jsx
Normal file
6
src/components/Icons/AppPasswordIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/apppasswordicon.svg?react'
|
||||
|
||||
const AppPasswordIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default AppPasswordIcon
|
||||
6
src/components/Icons/CsvIcon.jsx
Normal file
6
src/components/Icons/CsvIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/csvicon.svg?react'
|
||||
|
||||
const CsvIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default CsvIcon
|
||||
6
src/components/Icons/ExcelIcon.jsx
Normal file
6
src/components/Icons/ExcelIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/excelicon.svg?react'
|
||||
|
||||
const ExcelIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default ExcelIcon
|
||||
6
src/components/Icons/ExportIcon.jsx
Normal file
6
src/components/Icons/ExportIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/exporticon.svg?react'
|
||||
|
||||
const ExportIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default ExportIcon
|
||||
6
src/components/Icons/ODataIcon.jsx
Normal file
6
src/components/Icons/ODataIcon.jsx
Normal file
@ -0,0 +1,6 @@
|
||||
import Icon from '@ant-design/icons'
|
||||
import CustomIconSvg from '../../../assets/icons/odataicon.svg?react'
|
||||
|
||||
const ODataIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||
|
||||
export default ODataIcon
|
||||
@ -22,6 +22,7 @@ import { OrderItem } from './models/OrderItem'
|
||||
import { Shipment } from './models/Shipment'
|
||||
import { AuditLog } from './models/AuditLog'
|
||||
import { User } from './models/User'
|
||||
import { AppPassword } from './models/AppPassword.js'
|
||||
import { NoteType } from './models/NoteType'
|
||||
import { Note } from './models/Note'
|
||||
import { DocumentSize } from './models/DocumentSize.js'
|
||||
@ -61,6 +62,7 @@ export const objectModels = [
|
||||
Shipment,
|
||||
AuditLog,
|
||||
User,
|
||||
AppPassword,
|
||||
NoteType,
|
||||
Note,
|
||||
DocumentSize,
|
||||
@ -101,6 +103,7 @@ export {
|
||||
Shipment,
|
||||
AuditLog,
|
||||
User,
|
||||
AppPassword,
|
||||
NoteType,
|
||||
Note,
|
||||
DocumentSize,
|
||||
|
||||
139
src/database/models/AppPassword.js
Normal file
139
src/database/models/AppPassword.js
Normal file
@ -0,0 +1,139 @@
|
||||
import AppPasswordIcon from '../../components/Icons/AppPasswordIcon'
|
||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||
import EditIcon from '../../components/Icons/EditIcon'
|
||||
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||
import LockIcon from '../../components/Icons/LockIcon'
|
||||
|
||||
export const AppPassword = {
|
||||
name: 'appPassword',
|
||||
label: 'App Password',
|
||||
prefix: 'APP',
|
||||
icon: AppPasswordIcon,
|
||||
actions: [
|
||||
{
|
||||
name: 'info',
|
||||
label: 'Info',
|
||||
default: true,
|
||||
row: true,
|
||||
icon: InfoCircleIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/management/apppasswords/info?appPasswordId=${_id}`
|
||||
},
|
||||
{
|
||||
name: 'reload',
|
||||
label: 'Reload',
|
||||
icon: ReloadIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/management/apppasswords/info?appPasswordId=${_id}&action=reload`
|
||||
},
|
||||
|
||||
{
|
||||
name: 'edit',
|
||||
label: 'Edit',
|
||||
row: true,
|
||||
icon: EditIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/management/apppasswords/info?appPasswordId=${_id}&action=edit`,
|
||||
visible: (objectData) => {
|
||||
return !(objectData?._isEditing && objectData?._isEditing == true)
|
||||
},
|
||||
disabled: (objectData) => {
|
||||
return objectData?._user?._id != objectData?.user?._id
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'finishEdit',
|
||||
label: 'Save Edits',
|
||||
icon: CheckIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/management/apppasswords/info?appPasswordId=${_id}&action=finishEdit`,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'cancelEdit',
|
||||
label: 'Cancel Edits',
|
||||
icon: XMarkIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/management/apppasswords/info?appPasswordId=${_id}&action=cancelEdit`,
|
||||
visible: (objectData) => {
|
||||
return objectData?._isEditing && objectData?._isEditing == true
|
||||
}
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
name: 'regenerateSecret',
|
||||
label: 'Regenerate Secret',
|
||||
type: 'button',
|
||||
row: true,
|
||||
icon: LockIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/management/apppasswords/info?appPasswordId=${_id}&action=regenerateSecret`,
|
||||
disabled: (objectData) => {
|
||||
return objectData?._user?._id != objectData?.user?._id
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: ['name', '_reference', 'user', 'active', 'createdAt', 'updatedAt'],
|
||||
filters: ['_id', 'name', 'user', 'active', 'user._id'],
|
||||
sorters: ['name', 'user', 'active', 'createdAt', 'updatedAt'],
|
||||
properties: [
|
||||
{
|
||||
name: '_id',
|
||||
label: 'ID',
|
||||
columnFixed: 'left',
|
||||
type: 'id',
|
||||
objectType: 'appPassword',
|
||||
showCopy: true
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
columnFixed: 'left',
|
||||
required: true,
|
||||
type: 'text'
|
||||
},
|
||||
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true
|
||||
},
|
||||
{
|
||||
name: 'user',
|
||||
label: 'User',
|
||||
required: true,
|
||||
readOnly: (objectData) => {
|
||||
return objectData?.createdAt != null
|
||||
},
|
||||
type: 'object',
|
||||
objectType: 'user',
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'active',
|
||||
label: 'Active',
|
||||
required: true,
|
||||
type: 'bool'
|
||||
},
|
||||
{
|
||||
name: 'secret',
|
||||
label: 'Secret',
|
||||
type: 'password',
|
||||
required: false,
|
||||
readOnly: true,
|
||||
value: (objectData) =>
|
||||
objectData?._id ? '••••••••••••••••••••••••••••••••' : undefined
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
import PersonIcon from '../../components/Icons/PersonIcon'
|
||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||
import AppPasswordIcon from '../../components/Icons/AppPasswordIcon'
|
||||
|
||||
export const User = {
|
||||
name: 'user',
|
||||
@ -22,6 +23,17 @@ export const User = {
|
||||
icon: ReloadIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/management/users/info?userId=${_id}&action=reload`
|
||||
},
|
||||
{
|
||||
name: 'newAppPassword',
|
||||
label: 'New App Password',
|
||||
type: 'button',
|
||||
icon: AppPasswordIcon,
|
||||
url: (_id) =>
|
||||
`/dashboard/management/users/info?userId=${_id}&action=newAppPassword`,
|
||||
disabled: (objectData) => {
|
||||
return objectData?._user?._id != objectData?._id
|
||||
}
|
||||
}
|
||||
],
|
||||
columns: ['name', '_reference', 'username', 'email', 'role', 'createdAt'],
|
||||
|
||||
@ -21,6 +21,8 @@ const NoteTypeInfo = lazy(() => import('../components/Dashboard/Management/NoteT
|
||||
const NoteInfo = lazy(() => import('../components/Dashboard/Management/Notes/NoteInfo.jsx'))
|
||||
const Users = lazy(() => import('../components/Dashboard/Management/Users.jsx'))
|
||||
const UserInfo = lazy(() => import('../components/Dashboard/Management/Users/UserInfo.jsx'))
|
||||
const AppPasswords = lazy(() => import('../components/Dashboard/Management/AppPasswords.jsx'))
|
||||
const AppPasswordInfo = lazy(() => import('../components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx'))
|
||||
const Hosts = lazy(() => import('../components/Dashboard/Management/Hosts.jsx'))
|
||||
const HostInfo = lazy(() => import('../components/Dashboard/Management/Hosts/HostInfo.jsx'))
|
||||
const DocumentSizes = lazy(() => import('../components/Dashboard/Management/DocumentSizes.jsx'))
|
||||
@ -151,6 +153,16 @@ const ManagementRoutes = [
|
||||
element={<DocumentTemplateDesign />}
|
||||
/>,
|
||||
<Route key='users' path='management/users' element={<Users />} />,
|
||||
<Route
|
||||
key='apppasswords'
|
||||
path='management/apppasswords'
|
||||
element={<AppPasswords />}
|
||||
/>,
|
||||
<Route
|
||||
key='apppasswords-info'
|
||||
path='management/apppasswords/info'
|
||||
element={<AppPasswordInfo />}
|
||||
/>,
|
||||
<Route key='settings' path='management/settings' element={<Settings />} />,
|
||||
<Route key='auditlogs' path='management/auditlogs' element={<AuditLogs />} />,
|
||||
<Route key='taxrates' path='management/taxrates' element={<TaxRates />} />,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user