Compare commits

...

11 Commits

Author SHA1 Message Date
1466fe0f62 Enhance ObjectKanban and ObjectTable with Silent Loading Options
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Updated ObjectKanban and ObjectTable components to support silent loading, allowing for smoother user experience during data fetching without disrupting the UI.
- Refactored load functions to accept an options parameter, enabling conditional loading states based on the silent flag.
- Improved loading state management in ObjectKanbanColumn to maintain visual consistency during filter and sort updates.
- Adjusted loading behavior in ObjectTable to prevent unnecessary loading indicators when silent loading is enabled.
2026-09-03 03:17:38 +01:00
26d8c80d54 Enhance ObjectKanban with Skeleton Loading and Responsive Layout
- Introduced a skeleton loading state in the ObjectKanban component to improve user experience during data fetching.
- Updated CSS styles for the ObjectKanban and related components to ensure a responsive layout, accommodating various screen sizes.
- Refactored ObjectKanbanHeader to conditionally render skeletons based on loading state, enhancing visual feedback.
- Improved filter and sorter handling in ObjectKanbanColumn for better data management during loading.
2026-09-03 02:58:29 +01:00
0eae922a9e Enhance ObjectTableViewButton with View Mode Cycling and Typography Updates
- Refactored ObjectTableViewButton to include a cycling feature for view modes, allowing users to switch between list, cards, and kanban views seamlessly.
- Updated the component to utilize Ant Design's Typography for improved text styling and consistency.
- Removed icons from view mode options to streamline the interface and focused on text labels.
- Adjusted button properties to enhance user interaction and accessibility.
2026-09-03 01:42:33 +01:00
ab7919122a Add Kanban and Timeline Icons, Enhance Kanban Component Styles
- Introduced new SVG icons for Kanban and Timeline to enhance visual representation in the dashboard.
- Expanded App.css with new styles for the Kanban component, improving layout and responsiveness.
- Updated various dashboard components to integrate the new ObjectListViewPageContent for better view management and consistency across the application.
2026-09-03 01:30:44 +01:00
9b344b6a3d Enhance Dashboard Breadcrumb and ObjectListView Context with View Management
- Updated DashboardBreadcrumb to display a more informative name by incorporating the view name alongside the model label, improving user navigation clarity.
- Enhanced ObjectListViewProvider to synchronize the active view name with the dashboard breadcrumb, ensuring consistent view representation across components.
- Introduced session management for object list views in TableStateContext, allowing for persistent view states across user sessions.
2026-09-02 22:56:51 +01:00
9d1a032e10 Refactor Dashboard Components to Remove Unused State Management
- Removed unnecessary useState hooks for view state management in multiple dashboard components (Invoices, PaymentPolicies, Payments, TaxRecords, FilamentStocks, OrderItems, PartStocks, ProductStocks, PurchaseOrders, Shipments, StockAudits, StockEvents, StockLocations, StockTransfers, AppPasswords, AuditLogs, Couriers, CourierServices, DocumentJobs, DocumentPrinters, DocumentSizes, DocumentTemplates, Filaments, FilamentSkus).
- Updated ObjectListViewProvider integration to streamline component functionality and improve maintainability.
- Adjusted CSS variables in App.css for enhanced styling consistency across components.
2026-09-02 22:41:49 +01:00
444e694593 Update getModelByPluralName to use replaceAll for whitespace handling
- Modified the getModelByPluralName function to utilize replaceAll instead of replace for improved whitespace removal in formatted names, enhancing consistency in model name formatting.
2026-09-02 21:41:29 +01:00
b6834fecc6 Refactor ObjectTable and ObjectListViewContext for Simplified Filter Management
- Removed unnecessary state management for masterFilter and masterSort in ObjectTable, streamlining filter handling.
- Updated ObjectListViewContext to simplify view state restoration logic, enhancing clarity and maintainability.
- Adjusted references to masterFilter throughout ObjectTable to ensure consistent filter application.
2026-09-02 21:35:56 +01:00
73de7ac8ee Enhance Dashboard Components with Loading State and Styling Improvements
- Updated ListViewEditButtons to disable actions during initial loading, improving user experience.
- Refactored ListViewTabs to adjust icon styles based on loading state and removed unnecessary imports for cleaner code.
- Modified ObjectListViewContext to use viewId instead of view reference for better clarity and consistency.
- Updated TableStateContext to reflect changes in view parameter naming, enhancing code maintainability.
- Adjusted App.css for improved styling of segmented navigation items and loading indicators.
2026-09-02 21:26:39 +01:00
96b809b9d8 Refactor Dashboard Components to Integrate ListView Functionality
- Updated multiple dashboard components (Invoices, PaymentPolicies, Payments, TaxRecords, FilamentStocks, OrderItems, PartStocks, ProductStocks, PurchaseOrders, Shipments, StockAudits, StockEvents, StockLocations, StockTransfers, AppPasswords, AuditLogs) to incorporate ListViewTabs and ListViewEditButtons for enhanced user interaction.
- Introduced ObjectListViewProvider to manage view state and filter/sort changes across components, improving maintainability and user experience.
- Added state management for active views and filter/sort handling to streamline component functionality.
2026-09-02 20:56:08 +01:00
088ef79b11 Add LoadingIcon Component with SVG Animation
- Introduced a new LoadingIcon component that utilizes an SVG for a rotating loading animation.
- Added loadingicon.svg file to assets/icons for the visual representation of the loading state.
- Integrated the LoadingIcon component into the project for enhanced user experience during loading processes.
2026-09-02 19:02:30 +01:00
82 changed files with 5824 additions and 1057 deletions

View 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.779032,0,0,0.779032,3.073021,9.025537)">
<path d="M22.587,55.452L29.317,55.452L29.317,3.541L22.587,3.541L22.587,55.452ZM44.947,55.452L51.677,55.452L51.677,3.541L44.947,3.541L44.947,55.452ZM10.588,58.962L63.676,58.962C70.606,58.962 74.264,55.326 74.264,48.481L74.264,10.522C74.264,3.667 70.606,0.02 63.676,0.02L10.588,0.02C3.679,0.02 0,3.667 0,10.522L0,48.481C0,55.326 3.679,58.962 10.588,58.962ZM11.167,52.003C8.429,52.003 6.959,50.615 6.959,47.743L6.959,11.251C6.959,8.378 8.429,6.979 11.167,6.979L63.097,6.979C65.813,6.979 67.305,8.378 67.305,11.251L67.305,47.743C67.305,50.615 65.813,52.003 63.097,52.003L11.167,52.003Z" style="fill-rule:nonzero;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<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"
class="th-icon"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"
>
<g>
<path
d="M32,5.131C46.829,5.131 58.869,17.171 58.869,32"
style="fill:none;"
/>
<animateTransform
attributeName="transform"
attributeType="XML"
type="rotate"
from="0 32 32"
to="360 32 32"
dur="1s"
repeatCount="indefinite"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 774 B

View 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 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,-0.777065,0.777065,0,9.038886,69.94022)">
<path d="M75.315,48.595C75.315,55.44 71.658,59.097 64.728,59.097L21.503,59.097C14.606,59.097 10.906,55.461 10.906,48.595L10.906,36.765L17.865,36.765L17.865,48.26C17.865,50.888 19.229,52.138 21.722,52.138L64.499,52.138C66.981,52.138 68.356,50.888 68.356,48.26L68.356,36.765L75.315,36.765L75.315,48.595ZM75.315,10.657L75.315,22.436L68.356,22.436L68.356,10.982C68.356,8.354 66.981,7.114 64.499,7.114L21.722,7.114C19.229,7.114 17.865,8.354 17.865,10.982L17.865,22.436L10.906,22.436L10.906,10.657C10.906,3.781 14.606,0.155 21.503,0.155L64.728,0.155C71.658,0.155 75.315,3.802 75.315,10.657Z" style="fill-rule:nonzero;"/>
<path d="M88.562,55.043C88.562,57.276 86.765,59.094 84.532,59.094C82.3,59.094 80.481,57.276 80.481,55.043C80.481,52.81 82.3,51.013 84.532,51.013C86.765,51.013 88.562,52.81 88.562,55.043Z" style="fill-rule:nonzero;"/>
<path d="M88.562,42.32C88.562,44.553 86.765,46.34 84.532,46.34C82.3,46.34 80.481,44.553 80.481,42.32C80.481,40.066 82.3,38.269 84.532,38.269C86.765,38.269 88.562,40.066 88.562,42.32Z" style="fill-rule:nonzero;"/>
<path d="M88.562,16.774C88.562,19.007 86.765,20.825 84.532,20.825C82.3,20.825 80.481,19.007 80.481,16.774C80.481,14.541 82.3,12.744 84.532,12.744C86.765,12.744 88.562,14.541 88.562,16.774Z" style="fill-rule:nonzero;"/>
<path d="M88.562,4.02C88.562,6.284 86.765,8.071 84.532,8.071C82.3,8.071 80.481,6.284 80.481,4.02C80.481,1.787 82.3,0 84.532,0C86.765,0 88.562,1.787 88.562,4.02Z" style="fill-rule:nonzero;"/>
<path d="M84.582,34.107C87.085,34.107 89.11,32.082 89.11,29.579C89.11,27.076 87.085,25.041 84.582,25.041C82.961,25.041 81.507,25.903 80.707,27.224L10.916,27.224C9.558,27.224 8.54,28.242 8.54,29.6C8.54,30.958 9.558,31.998 10.916,31.998L80.75,31.998C81.55,33.276 82.961,34.107 84.582,34.107Z" style="fill-rule:nonzero;"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -22,7 +22,7 @@
.ant-form-item-control-input,
.ant-input,
.ant-input-number .ant-input-number-input,
.ant-segmented-item-label,
.segmented-nav-item-label,
.ant-badge-status-text,
.ant-tree-title,
.ant-select,
@ -47,6 +47,12 @@
font-family: 'DM Sans';
}
:root {
--color-list-view-tabs-border: #8282822e;
--color-button-border: #8282822e;
--color-button-background: #8282822e;
}
.ant-dropdown {
z-index: 999;
}
@ -665,6 +671,11 @@ body {
bottom: 8px;
}
.scrollbox-horizontal > [data-simplebar],
.scrollbox-horizontal .simplebar-wrapper {
width: 100%;
}
.simplebar-scrollbar:before {
background: #78787854 !important;
}
@ -1361,16 +1372,193 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton {
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
}
.objectTableCardsContainer > .ant-spin-nested-loading,
.objectTableCardsContainer > .ant-spin-nested-loading > .ant-spin-container,
.objectTableCardsContainer .ant-spin-nested-loading {
width: 100%;
height: 100%;
min-height: 0;
flex: 1;
display: flex;
flex-direction: column;
}
.objectTableCardsContainer .ant-spin-container {
width: 100%;
height: 100%;
min-height: 0;
flex: 1;
display: flex;
flex-direction: column;
}
.objectKanbanContainerWrapper {
position: relative;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
}
.objectKanbanContainerWrapper > .ant-spin-nested-loading,
.objectKanbanContainerWrapper > .ant-spin-nested-loading > .ant-spin-container {
height: 100%;
min-height: 0;
flex: 1;
display: flex;
flex-direction: column;
}
.objectKanbanContainerSkeletons {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
min-height: 0;
}
.objectKanbanKanbanBody {
position: relative;
flex: 1;
min-width: 0;
min-height: 0;
}
.objectKanbanContainer {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
z-index: 1;
pointer-events: auto;
}
.objectKanbanWrap {
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
}
.objectKanbanWrap > .ant-spin-nested-loading,
.objectKanbanWrap > .ant-spin-nested-loading > .ant-spin-container {
height: 100%;
min-height: 0;
}
.objectKanbanScroll {
flex: 1;
width: 100%;
min-width: 0;
min-height: 0;
}
.objectKanbanScroll .simplebar-content-wrapper {
height: 100%;
}
.objectKanbanScroll .simplebar-content {
min-height: 100%;
}
.objectKanbanHeader {
flex-shrink: 0;
width: 100%;
min-width: 0;
overflow: hidden;
}
.objectKanbanHeaderTrack {
width: max-content;
min-width: 100%;
will-change: transform;
}
.objectKanbanHeaderRow {
width: max-content;
min-width: 100%;
align-items: flex-start;
}
.objectKanbanHeaderCell {
flex: 0 0 360px;
min-width: 360px;
flex-shrink: 0;
padding: 8px 8px;
border: 1px solid var(--ant-color-border-secondary);
border-bottom: 1px solid var(--color-descriptions-border);
border-radius: 12px 12px 0 0;
background-color: var(--layout-header-bg);
}
.objectKanban {
width: max-content;
min-width: 100%;
height: 100%;
min-height: 100%;
align-items: stretch;
padding-bottom: 4px;
}
.objectKanbanSkeletonColumns {
position: absolute;
inset: 0;
z-index: 0;
overflow: hidden;
pointer-events: none;
}
.objectKanbanSkeletonTrack {
width: max-content;
min-width: 100%;
height: 100%;
will-change: transform;
}
.objectKanbanSkeletonRow {
width: max-content;
min-width: 100%;
height: 100%;
align-items: stretch;
}
.objectKanbanColumn,
.objectKanbanColumnSkeleton {
flex: 0 0 360px;
min-width: 360px;
min-height: 0;
border-top: none;
border-radius: 0 0 12px 12px;
}
.objectKanbanColumnSkeleton {
border-radius: 0 0 12px 12px;
overflow: hidden;
}
.objectKanbanColumnCards {
flex: 1;
border-radius: 0 0 12px 12px;
height: 100%;
}
.objectKanbanColumnSkeleton .objectKanbanColumnCards {
background-color: var(--layout-header-bg);
}
.objectKanbanColumnCardsInner {
padding: 16px;
}
.electron-body .ant-modal-wrap,
@ -2544,3 +2732,253 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton {
height: 0;
pointer-events: none;
}
.list-view-tabs {
flex: 1;
min-width: 0;
}
.list-view-tabs .ant-input-sm {
border-radius: 0;
}
.list-view-tabs-spin {
flex: 1;
min-width: 0;
}
.list-view-tabs-spin > .ant-spin-container {
display: flex;
flex: 1;
min-width: 0;
}
.list-view-tabs-segmented-wrap {
flex: 1;
min-width: 0;
}
.list-view-tabs-segmented-wrap .simplebar-track.simplebar-vertical {
display: none;
}
.list-view-tabs-segmented-inner {
width: max-content;
}
.segmented-nav {
display: inline-flex;
width: max-content;
flex-shrink: 0;
padding: 0;
}
.segmented-nav-group {
position: relative;
display: flex;
align-items: center;
gap: 8px;
}
.segmented-nav-thumb {
position: absolute;
top: 0;
left: 0;
z-index: 0;
border-radius: 12px;
background: var(--color-button-background);
pointer-events: none;
will-change: transform, width;
transition:
transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
height 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.segmented-nav-item {
position: relative;
z-index: 1;
display: inline-flex;
align-items: center;
justify-content: center;
margin: 0;
padding: 0;
border: 1px solid var(--color-list-view-tabs-border);
border-radius: 12px;
background: transparent;
cursor: pointer;
font-family: inherit;
font-size: inherit;
line-height: inherit;
color: inherit;
box-shadow: none;
outline: none;
transition:
background 0.2s ease,
border 0.2s ease;
}
.segmented-nav-item-label {
position: relative;
z-index: 1;
display: flex;
align-items: center;
min-height: 30px;
line-height: 30px;
padding-inline: 12px;
}
.segmented-nav-item-icon {
padding-inline: 8px;
min-width: 32px;
}
.segmented-nav-item-icon-contents {
position: relative;
z-index: 1;
display: flex;
align-items: center;
min-height: 30px;
line-height: 30px;
}
.segmented-nav-item:focus-visible {
outline: 2px solid
color-mix(in srgb, var(--color-primary, #1677ff) 45%, transparent);
outline-offset: 2px;
}
.segmented-nav-item-selected,
.segmented-nav-item:not(.segmented-nav-item-disabled):hover {
background: var(--color-button-background);
border: 1px solid
color-mix(
in srgb,
var(--color-button-border) 40%,
var(--color-button-background)
);
}
.segmented-nav-item-disabled {
cursor: not-allowed;
opacity: 0.45;
}
.segmented-nav-disabled .segmented-nav-item {
cursor: not-allowed;
}
.list-view-tabs-label {
height: 30px;
}
.list-view-tabs-label-editing .ant-input {
font-size: inherit;
}
.list-view-tabs-label-editing {
margin: 0px -8px;
max-width: none;
min-width: 0;
}
.list-view-tabs-label-editing .list-view-tabs-name-input-wrap {
flex: 0 0 auto;
width: auto;
}
.list-view-tabs-name-input-wrap {
display: inline-block;
position: relative;
vertical-align: top;
min-width: 0;
}
.list-view-tabs-name-input-sizer {
visibility: hidden;
white-space: pre;
font-size: inherit;
line-height: inherit;
font-family: inherit;
min-height: 1em;
}
.list-view-tabs-label-editing .list-view-tabs-name-input {
position: absolute;
inset: 0;
width: 100%;
min-width: 0;
padding-inline: 0;
}
.list-view-tabs-color-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.list-view-tabs-editing
.segmented-nav-item
.ant-color-picker-trigger.ant-color-picker-sm {
border-radius: 8px;
}
.list-view-tabs-privacy-button,
.list-view-tabs-options-button {
padding-inline: 4px;
min-width: 20px;
}
.list-view-tabs-options-dropdown {
display: flex;
flex-direction: column;
}
.list-view-tabs-options-dropdown-privacy {
margin: 3px 12px 14px 16px;
}
.segmented-nav-drag-handle {
flex-shrink: 0;
}
.segmented-nav-item-dragging {
opacity: 0.4;
}
.segmented-nav-item-drop-before::before,
.segmented-nav-item-drop-after::after {
content: '';
position: absolute;
top: 4px;
bottom: 4px;
width: 2px;
border-radius: 1px;
background: var(--color-primary, #1677ff);
pointer-events: none;
}
.segmented-nav-item-drop-before::before {
left: -5px;
}
.segmented-nav-item-drop-after::after {
right: -5px;
}
.segmented-nav-reordering
.segmented-nav-item:not(.segmented-nav-item-dragging) {
cursor: default;
}
.list-view-tabs-add-button {
padding-inline: 4px;
min-width: 20px;
}
.state-tag-loading svg path {
stroke: currentColor;
stroke-width: 12px;
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Invoices = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('invoices')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('invoices')
@ -28,10 +28,13 @@ const Invoices = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='invoice'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='invoice'
pageName='list'
@ -45,7 +48,8 @@ const Invoices = () => {
/>
<ExportListButton objectType='invoice' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const Invoices = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='invoice'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='invoice'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const Invoices = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,15 +6,16 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const PaymentPolicies = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('paymentPolicy')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('paymentPolicy')
const [showFilterSidebar, setShowFilterSidebar] =
@ -23,9 +24,13 @@ const PaymentPolicies = () => {
useSortSidebarVisibility('PaymentPolicies')
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='paymentPolicy'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='paymentPolicy'
pageName='list'
@ -39,7 +44,8 @@ const PaymentPolicies = () => {
/>
<ExportListButton objectType='paymentPolicy' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -49,17 +55,16 @@ const PaymentPolicies = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='paymentPolicy'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='paymentPolicy'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -72,7 +77,8 @@ const PaymentPolicies = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Payments = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('payments')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('payments')
@ -28,10 +28,13 @@ const Payments = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='payment'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='payment'
pageName='list'
@ -45,7 +48,8 @@ const Payments = () => {
/>
<ExportListButton objectType='payment' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const Payments = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='payment'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='payment'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const Payments = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const TaxRecords = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('taxRecord')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('taxRecord')
@ -28,10 +28,13 @@ const TaxRecords = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='taxRecord'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='taxRecord'
pageName='list'
@ -45,7 +48,8 @@ const TaxRecords = () => {
/>
<ExportListButton objectType='taxRecord' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const TaxRecords = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='taxRecord'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='taxRecord'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const TaxRecords = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -10,18 +10,17 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const FilamentStocks = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('filamentStocks')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('filamentStocks')
@ -33,10 +32,13 @@ const FilamentStocks = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='filamentStock'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='filamentStock'
pageName='list'
@ -50,7 +52,8 @@ const FilamentStocks = () => {
/>
<ExportListButton objectType='filamentStock' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -60,18 +63,15 @@ const FilamentStocks = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='filamentStock'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='filamentStock'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -83,9 +83,10 @@ const FilamentStocks = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const OrderItems = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('orderItems')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('orderItems')
@ -28,10 +28,13 @@ const OrderItems = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='orderItem'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='orderItem'
pageName='list'
@ -45,7 +48,8 @@ const OrderItems = () => {
/>
<ExportListButton objectType='orderItem' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const OrderItems = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='orderItem'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='orderItem'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const OrderItems = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -10,18 +10,17 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const PartStocks = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('partStocks')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('partStocks')
@ -33,10 +32,13 @@ const PartStocks = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='partStock'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='partStock'
pageName='list'
@ -50,7 +52,8 @@ const PartStocks = () => {
/>
<ExportListButton objectType='partStock' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -60,18 +63,15 @@ const PartStocks = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='partStock'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='partStock'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -83,9 +83,10 @@ const PartStocks = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -11,18 +11,17 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const ProductStocks = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('productStocks')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('productStock')
@ -34,10 +33,13 @@ const ProductStocks = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='productStock'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='productStock'
pageName='list'
@ -51,7 +53,8 @@ const ProductStocks = () => {
/>
<ExportListButton objectType='productStock' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -61,18 +64,15 @@ const ProductStocks = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='productStock'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='productStock'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -84,9 +84,10 @@ const ProductStocks = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const PurchaseOrders = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('purchaseOrders')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('purchaseOrders')
@ -28,10 +28,13 @@ const PurchaseOrders = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='purchaseOrder'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='purchaseOrder'
pageName='list'
@ -45,7 +48,8 @@ const PurchaseOrders = () => {
/>
<ExportListButton objectType='purchaseOrder' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const PurchaseOrders = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='purchaseOrder'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='purchaseOrder'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const PurchaseOrders = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Shipments = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('shipments')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('shipments')
@ -28,10 +28,13 @@ const Shipments = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='shipment'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='shipment'
pageName='list'
@ -45,7 +48,8 @@ const Shipments = () => {
/>
<ExportListButton objectType='shipment' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const Shipments = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='shipment'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='shipment'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const Shipments = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -10,18 +10,17 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const StockAudits = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('stockAudits')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('stockAudits')
@ -33,10 +32,13 @@ const StockAudits = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='stockAudit'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='stockAudit'
pageName='list'
@ -50,7 +52,8 @@ const StockAudits = () => {
/>
<ExportListButton objectType='stockAudit' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -60,18 +63,15 @@ const StockAudits = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='stockAudit'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='stockAudit'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -83,9 +83,10 @@ const StockAudits = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,15 +6,16 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const StockEvents = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('stockEvents')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('stockEvent')
@ -27,10 +28,13 @@ const StockEvents = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='stockEvent'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='stockEvent'
pageName='list'
@ -44,7 +48,8 @@ const StockEvents = () => {
/>
<ExportListButton objectType='stockEvent' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -54,18 +59,15 @@ const StockEvents = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='stockEvent'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='stockEvent'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -77,9 +79,10 @@ const StockEvents = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -8,18 +8,17 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const StockLocations = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('stockLocations')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('stockLocation')
@ -31,10 +30,13 @@ const StockLocations = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='stockLocation'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='stockLocation'
pageName='list'
@ -48,7 +50,8 @@ const StockLocations = () => {
/>
<ExportListButton objectType='stockLocation' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -58,18 +61,15 @@ const StockLocations = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='stockLocation'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='stockLocation'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -81,9 +81,10 @@ const StockLocations = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -8,18 +8,17 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const StockTransfers = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('stockTransfers')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('stockTransfer')
@ -31,10 +30,13 @@ const StockTransfers = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='stockTransfer'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='stockTransfer'
pageName='list'
@ -48,7 +50,8 @@ const StockTransfers = () => {
/>
<ExportListButton objectType='stockTransfer' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -58,18 +61,15 @@ const StockTransfers = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='stockTransfer'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='stockTransfer'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -81,9 +81,10 @@ const StockTransfers = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -7,15 +7,15 @@ import ObjectActions from '../common/ObjectActions'
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 ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const AppPasswords = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('appPassword')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('appPassword')
@ -27,10 +27,13 @@ const AppPasswords = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space>
<ObjectListViewProvider
objectType='appPassword'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
type='appPassword'
pageName='list'
@ -44,7 +47,8 @@ const AppPasswords = () => {
/>
<ExportListButton objectType='appPassword' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -54,17 +58,14 @@ const AppPasswords = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='appPassword'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type='appPassword'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
@ -77,10 +78,10 @@ const AppPasswords = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -10,6 +10,9 @@ import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const AuditLogs = () => {
const tableRef = useRef()
@ -25,10 +28,13 @@ const AuditLogs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='auditLog'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='auditLog'
pageName='list'
@ -42,7 +48,8 @@ const AuditLogs = () => {
/>
<ExportListButton objectType='auditLog' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -51,9 +58,9 @@ const AuditLogs = () => {
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -69,9 +76,10 @@ const AuditLogs = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const CourierServices = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('courierService')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('courierService')
@ -28,10 +28,13 @@ const CourierServices = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='courierService'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='courierService'
pageName='list'
@ -45,7 +48,8 @@ const CourierServices = () => {
/>
<ExportListButton objectType='courierService' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const CourierServices = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='courierService'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='courierService'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const CourierServices = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Couriers = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('courier')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('courier')
const [showFilterSidebar, setShowFilterSidebar] =
@ -27,10 +27,13 @@ const Couriers = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='courier'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='courier'
pageName='list'
@ -44,7 +47,8 @@ const Couriers = () => {
/>
<ExportListButton objectType='courier' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -54,17 +58,16 @@ const Couriers = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='courier'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='courier'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -77,8 +80,8 @@ const Couriers = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const DocumentJobs = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('documentJob')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('documentJob')
@ -28,10 +28,13 @@ const DocumentJobs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='documentJob'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='documentJob'
pageName='list'
@ -45,7 +48,8 @@ const DocumentJobs = () => {
/>
<ExportListButton objectType='documentJob' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const DocumentJobs = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='documentJob'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='documentJob'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const DocumentJobs = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,15 +6,16 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const DocumentPrinters = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('documentPrinter')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('documentPrinter')
@ -27,10 +28,13 @@ const DocumentPrinters = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='documentPrinter'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='documentPrinter'
pageName='list'
@ -44,7 +48,8 @@ const DocumentPrinters = () => {
/>
<ExportListButton objectType='documentPrinter' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -54,17 +59,16 @@ const DocumentPrinters = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='documentPrinter'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='documentPrinter'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -77,8 +81,8 @@ const DocumentPrinters = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,15 +6,16 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const DocumentSizes = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('documentSize')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('documentSize')
@ -26,10 +27,13 @@ const DocumentSizes = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='documentSize'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='documentSize'
pageName='list'
@ -43,7 +47,8 @@ const DocumentSizes = () => {
/>
<ExportListButton objectType='documentSize' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -53,17 +58,16 @@ const DocumentSizes = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='documentSize'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='documentSize'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -76,8 +80,8 @@ const DocumentSizes = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const DocumentTemplates = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('documentTemplate')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('documentTemplate')
@ -28,10 +28,13 @@ const DocumentTemplates = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='documentTemplate'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='documentTemplate'
pageName='list'
@ -45,7 +48,8 @@ const DocumentTemplates = () => {
/>
<ExportListButton objectType='documentTemplate' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const DocumentTemplates = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='documentTemplate'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='documentTemplate'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const DocumentTemplates = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -8,18 +8,17 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const FilamentSkus = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('filamentSkus')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('filamentSku')
@ -31,10 +30,13 @@ const FilamentSkus = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='filamentSku'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='filamentSku'
pageName='list'
@ -48,7 +50,8 @@ const FilamentSkus = () => {
/>
<ExportListButton objectType='filamentSku' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -58,18 +61,15 @@ const FilamentSkus = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='filamentSku'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='filamentSku'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -81,9 +81,10 @@ const FilamentSkus = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -8,11 +8,13 @@ import useColumnVisibility from '../hooks/useColumnVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
import useViewMode from '../hooks/useViewMode'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
import ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
@ -20,7 +22,6 @@ const Filaments = () => {
const tableRef = useRef()
// View mode state (cards/list), persisted in sessionStorage via custom hook
const [viewMode, setViewMode] = useViewMode('filament')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('filament')
@ -33,10 +34,13 @@ const Filaments = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space>
<ObjectListViewProvider
objectType='filament'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
type='filament'
pageName='list'
@ -50,7 +54,8 @@ const Filaments = () => {
/>
<ExportListButton objectType='filament' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -60,17 +65,14 @@ const Filaments = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='filament'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type='filament'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
@ -83,10 +85,10 @@ const Filaments = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Files = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('file')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('file')
const [showFilterSidebar, setShowFilterSidebar] =
@ -25,11 +25,13 @@ const Files = () => {
const [showSortSidebar, setShowSortSidebar] =
useSortSidebarVisibility('Files')
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<ObjectListViewProvider
objectType='file'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='file'
@ -44,6 +46,7 @@ const Files = () => {
/>
<ExportListButton objectType='file' />
</Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
@ -54,17 +57,15 @@ const Files = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='file'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='file'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,7 +79,7 @@ const Files = () => {
useSortInUrl={true}
/>
</Flex>
</>
</ObjectListViewProvider>
)
}

View File

@ -7,9 +7,11 @@ import { Flex, Space } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
import useViewMode from '../hooks/useViewMode'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
@ -20,7 +22,6 @@ const Hosts = () => {
const tableRef = useRef()
// View mode state (cards/list), persisted in sessionStorage via custom hook
const [viewMode, setViewMode] = useViewMode('host')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('host')
@ -32,10 +33,13 @@ const Hosts = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space>
<ObjectListViewProvider
objectType='host'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
type='host'
pageName='list'
@ -49,7 +53,8 @@ const Hosts = () => {
/>
<ExportListButton objectType='host' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -59,17 +64,14 @@ const Hosts = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='host'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type='host'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
@ -82,10 +84,10 @@ const Hosts = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -9,16 +9,16 @@ import ObjectActions from '../common/ObjectActions'
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 ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Materials = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('material')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('material')
@ -30,10 +30,13 @@ const Materials = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space>
<ObjectListViewProvider
objectType='material'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
type='material'
pageName='list'
@ -47,7 +50,8 @@ const Materials = () => {
/>
<ExportListButton objectType='material' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -57,17 +61,14 @@ const Materials = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='material'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type='material'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
@ -80,10 +81,10 @@ const Materials = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const NoteTypes = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('noteType')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('noteType')
@ -28,10 +28,13 @@ const NoteTypes = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='noteType'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='noteType'
pageName='list'
@ -45,7 +48,8 @@ const NoteTypes = () => {
/>
<ExportListButton objectType='noteType' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const NoteTypes = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='noteType'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='noteType'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const NoteTypes = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -8,18 +8,17 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const PartSkus = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('partSkus')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('partSku')
const [showFilterSidebar, setShowFilterSidebar] =
@ -30,10 +29,13 @@ const PartSkus = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='partSku'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='partSku'
pageName='list'
@ -47,7 +49,8 @@ const PartSkus = () => {
/>
<ExportListButton objectType='partSku' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -57,18 +60,15 @@ const PartSkus = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='partSku'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='partSku'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -80,9 +80,10 @@ const PartSkus = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -9,7 +9,6 @@ import ObjectActions from '../common/ObjectActions'
import useColumnVisibility from '../hooks/useColumnVisibility'
import useViewMode from '../hooks/useViewMode'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
@ -18,10 +17,12 @@ import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Parts = (filter) => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('part')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('part')
const [showFilterSidebar, setShowFilterSidebar] =
@ -32,10 +33,13 @@ const Parts = (filter) => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='part'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='part'
pageName='list'
@ -49,7 +53,8 @@ const Parts = (filter) => {
/>
<ExportListButton objectType='part' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -59,17 +64,16 @@ const Parts = (filter) => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='part'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='part'
cards={viewMode === 'cards'}
filter={filter}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
@ -83,8 +87,8 @@ const Parts = (filter) => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const PermissionSettingsList = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('permissionSetting')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('permissionSetting')
@ -28,10 +28,13 @@ const PermissionSettingsList = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='permissionSetting'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='permissionSetting'
pageName='list'
@ -45,7 +48,8 @@ const PermissionSettingsList = () => {
/>
<ExportListButton objectType='permissionSetting' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const PermissionSettingsList = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='permissionSetting'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='permissionSetting'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const PermissionSettingsList = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -9,16 +9,16 @@ import ObjectActions from '../common/ObjectActions'
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 ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const ProductCategories = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('productCategory')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('productCategory')
@ -30,10 +30,13 @@ const ProductCategories = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space>
<ObjectListViewProvider
objectType='productCategory'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
type='productCategory'
pageName='list'
@ -47,7 +50,8 @@ const ProductCategories = () => {
/>
<ExportListButton objectType='productCategory' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -57,17 +61,14 @@ const ProductCategories = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='productCategory'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type='productCategory'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
@ -80,10 +81,10 @@ const ProductCategories = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -8,18 +8,17 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const ProductSkus = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('productSkus')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('productSku')
@ -31,10 +30,13 @@ const ProductSkus = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='productSku'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='productSku'
pageName='list'
@ -48,7 +50,8 @@ const ProductSkus = () => {
/>
<ExportListButton objectType='productSku' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -58,18 +61,15 @@ const ProductSkus = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='productSku'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='productSku'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -81,9 +81,10 @@ const ProductSkus = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -25,17 +25,17 @@ 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 ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Products = () => {
const navigate = useNavigate()
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('Products')
const getProductActionItems = (id) => {
return {
items: [
@ -302,10 +302,13 @@ const Products = () => {
}
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='product'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='product'
pageName='list'
@ -320,7 +323,8 @@ const Products = () => {
<Button>View</Button>
</Popover>
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -330,16 +334,15 @@ const Products = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='product'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type={'product'}
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -352,8 +355,8 @@ const Products = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const StockAuditLevels = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('stockAuditLevel')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('stockAuditLevel')
@ -27,9 +27,13 @@ const StockAuditLevels = () => {
useSortSidebarVisibility('StockAuditLevels')
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='stockAuditLevel'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='stockAuditLevel'
pageName='list'
@ -43,7 +47,8 @@ const StockAuditLevels = () => {
/>
<ExportListButton objectType='stockAuditLevel' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -53,17 +58,16 @@ const StockAuditLevels = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='stockAuditLevel'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='stockAuditLevel'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -76,7 +80,8 @@ const StockAuditLevels = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const TaxRates = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('taxRate')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('taxRate')
const [showFilterSidebar, setShowFilterSidebar] =
@ -27,10 +27,13 @@ const TaxRates = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='taxRate'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='taxRate'
pageName='list'
@ -44,7 +47,8 @@ const TaxRates = () => {
/>
<ExportListButton objectType='taxRate' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -54,17 +58,16 @@ const TaxRates = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='taxRate'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='taxRate'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -77,8 +80,8 @@ const TaxRates = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const UserGroups = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('userGroup')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('userGroup')
const [showFilterSidebar, setShowFilterSidebar] =
@ -27,10 +27,13 @@ const UserGroups = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='userGroup'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='userGroup'
pageName='list'
@ -44,7 +47,8 @@ const UserGroups = () => {
/>
<ExportListButton objectType='userGroup' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -54,17 +58,16 @@ const UserGroups = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='userGroup'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='userGroup'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -77,8 +80,8 @@ const UserGroups = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Users = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('user')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('user')
const [showFilterSidebar, setShowFilterSidebar] =
@ -27,9 +27,13 @@ const Users = () => {
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='user'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='user'
pageName='list'
@ -43,7 +47,8 @@ const Users = () => {
/>
<ExportListButton objectType='user' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -53,16 +58,16 @@ const Users = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
objectType='user'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type={'user'}
visibleColumns={columnVisibility}
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -75,7 +80,8 @@ const Users = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Vendors = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('vendor')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('vendor')
const [showFilterSidebar, setShowFilterSidebar] =
@ -25,11 +25,13 @@ const Vendors = () => {
const [showSortSidebar, setShowSortSidebar] =
useSortSidebarVisibility('Vendors')
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<ObjectListViewProvider
objectType='vendor'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='vendor'
@ -44,6 +46,7 @@ const Vendors = () => {
/>
<ExportListButton objectType='vendor' />
</Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
@ -54,17 +57,15 @@ const Vendors = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='vendor'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='vendor'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,7 +79,7 @@ const Vendors = () => {
useSortInUrl={true}
/>
</Flex>
</>
</ObjectListViewProvider>
)
}

View File

@ -2,6 +2,9 @@ import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
import ObjectTable from '../common/ObjectTable'
@ -10,11 +13,9 @@ import ObjectTableViewButton from '../common/ObjectTableViewButton'
import useColumnVisibility from '../hooks/useColumnVisibility'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import useViewMode from '../hooks/useViewMode'
const FilamentProfiles = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('FilamentProfiles')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('filamentProfile')
const [showFilterSidebar, setShowFilterSidebar] =
@ -25,10 +26,13 @@ const FilamentProfiles = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space>
<ObjectListViewProvider
objectType='filamentProfile'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
type='filamentProfile'
pageName='list'
@ -41,7 +45,8 @@ const FilamentProfiles = () => {
/>
<ExportListButton objectType='filamentProfile' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -51,17 +56,14 @@ const FilamentProfiles = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='filamentProfile'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type='filamentProfile'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
@ -74,9 +76,10 @@ const FilamentProfiles = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -8,16 +8,17 @@ import ObjectActions from '../common/ObjectActions'
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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const GCodeFiles = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('gcodeFile')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('gcodeFile')
@ -30,10 +31,13 @@ const GCodeFiles = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space>
<ObjectListViewProvider
objectType='gcodeFile'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
type='gcodeFile'
pageName='list'
@ -47,7 +51,8 @@ const GCodeFiles = () => {
/>
<ExportListButton objectType='gcodeFile' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -57,16 +62,15 @@ const GCodeFiles = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='gcodeFile'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type='gcodeFile'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
@ -80,8 +84,8 @@ const GCodeFiles = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -5,7 +5,6 @@ import { Flex, Space } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility.jsx'
import ObjectTable from '../common/ObjectTable.jsx'
import ObjectActions from '../common/ObjectActions'
import useViewMode from '../hooks/useViewMode.jsx'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
@ -13,10 +12,12 @@ import ColumnViewButton from '../common/ColumnViewButton.jsx'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility.jsx'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ExportListButton from '../common/ExportListButton.jsx'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Jobs = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('job')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('job')
@ -28,10 +29,13 @@ const Jobs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='job'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='job'
pageName='list'
@ -45,8 +49,8 @@ const Jobs = () => {
/>
<ExportListButton objectType='job' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -56,18 +60,15 @@ const Jobs = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='job'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type={'job'}
visibleColumns={columnVisibility}
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -79,9 +80,10 @@ const Jobs = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -2,6 +2,9 @@ import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
import ObjectTable from '../common/ObjectTable'
@ -10,10 +13,8 @@ import ObjectTableViewButton from '../common/ObjectTableViewButton'
import useColumnVisibility from '../hooks/useColumnVisibility'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import useViewMode from '../hooks/useViewMode'
const PrinterProfiles = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('PrinterProfiles')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('printerProfile')
const [showFilterSidebar, setShowFilterSidebar] =
@ -24,10 +25,13 @@ const PrinterProfiles = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space>
<ObjectListViewProvider
objectType='printerProfile'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
type='printerProfile'
pageName='list'
@ -40,7 +44,8 @@ const PrinterProfiles = () => {
/>
<ExportListButton objectType='printerProfile' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -50,17 +55,14 @@ const PrinterProfiles = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='printerProfile'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type='printerProfile'
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
@ -73,9 +75,10 @@ const PrinterProfiles = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,11 +6,13 @@ import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
import useViewMode from '../hooks/useViewMode'
import useColumnVisibility from '../hooks/useColumnVisibility'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
@ -19,7 +21,6 @@ const Printers = () => {
const tableRef = useRef()
// View mode state (cards/list), persisted in sessionStorage via custom hook
const [viewMode, setViewMode] = useViewMode('Printers')
// Column visibility state, persisted in sessionStorage via custom hook
const [columnVisibility, setColumnVisibility] = useColumnVisibility('printer')
@ -33,10 +34,13 @@ const Printers = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space>
<ObjectListViewProvider
objectType='printer'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
type='printer'
pageName='list'
@ -50,7 +54,8 @@ const Printers = () => {
/>
<ExportListButton objectType='printer' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -60,19 +65,16 @@ const Printers = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='printer'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type='printer'
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
cards={viewMode === 'cards'}
visibleColumns={columnVisibility}
expandHeight={true}
saveFilterInSession={true}
@ -83,10 +85,10 @@ const Printers = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -5,7 +5,6 @@ import { Flex, Space } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility.jsx'
import ObjectTable from '../common/ObjectTable.jsx'
import ObjectActions from '../common/ObjectActions'
import useViewMode from '../hooks/useViewMode.jsx'
import ObjectTableViewButton from '../common/ObjectTableViewButton'
import FilterSidebarButton from '../common/FilterSidebarButton'
import SortSidebarButton from '../common/SortSidebarButton'
@ -13,10 +12,12 @@ import ColumnViewButton from '../common/ColumnViewButton.jsx'
import useFilterSidebarVisibility from '../hooks/useFilterSidebarVisibility.jsx'
import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import ExportListButton from '../common/ExportListButton.jsx'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const SubJobs = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('subJob')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('subJob')
@ -28,10 +29,13 @@ const SubJobs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='subJob'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='subJob'
pageName='list'
@ -45,8 +49,8 @@ const SubJobs = () => {
/>
<ExportListButton objectType='subJob' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -56,18 +60,15 @@ const SubJobs = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='subJob'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
type={'subJob'}
visibleColumns={columnVisibility}
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -79,9 +80,10 @@ const SubJobs = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Clients = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('client')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('client')
const [showFilterSidebar, setShowFilterSidebar] =
@ -27,10 +27,13 @@ const Clients = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='client'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='client'
pageName='list'
@ -44,7 +47,8 @@ const Clients = () => {
/>
<ExportListButton objectType='client' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -54,17 +58,16 @@ const Clients = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='client'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='client'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -77,8 +80,8 @@ const Clients = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,15 +6,16 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const FulfillmentPolicies = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('fulfillmentPolicy')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('fulfillmentPolicy')
const [showFilterSidebar, setShowFilterSidebar] =
@ -23,9 +24,13 @@ const FulfillmentPolicies = () => {
useSortSidebarVisibility('FulfillmentPolicies')
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='fulfillmentPolicy'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='fulfillmentPolicy'
pageName='list'
@ -39,7 +44,8 @@ const FulfillmentPolicies = () => {
/>
<ExportListButton objectType='fulfillmentPolicy' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -49,17 +55,16 @@ const FulfillmentPolicies = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='fulfillmentPolicy'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='fulfillmentPolicy'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -72,7 +77,8 @@ const FulfillmentPolicies = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const ListingVarients = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('listingVarients')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('listingVarients')
@ -28,10 +28,13 @@ const ListingVarients = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='listingVarient'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='listingVarient'
pageName='list'
@ -45,7 +48,8 @@ const ListingVarients = () => {
/>
<ExportListButton objectType='listingVarient' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const ListingVarients = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='listingVarient'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='listingVarient'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const ListingVarients = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Listings = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('listings')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('listings')
@ -28,10 +28,13 @@ const Listings = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='listing'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='listing'
pageName='list'
@ -45,7 +48,8 @@ const Listings = () => {
/>
<ExportListButton objectType='listing' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const Listings = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='listing'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='listing'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const Listings = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Marketplaces = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('marketplaces')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('marketplaces')
@ -28,10 +28,13 @@ const Marketplaces = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='marketplace'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='marketplace'
pageName='list'
@ -45,7 +48,8 @@ const Marketplaces = () => {
/>
<ExportListButton objectType='marketplace' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const Marketplaces = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='marketplace'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='marketplace'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const Marketplaces = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,15 +6,16 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const ReturnPolicies = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('returnPolicy')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('returnPolicy')
const [showFilterSidebar, setShowFilterSidebar] =
useFilterSidebarVisibility('ReturnPolicies')
@ -22,9 +23,13 @@ const ReturnPolicies = () => {
useSortSidebarVisibility('ReturnPolicies')
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='returnPolicy'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='returnPolicy'
pageName='list'
@ -38,7 +43,8 @@ const ReturnPolicies = () => {
/>
<ExportListButton objectType='returnPolicy' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -48,17 +54,16 @@ const ReturnPolicies = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='returnPolicy'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='returnPolicy'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -71,7 +76,8 @@ const ReturnPolicies = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -6,17 +6,17 @@ 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'
import ListViewTabs from '../common/ListViewTabs'
import ListViewEditButtons from '../common/ListViewEditButtons'
import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const SalesOrders = () => {
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('salesOrders')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('salesOrders')
@ -28,10 +28,13 @@ const SalesOrders = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify={'space-between'} style={{ minHeight: 0 }}>
<Space size='small'>
<ObjectListViewProvider
objectType='salesOrder'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='salesOrder'
pageName='list'
@ -45,7 +48,8 @@ const SalesOrders = () => {
/>
<ExportListButton objectType='salesOrder' />
</Space>
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -55,17 +59,16 @@ const SalesOrders = () => {
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
showEndingDivider={true}
objectType='salesOrder'
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
type='salesOrder'
cards={viewMode === 'cards'}
showFilterSidebar={showFilterSidebar}
showSortSidebar={showSortSidebar}
expandHeight={true}
@ -78,8 +81,8 @@ const SalesOrders = () => {
useSortInSession={true}
useSortInUrl={true}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -2,7 +2,16 @@ import { useState, useEffect } from 'react'
import { ColorPicker, Checkbox, Flex, Row, Col, Divider } from 'antd'
import PropTypes from 'prop-types'
const ColorSelector = ({ value, onChange, disabled, required = false }) => {
const ColorSelector = ({
value,
onChange,
disabled,
required = false,
size,
showText = true,
className,
onClick
}) => {
// Determine initial enabled state based on value
const [colorEnabled, setColorEnabled] = useState(!!value)
@ -97,18 +106,24 @@ const ColorSelector = ({ value, onChange, disabled, required = false }) => {
]
return (
<Flex gap={'middle'}>
<Flex gap={showText ? 'middle' : 0} align='center'>
<ColorPicker
showText
size={size}
showText={showText}
disabledAlpha
style={{ width: '100%', justifyContent: 'start' }}
style={
showText
? { width: '100%', justifyContent: 'start' }
: undefined
}
disabled={(!colorEnabled && !required) || disabled}
value={value || '#000000'}
onChange={handleColorChange}
onClick={onClick}
presets={presets}
panelRender={customPanelRender}
arrow={false}
className='ant-input-outlined'
className={className ?? (showText ? 'ant-input-outlined' : undefined)}
/>
{!required && (
<Checkbox
@ -125,7 +140,11 @@ ColorSelector.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func,
disabled: PropTypes.bool,
required: PropTypes.bool
required: PropTypes.bool,
size: PropTypes.string,
showText: PropTypes.bool,
className: PropTypes.string,
onClick: PropTypes.func
}
export default ColorSelector

View File

@ -28,7 +28,7 @@ const mainSections = ['production', 'inventory', 'management', 'developer']
const DashboardBreadcrumb = () => {
const location = useLocation()
const navigate = useNavigate()
const { hasPageFilter, hasStoredFilter } = useTableState()
const { hasPageFilter, hasStoredFilter, getObjectListView } = useTableState()
const pathSnippets = location.pathname.split('/').filter((i) => i)
function segmentToModel(segment) {
@ -45,7 +45,15 @@ const DashboardBreadcrumb = () => {
const isMainSection = mainSections.includes(segment)
const model = segmentToModel(segment)
const modelLabelPlural = model?.labelPlural ? model.labelPlural : null
const viewName =
modelLabelPlural && model?.name && model.name !== 'unknown'
? getObjectListView(model.name)?.name
: null
const name = breadcrumbNameMap[segment] || modelLabelPlural || segment
const displayName =
modelLabelPlural && viewName
? `${modelLabelPlural} - ${viewName}`
: name
const showFilterIcon =
hasPageFilter(url) ||
(model?.name && model.name !== 'unknown' && hasStoredFilter(model.name))
@ -53,7 +61,7 @@ const DashboardBreadcrumb = () => {
return {
title: (
<span style={{ padding: '0 12px' }} key={segment}>
{name}
{displayName}
</span>
),
key: segment
@ -71,7 +79,7 @@ const DashboardBreadcrumb = () => {
gap: 6
}}
>
{name}
{displayName}
{showFilterIcon && <FilterIcon style={{ fontSize: 12 }} />}
</Link>
),

View File

@ -1027,7 +1027,7 @@ const FilterInput = ({
}
}, [])
const showPlaceholder = !internalValue && !focused
const showPlaceholder = Boolean(placeholder) && !internalValue
const showClear = allowClear && !!internalValue && !disabled
const affixCls = `${prefixCls}-affix-wrapper`
@ -1076,6 +1076,7 @@ const FilterInput = ({
className={classNames(prefixCls, 'filter-input-editor')}
role='textbox'
aria-multiline='false'
aria-placeholder={showPlaceholder ? placeholder : undefined}
aria-disabled={disabled || undefined}
contentEditable={!disabled}
suppressContentEditableWarning

View File

@ -0,0 +1,41 @@
import PropTypes from 'prop-types'
import { Divider, Flex } from 'antd'
import EditButtons from './EditButtons'
import { useObjectListView } from '../context/ObjectListViewContext'
const ListViewEditButtons = ({ showStartingDivider = false }) => {
const {
isEditing,
initialLoading,
saving,
saveEdits,
cancelEdits,
startEditing
} = useObjectListView()
return (
<Flex align='center' gap='small'>
{showStartingDivider && (
<Divider type='vertical' style={{ margin: '0 4px', height: '18px' }} />
)}
<EditButtons
isEditing={isEditing}
handleUpdate={async () => {
await saveEdits()
}}
cancelEditing={cancelEdits}
startEditing={startEditing}
formValid={true}
disabled={initialLoading}
loading={saving}
requirePermission={false}
/>
</Flex>
)
}
ListViewEditButtons.propTypes = {
showStartingDivider: PropTypes.bool
}
export default ListViewEditButtons

View File

@ -0,0 +1,320 @@
import PropTypes from 'prop-types'
import React, { useEffect, useMemo, useState } from 'react'
import {
Input,
Dropdown,
Checkbox,
Button,
Flex,
Typography,
Divider,
theme
} from 'antd'
const { useToken } = theme
import SegmentedNav, { SegmentedNavDragHandle } from './SegmentedNav'
import { CaretDownFilled, LoadingOutlined } from '@ant-design/icons'
import { getModelByName } from '../../../database/ObjectModels'
import { useObjectListView } from '../context/ObjectListViewContext'
import classNames from 'classnames'
import PlusIcon from '../../Icons/PlusIcon'
import BinIcon from '../../Icons/BinIcon'
import ScrollBox from './ScrollBox'
import ColorSelector from './ColorSelector'
const { Text } = Typography
const ViewColorDot = ({ color }) => (
<span
className='list-view-tabs-color-dot'
style={{ backgroundColor: color || '#3498DB' }}
/>
)
ViewColorDot.propTypes = {
color: PropTypes.string
}
const ViewOptionsDropdown = ({ view, isEditing, onChange, onDelete }) => {
const menu = {
items: [
{
key: 'delete',
label: 'Delete',
icon: <BinIcon />,
danger: true,
onClick: ({ domEvent }) => {
domEvent.stopPropagation()
onDelete?.()
}
},
{ type: 'divider' }
]
}
const menuStyle = {
boxShadow: 'none'
}
const { token } = useToken()
const contentStyle = {
backgroundColor: token.colorBgElevated,
borderRadius: token.borderRadiusLG,
boxShadow: token.boxShadowSecondary
}
return (
<Dropdown
menu={menu}
trigger={['hover']}
placement='bottomLeft'
popupRender={(menu) => (
<div className='list-view-tabs-options-dropdown' style={contentStyle}>
{React.cloneElement(menu, { style: menuStyle })}
<Flex
vertical
gap='middle'
className='list-view-tabs-options-dropdown-privacy'
>
<Checkbox
checked={view.private !== false}
disabled={!isEditing}
onChange={(event) => onChange({ private: event.target.checked })}
onClick={(event) => event.stopPropagation()}
>
Private
</Checkbox>
</Flex>
</div>
)}
>
<Button
type='text'
size='small'
className='list-view-tabs-options-button'
icon={<CaretDownFilled style={{ fontSize: 14, marginTop: 4 }} />}
disabled={!isEditing}
onClick={(event) => event.stopPropagation()}
/>
</Dropdown>
)
}
ViewOptionsDropdown.propTypes = {
view: PropTypes.object.isRequired,
isEditing: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
onDelete: PropTypes.func
}
const ViewTabNameInput = ({ value, onClick, onChange }) => (
<span className='list-view-tabs-name-input-wrap'>
<span aria-hidden className='list-view-tabs-name-input-sizer'>
{value}
</span>
<Input
size='small'
variant='borderless'
className='list-view-tabs-name-input'
value={value}
onClick={onClick}
onChange={onChange}
/>
</span>
)
ViewTabNameInput.propTypes = {
value: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired
}
const ViewTabLabel = ({
view,
isEditing,
onDraftChange,
onRemove,
onSelect
}) => {
if (!isEditing) {
return (
<Flex align='center' gap={8} className='list-view-tabs-label'>
<ViewColorDot color={view.color} />
<Text ellipsis style={{ maxWidth: 160 }}>
{view.name}
</Text>
</Flex>
)
}
return (
<Flex
align='center'
gap={6}
className='list-view-tabs-label list-view-tabs-label-editing'
onFocusCapture={() => onSelect?.()}
>
<div style={{ marginRight: 2 }}>
<ColorSelector
size='small'
showText={false}
required
value={view.color || '#3498DB'}
onChange={(color) => onDraftChange({ color })}
onClick={(event) => event.stopPropagation()}
/>
</div>
<ViewTabNameInput
value={view.name}
onClick={(event) => event.stopPropagation()}
onChange={(event) => onDraftChange({ name: event.target.value })}
/>
<Flex align='center' gap={2}>
<ViewOptionsDropdown
view={view}
isEditing={isEditing}
onChange={onDraftChange}
onDelete={onRemove}
/>
<SegmentedNavDragHandle value={String(view._id)} />
</Flex>
</Flex>
)
}
ViewTabLabel.propTypes = {
view: PropTypes.object.isRequired,
isEditing: PropTypes.bool.isRequired,
onDraftChange: PropTypes.func.isRequired,
onRemove: PropTypes.func,
onSelect: PropTypes.func
}
const ListViewTabs = () => {
const {
objectType,
ALL_TAB_KEY,
views,
activeTabKey,
initialLoading,
isEditing,
selectTab,
handleTabEdit,
updateDraftView,
reorderDraftViews,
viewTabSignatures
} = useObjectListView()
const [animated, setAnimated] = useState(false)
useEffect(() => {
if (initialLoading) {
setAnimated(false)
return undefined
}
const timeoutId = setTimeout(() => setAnimated(true), 10)
return () => clearTimeout(timeoutId)
}, [initialLoading])
const model = getModelByName(objectType)
const options = useMemo(() => {
const Icon = initialLoading ? LoadingOutlined : model?.icon
const styles = initialLoading
? { fontSize: 12, color: 'var(--color-button-border)' }
: { fontSize: 14 }
const allOption = {
value: ALL_TAB_KEY,
icon: Icon ? (
<Icon
style={styles}
color={'secondary'}
spin={initialLoading || undefined}
/>
) : null
}
const viewOptions = views.map((view) => ({
value: String(view._id),
reorderable: isEditing,
label: (
<ViewTabLabel
view={view}
isEditing={isEditing}
onDraftChange={(changes) => updateDraftView(view._id, changes)}
onRemove={
isEditing
? () => handleTabEdit(String(view._id), 'remove')
: undefined
}
onSelect={() => {
const tabKey = String(view._id)
if (activeTabKey !== tabKey) {
selectTab(tabKey)
}
}}
/>
)
}))
return [allOption, ...viewOptions]
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
ALL_TAB_KEY,
activeTabKey,
handleTabEdit,
initialLoading,
isEditing,
model?.icon,
selectTab,
updateDraftView,
viewTabSignatures
])
return (
<Flex
align='center'
gap='small'
style={{ minHeight: 0, width: '100%', minWidth: 0 }}
>
<Divider type='vertical' style={{ margin: '0 4px', height: '18px' }} />
<Flex
align='center'
gap={4}
className={classNames('list-view-tabs', {
'list-view-tabs-editing': isEditing
})}
style={{ flex: 1, minWidth: 0 }}
>
<ScrollBox className='list-view-tabs-segmented-wrap' horizontal>
<Flex
align='center'
gap={8}
className='list-view-tabs-segmented-inner'
>
<SegmentedNav
className='list-view-tabs-segmented'
value={activeTabKey}
options={options}
onChange={selectTab}
onReorder={isEditing ? reorderDraftViews : undefined}
animated={animated}
/>
{isEditing && (
<Button
type='text'
size='small'
className='list-view-tabs-add-button'
onClick={() => handleTabEdit(null, 'add')}
icon={<PlusIcon style={{ fontSize: 14, marginTop: 3 }} />}
/>
)}
</Flex>
</ScrollBox>
</Flex>
</Flex>
)
}
export default ListViewTabs

View File

@ -0,0 +1,426 @@
import {
forwardRef,
useCallback,
useContext,
useImperativeHandle,
useLayoutEffect,
useMemo,
useRef,
useState
} from 'react'
import { Empty, Flex, Spin } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import PropTypes from 'prop-types'
import { ApiServerContext } from '../context/ApiServerContext'
import ObjectCard from './ObjectCard'
import ObjectKanbanColumn from './ObjectKanbanColumn'
import ObjectKanbanHeader from './ObjectKanbanHeader'
import ScrollBox from './ScrollBox'
import { getCategoryValueKey } from './viewModeUtils'
const KANBAN_COLUMN_WIDTH = 360
const KANBAN_GAP = 16
const KANBAN_CARD_MIN_HEIGHT = 136
const getKanbanSkeletonLayout = (width, height) => {
const columnCount =
Math.max(
1,
Math.floor((width + KANBAN_GAP) / (KANBAN_COLUMN_WIDTH + KANBAN_GAP))
) + 1
const cardCount = Math.max(
1,
Math.floor((height - 32) / KANBAN_CARD_MIN_HEIGHT)
)
return { columnCount, cardCount }
}
const ObjectKanbanSkeletonColumns = ({
columnCount,
cardCount = 0,
trackRef,
model,
modelProperties,
visibleColumns,
keyPrefix = 'skeleton'
}) => (
<div className='objectKanbanSkeletonColumns objectKanbanSkeletonColumns--loading'>
<div className='objectKanbanSkeletonTrack' ref={trackRef}>
<Flex gap='middle' className='objectKanbanSkeletonRow'>
{Array.from({ length: columnCount }).map((_, columnIndex) => (
<div
key={`${keyPrefix}-${columnIndex}`}
className='objectKanbanColumnSkeleton'
>
<div className='objectKanbanColumnCards'>
{cardCount > 0 && (
<Flex
vertical
gap='middle'
className='objectKanbanColumnCardsInner'
>
{Array.from({ length: cardCount }).map((_, cardIndex) => (
<ObjectCard
key={cardIndex}
isSkeleton
model={model}
modelProperties={modelProperties}
visibleColumns={visibleColumns}
record={{
_id: `${keyPrefix}-${columnIndex}-${cardIndex}`
}}
cardStyle='bordered'
/>
))}
</Flex>
)}
</div>
</div>
))}
</Flex>
</div>
</div>
)
ObjectKanbanSkeletonColumns.propTypes = {
columnCount: PropTypes.number.isRequired,
cardCount: PropTypes.number,
trackRef: PropTypes.object,
model: PropTypes.object.isRequired,
modelProperties: PropTypes.array.isRequired,
visibleColumns: PropTypes.object,
keyPrefix: PropTypes.string
}
const ObjectKanban = forwardRef(
(
{
type,
categoryProperty,
baseFilter = {},
masterFilter = {},
sorter = {},
pageSize = 25,
model,
modelProperties,
visibleColumns = {},
isEditing = false,
rowActions = [],
renderActions
},
ref
) => {
const { getModelPropertyValues } = useContext(ApiServerContext)
const columnRefs = useRef({})
const headerTrackRef = useRef(null)
const skeletonTrackRef = useRef(null)
const kanbanBodyRef = useRef(null)
const [scrollElement, setScrollElement] = useState(null)
const [categoryValues, setCategoryValues] = useState([])
const [hasLoaded, setHasLoaded] = useState(false)
const [loadedFilter, setLoadedFilter] = useState(baseFilter)
const [loadedSorter, setLoadedSorter] = useState(sorter)
const [skeletonLayout, setSkeletonLayout] = useState({
columnCount: 3,
cardCount: 3
})
const baseFilterRef = useRef(baseFilter)
const masterFilterRef = useRef(masterFilter)
const sorterRef = useRef(sorter)
const getModelPropertyValuesRef = useRef(getModelPropertyValues)
const lastCategoryQueryKeyRef = useRef(null)
const hasLoadedRef = useRef(false)
const loadGenerationRef = useRef(0)
baseFilterRef.current = baseFilter
masterFilterRef.current = masterFilter
sorterRef.current = sorter
getModelPropertyValuesRef.current = getModelPropertyValues
const setScrollContainerRef = useCallback((node) => {
setScrollElement(node)
}, [])
useLayoutEffect(() => {
const kanbanBody = kanbanBodyRef.current
if (!kanbanBody) return undefined
const updateLayout = () => {
const { width, height } = kanbanBody.getBoundingClientRect()
setSkeletonLayout(getKanbanSkeletonLayout(width, height))
}
updateLayout()
const observer = new ResizeObserver(updateLayout)
observer.observe(kanbanBody)
return () => observer.disconnect()
}, [categoryProperty])
useLayoutEffect(() => {
const mainScroll = scrollElement
const headerTrack = headerTrackRef.current
const skeletonTrack = skeletonTrackRef.current
if (!mainScroll || !headerTrack) return undefined
const handleMainScroll = () => {
const offset = `translate3d(-${mainScroll.scrollLeft}px, 0, 0)`
headerTrack.style.transform = offset
if (skeletonTrack) {
skeletonTrack.style.transform = offset
}
}
handleMainScroll()
mainScroll.addEventListener('scroll', handleMainScroll, { passive: true })
return () => {
mainScroll.removeEventListener('scroll', handleMainScroll)
}
}, [scrollElement, categoryValues])
const categoryPropertyDef = useMemo(
() =>
model?.properties?.find(
(property) => property.name === categoryProperty
),
[categoryProperty, model]
)
const buildCategoryQueryKey = useCallback(
(filter, activeSorter) =>
JSON.stringify({
type,
categoryProperty,
filter,
masterFilter: masterFilterRef.current,
sorter: activeSorter
}),
[categoryProperty, type]
)
const reloadColumns = useCallback(async () => {
await Promise.all(
Object.values(columnRefs.current)
.filter(Boolean)
.map((columnRef) => columnRef.reload?.())
)
}, [])
const load = useCallback(
async (filter = null, sorterArg = null, options = {}) => {
const silent = options.silent === true
const generation = ++loadGenerationRef.current
if (!categoryProperty) {
lastCategoryQueryKeyRef.current = null
hasLoadedRef.current = true
setCategoryValues([])
setHasLoaded(true)
return
}
const activeFilter = filter ?? baseFilterRef.current
const activeSorter = sorterArg ?? sorterRef.current
const activeMasterFilter = masterFilterRef.current
const queryKey = buildCategoryQueryKey(activeFilter, activeSorter)
const canReuseColumns =
lastCategoryQueryKeyRef.current === queryKey && hasLoadedRef.current
if (canReuseColumns) {
await reloadColumns()
if (generation !== loadGenerationRef.current) return
hasLoadedRef.current = true
setHasLoaded(true)
return
}
// Filter/sort changes keep current columns visible; objectView /
// initial loads clear to skeleton + Spin.
if (!silent || !hasLoadedRef.current) {
hasLoadedRef.current = false
setHasLoaded(false)
}
try {
const values = await getModelPropertyValuesRef.current(
type,
categoryProperty,
{
filter: activeFilter,
masterFilter: activeMasterFilter
}
)
if (generation !== loadGenerationRef.current) return
const nextValues = Array.isArray(values) ? values : []
lastCategoryQueryKeyRef.current = queryKey
hasLoadedRef.current = true
setLoadedFilter(activeFilter)
setLoadedSorter(activeSorter)
setCategoryValues(nextValues)
setHasLoaded(true)
} catch (error) {
if (generation !== loadGenerationRef.current) return
console.error('Error fetching kanban category values:', error)
lastCategoryQueryKeyRef.current = null
hasLoadedRef.current = true
setCategoryValues([])
setHasLoaded(true)
throw error
}
},
[buildCategoryQueryKey, categoryProperty, reloadColumns, type]
)
const reload = useCallback(async () => {
lastCategoryQueryKeyRef.current = null
hasLoadedRef.current = false
await load(baseFilterRef.current, sorterRef.current)
}, [load])
useImperativeHandle(
ref,
() => ({
load,
reload
}),
[load, reload]
)
if (!categoryProperty) {
return (
<Flex align='center' justify='center' style={{ height: '100%' }}>
<Empty description='Select a category property for kanban view' />
</Flex>
)
}
if (hasLoaded && categoryValues.length === 0) {
return (
<Flex align='center' justify='center' style={{ height: '100%' }}>
<Empty description='No category values found' />
</Flex>
)
}
const showSkeleton = !hasLoaded
return (
<div className='objectKanbanContainerWrapper'>
<Spin
indicator={<LoadingOutlined />}
spinning={showSkeleton}
style={{ height: '100%', flex: 1, minHeight: 0 }}
>
<div className='objectKanbanContainerSkeletons'>
<ObjectKanbanHeader
categoryValues={categoryValues}
categoryProperty={categoryProperty}
categoryPropertyDef={categoryPropertyDef}
trackRef={headerTrackRef}
skeletonColumnCount={
showSkeleton ? skeletonLayout.columnCount : 0
}
/>
<div className='objectKanbanKanbanBody' ref={kanbanBodyRef}>
{!showSkeleton && (
<div className='objectKanbanContainer'>
<ScrollBox
className='objectKanbanScroll'
scrollableNodeProps={{ ref: setScrollContainerRef }}
>
<Flex gap='middle' className='objectKanban'>
{categoryValues.map((categoryValue) => {
const columnKey = getCategoryValueKey(categoryValue)
return (
<ObjectKanbanColumn
key={columnKey}
ref={(node) => {
if (node) {
columnRefs.current[columnKey] = node
} else {
delete columnRefs.current[columnKey]
}
}}
scrollElement={scrollElement}
type={type}
categoryProperty={categoryProperty}
categoryValue={categoryValue}
baseFilter={loadedFilter}
masterFilter={masterFilter}
sorter={loadedSorter}
pageSize={pageSize}
model={model}
modelProperties={modelProperties}
visibleColumns={visibleColumns}
isEditing={isEditing}
rowActions={rowActions}
renderActions={renderActions}
/>
)
})}
</Flex>
</ScrollBox>
</div>
)}
{showSkeleton ? (
<ObjectKanbanSkeletonColumns
columnCount={skeletonLayout.columnCount}
cardCount={skeletonLayout.cardCount}
trackRef={skeletonTrackRef}
model={model}
modelProperties={modelProperties}
visibleColumns={visibleColumns}
/>
) : (
<div className='objectKanbanSkeletonColumns'>
<div
className='objectKanbanSkeletonTrack'
ref={skeletonTrackRef}
>
<Flex gap='middle' className='objectKanbanSkeletonRow'>
{categoryValues.map((categoryValue) => {
const columnKey = getCategoryValueKey(categoryValue)
return (
<div
key={columnKey}
className='objectKanbanColumnSkeleton'
>
<div className='objectKanbanColumnCards' />
</div>
)
})}
</Flex>
</div>
</div>
)}
</div>
</div>
</Spin>
</div>
)
}
)
ObjectKanban.displayName = 'ObjectKanban'
ObjectKanban.propTypes = {
type: PropTypes.string.isRequired,
categoryProperty: PropTypes.string,
baseFilter: PropTypes.object,
masterFilter: PropTypes.object,
sorter: PropTypes.object,
pageSize: PropTypes.number,
model: PropTypes.object.isRequired,
modelProperties: PropTypes.array.isRequired,
visibleColumns: PropTypes.object,
isEditing: PropTypes.bool,
rowActions: PropTypes.array,
renderActions: PropTypes.func
}
export default ObjectKanban

View File

@ -0,0 +1,567 @@
import {
forwardRef,
useCallback,
useContext,
useEffect,
useImperativeHandle,
useLayoutEffect,
useMemo,
useRef,
useState
} from 'react'
import { Flex, Spin } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import PropTypes from 'prop-types'
import { ApiServerContext } from '../context/ApiServerContext'
import ObjectCard from './ObjectCard'
import { toCategoryFilterValue } from './viewModeUtils'
const SCROLL_THRESHOLD = 50
const ObjectKanbanColumn = forwardRef(
(
{
type,
categoryProperty,
categoryValue,
baseFilter = {},
masterFilter = {},
sorter = {},
pageSize = 25,
model,
modelProperties,
visibleColumns = {},
isEditing = false,
rowActions = [],
renderActions,
scrollElement = null
},
ref
) => {
const { fetchObjects } = useContext(ApiServerContext)
const containerRef = useRef(null)
const pagesRef = useRef([])
const loadingPagesRef = useRef(new Set())
const dataLoadGenerationRef = useRef(0)
const pendingScrollAnchorRef = useRef(null)
const lastColumnQueryKeyRef = useRef(null)
const baseFilterRef = useRef(baseFilter)
const masterFilterRef = useRef(masterFilter)
const sorterRef = useRef(sorter)
const fetchObjectsRef = useRef(fetchObjects)
baseFilterRef.current = baseFilter
masterFilterRef.current = masterFilter
sorterRef.current = sorter
fetchObjectsRef.current = fetchObjects
const [pages, setPages] = useState([])
const [loading, setLoading] = useState(true)
const columnFilter = useMemo(
() => ({
...baseFilter,
[categoryProperty]: toCategoryFilterValue(categoryValue)
}),
[baseFilter, categoryProperty, categoryValue]
)
const columnQueryKey = useMemo(
() =>
JSON.stringify({
type,
categoryProperty,
categoryValue: toCategoryFilterValue(categoryValue),
filter: columnFilter,
sorter
}),
[columnFilter, categoryProperty, categoryValue, sorter, type]
)
const tableData = useMemo(() => {
const items = pages.flatMap((page) => page.items)
const seen = new Set()
return items.filter((item) => {
const id = item?._id
if (id == null) return true
if (seen.has(id)) return false
seen.add(id)
return true
})
}, [pages])
const createSkeletonData = useCallback(
(pageNum) =>
Array(pageSize)
.fill(null)
.map((_, index) => ({
_id: `skeleton-${categoryProperty}-${pageNum}-${index}`,
isSkeleton: true
})),
[categoryProperty, pageSize]
)
const createSkeletonPage = useCallback(
(pageNum) => ({
pageNum,
items: createSkeletonData(pageNum),
isSkeletonPage: true
}),
[createSkeletonData]
)
const createPageWindow = useCallback(
(loadedPages, direction) => {
const dedupedByPageNum = new Map()
loadedPages.forEach((page) => {
dedupedByPageNum.set(page.pageNum, page)
})
const sortedPages = [...dedupedByPageNum.values()].sort(
(a, b) => a.pageNum - b.pageNum
)
const visiblePages =
direction === 'previous'
? sortedPages.slice(0, 2)
: sortedPages.slice(-2)
const firstPage = visiblePages[0]
const lastPage = visiblePages[visiblePages.length - 1]
const nextPages = []
if (firstPage?.pageNum > 1) {
nextPages.push(createSkeletonPage(firstPage.pageNum - 1))
}
nextPages.push(...visiblePages)
if (lastPage?.hasMore) {
nextPages.push(createSkeletonPage(lastPage.pageNum + 1))
}
return nextPages
},
[createSkeletonPage]
)
const mergeLoadedPage = useCallback((currentPages, loadedPage) => {
const withoutSkeletons = currentPages.filter(
(page) => !page.isSkeletonPage
)
const withoutDuplicate = withoutSkeletons.filter(
(page) => page.pageNum !== loadedPage.pageNum
)
return [...withoutDuplicate, loadedPage].sort(
(a, b) => a.pageNum - b.pageNum
)
}, [])
const setTablePages = useCallback((nextPages) => {
pagesRef.current = nextPages
setPages(nextPages)
}, [])
const isStaleDataLoad = useCallback(
(generation) => generation !== dataLoadGenerationRef.current,
[]
)
const fetchPage = useCallback(
async (pageNum, overrides = {}) => {
const filter = overrides.filter ?? {
...masterFilterRef.current,
...baseFilterRef.current,
[categoryProperty]: toCategoryFilterValue(categoryValue)
}
const sorter = overrides.sorter ?? sorterRef.current
return fetchObjectsRef.current(type, {
page: pageNum,
limit: pageSize,
filter,
sorter
})
},
[categoryProperty, categoryValue, pageSize, type]
)
const fetchData = useCallback(
async (pageNum) => {
const generation = dataLoadGenerationRef.current
try {
const result = await fetchPage(pageNum)
if (isStaleDataLoad(generation)) return []
const loadedPage = {
pageNum,
items: result.data || [],
hasMore: result.hasMore
}
setPages((prev) =>
prev.map((page) => (page.pageNum === pageNum ? loadedPage : page))
)
return result.data || []
} catch (error) {
console.error(`Error loading kanban page ${pageNum}:`, error)
return []
}
},
[fetchPage, isStaleDataLoad]
)
const findRenderedCard = useCallback((scrollTarget, id) => {
if (!scrollTarget || id == null) return null
return scrollTarget.querySelector(`[data-kanban-card-id="${String(id)}"]`)
}, [])
const captureScrollAnchor = useCallback(
(scrollTarget, placeholderPage, direction, currentPages) => {
if (!scrollTarget) return null
const targetTop = scrollTarget.getBoundingClientRect().top
const closestCard = (items) =>
items
.map((item, index) => {
const element = findRenderedCard(scrollTarget, item._id)
return element
? {
index,
id: item._id,
top: element.getBoundingClientRect().top
}
: null
})
.filter(Boolean)
.sort(
(a, b) =>
Math.abs(a.top - targetTop) - Math.abs(b.top - targetTop)
)[0]
const placeholderAnchor = closestCard(placeholderPage.items)
const loadedPages = currentPages.filter((page) => !page.isSkeletonPage)
const retainedPage =
direction === 'previous'
? loadedPages[0]
: loadedPages[loadedPages.length - 1]
const fallbackAnchor = retainedPage
? closestCard(retainedPage.items)
: null
return { placeholderAnchor, fallbackAnchor }
},
[findRenderedCard]
)
const restoreScrollAnchor = useCallback(
(scrollTarget, anchor, loadedPage) => {
if (!scrollTarget || !anchor) return
const loadedItem = loadedPage.items[anchor.placeholderAnchor?.index]
const anchorId = loadedItem?._id ?? anchor.fallbackAnchor?.id
const previousTop = loadedItem
? anchor.placeholderAnchor?.top
: anchor.fallbackAnchor?.top
if (anchorId == null || previousTop == null) return
const element = findRenderedCard(scrollTarget, anchorId)
if (!element) return
const renderedTop = element.getBoundingClientRect().top
scrollTarget.scrollTop += renderedTop - previousTop
},
[findRenderedCard]
)
useLayoutEffect(() => {
const pendingAnchor = pendingScrollAnchorRef.current
if (!pendingAnchor) return
pendingScrollAnchorRef.current = null
restoreScrollAnchor(
pendingAnchor.scrollTarget,
pendingAnchor.anchor,
pendingAnchor.loadedPage
)
}, [pages, restoreScrollAnchor])
const loadBoundaryPage = useCallback(
async (pageNum, direction, scrollTarget) => {
if (loadingPagesRef.current.size > 0) return
const currentPages = pagesRef.current
const placeholderPage = currentPages.find(
(page) => page.pageNum === pageNum && page.isSkeletonPage
)
if (!placeholderPage) return
const scrollAnchor = captureScrollAnchor(
scrollTarget,
placeholderPage,
direction,
currentPages
)
const generation = dataLoadGenerationRef.current
loadingPagesRef.current.add(pageNum)
try {
const result = await fetchPage(pageNum)
if (isStaleDataLoad(generation)) return
const loadedPage = {
pageNum,
items: result.data || [],
hasMore: result.hasMore
}
pendingScrollAnchorRef.current = {
scrollTarget,
anchor: scrollAnchor,
loadedPage
}
setPages((prev) => {
if (
!prev.some(
(page) => page.pageNum === pageNum && page.isSkeletonPage
)
) {
return prev
}
const loadedPages = mergeLoadedPage(prev, loadedPage)
return createPageWindow(loadedPages, direction)
})
} catch (error) {
console.error(`Error loading kanban page ${pageNum}:`, error)
} finally {
loadingPagesRef.current.delete(pageNum)
}
},
[
captureScrollAnchor,
createPageWindow,
fetchPage,
isStaleDataLoad,
mergeLoadedPage
]
)
const loadNextPage = useCallback(
(scrollTarget) => {
const nextPage = pagesRef.current[pagesRef.current.length - 1]
if (nextPage?.isSkeletonPage) {
loadBoundaryPage(nextPage.pageNum, 'next', scrollTarget)
}
},
[loadBoundaryPage]
)
const loadPreviousPage = useCallback(
(scrollTarget) => {
const previousPage = pagesRef.current[0]
if (previousPage?.isSkeletonPage) {
loadBoundaryPage(previousPage.pageNum, 'previous', scrollTarget)
}
},
[loadBoundaryPage]
)
const loadInitialPage = useCallback(async ({ silent = false } = {}) => {
dataLoadGenerationRef.current += 1
loadingPagesRef.current.clear()
pendingScrollAnchorRef.current = null
const generation = dataLoadGenerationRef.current
const filter = {
...masterFilterRef.current,
...baseFilterRef.current,
[categoryProperty]: toCategoryFilterValue(categoryValue)
}
const sorter = sorterRef.current
if (!silent) {
pagesRef.current = []
setPages([])
setLoading(true)
const skeletonPage = createSkeletonPage(1)
setTablePages([skeletonPage])
} else {
setLoading(false)
}
try {
const firstResult = await fetchPage(1, { filter, sorter })
if (isStaleDataLoad(generation)) return
const loadedPages = [
{
pageNum: 1,
items: firstResult.data || [],
hasMore: firstResult.hasMore
}
]
if (!isStaleDataLoad(generation)) {
setTablePages(createPageWindow(loadedPages, 'next'))
}
} catch {
if (!silent && !isStaleDataLoad(generation)) {
setTablePages([])
}
} finally {
if (!silent && !isStaleDataLoad(generation)) {
setLoading(false)
}
}
}, [
categoryProperty,
categoryValue,
createPageWindow,
createSkeletonPage,
fetchPage,
isStaleDataLoad,
setTablePages
])
const reloadLoadedPages = useCallback(async () => {
const loadedPages = pagesRef.current.filter(
(page) => !page.isSkeletonPage
)
for (let i = 0; i < loadedPages.length; i++) {
await fetchData(loadedPages[i].pageNum)
}
}, [fetchData])
useImperativeHandle(ref, () => ({
reload: reloadLoadedPages
}))
useEffect(() => {
if (lastColumnQueryKeyRef.current === columnQueryKey) return
// First mount shows skeleton; later filter/sort updates load silently.
const silent = lastColumnQueryKeyRef.current != null
let cancelled = false
const runLoad = async () => {
await loadInitialPage({ silent })
if (!cancelled) {
lastColumnQueryKeyRef.current = columnQueryKey
}
}
runLoad()
return () => {
cancelled = true
}
}, [columnQueryKey, loadInitialPage])
const skeletonBoundaryIds = useMemo(() => {
const firstPage = pages[0]
const lastPage = pages[pages.length - 1]
return {
previous:
firstPage?.isSkeletonPage && firstPage.items.length > 0
? firstPage.items[firstPage.items.length - 1]._id
: null,
next:
lastPage?.isSkeletonPage && lastPage.items[0]
? lastPage.items[0]._id
: null
}
}, [pages])
useLayoutEffect(() => {
const container = containerRef.current
if (!container || !scrollElement) return
const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue
const boundary = entry.target.dataset.skeletonBoundary
if (boundary === 'next') {
loadNextPage(scrollElement)
} else if (boundary === 'previous') {
loadPreviousPage(scrollElement)
}
}
},
{
root: scrollElement,
rootMargin: `${SCROLL_THRESHOLD}px`,
threshold: 0
}
)
container
.querySelectorAll('[data-skeleton-boundary]')
.forEach((el) => observer.observe(el))
return () => observer.disconnect()
}, [
scrollElement,
skeletonBoundaryIds,
loadNextPage,
loadPreviousPage,
tableData
])
return (
<Flex vertical className='objectKanbanColumn'>
<div className='objectKanbanColumnCards' ref={containerRef}>
<Spin indicator={<LoadingOutlined />} spinning={loading}>
<Flex
vertical
gap='middle'
className='objectKanbanColumnCardsInner'
>
{tableData.map((record) => {
if (record?._id == undefined) return null
const skeletonBoundary =
record._id === skeletonBoundaryIds.next
? 'next'
: record._id === skeletonBoundaryIds.previous
? 'previous'
: undefined
return (
<div
key={record._id}
data-kanban-card-id={record._id}
data-skeleton-boundary={skeletonBoundary}
>
<ObjectCard
isSkeleton={record?.isSkeleton || false}
model={model}
modelProperties={modelProperties}
visibleColumns={visibleColumns}
record={record}
isEditing={isEditing}
rowActions={rowActions}
renderActions={renderActions}
cardStyle='bordered'
/>
</div>
)
})}
</Flex>
</Spin>
</div>
</Flex>
)
}
)
ObjectKanbanColumn.displayName = 'ObjectKanbanColumn'
ObjectKanbanColumn.propTypes = {
type: PropTypes.string.isRequired,
categoryProperty: PropTypes.string.isRequired,
categoryValue: PropTypes.any,
baseFilter: PropTypes.object,
masterFilter: PropTypes.object,
sorter: PropTypes.object,
pageSize: PropTypes.number,
model: PropTypes.object.isRequired,
modelProperties: PropTypes.array.isRequired,
visibleColumns: PropTypes.object,
isEditing: PropTypes.bool,
rowActions: PropTypes.array,
renderActions: PropTypes.func,
scrollElement: PropTypes.object
}
export default ObjectKanbanColumn

View File

@ -0,0 +1,63 @@
import { Flex, Skeleton } from 'antd'
import PropTypes from 'prop-types'
import ObjectProperty from './ObjectProperty'
import { getCategoryValueKey } from './viewModeUtils'
const ObjectKanbanHeader = ({
categoryValues,
categoryProperty,
categoryPropertyDef,
trackRef,
skeletonColumnCount = 0
}) => {
const showSkeleton = skeletonColumnCount > 0
return (
<div className='objectKanbanHeader'>
<div className='objectKanbanHeaderTrack' ref={trackRef}>
<Flex gap='middle' className='objectKanbanHeaderRow'>
{showSkeleton
? Array.from({ length: skeletonColumnCount }).map((_, index) => (
<div
key={`skeleton-header-${index}`}
className='objectKanbanHeaderCell'
>
<Skeleton.Input
active
size='small'
style={{ width: '60%', height: 24 }}
/>
</div>
))
: categoryValues.map((categoryValue) => {
const columnKey = getCategoryValueKey(categoryValue)
return (
<div key={columnKey} className='objectKanbanHeaderCell'>
{categoryPropertyDef ? (
<ObjectProperty
{...categoryPropertyDef}
value={categoryValue}
objectData={{ [categoryProperty]: categoryValue }}
name={categoryProperty}
/>
) : null}
</div>
)
})}
</Flex>
</div>
</div>
)
}
ObjectKanbanHeader.displayName = 'ObjectKanbanHeader'
ObjectKanbanHeader.propTypes = {
categoryValues: PropTypes.array.isRequired,
categoryProperty: PropTypes.string.isRequired,
categoryPropertyDef: PropTypes.object,
trackRef: PropTypes.object,
skeletonColumnCount: PropTypes.number
}
export default ObjectKanbanHeader

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ import { ApiServerContext } from '../context/ApiServerContext'
import { AuthContext } from '../context/AuthContext'
import {
getActiveFilterValues,
useTableState,
useTableStatePersistence
} from '../context/TableStateContext'
import KeyboardShortcut from './KeyboardShortcut.jsx'
@ -25,6 +26,7 @@ const ObjectTableNavigationButtons = ({
const [neighbors, setNeighbors] = useState({ next: null, previous: null })
const [loading, setLoading] = useState(false)
const { getObjectListFilter, getObjectListSorter } = useTableState()
const { getPersistedFilter, getPersistedSorter } = useTableStatePersistence({
scope: objectType,
useFilterInSession: true,
@ -34,6 +36,12 @@ const ObjectTableNavigationButtons = ({
const getObjectNeighborsRef = useRef(getObjectNeighbors)
getObjectNeighborsRef.current = getObjectNeighbors
const getObjectListFilterRef = useRef(getObjectListFilter)
getObjectListFilterRef.current = getObjectListFilter
const getObjectListSorterRef = useRef(getObjectListSorter)
getObjectListSorterRef.current = getObjectListSorter
const getPersistedFilterRef = useRef(getPersistedFilter)
getPersistedFilterRef.current = getPersistedFilter
@ -51,8 +59,21 @@ const ObjectTableNavigationButtons = ({
const loadNeighbors = async () => {
setLoading(true)
const filter = getActiveFilterValues(getPersistedFilterRef.current())
const sorter = getPersistedSorterRef.current()
// Prefer the effective list filter (view + user filters) when available
const listFilter = getActiveFilterValues(
getObjectListFilterRef.current(objectType)
)
const persistedFilter = getActiveFilterValues(
getPersistedFilterRef.current()
)
const filter =
Object.keys(listFilter).length > 0 ? listFilter : persistedFilter
const listSorter = getObjectListSorterRef.current(objectType)
const persistedSorter = getPersistedSorterRef.current()
const sorter =
listSorter?.field && listSorter?.order ? listSorter : persistedSorter
const result = await getObjectNeighborsRef.current(objectType, {
id: _id,
filter,

View File

@ -1,24 +1,188 @@
import PropTypes from 'prop-types'
import { Button, Divider, Flex } from 'antd'
import { useContext, useMemo, useState } from 'react'
import ObjectListViewContext from '../context/ObjectListViewContext'
import {
Button,
Divider,
Flex,
Modal,
Popover,
Radio,
Select,
Typography
} from 'antd'
import GridIcon from '../../Icons/GridIcon'
import ListIcon from '../../Icons/ListIcon'
import KanbanIcon from '../../Icons/KanbanIcon'
import SettingsIcon from '../../Icons/SettingsIcon'
import { getModelByName } from '../../../database/ObjectModels'
import { getViewModeType, normalizeViewMode } from './viewModeUtils'
const VIEW_MODE_OPTIONS = [
{ type: 'list', label: 'List' },
{ type: 'cards', label: 'Cards' },
{ type: 'kanban', label: 'Kanban' }
]
const { Text } = Typography
const ObjectTableViewButton = ({
viewMode,
setViewMode,
objectType,
viewMode: viewModeProp,
setViewMode: setViewModeProp,
showEndingDivider = false,
...buttonProps
}) => {
const content = (
<Button
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
onClick={() => setViewMode(viewMode === 'cards' ? 'list' : 'cards')}
title={
viewMode === 'cards' ? 'Switch to list view' : 'Switch to card view'
}
{...buttonProps}
/>
const objectListView = useContext(ObjectListViewContext)
const viewMode = viewModeProp ?? objectListView?.viewMode
const setViewMode = setViewModeProp ?? objectListView?.setViewMode
const [settingsOpen, setSettingsOpen] = useState(false)
const normalizedViewMode = normalizeViewMode(viewMode)
const model = getModelByName(objectType)
const stateProperties = useMemo(
() =>
model?.properties?.filter((property) => property.type === 'state') || [],
[model]
)
const hasKanbanOption = stateProperties.length > 0
const defaultCategoryProperty = stateProperties[0]?.name
const availableViewModes = useMemo(
() =>
VIEW_MODE_OPTIONS.filter(
(option) => option.type !== 'kanban' || hasKanbanOption
).map((option) => option.type),
[hasKanbanOption]
)
const handleTypeChange = (nextType) => {
if (nextType === 'kanban') {
setViewMode({
type: 'kanban',
settings: {
categoryProperty:
normalizedViewMode.type === 'kanban'
? normalizedViewMode.settings?.categoryProperty ||
defaultCategoryProperty
: defaultCategoryProperty
}
})
return
}
setViewMode({ type: nextType })
}
const handleCategoryPropertyChange = (categoryProperty) => {
setViewMode({
type: 'kanban',
settings: { categoryProperty }
})
}
const { onClick: buttonOnClick, ...restButtonProps } = buttonProps
const handleCycleView = (event) => {
buttonOnClick?.(event)
const currentType = getViewModeType(normalizedViewMode)
const currentIndex = availableViewModes.indexOf(currentType)
const nextIndex =
currentIndex === -1 ? 0 : (currentIndex + 1) % availableViewModes.length
handleTypeChange(availableViewModes[nextIndex])
}
const activeIcon =
normalizedViewMode.type === 'cards' ? (
<GridIcon />
) : normalizedViewMode.type === 'kanban' ? (
<KanbanIcon />
) : (
<ListIcon />
)
const content = (
<>
<Popover
content={
<Flex vertical>
<Radio.Group
value={getViewModeType(normalizedViewMode)}
onChange={(e) => handleTypeChange(e.target.value)}
>
<Flex vertical gap='middle' style={{ margin: '4px 8px' }}>
{VIEW_MODE_OPTIONS.filter(
(option) => option.type !== 'kanban' || hasKanbanOption
).map((option) => (
<Radio key={option.type} value={option.type}>
<Flex
gap='8px'
align='center'
style={{ marginLeft: '4px' }}
>
<Text>{option.label}</Text>
{option.type === 'kanban' && (
<Button
type='text'
size='small'
icon={
<SettingsIcon
style={{ fontSize: '14px', marginTop: '2.5px' }}
/>
}
disabled={normalizedViewMode.type !== 'kanban'}
onClick={(event) => {
event.preventDefault()
event.stopPropagation()
setSettingsOpen(true)
}}
/>
)}
</Flex>
</Radio>
))}
</Flex>
</Radio.Group>
</Flex>
}
placement='bottomLeft'
arrow={false}
trigger='hover'
>
<Button
icon={activeIcon}
title='View mode'
onClick={handleCycleView}
{...restButtonProps}
/>
</Popover>
<Modal
title='Kanban settings'
open={settingsOpen}
onCancel={() => setSettingsOpen(false)}
onOk={() => setSettingsOpen(false)}
destroyOnClose
>
<Select
style={{ width: '100%' }}
placeholder='Select category property'
value={
normalizedViewMode.settings?.categoryProperty ||
defaultCategoryProperty
}
onChange={handleCategoryPropertyChange}
options={stateProperties.map((property) => ({
value: property.name,
label: property.label || property.name
}))}
/>
</Modal>
</>
)
if (showEndingDivider) {
return (
<Flex gap='small' align='center'>
@ -31,8 +195,9 @@ const ObjectTableViewButton = ({
}
ObjectTableViewButton.propTypes = {
viewMode: PropTypes.oneOf(['list', 'cards']).isRequired,
setViewMode: PropTypes.func.isRequired,
objectType: PropTypes.string.isRequired,
viewMode: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
setViewMode: PropTypes.func,
showEndingDivider: PropTypes.bool
}

View File

@ -87,6 +87,7 @@ const PropertyChanges = ({ type, value }) => {
longId={false}
minimal={true}
showPreview={false}
showProgress={false}
objectData={value?.old}
maxWidth='200px'
/>
@ -102,6 +103,7 @@ const PropertyChanges = ({ type, value }) => {
longId={false}
minimal={true}
showPreview={false}
showProgress={false}
objectData={value?.new}
maxWidth='200px'
/>

View File

@ -8,21 +8,30 @@ const ScrollBox = ({
horizontalBottomPadding = 16,
verticalRightPadding = 16,
inner = false,
horizontal = false,
smallPadding = false,
className = '',
...rest
}) => {
const outerStyle = {
minHeight: '0',
minWidth: horizontal ? 0 : undefined,
'--scrollbox-vertical-right-padding': `${verticalRightPadding}px`,
'--scrollbox-horizontal-bottom-padding': `${horizontalBottomPadding}px`,
...(horizontal ? { width: '100%', height: 'auto' } : { height: '100%' })
}
const simpleBarStyle = {
...(horizontal ? { width: '100%', height: 'auto' } : { height: '100%' }),
...style
}
return (
<div
style={{
height: '100%',
minHeight: '0',
'--scrollbox-vertical-right-padding': `${verticalRightPadding}px`,
'--scrollbox-horizontal-bottom-padding': `${horizontalBottomPadding}px`
}}
className={`${inner ? 'scrollbox-inner' : ''} ${smallPadding ? 'scrollbox-inner-small-padding' : ''} ${className}`}
style={outerStyle}
className={`${inner ? 'scrollbox-inner' : ''} ${smallPadding ? 'scrollbox-inner-small-padding' : ''} ${horizontal ? 'scrollbox-horizontal' : ''} ${className}`}
>
<SimpleBar style={{ height: '100%', ...style }} {...rest}>
<SimpleBar style={simpleBarStyle} {...rest}>
{children}
</SimpleBar>
</div>
@ -35,6 +44,7 @@ ScrollBox.propTypes = {
horizontalBottomPadding: PropTypes.number,
verticalRightPadding: PropTypes.number,
inner: PropTypes.bool,
horizontal: PropTypes.bool,
smallPadding: PropTypes.bool,
className: PropTypes.string
}

View File

@ -0,0 +1,439 @@
import PropTypes from 'prop-types'
import {
createContext,
useCallback,
useContext,
useLayoutEffect,
useRef,
useState
} from 'react'
import { HolderOutlined } from '@ant-design/icons'
import classNames from 'classnames'
const SegmentedNavReorderContext = createContext(null)
const getEnabledOptions = (options, disabled) =>
options.filter((option) => !disabled && !option.disabled)
const getItemMetrics = (element) => {
if (!element) {
return null
}
return {
left: element.offsetLeft,
top: element.offsetTop,
width: element.offsetWidth,
height: element.offsetHeight
}
}
export const SegmentedNavDragHandle = ({ value }) => {
const reorder = useContext(SegmentedNavReorderContext)
const dragProps = reorder?.getDragHandleProps?.(value)
if (!dragProps) {
return null
}
return (
<span
className='segmented-nav-drag-handle overview-drag-handle'
draggable
onClick={(event) => event.stopPropagation()}
onMouseDown={(event) => event.stopPropagation()}
onDragStart={(event) => dragProps.onDragStart(event)}
onDragEnd={dragProps.onDragEnd}
>
<HolderOutlined />
</span>
)
}
SegmentedNavDragHandle.propTypes = {
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
}
const SegmentedNav = ({
value,
options = [],
onChange,
onReorder,
className,
disabled = false,
animated = true
}) => {
const groupRef = useRef(null)
const thumbRef = useRef(null)
const itemRefs = useRef(new Map())
const prevValueRef = useRef(value)
const transitionGenRef = useRef(0)
const finishGenRef = useRef(0)
const [isAnimating, setIsAnimating] = useState(false)
const [thumbRect, setThumbRect] = useState(null)
const [draggedValue, setDraggedValue] = useState(null)
const [dropTarget, setDropTarget] = useState(null)
const enabledOptions = getEnabledOptions(options, disabled)
const isReordering = Boolean(onReorder && draggedValue != null)
const setItemRef = useCallback((optionValue, element) => {
if (element) {
itemRefs.current.set(optionValue, element)
} else {
itemRefs.current.delete(optionValue)
}
}, [])
useLayoutEffect(() => {
if (prevValueRef.current === value) {
return undefined
}
const fromMetrics = getItemMetrics(
itemRefs.current.get(prevValueRef.current)
)
const toMetrics = getItemMetrics(itemRefs.current.get(value))
prevValueRef.current = value
if (!animated) {
setIsAnimating(false)
setThumbRect(null)
return undefined
}
if (!fromMetrics || !toMetrics) {
return undefined
}
const transitionGen = transitionGenRef.current + 1
transitionGenRef.current = transitionGen
setIsAnimating(true)
setThumbRect(fromMetrics)
let endFrame = 0
const startFrame = requestAnimationFrame(() => {
endFrame = requestAnimationFrame(() => {
if (transitionGenRef.current === transitionGen) {
finishGenRef.current = transitionGen
setThumbRect(toMetrics)
}
})
})
return () => {
cancelAnimationFrame(startFrame)
cancelAnimationFrame(endFrame)
}
}, [value, animated])
const finishAnimation = useCallback(() => {
setIsAnimating(false)
setThumbRect(null)
}, [])
const handleThumbTransitionEnd = useCallback(
(event) => {
if (event.target !== thumbRef.current) {
return
}
if (
(event.propertyName === 'transform' ||
event.propertyName === 'width') &&
finishGenRef.current === transitionGenRef.current
) {
finishAnimation()
}
},
[finishAnimation]
)
const clearDragState = useCallback(() => {
setDraggedValue(null)
setDropTarget(null)
}, [])
const handleDragStart = useCallback(
(event, optionValue) => {
if (!onReorder) return
event.stopPropagation()
const item = event.currentTarget.closest('.segmented-nav-item')
if (item) {
const rect = item.getBoundingClientRect()
event.dataTransfer.setDragImage(
item,
event.clientX - rect.left,
event.clientY - rect.top
)
}
setDraggedValue(optionValue)
event.dataTransfer.effectAllowed = 'move'
event.dataTransfer.setData('text/plain', String(optionValue))
},
[onReorder]
)
const handleDragEnd = useCallback(() => {
clearDragState()
}, [clearDragState])
const handleItemDragOver = useCallback(
(event, option) => {
if (!onReorder || draggedValue == null || !option.reorderable) {
return
}
if (String(option.value) === String(draggedValue)) {
return
}
event.preventDefault()
event.stopPropagation()
event.dataTransfer.dropEffect = 'move'
const rect = event.currentTarget.getBoundingClientRect()
setDropTarget({
value: option.value,
insertBefore: event.clientX < rect.left + rect.width / 2
})
},
[draggedValue, onReorder]
)
const handleItemDrop = useCallback(
(event, option) => {
if (!onReorder || draggedValue == null || !option.reorderable) {
return
}
event.preventDefault()
event.stopPropagation()
const target = dropTarget || {
value: option.value,
insertBefore: true
}
if (String(draggedValue) !== String(target.value)) {
onReorder(draggedValue, target.value, target.insertBefore)
}
clearDragState()
},
[clearDragState, draggedValue, dropTarget, onReorder]
)
const handleGroupDragOver = useCallback(
(event) => {
if (!onReorder || draggedValue == null) {
return
}
const reorderableOptions = options.filter((option) => option.reorderable)
if (reorderableOptions.length === 0) {
return
}
if (
event.target instanceof Element &&
event.target.closest('.segmented-nav-item')
) {
return
}
event.preventDefault()
event.dataTransfer.dropEffect = 'move'
const lastOption = reorderableOptions[reorderableOptions.length - 1]
setDropTarget({
value: lastOption.value,
insertBefore: false
})
},
[draggedValue, onReorder, options]
)
const handleGroupDrop = useCallback(
(event) => {
if (!onReorder || draggedValue == null) {
return
}
if (
event.target instanceof Element &&
event.target.closest('.segmented-nav-item')
) {
return
}
event.preventDefault()
event.stopPropagation()
const reorderableOptions = options.filter((option) => option.reorderable)
const lastOption = reorderableOptions[reorderableOptions.length - 1]
if (lastOption && String(draggedValue) !== String(lastOption.value)) {
onReorder(draggedValue, lastOption.value, false)
}
clearDragState()
},
[clearDragState, draggedValue, onReorder, options]
)
const getDragHandleProps = useCallback(
(optionValue) => {
const option = options.find(
(item) => String(item.value) === String(optionValue)
)
if (!onReorder || !option?.reorderable) {
return null
}
return {
onDragStart: (event) => handleDragStart(event, optionValue),
onDragEnd: handleDragEnd
}
},
[handleDragEnd, handleDragStart, onReorder, options]
)
const handleKeyDown = (event, optionIndex) => {
if (event.key !== 'ArrowRight' && event.key !== 'ArrowLeft') {
return
}
event.preventDefault()
const direction = event.key === 'ArrowRight' ? 1 : -1
const currentEnabledIndex = enabledOptions.findIndex(
(option) => option.value === value
)
const startIndex =
currentEnabledIndex === -1 ? optionIndex : currentEnabledIndex
const nextIndex =
(startIndex + direction + enabledOptions.length) % enabledOptions.length
onChange?.(enabledOptions[nextIndex].value)
}
const reorderContextValue = onReorder ? { getDragHandleProps } : null
return (
<SegmentedNavReorderContext.Provider value={reorderContextValue}>
<div
className={classNames('segmented-nav', className, {
'segmented-nav-disabled': disabled,
'segmented-nav-reordering': isReordering
})}
role='radiogroup'
>
<div
className={classNames('segmented-nav-group', {
'segmented-nav-animating': isAnimating
})}
ref={groupRef}
onDragOver={handleGroupDragOver}
onDrop={handleGroupDrop}
>
{isAnimating && thumbRect && (
<div
ref={thumbRef}
className='segmented-nav-thumb'
style={{
width: thumbRect.width,
height: thumbRect.height,
transform: `translate3d(${thumbRect.left}px, ${thumbRect.top}px, 0)`
}}
onTransitionEnd={handleThumbTransitionEnd}
aria-hidden='true'
/>
)}
{options.map((option, index) => {
const isSelected = value === option.value
const isDisabled = disabled || option.disabled
const isDragging =
draggedValue != null &&
String(draggedValue) === String(option.value)
const isDropTarget =
dropTarget != null &&
String(dropTarget.value) === String(option.value) &&
String(draggedValue) !== String(option.value)
return (
<button
key={option.value}
ref={(element) => setItemRef(option.value, element)}
type='button'
role='radio'
aria-checked={isSelected}
disabled={isDisabled}
tabIndex={isSelected ? 0 : -1}
className={classNames('segmented-nav-item', option.className, {
'segmented-nav-item-selected': isSelected,
'segmented-nav-item-disabled': isDisabled,
'segmented-nav-item-dragging': isDragging,
'segmented-nav-item-drop-before':
isDropTarget && dropTarget.insertBefore,
'segmented-nav-item-drop-after':
isDropTarget && !dropTarget.insertBefore,
'segmented-nav-item-icon': option.icon
})}
onClick={() => {
if (!isDisabled) {
onChange?.(option.value)
}
}}
onKeyDown={(event) => handleKeyDown(event, index)}
onDragOver={
option.reorderable
? (event) => handleItemDragOver(event, option)
: undefined
}
onDrop={
option.reorderable
? (event) => handleItemDrop(event, option)
: undefined
}
>
{option.icon && (
<span className='segmented-nav-item-icon-contents'>
{option.icon}
</span>
)}
{option.label && (
<span className='segmented-nav-item-label'>
{option.label}
</span>
)}
</button>
)
})}
</div>
</div>
</SegmentedNavReorderContext.Provider>
)
}
SegmentedNav.propTypes = {
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
options: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
label: PropTypes.node,
disabled: PropTypes.bool,
reorderable: PropTypes.bool,
className: PropTypes.string
})
),
onChange: PropTypes.func,
onReorder: PropTypes.func,
className: PropTypes.string,
disabled: PropTypes.bool,
animated: PropTypes.bool
}
export default SegmentedNav

View File

@ -34,20 +34,27 @@ const StateDisplay = ({
'syncing'
]
const circularLoadingTypes = ['loading', 'processing', 'initializing']
const currentState = state || {
type: 'unknown',
progress: 0
}
const [showMessageModal, setShowMessageModal] = useState(false)
const progressValue = Math.round(currentState.progress * 1000) / 10
const isCircularLoading = circularLoadingTypes.includes(currentState.type)
const showCircularLoading = isCircularLoading && showProgress
return (
<>
<Flex gap='small' align={'center'}>
{showState && (
<Space>
<StateTag state={currentState.type} />
<StateTag state={currentState.type} loading={showCircularLoading} />
</Space>
)}
{showProgress &&
loadingProgressTypes.includes(currentState.type) &&
currentState?.progress &&

View File

@ -1,8 +1,15 @@
import PropTypes from 'prop-types'
import { Badge, Flex, Tag } from 'antd'
import { useMemo } from 'react'
import LoadingIcon from '../../Icons/LoadingIcon'
const StateTag = ({ state, showBadge = true, showTag = true, style = {} }) => {
const StateTag = ({
state,
showBadge = true,
showTag = true,
loading = false,
style = {}
}) => {
const { badgeStatus, badgeText } = useMemo(() => {
let status = 'default'
let text = 'Unknown'
@ -216,10 +223,17 @@ const StateTag = ({ state, showBadge = true, showTag = true, style = {} }) => {
return <Badge {...badgeProps} style={{ ...style }} />
}
const showLoading = loading && showBadge
const padding = showLoading ? '0 6px' : '0 8px'
return (
<Tag color={badgeStatus} style={{ marginRight: 0, ...style }}>
<Tag color={badgeStatus} style={{ marginRight: 0, ...style, padding }}>
<Flex gap={6}>
{showBadge && <Badge {...badgeProps} />}
{showBadge && !loading && <Badge {...badgeProps} />}
{showLoading && (
<LoadingIcon style={{ fontSize: 8 }} className='state-tag-loading' />
)}
{badgeText}
</Flex>
</Tag>
@ -230,7 +244,8 @@ StateTag.propTypes = {
state: PropTypes.string,
showBadge: PropTypes.bool,
style: PropTypes.object,
showTag: PropTypes.bool
showTag: PropTypes.bool,
loading: PropTypes.bool
}
export default StateTag

View File

@ -0,0 +1,31 @@
export const DEFAULT_VIEW_MODE = { type: 'list' }
export const normalizeViewMode = (value) => {
if (!value) return DEFAULT_VIEW_MODE
if (typeof value === 'string') {
return {
type: value === 'cards' ? 'cards' : value === 'kanban' ? 'kanban' : 'list'
}
}
if (value?.type) return value
return DEFAULT_VIEW_MODE
}
export const isCardsView = (vm) => normalizeViewMode(vm).type === 'cards'
export const isKanbanView = (vm) => normalizeViewMode(vm).type === 'kanban'
export const getViewModeType = (vm) => normalizeViewMode(vm).type
export const toCategoryFilterValue = (value) => {
if (value && typeof value === 'object') {
return value.type ?? value._id ?? JSON.stringify(value)
}
return String(value)
}
export const getCategoryValueKey = (value) => {
if (value && typeof value === 'object' && value.type != null) {
return String(value.type)
}
if (value != null) return String(value)
return 'null'
}

View File

@ -2532,6 +2532,51 @@ const ApiServerProvider = ({ children }) => {
}
}
const fetchObjectViews = async (objectType) => {
if (!objectType) return { data: [] }
try {
const result = await fetchObjects('objectView', {
filter: { objectType },
limit: 100
})
return result || { data: [] }
} catch (err) {
console.error(err)
showError(err, () => fetchObjectViews(objectType))
return { data: [] }
}
}
const createObjectView = async (values) => {
try {
return await createObject('objectView', values)
} catch (err) {
console.error(err)
showError(err, () => createObjectView(values))
return null
}
}
const updateObjectView = async (objectViewId, updates) => {
try {
return await updateObject(objectViewId, 'objectView', updates)
} catch (err) {
console.error(err)
showError(err, () => updateObjectView(objectViewId, updates))
return null
}
}
const deleteObjectView = async (objectViewId) => {
try {
return await deleteObject(objectViewId, 'objectView')
} catch (err) {
console.error(err)
showError(err, () => deleteObjectView(objectViewId))
return null
}
}
const fetchNotificationsApi = useCallback(async () => {
const response = await axios.get(`${config.backendUrl}/notifications`, {
params: { limit: 100, sortProperty: 'createdAt', sortOrder: 'descend' },
@ -2848,6 +2893,10 @@ const ApiServerProvider = ({ children }) => {
fetchAllUserNotifiersForObject,
toggleUserNotifier,
editUserNotifier,
fetchObjectViews,
createObjectView,
updateObjectView,
deleteObjectView,
fetchNotificationsApi,
markNotificationAsReadApi,
markAllNotificationsAsReadApi,

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,9 @@ import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState
} from 'react'
import { useSearchParams } from 'react-router-dom'
@ -12,9 +14,13 @@ const TableStateContext = createContext()
export const FILTER_URL_PARAM = 'filter'
export const SORT_URL_PARAM = 'sort'
export const VIEW_URL_PARAM = 'viewId'
const getSessionFilterKey = (scope) => `tableState:${scope}:filter`
const getSessionSortKey = (scope) => `tableState:${scope}:sort`
const getSessionListFilterKey = (scope) => `tableState:${scope}:listFilter`
const getSessionListSortKey = (scope) => `tableState:${scope}:listSort`
const getSessionListViewKey = (scope) => `tableState:${scope}:listView`
// eslint-disable-next-line react-refresh/only-export-components
export const getActiveFilterValues = (filterState) => {
@ -44,6 +50,8 @@ const readSortFromUrl = (searchParams) => {
return { field: parsed.field, order: parsed.order }
}
const readViewFromUrl = (searchParams) => searchParams.get(VIEW_URL_PARAM) || null
const readFilterFromSession = (scope) => {
try {
return parseJsonParam(sessionStorage.getItem(getSessionFilterKey(scope)))
@ -86,6 +94,67 @@ const writeSortToSession = (scope, sorter) => {
}
}
const readListFilterFromSession = (scope) => {
try {
return parseJsonParam(sessionStorage.getItem(getSessionListFilterKey(scope)))
} catch {
return null
}
}
const readListSortFromSession = (scope) => {
try {
const parsed = parseJsonParam(sessionStorage.getItem(getSessionListSortKey(scope)))
if (!parsed?.field || !parsed?.order) return null
return { field: parsed.field, order: parsed.order }
} catch {
return null
}
}
const writeListFilterToSession = (scope, filter) => {
const key = getSessionListFilterKey(scope)
if (Object.keys(filter).length > 0) {
sessionStorage.setItem(key, JSON.stringify(filter))
} else {
sessionStorage.removeItem(key)
}
}
const writeListSortToSession = (scope, sorter) => {
const key = getSessionListSortKey(scope)
if (sorter?.field && sorter?.order) {
sessionStorage.setItem(key, JSON.stringify(sorter))
} else {
sessionStorage.removeItem(key)
}
}
const readListViewFromSession = (scope) => {
try {
const parsed = parseJsonParam(sessionStorage.getItem(getSessionListViewKey(scope)))
if (!parsed?.id) return null
return {
id: String(parsed.id),
name: parsed.name || null
}
} catch {
return null
}
}
const writeListViewToSession = (scope, view) => {
const key = getSessionListViewKey(scope)
if (view?.id) {
sessionStorage.setItem(
key,
JSON.stringify({ id: String(view.id), name: view.name || null })
)
} else {
sessionStorage.removeItem(key)
}
}
const normalizeSorter = (sorter) => {
if (sorter?.field && sorter?.order) {
return { field: sorter.field, order: sorter.order }
@ -97,6 +166,50 @@ export const TableStateProvider = ({ children }) => {
const [searchParams, setSearchParams] = useSearchParams()
const [pageFilters, setPageFilters] = useState({})
const [pageSorters, setPageSorters] = useState({})
// Effective list filters (view filter + user filters). Survives navigating
// from the list page to an info page so breadcrumb/neighbors keep working.
const [objectListFilters, setObjectListFilters] = useState({})
const [objectListSorters, setObjectListSorters] = useState({})
// Active list view (id + name) per object type — same survival pattern as filters.
const [objectListViews, setObjectListViews] = useState({})
const searchParamsRef = useRef(searchParams)
// Tracks an in-flight optimistic URL write so a render with stale router
// searchParams cannot clobber searchParamsRef and resurrect deleted params
// (e.g. viewId briefly cleared on All, then written back by persistTableState).
const pendingSearchParamsRef = useRef(null)
const previousSearchParamsRef = useRef(searchParams.toString())
useEffect(() => {
const current = searchParams.toString()
if (pendingSearchParamsRef.current != null) {
if (current === pendingSearchParamsRef.current) {
pendingSearchParamsRef.current = null
} else if (current === previousSearchParamsRef.current) {
// Stale snapshot from before our write — keep the optimistic ref.
return
} else {
// External navigation while a write was pending — accept it.
pendingSearchParamsRef.current = null
}
}
previousSearchParamsRef.current = current
searchParamsRef.current = searchParams
}, [searchParams])
// Keep sequential URL writes in the same tick from clobbering each other
// (e.g. persistView then persistTableState both calling setSearchParams).
const updateSearchParams = useCallback(
(mutator) => {
const previous = searchParamsRef.current.toString()
const next = new URLSearchParams(searchParamsRef.current)
mutator(next)
previousSearchParamsRef.current = previous
searchParamsRef.current = next
pendingSearchParamsRef.current = next.toString()
setSearchParams(next, { replace: true })
},
[setSearchParams]
)
const getPersistedFilter = useCallback(
(scope, { useFilterInUrl = false, useFilterInSession = false } = {}) => {
@ -128,6 +241,31 @@ export const TableStateProvider = ({ children }) => {
[searchParams]
)
const getViewFromUrl = useCallback(
() => readViewFromUrl(searchParams),
[searchParams]
)
const persistView = useCallback(
(viewId, { clearFilterSort = Boolean(viewId) } = {}) => {
updateSearchParams((next) => {
// Drop legacy `view` param that previously stored `_reference`
next.delete('view')
if (viewId) {
next.set(VIEW_URL_PARAM, String(viewId))
} else {
next.delete(VIEW_URL_PARAM)
}
// View filter/sort live on the view object — never in the URL
if (clearFilterSort) {
next.delete(FILTER_URL_PARAM)
next.delete(SORT_URL_PARAM)
}
})
},
[updateSearchParams]
)
const persistFilter = useCallback(
(
scope,
@ -139,16 +277,16 @@ export const TableStateProvider = ({ children }) => {
writeFilterToSession(scope, active)
}
if (saveFilterInUrl) {
const next = new URLSearchParams(searchParams)
if (Object.keys(active).length > 0) {
next.set(FILTER_URL_PARAM, JSON.stringify(active))
} else {
next.delete(FILTER_URL_PARAM)
}
setSearchParams(next, { replace: true })
updateSearchParams((next) => {
if (Object.keys(active).length > 0) {
next.set(FILTER_URL_PARAM, JSON.stringify(active))
} else {
next.delete(FILTER_URL_PARAM)
}
})
}
},
[searchParams, setSearchParams]
[updateSearchParams]
)
const persistSort = useCallback(
@ -162,16 +300,16 @@ export const TableStateProvider = ({ children }) => {
writeSortToSession(scope, nextSorter)
}
if (saveSortInUrl) {
const next = new URLSearchParams(searchParams)
if (nextSorter.field && nextSorter.order) {
next.set(SORT_URL_PARAM, JSON.stringify(nextSorter))
} else {
next.delete(SORT_URL_PARAM)
}
setSearchParams(next, { replace: true })
updateSearchParams((next) => {
if (nextSorter.field && nextSorter.order) {
next.set(SORT_URL_PARAM, JSON.stringify(nextSorter))
} else {
next.delete(SORT_URL_PARAM)
}
})
}
},
[searchParams, setSearchParams]
[updateSearchParams]
)
const persistTableState = useCallback(
@ -197,25 +335,25 @@ export const TableStateProvider = ({ children }) => {
}
if (saveFilterInUrl || saveSortInUrl) {
const next = new URLSearchParams(searchParams)
if (saveFilterInUrl) {
if (Object.keys(active).length > 0) {
next.set(FILTER_URL_PARAM, JSON.stringify(active))
} else {
next.delete(FILTER_URL_PARAM)
updateSearchParams((next) => {
if (saveFilterInUrl) {
if (Object.keys(active).length > 0) {
next.set(FILTER_URL_PARAM, JSON.stringify(active))
} else {
next.delete(FILTER_URL_PARAM)
}
}
}
if (saveSortInUrl) {
if (nextSorter.field && nextSorter.order) {
next.set(SORT_URL_PARAM, JSON.stringify(nextSorter))
} else {
next.delete(SORT_URL_PARAM)
if (saveSortInUrl) {
if (nextSorter.field && nextSorter.order) {
next.set(SORT_URL_PARAM, JSON.stringify(nextSorter))
} else {
next.delete(SORT_URL_PARAM)
}
}
}
setSearchParams(next, { replace: true })
})
}
},
[searchParams, setSearchParams]
[updateSearchParams]
)
const setPageFilter = useCallback((path, filter) => {
@ -245,39 +383,157 @@ export const TableStateProvider = ({ children }) => {
})
}, [])
const setObjectListFilter = useCallback((objectType, filter) => {
if (!objectType) return
const active = getActiveFilterValues(filter)
writeListFilterToSession(objectType, active)
setObjectListFilters((prev) => {
const hasActive = Object.keys(active).length > 0
if (!hasActive) {
if (!(objectType in prev)) return prev
const next = { ...prev }
delete next[objectType]
return next
}
return { ...prev, [objectType]: active }
})
}, [])
const setObjectListSorter = useCallback((objectType, sorter) => {
if (!objectType) return
const nextSorter = normalizeSorter(sorter)
writeListSortToSession(objectType, nextSorter)
setObjectListSorters((prev) => {
const hasSort = nextSorter?.field && nextSorter?.order
if (!hasSort) {
if (!(objectType in prev)) return prev
const next = { ...prev }
delete next[objectType]
return next
}
return { ...prev, [objectType]: nextSorter }
})
}, [])
const setObjectListView = useCallback((objectType, view) => {
if (!objectType) return
const nextView =
view?.id != null
? { id: String(view.id), name: view.name || null }
: null
writeListViewToSession(objectType, nextView)
setObjectListViews((prev) => {
if (!nextView) {
if (!(objectType in prev)) return prev
const next = { ...prev }
delete next[objectType]
return next
}
const existing = prev[objectType]
if (
existing &&
existing.id === nextView.id &&
existing.name === nextView.name
) {
return prev
}
return { ...prev, [objectType]: nextView }
})
}, [])
const getObjectListFilter = useCallback(
(objectType) => {
if (!objectType) return {}
if (objectListFilters[objectType]) {
return objectListFilters[objectType]
}
return getActiveFilterValues(readListFilterFromSession(objectType) || {})
},
[objectListFilters]
)
const getObjectListSorter = useCallback(
(objectType) => {
if (!objectType) return {}
if (objectListSorters[objectType]) {
return objectListSorters[objectType]
}
return readListSortFromSession(objectType) || {}
},
[objectListSorters]
)
const getObjectListView = useCallback(
(objectType) => {
if (!objectType) return null
if (objectListViews[objectType]) {
return objectListViews[objectType]
}
return readListViewFromSession(objectType)
},
[objectListViews]
)
const hasPageFilter = useCallback(
(path) => Object.keys(pageFilters[path] || {}).length > 0,
[pageFilters]
)
const hasStoredFilter = useCallback((scope) => hasStoredSessionFilter(scope), [])
const hasStoredFilter = useCallback((scope) => {
if (!scope) return false
if (hasStoredSessionFilter(scope)) return true
if (Object.keys(objectListFilters[scope] || {}).length > 0) return true
return Object.keys(getActiveFilterValues(readListFilterFromSession(scope) || {})).length > 0
}, [objectListFilters])
const value = useMemo(
() => ({
getPersistedFilter,
getPersistedSorter,
getViewFromUrl,
persistView,
persistFilter,
persistSort,
persistTableState,
setPageFilter,
setPageSorter,
setObjectListFilter,
setObjectListSorter,
setObjectListView,
getObjectListFilter,
getObjectListSorter,
getObjectListView,
hasPageFilter,
hasStoredFilter,
pageFilters,
pageSorters
pageSorters,
objectListFilters,
objectListSorters,
objectListViews
}),
[
getPersistedFilter,
getPersistedSorter,
getViewFromUrl,
persistView,
persistFilter,
persistSort,
persistTableState,
setPageFilter,
setPageSorter,
setObjectListFilter,
setObjectListSorter,
setObjectListView,
getObjectListFilter,
getObjectListSorter,
getObjectListView,
hasPageFilter,
hasStoredFilter,
pageFilters,
pageSorters
pageSorters,
objectListFilters,
objectListSorters,
objectListViews
]
)
@ -300,6 +556,7 @@ export const useTableState = () => {
// eslint-disable-next-line react-refresh/only-export-components
export const useTableStatePersistence = ({
scope,
viewId = null,
pagePath,
saveFilterInSession = false,
saveFilterInUrl = false,
@ -317,15 +574,58 @@ export const useTableStatePersistence = ({
persistSort: persistSortCtx,
persistTableState: persistTableStateCtx,
setPageFilter,
setPageSorter
setPageSorter,
setObjectListFilter,
setObjectListSorter
} = useTableState()
// View filter/sort belong to the view object — never read/write them via
// the shared filter/sort URL or session keys used by the "All" tab.
const isViewScope = Boolean(viewId)
const readOptions = useMemo(
() => ({ useFilterInUrl, useFilterInSession, useSortInUrl, useSortInSession }),
[useFilterInUrl, useFilterInSession, useSortInUrl, useSortInSession]
() => ({
useFilterInUrl: isViewScope ? false : useFilterInUrl,
useFilterInSession: isViewScope ? false : useFilterInSession,
useSortInUrl: isViewScope ? false : useSortInUrl,
useSortInSession: isViewScope ? false : useSortInSession
}),
[
isViewScope,
useFilterInUrl,
useFilterInSession,
useSortInUrl,
useSortInSession
]
)
const saveOptions = useMemo(
() => ({
saveFilterInSession: isViewScope ? false : saveFilterInSession,
saveFilterInUrl: isViewScope ? false : saveFilterInUrl,
saveSortInSession: isViewScope ? false : saveSortInSession,
saveSortInUrl: isViewScope ? false : saveSortInUrl
}),
[
isViewScope,
saveFilterInSession,
saveFilterInUrl,
saveSortInSession,
saveSortInUrl
]
)
const allTabReadOptions = useMemo(
() => ({
useFilterInUrl,
useFilterInSession,
useSortInUrl,
useSortInSession
}),
[useFilterInUrl, useFilterInSession, useSortInUrl, useSortInSession]
)
const allTabSaveOptions = useMemo(
() => ({
saveFilterInSession,
saveFilterInUrl,
@ -345,6 +645,18 @@ export const useTableStatePersistence = ({
[getPersistedSorterCtx, scope, readOptions]
)
// Always reads the All-tab filter/sort, even while a view is still selected
// (used when switching back to All before listViewId has cleared).
const getAllTabPersistedFilter = useCallback(
() => getPersistedFilterCtx(scope, allTabReadOptions),
[getPersistedFilterCtx, scope, allTabReadOptions]
)
const getAllTabPersistedSorter = useCallback(
() => getPersistedSorterCtx(scope, allTabReadOptions),
[getPersistedSorterCtx, scope, allTabReadOptions]
)
const persistFilter = useCallback(
(filterState) => persistFilterCtx(scope, filterState, saveOptions),
[persistFilterCtx, scope, saveOptions]
@ -361,6 +673,12 @@ export const useTableStatePersistence = ({
[persistTableStateCtx, scope, saveOptions]
)
const persistAllTabTableState = useCallback(
(filterState, sorter) =>
persistTableStateCtx(scope, filterState, sorter, allTabSaveOptions),
[persistTableStateCtx, scope, allTabSaveOptions]
)
const registerPageFilter = useCallback(
(filterState) => {
if (pagePath) {
@ -379,14 +697,38 @@ export const useTableStatePersistence = ({
[pagePath, setPageSorter]
)
const registerObjectListFilter = useCallback(
(filterState) => {
if (scope) {
setObjectListFilter(scope, filterState)
}
},
[scope, setObjectListFilter]
)
const registerObjectListSorter = useCallback(
(sorter) => {
if (scope) {
setObjectListSorter(scope, sorter)
}
},
[scope, setObjectListSorter]
)
return {
persistenceScope: scope,
getPersistedFilter,
getPersistedSorter,
getAllTabPersistedFilter,
getAllTabPersistedSorter,
persistFilter,
persistSort,
persistTableState,
persistAllTabTableState,
registerPageFilter,
registerPageSorter,
registerObjectListFilter,
registerObjectListSorter,
getActiveFilterValues
}
}

View File

@ -171,11 +171,27 @@ export const ThemeProvider = ({ children }) => {
'--layout-modal-bg',
isDarkMode ? '#1f1f1f' : '#ffffff'
)
root.style.setProperty(
'--color-background',
isDarkMode ? '#000000' : '#ffffff'
)
root.style.setProperty('--color-text', isDarkMode ? '#ffffff' : '#000000')
root.style.setProperty(
'--color-descriptions-border',
isDarkMode ? 'rgba(253,253,253,0.12)' : 'rgba(5,5,5,0.06)'
)
root.style.setProperty(
'--color-list-view-tabs-border',
isDarkMode ? '#1f1f1f' : '#E7E7E7'
)
root.style.setProperty(
'--color-button-background',
isDarkMode ? '#141414' : '#FFFFFF'
)
root.style.setProperty(
'--color-button-border',
isDarkMode ? '#424242' : '#d9d9d9'
)
}, [isDarkMode, primaryColorOverride])
const themeConfig = {

View File

@ -1,14 +1,21 @@
import { useCallback, useContext } from 'react'
import { ApiServerContext } from '../context/ApiServerContext'
import {
DEFAULT_VIEW_MODE,
normalizeViewMode
} from '../common/viewModeUtils'
const useViewMode = (componentName, defaultMode = 'list') => {
const useViewMode = (componentName, defaultMode = DEFAULT_VIEW_MODE) => {
const { userSettings, updateUserSettings } = useContext(ApiServerContext)
const viewMode = userSettings.viewMode[componentName] ?? defaultMode
const viewMode = normalizeViewMode(
userSettings.viewMode[componentName] ?? defaultMode
)
const setViewMode = useCallback(
(nextMode) => {
const mode =
const mode = normalizeViewMode(
typeof nextMode === 'function' ? nextMode(viewMode) : nextMode
)
updateUserSettings('viewMode', componentName, mode)
},
[componentName, updateUserSettings, viewMode]

View File

@ -0,0 +1,6 @@
import Icon from '@ant-design/icons'
import CustomIconSvg from '../../../assets/icons/kanbanicon.svg?react'
const KanbanIcon = (props) => <Icon component={CustomIconSvg} {...props} />
export default KanbanIcon

View File

@ -0,0 +1,6 @@
import Icon from '@ant-design/icons'
import CustomIconSvg from '../../../assets/icons/loadingicon.svg?react'
const LoadingIcon = (props) => <Icon component={CustomIconSvg} {...props} />
export default LoadingIcon

View File

@ -194,7 +194,7 @@ export function getModelByName(name, ignoreCase = false) {
export function getModelByPluralName(pluralName, ignoreCase = false) {
function formatName(formattedName) {
if (ignoreCase == true) {
formattedName = formattedName.toUpperCase().replace(' ', '')
formattedName = formattedName.toUpperCase().replaceAll(' ', '')
}
return formattedName
}

View File

@ -110,7 +110,7 @@ export const DocumentPrinter = {
],
sorters: [
'name',
'documentSize',
'currentDocumentSize',
'connectedAt',
'connection.port',
'updatedAt',
@ -261,15 +261,47 @@ export const DocumentPrinter = {
},
columnWidth: 85
},
{
name: 'connection.username',
label: 'Username',
type: 'text',
required: false,
visible: (objectData) => objectData?.connection?.interface === 'cups',
columnWidth: 150
},
{
name: 'connection.password',
label: 'Password',
type: 'secret',
required: false,
visible: (objectData) => objectData?.connection?.interface === 'cups',
columnWidth: 150
},
{
name: 'currentDocumentSize',
label: 'Document Size',
label: 'Current Document Size',
required: false,
type: 'object',
objectType: 'documentSize',
showHyperlink: true,
columnWidth: 200
},
{
name: 'rotateOrientation',
label: 'Rotate Orientation',
required: false,
type: 'bool',
columnWidth: 150
},
{
name: 'supportedDocumentSizes',
label: 'Supported Document Sizes',
required: false,
type: 'objectList',
objectType: 'documentSize',
showHyperlink: true,
columnWidth: 260
},
{
name: 'tags',
label: 'Tags',