Compare commits

...

2 Commits

Author SHA1 Message Date
8e393e229f Implemented market places.
Some checks failed
farmcontrol/farmcontrol-ui/pipeline/head There was a failure building this commit
2026-03-13 23:32:23 +00:00
120fb89837 Updated vendor icon. 2026-03-13 22:56:24 +00:00
13 changed files with 726 additions and 10 deletions

View File

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

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

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

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

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

View File

@ -0,0 +1,200 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex } from 'antd'
import loglevel from 'loglevel'
import config from '../../../../config'
import useCollapseState from '../../hooks/useCollapseState'
import NotesPanel from '../../common/NotesPanel'
import InfoCollapse from '../../common/InfoCollapse'
import ObjectInfo from '../../common/ObjectInfo'
import ViewButton from '../../common/ViewButton'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
import NoteIcon from '../../../Icons/NoteIcon.jsx'
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
import ObjectForm from '../../common/ObjectForm'
import EditButtons from '../../common/EditButtons'
import LockIndicator from '../../common/LockIndicator.jsx'
import ActionHandler from '../../common/ActionHandler.jsx'
import ObjectActions from '../../common/ObjectActions.jsx'
import ObjectTable from '../../common/ObjectTable.jsx'
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
import DocumentPrintButton from '../../common/DocumentPrintButton.jsx'
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
import ScrollBox from '../../common/ScrollBox.jsx'
import { Card } from 'antd'
const log = loglevel.getLogger('MarketplaceInfo')
log.setLevel(config.logLevel)
const MarketplaceInfo = () => {
const location = useLocation()
const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const marketplaceId = new URLSearchParams(location.search).get('marketplaceId')
const [collapseState, updateCollapseState] = useCollapseState('MarketplaceInfo', {
info: true,
notes: true,
auditLogs: true
})
const [objectFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
lock: null,
loading: false,
objectData: {}
})
const actions = {
reload: () => {
objectFormRef?.current?.handleFetchObject?.()
return true
},
edit: () => {
objectFormRef?.current?.startEditing?.()
return false
},
cancelEdit: () => {
objectFormRef?.current?.cancelEditing?.()
return true
},
finishEdit: () => {
objectFormRef?.current?.handleUpdate?.()
return true
},
delete: () => {
objectFormRef?.current?.handleDelete?.()
return true
}
}
return (
<>
<Flex
gap='large'
vertical='true'
style={{ maxHeight: '100%', minHeight: 0 }}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='marketplace'
id={marketplaceId}
disabled={objectFormState.loading}
objectData={objectFormState.objectData}
/>
<ViewButton
disabled={objectFormState.loading}
items={[
{ key: 'info', label: 'Marketplace Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
<UserNotifierToggle
type='marketplace'
objectData={objectFormState.objectData}
disabled={objectFormState.loading}
/>
<DocumentPrintButton
type='marketplace'
objectData={objectFormState.objectData}
disabled={objectFormState.loading}
/>
</Space>
<LockIndicator lock={objectFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={objectFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={objectFormState.editLoading}
formValid={objectFormState.formValid}
disabled={objectFormState.lock?.locked || objectFormState.loading}
loading={objectFormState.editLoading}
/>
</Space>
</Flex>
<ScrollBox>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={objectFormState.loading}
ref={actionHandlerRef}
>
<InfoCollapse
title='Marketplace Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<ObjectForm
id={marketplaceId}
type='marketplace'
style={{ height: '100%' }}
ref={objectFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='marketplace'
objectData={objectData}
/>
)}
</ObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={marketplaceId} type='marketplace' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{objectFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': marketplaceId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</ScrollBox>
</Flex>
</>
)
}
export default MarketplaceInfo

View File

@ -0,0 +1,103 @@
import PropTypes from 'prop-types'
import ObjectInfo from '../../common/ObjectInfo'
import NewObjectForm from '../../common/NewObjectForm'
import WizardView from '../../common/WizardView'
const NewMarketplace = ({ onOk, defaultValues }) => {
return (
<NewObjectForm
type={'marketplace'}
defaultValues={{ active: true, ...defaultValues }}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [
{
title: 'Required',
key: 'required',
content: (
<ObjectInfo
type='marketplace'
column={1}
bordered={false}
isEditing={true}
required={true}
objectData={objectData}
/>
)
},
{
title: 'API Configuration',
key: 'config',
content: (
<ObjectInfo
type='marketplace'
column={1}
bordered={false}
isEditing={true}
required={false}
objectData={objectData}
visibleProperties={{
_id: false,
_reference: false,
createdAt: false,
updatedAt: false,
name: false,
provider: false,
active: false
}}
/>
)
},
{
title: 'Summary',
key: 'summary',
content: (
<ObjectInfo
type='marketplace'
column={1}
bordered={false}
visibleProperties={{
_id: false,
createdAt: false,
updatedAt: false,
'config.appId': false,
'config.certId': false,
'config.devId': false,
'config.userToken': false,
'config.accessToken': false,
'config.refreshToken': false,
'config.appKey': false,
'config.appSecret': false
}}
isEditing={false}
objectData={objectData}
/>
)
}
]
return (
<WizardView
steps={steps}
loading={submitLoading}
formValid={formValid}
title='New Marketplace'
onSubmit={async () => {
const result = await handleSubmit()
if (result) {
onOk()
}
}}
/>
)
}}
</NewObjectForm>
)
}
NewMarketplace.propTypes = {
onOk: PropTypes.func.isRequired,
reset: PropTypes.bool,
defaultValues: PropTypes.object
}
export default NewMarketplace

View File

@ -2,8 +2,8 @@ import { useLocation } from 'react-router-dom'
import DashboardSidebar from '../common/DashboardSidebar'
import ClientIcon from '../../Icons/ClientIcon'
import SalesIcon from '../../Icons/SalesIcon'
import SalesOrderIcon from '../../Icons/SalesOrderIcon'
import MarketplaceIcon from '../../Icons/MarketplaceIcon'
const items = [
{
@ -24,13 +24,20 @@ const items = [
label: 'Sales Orders',
icon: <SalesOrderIcon />,
path: '/dashboard/sales/salesorders'
},
{
key: 'marketplaces',
label: 'Marketplaces',
icon: <MarketplaceIcon />,
path: '/dashboard/sales/marketplaces'
}
]
const routeKeyMap = {
'/dashboard/sales/overview': 'overview',
'/dashboard/sales/clients': 'clients',
'/dashboard/sales/salesorders': 'salesorders'
'/dashboard/sales/salesorders': 'salesorders',
'/dashboard/sales/marketplaces': 'marketplaces'
}
const SalesSidebar = (props) => {

View File

@ -63,6 +63,11 @@ const ObjectInfo = ({
items = items.filter((item) => {
const propertyName = item.name
// Support property.visible as a function (objectData) => boolean
if (typeof item.visible === 'function') {
const visible = item.visible(objectData || combinedObjectData || {})
if (!visible) return false
}
if (isWhitelistMode) {
// Whitelist mode: only show properties that are explicitly set to true
return visibleProperties[propertyName] === true

View File

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

View File

@ -39,6 +39,7 @@ import { Invoice } from './models/Invoice.js'
import { Payment } from './models/Payment.js'
import { Client } from './models/Client.js'
import { SalesOrder } from './models/SalesOrder.js'
import { Marketplace } from './models/Marketplace.js'
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
export const objectModels = [
@ -82,7 +83,8 @@ export const objectModels = [
Invoice,
Payment,
Client,
SalesOrder
SalesOrder,
Marketplace
]
// Re-export individual models for direct access
@ -127,7 +129,8 @@ export {
Invoice,
Payment,
Client,
SalesOrder
SalesOrder,
Marketplace
}
export function getModelByName(name, ignoreCase = false) {

View File

@ -84,6 +84,7 @@ export const Client = {
'email',
'phone',
'active',
'marketplace',
'createdAt',
'updatedAt'
],
@ -186,6 +187,16 @@ export const Client = {
readOnly: false,
required: false,
columnWidth: 250
},
{
name: 'marketplace',
label: 'Marketplace',
type: 'object',
objectType: 'marketplace',
showHyperlink: true,
readOnly: false,
required: false,
columnWidth: 200
}
]
}

View File

@ -0,0 +1,244 @@
import MarketplaceIcon from '../../components/Icons/MarketplaceIcon'
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
import EditIcon from '../../components/Icons/EditIcon'
import CheckIcon from '../../components/Icons/CheckIcon'
import XMarkIcon from '../../components/Icons/XMarkIcon'
import ReloadIcon from '../../components/Icons/ReloadIcon'
import BinIcon from '../../components/Icons/BinIcon'
export const Marketplace = {
name: 'marketplace',
label: 'Marketplace',
prefix: 'MKT',
icon: MarketplaceIcon,
actions: [
{
name: 'info',
label: 'Info',
default: true,
row: true,
icon: InfoCircleIcon,
url: (_id) => `/dashboard/sales/marketplaces/info?marketplaceId=${_id}`
},
{
name: 'reload',
label: 'Reload',
icon: ReloadIcon,
url: (_id) =>
`/dashboard/sales/marketplaces/info?marketplaceId=${_id}&action=reload`
},
{
name: 'edit',
label: 'Edit',
row: true,
icon: EditIcon,
url: (_id) =>
`/dashboard/sales/marketplaces/info?marketplaceId=${_id}&action=edit`,
visible: (objectData) => {
return !(objectData?._isEditing && objectData?._isEditing == true)
}
},
{
name: 'finishEdit',
label: 'Save Edits',
icon: CheckIcon,
url: (_id) =>
`/dashboard/sales/marketplaces/info?marketplaceId=${_id}&action=finishEdit`,
visible: (objectData) => {
return objectData?._isEditing && objectData?._isEditing == true
}
},
{
name: 'cancelEdit',
label: 'Cancel Edits',
icon: XMarkIcon,
url: (_id) =>
`/dashboard/sales/marketplaces/info?marketplaceId=${_id}&action=cancelEdit`,
visible: (objectData) => {
return objectData?._isEditing && objectData?._isEditing == true
}
},
{ type: 'divider' },
{
name: 'delete',
label: 'Delete',
icon: BinIcon,
danger: true,
url: (_id) =>
`/dashboard/sales/marketplaces/info?marketplaceId=${_id}&action=delete`
}
],
columns: [
'_reference',
'name',
'provider',
'active',
'createdAt',
'updatedAt'
],
filters: ['name', '_id', 'provider', 'active', 'createdAt', 'updatedAt'],
sorters: ['name', 'provider', 'active', 'createdAt', 'updatedAt', '_id'],
group: ['provider'],
properties: [
{
name: '_id',
label: 'ID',
columnFixed: 'left',
type: 'id',
objectType: 'marketplace',
showCopy: true,
columnWidth: 140
},
{
name: 'createdAt',
label: 'Created At',
type: 'dateTime',
readOnly: true,
columnWidth: 175
},
{
name: '_reference',
label: 'Reference',
type: 'reference',
columnFixed: 'left',
objectType: 'marketplace',
showCopy: true,
readOnly: true
},
{
name: 'updatedAt',
label: 'Updated At',
type: 'dateTime',
readOnly: true,
columnWidth: 175
},
{
name: 'name',
label: 'Name',
columnFixed: 'left',
required: true,
type: 'text',
columnWidth: 200
},
{
name: 'active',
label: 'Active',
type: 'bool',
readOnly: false,
required: true,
columnWidth: 125
},
{
name: 'provider',
label: 'Provider',
type: 'select',
required: true,
options: [
{ value: 'ebay', label: 'eBay' },
{ value: 'etsy', label: 'Etsy' },
{ value: 'tiktokShop', label: 'TikTok Shop' }
],
columnWidth: 150
},
{
name: 'config.appId',
label: 'App ID',
type: 'secret',
readOnly: false,
required: false,
columnWidth: 200,
visible: (objectData) => objectData?.provider === 'ebay'
},
{
name: 'config.certId',
label: 'Cert ID',
type: 'secret',
readOnly: false,
required: false,
columnWidth: 200,
visible: (objectData) => objectData?.provider === 'ebay'
},
{
name: 'config.devId',
label: 'Dev ID',
type: 'secret',
readOnly: false,
required: false,
columnWidth: 200,
visible: (objectData) => objectData?.provider === 'ebay'
},
{
name: 'config.userToken',
label: 'User Token',
type: 'secret',
readOnly: false,
required: false,
columnWidth: 200,
visible: (objectData) => objectData?.provider === 'ebay'
},
{
name: 'config.siteId',
label: 'Site ID',
type: 'text',
readOnly: false,
required: false,
columnWidth: 120,
visible: (objectData) => objectData?.provider === 'ebay'
},
{
name: 'config.accessToken',
label: 'Access Token',
type: 'secret',
readOnly: false,
required: false,
columnWidth: 200,
visible: (objectData) => objectData?.provider === 'etsy'
},
{
name: 'config.refreshToken',
label: 'Refresh Token',
type: 'secret',
readOnly: false,
required: false,
columnWidth: 200,
visible: (objectData) => objectData?.provider === 'etsy'
},
{
name: 'config.shopId',
label: 'Shop ID',
type: 'text',
readOnly: false,
required: false,
columnWidth: 200,
visible: (objectData) => objectData?.provider === 'etsy'
},
{
name: 'config.appKey',
label: 'App Key',
type: 'secret',
readOnly: false,
required: false,
columnWidth: 200,
visible: (objectData) => objectData?.provider === 'tiktokShop'
},
{
name: 'config.appSecret',
label: 'App Secret',
type: 'secret',
readOnly: false,
required: false,
columnWidth: 200,
visible: (objectData) => objectData?.provider === 'tiktokShop'
},
{
name: 'config.shopCipher',
label: 'Shop Cipher',
type: 'text',
readOnly: false,
required: false,
columnWidth: 200,
visible: (objectData) => objectData?.provider === 'tiktokShop'
}
]
}

View File

@ -170,13 +170,14 @@ export const SalesOrder = {
}
}
],
group: ['client'],
filters: ['client'],
group: ['client', 'marketplace'],
filters: ['client', 'marketplace'],
sorters: ['createdAt', 'state', 'updatedAt'],
columns: [
'_reference',
'state',
'client',
'marketplace',
'totalAmount',
'totalAmountWithTax',
'totalTaxAmount',
@ -245,6 +246,16 @@ export const SalesOrder = {
showHyperlink: true,
columnWidth: 200
},
{
name: 'marketplace',
label: 'Marketplace',
type: 'object',
objectType: 'marketplace',
showHyperlink: true,
readOnly: false,
required: false,
columnWidth: 200
},
{
name: 'confirmedAt',
label: 'Confirmed At',

View File

@ -16,6 +16,12 @@ const SalesOrderInfo = lazy(
const SalesOverview = lazy(
() => import('../components/Dashboard/Sales/SalesOverview.jsx')
)
const Marketplaces = lazy(
() => import('../components/Dashboard/Sales/Marketplaces.jsx')
)
const MarketplaceInfo = lazy(
() => import('../components/Dashboard/Sales/Marketplaces/MarketplaceInfo.jsx')
)
const SalesRoutes = [
<Route
@ -34,6 +40,12 @@ const SalesRoutes = [
key='salesorders-info'
path='sales/salesorders/info'
element={<SalesOrderInfo />}
/>,
<Route key='marketplaces' path='sales/marketplaces' element={<Marketplaces />} />,
<Route
key='marketplaces-info'
path='sales/marketplaces/info'
element={<MarketplaceInfo />}
/>
]