Compare commits
30 Commits
9fd2b8ffa6
...
97d5bfcee7
| Author | SHA1 | Date | |
|---|---|---|---|
| 97d5bfcee7 | |||
| ee90e75133 | |||
| 2eccf6736f | |||
| 963f36194e | |||
| a18831e67a | |||
| 8e823603f7 | |||
| 11c76a0725 | |||
| 9a406bb7df | |||
| b4763e01af | |||
| dcf90469f4 | |||
| e423f32493 | |||
| 08dbbefada | |||
| 27e843e183 | |||
| df1115022a | |||
| b74a4bb174 | |||
| b45df50da5 | |||
| 653e4e6ab2 | |||
| 177b439c48 | |||
| 678d5a0e90 | |||
| 4201f2b4a3 | |||
| a9a2801e27 | |||
| a6a52fa81b | |||
| 93848602bb | |||
| e4ab4ea9c7 | |||
| e904cc10b4 | |||
| aa41ad0192 | |||
| 257cebf15c | |||
| ed322436e6 | |||
| ec2d656b6e | |||
| c73b6fb33d |
29714
package-lock.json
generated
Normal file
16
package.json
@ -10,9 +10,25 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/charts": "^2.6.2",
|
"@ant-design/charts": "^2.6.2",
|
||||||
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
|
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
|
||||||
|
"@codemirror/lang-cpp": "^6.0.3",
|
||||||
|
"@codemirror/lang-css": "^6.3.1",
|
||||||
|
"@codemirror/lang-go": "^6.0.1",
|
||||||
|
"@codemirror/lang-html": "^6.4.9",
|
||||||
|
"@codemirror/lang-java": "^6.0.2",
|
||||||
|
"@codemirror/lang-javascript": "^6.2.4",
|
||||||
|
"@codemirror/lang-json": "^6.0.2",
|
||||||
|
"@codemirror/lang-markdown": "^6.3.4",
|
||||||
|
"@codemirror/lang-php": "^6.0.2",
|
||||||
|
"@codemirror/lang-python": "^6.2.1",
|
||||||
|
"@codemirror/lang-rust": "^6.0.2",
|
||||||
|
"@codemirror/lang-sql": "^6.9.1",
|
||||||
|
"@codemirror/lang-xml": "^6.1.0",
|
||||||
|
"@codemirror/lang-yaml": "^6.1.2",
|
||||||
|
"@codemirror/theme-one-dark": "^6.1.3",
|
||||||
"@simplewebauthn/browser": "^13.1.2",
|
"@simplewebauthn/browser": "^13.1.2",
|
||||||
"@tsparticles/react": "^3.0.0",
|
"@tsparticles/react": "^3.0.0",
|
||||||
"@tsparticles/slim": "^3.9.1",
|
"@tsparticles/slim": "^3.9.1",
|
||||||
|
"@uiw/react-codemirror": "^4.25.1",
|
||||||
"antd": "^5.27.0",
|
"antd": "^5.27.0",
|
||||||
"antd-style": "^3.7.1",
|
"antd-style": "^3.7.1",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
|
|||||||
189
src/App.jsx
@ -8,69 +8,29 @@ import {
|
|||||||
} from 'react-router-dom'
|
} from 'react-router-dom'
|
||||||
import { App, ConfigProvider } from 'antd'
|
import { App, ConfigProvider } from 'antd'
|
||||||
|
|
||||||
import ProductionOverview from './components/Dashboard/Production/ProductionOverview'
|
|
||||||
|
|
||||||
import Printers from './components/Dashboard/Production/Printers'
|
|
||||||
import ControlPrinter from './components/Dashboard/Production/Printers/ControlPrinter.jsx'
|
|
||||||
import PrinterInfo from './components/Dashboard/Production/Printers/PrinterInfo.jsx'
|
|
||||||
|
|
||||||
import Jobs from './components/Dashboard/Production/Jobs.jsx'
|
|
||||||
import JobInfo from './components/Dashboard/Production/Jobs/JobInfo.jsx'
|
|
||||||
|
|
||||||
import Filaments from './components/Dashboard/Management/Filaments'
|
|
||||||
import FilamentInfo from './components/Dashboard/Management/Filaments/FilamentInfo.jsx'
|
|
||||||
|
|
||||||
import GCodeFiles from './components/Dashboard/Production/GCodeFiles'
|
|
||||||
import GCodeFileInfo from './components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx'
|
|
||||||
|
|
||||||
import Parts from './components/Dashboard/Management/Parts.jsx'
|
|
||||||
import PartInfo from './components/Dashboard/Management/Parts/PartInfo.jsx'
|
|
||||||
|
|
||||||
import Products from './components/Dashboard/Management/Products.jsx'
|
|
||||||
import ProductInfo from './components/Dashboard/Management/Products/ProductInfo.jsx'
|
|
||||||
|
|
||||||
import Vendors from './components/Dashboard/Management/Vendors'
|
|
||||||
import VendorInfo from './components/Dashboard/Management/Vendors/VendorInfo'
|
|
||||||
|
|
||||||
import Materials from './components/Dashboard/Management/Materials'
|
|
||||||
|
|
||||||
import FilamentStocks from './components/Dashboard/Inventory/FilamentStocks.jsx'
|
|
||||||
import FilamentStockInfo from './components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx'
|
|
||||||
|
|
||||||
import PartStocks from './components/Dashboard/Inventory/PartStocks.jsx'
|
|
||||||
|
|
||||||
import StockAudits from './components/Dashboard/Inventory/StockAudits.jsx'
|
|
||||||
import StockAuditInfo from './components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx'
|
|
||||||
|
|
||||||
import Dashboard from './components/Dashboard/Dashboard.jsx'
|
import Dashboard from './components/Dashboard/Dashboard.jsx'
|
||||||
import PrivateRoute from './components/PrivateRoute'
|
import PrivateRoute from './components/PrivateRoute'
|
||||||
import './App.css'
|
import './assets/stylesheets/App.css'
|
||||||
import { PrintServerProvider } from './components/Dashboard/context/PrintServerContext.js'
|
import { PrintServerProvider } from './components/Dashboard/context/PrintServerContext.js'
|
||||||
import { AuthProvider } from './components/Dashboard/context/AuthContext.js'
|
import { AuthProvider } from './components/Dashboard/context/AuthContext.js'
|
||||||
import { SpotlightProvider } from './components/Dashboard/context/SpotlightContext.js'
|
import { SpotlightProvider } from './components/Dashboard/context/SpotlightContext.js'
|
||||||
import StockEvents from './components/Dashboard/Inventory/StockEvents.jsx'
|
|
||||||
|
|
||||||
import Settings from './components/Dashboard/Management/Settings'
|
|
||||||
import AuditLogs from './components/Dashboard/Management/AuditLogs.jsx'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ThemeProvider,
|
ThemeProvider,
|
||||||
useThemeContext
|
useThemeContext
|
||||||
} from './components/Dashboard/context/ThemeContext'
|
} from './components/Dashboard/context/ThemeContext'
|
||||||
import AppError from './components/App/AppError'
|
import AppError from './components/App/AppError'
|
||||||
import NoteTypes from './components/Dashboard/Management/NoteTypes.jsx'
|
|
||||||
import NoteTypeInfo from './components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx'
|
|
||||||
import SessionStorage from './components/Dashboard/Developer/SessionStorage.jsx'
|
|
||||||
import AuthContextDebug from './components/Dashboard/Developer/AuthContextDebug.jsx'
|
|
||||||
import PrintServerContextDebug from './components/Dashboard/Developer/PrintServerContextDebug.jsx'
|
|
||||||
import { ApiServerProvider } from './components/Dashboard/context/ApiServerContext.js'
|
import { ApiServerProvider } from './components/Dashboard/context/ApiServerContext.js'
|
||||||
import Users from './components/Dashboard/Management/Users.jsx'
|
|
||||||
import UserInfo from './components/Dashboard/Management/Users/UserInfo.jsx'
|
|
||||||
import SubJobs from './components/Dashboard/Production/SubJobs.jsx'
|
|
||||||
import Hosts from './components/Dashboard/Management/Hosts.jsx'
|
|
||||||
import { ElectronProvider } from './components/Dashboard/context/ElectronContext.js'
|
import { ElectronProvider } from './components/Dashboard/context/ElectronContext.js'
|
||||||
import AuthCallback from './components/App/AuthCallback.jsx'
|
import AuthCallback from './components/App/AuthCallback.jsx'
|
||||||
|
|
||||||
|
import {
|
||||||
|
ProductionRoutes,
|
||||||
|
InventoryRoutes,
|
||||||
|
ManagementRoutes,
|
||||||
|
DeveloperRoutes
|
||||||
|
} from './routes'
|
||||||
|
|
||||||
const getRouter = () => {
|
const getRouter = () => {
|
||||||
if (
|
if (
|
||||||
typeof window !== 'undefined' &&
|
typeof window !== 'undefined' &&
|
||||||
@ -115,135 +75,10 @@ const AppContent = () => {
|
|||||||
<PrivateRoute component={() => <Dashboard />} />
|
<PrivateRoute component={() => <Dashboard />} />
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{/* Production Routes */}
|
{ProductionRoutes}
|
||||||
<Route
|
{InventoryRoutes}
|
||||||
path='production/overview'
|
{ManagementRoutes}
|
||||||
element={<ProductionOverview />}
|
{DeveloperRoutes}
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='production/printers'
|
|
||||||
element={<Printers />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='production/printers/control'
|
|
||||||
element={<ControlPrinter />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='production/printers/info'
|
|
||||||
element={<PrinterInfo />}
|
|
||||||
/>
|
|
||||||
<Route path='production/jobs' element={<Jobs />} />
|
|
||||||
<Route
|
|
||||||
path='production/subjobs'
|
|
||||||
element={<SubJobs />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='production/jobs/info'
|
|
||||||
element={<JobInfo />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='production/gcodefiles'
|
|
||||||
element={<GCodeFiles />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='production/gcodefiles/info'
|
|
||||||
element={<GCodeFileInfo />}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Inventory Routes */}
|
|
||||||
<Route
|
|
||||||
path='inventory/filamentstocks'
|
|
||||||
element={<FilamentStocks />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='inventory/filamentstocks/info'
|
|
||||||
element={<FilamentStockInfo />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='inventory/partstocks'
|
|
||||||
element={<PartStocks />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='inventory/stockevents'
|
|
||||||
element={<StockEvents />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='inventory/stockaudits'
|
|
||||||
element={<StockAudits />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='inventory/stockaudits/info'
|
|
||||||
element={<StockAuditInfo />}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Management Routes */}
|
|
||||||
<Route
|
|
||||||
path='management/filaments'
|
|
||||||
element={<Filaments />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='management/filaments/info'
|
|
||||||
element={<FilamentInfo />}
|
|
||||||
/>
|
|
||||||
<Route path='management/parts' element={<Parts />} />
|
|
||||||
<Route
|
|
||||||
path='management/parts/info'
|
|
||||||
element={<PartInfo />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='management/products'
|
|
||||||
element={<Products />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='management/products/info'
|
|
||||||
element={<ProductInfo />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='management/vendors'
|
|
||||||
element={<Vendors />}
|
|
||||||
/>
|
|
||||||
<Route path='management/hosts' element={<Hosts />} />
|
|
||||||
<Route
|
|
||||||
path='management/users/info'
|
|
||||||
element={<UserInfo />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='management/vendors/info'
|
|
||||||
element={<VendorInfo />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='management/materials'
|
|
||||||
element={<Materials />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='management/notetypes'
|
|
||||||
element={<NoteTypes />}
|
|
||||||
/>
|
|
||||||
<Route path='management/users' element={<Users />} />
|
|
||||||
<Route
|
|
||||||
path='management/notetypes/info'
|
|
||||||
element={<NoteTypeInfo />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='management/settings'
|
|
||||||
element={<Settings />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='management/auditlogs'
|
|
||||||
element={<AuditLogs />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='developer/sessionstorage'
|
|
||||||
element={<SessionStorage />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='developer/authcontextdebug'
|
|
||||||
element={<AuthContextDebug />}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path='developer/printservercontextdebug'
|
|
||||||
element={<PrintServerContextDebug />}
|
|
||||||
/>
|
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
<Route
|
||||||
path='*'
|
path='*'
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 64 64"><path d="M13.734 57.726h62.96c9.856 0 14.795-6.142 13.535-16.562l-3.392-28.218C85.843 4.532 81.097 0 73.389 0h-56.35C9.33 0 4.579 4.532 3.585 12.946L.194 41.164c-1.261 10.42 3.678 16.562 13.54 16.562m0-6.122c-5.47 0-8.202-3.888-7.404-10.554l3.315-27.376c.597-4.891 3.173-7.552 7.394-7.552h56.35c4.221 0 6.791 2.661 7.368 7.552l3.309 27.376c.83 6.691-1.907 10.554-7.372 10.554z" style="fill-rule:nonzero" transform="translate(0 11.571)scale(.70779)"/><path d="M24.535 37.579c1.291 0 2.316-.613 2.316-1.49v-8.638c3.169-.937 5.499-3.927 5.499-7.436 0-4.301-3.514-7.821-7.815-7.821-4.327 0-7.827 3.545-7.827 7.821 0 3.535 2.336 6.504 5.536 7.487v8.587c0 .877 1.005 1.49 2.291 1.49m.006 6.861c6.461 0 10.983-3.479 10.983-8.382 0-3.397-2.182-6.087-5.613-7.445v4.398c1.026.747 1.635 1.799 1.635 3.047 0 2.844-2.869 4.833-7.005 4.833-4.142 0-7.043-1.989-7.043-4.833 0-1.274.629-2.331 1.687-3.079v-4.36c-3.478 1.32-5.66 4.042-5.66 7.439 0 4.903 4.554 8.382 11.016 8.382m18.255-17.511c2.605 0 4.675-2.065 4.675-4.643a4.65 4.65 0 0 0-4.675-4.675 4.65 4.65 0 0 0-4.649 4.675 4.64 4.64 0 0 0 4.649 4.643m13.331 0c2.574 0 4.675-2.065 4.675-4.643a4.67 4.67 0 0 0-4.675-4.675 4.654 4.654 0 0 0-4.675 4.675c0 2.578 2.077 4.643 4.675 4.643m13.258 0c2.579 0 4.675-2.065 4.675-4.643a4.666 4.666 0 0 0-4.675-4.675 4.673 4.673 0 0 0-4.675 4.675c0 2.578 2.107 4.643 4.675 4.643M47.112 40.193a4.63 4.63 0 0 0 4.65-4.644 4.645 4.645 0 0 0-4.65-4.675 4.657 4.657 0 0 0-4.675 4.675c0 2.579 2.082 4.644 4.675 4.644m13.306 0c2.599 0 4.675-2.065 4.675-4.644a4.653 4.653 0 0 0-4.675-4.675 4.65 4.65 0 0 0-4.649 4.675 4.634 4.634 0 0 0 4.649 4.644m13.289 0c2.599 0 4.675-2.065 4.675-4.644a4.653 4.653 0 0 0-4.675-4.675 4.65 4.65 0 0 0-4.65 4.675 4.635 4.635 0 0 0 4.65 4.644" style="fill-rule:nonzero" transform="translate(0 11.571)scale(.70779)"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 64 64"><path d="M13.734 57.726h62.96c9.856 0 14.795-6.142 13.535-16.562l-3.392-28.218C85.843 4.532 81.097 0 73.389 0h-56.35C9.33 0 4.579 4.532 3.585 12.946L.194 41.164c-1.261 10.42 3.678 16.562 13.54 16.562m0-6.122c-5.47 0-8.202-3.888-7.404-10.554l3.315-27.376c.597-4.891 3.173-7.552 7.394-7.552h56.35c4.221 0 6.791 2.661 7.368 7.552l3.309 27.376c.83 6.691-1.907 10.554-7.372 10.554z" style="fill-rule:nonzero" transform="translate(0 11.571)scale(.7078)"/><path d="M24.535 37.579c1.291 0 2.316-.613 2.316-1.49v-8.638c3.169-.937 5.499-3.927 5.499-7.436 0-4.301-3.514-7.821-7.815-7.821-4.327 0-7.827 3.545-7.827 7.821 0 3.535 2.336 6.504 5.536 7.487v8.587c0 .877 1.005 1.49 2.291 1.49m.006 6.861c6.461 0 10.983-3.479 10.983-8.382 0-3.397-2.182-6.087-5.613-7.445v4.398c1.026.747 1.635 1.799 1.635 3.047 0 2.844-2.869 4.833-7.005 4.833-4.142 0-7.043-1.989-7.043-4.833 0-1.274.629-2.331 1.687-3.079v-4.36c-3.478 1.32-5.66 4.042-5.66 7.439 0 4.903 4.554 8.382 11.016 8.382m18.255-17.511c2.605 0 4.675-2.065 4.675-4.643a4.65 4.65 0 0 0-4.675-4.675 4.65 4.65 0 0 0-4.649 4.675 4.64 4.64 0 0 0 4.649 4.643m13.331 0c2.574 0 4.675-2.065 4.675-4.643a4.67 4.67 0 0 0-4.675-4.675 4.654 4.654 0 0 0-4.675 4.675c0 2.578 2.077 4.643 4.675 4.643m13.258 0c2.579 0 4.675-2.065 4.675-4.643a4.666 4.666 0 0 0-4.675-4.675 4.673 4.673 0 0 0-4.675 4.675c0 2.578 2.107 4.643 4.675 4.643M47.112 40.193a4.63 4.63 0 0 0 4.65-4.644 4.645 4.645 0 0 0-4.65-4.675 4.657 4.657 0 0 0-4.675 4.675c0 2.579 2.082 4.644 4.675 4.644m13.306 0c2.599 0 4.675-2.065 4.675-4.644a4.653 4.653 0 0 0-4.675-4.675 4.65 4.65 0 0 0-4.649 4.675 4.634 4.634 0 0 0 4.649 4.644m13.289 0c2.599 0 4.675-2.065 4.675-4.644a4.653 4.653 0 0 0-4.675-4.675 4.65 4.65 0 0 0-4.65 4.675 4.635 4.635 0 0 0 4.65 4.644" style="fill-rule:nonzero" transform="translate(0 11.571)scale(.7078)"/></svg>
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
src/assets/icons/designicon.afdesign
Normal file
1
src/assets/icons/designicon.min.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 64 64"><path d="M4.098 71.551c5.422 5.426 11.763 5.489 17.102.161 3.719-3.719 7.626-12.355 10.53-15.945l6.498 6.53c2.612 2.643 5.814 2.654 8.358.099l4.099-4.13c2.555-2.586 2.538-5.689-.1-8.326L25.74 25.058c-2.643-2.649-5.777-2.674-8.363-.088l-4.093 4.119c-2.561 2.56-2.581 5.708.074 8.357l6.519 6.499c-3.559 2.904-12.195 6.791-15.909 10.504-5.328 5.339-5.296 11.706.13 17.102m14.21-39.074 2.43-2.384c.706-.7 1.474-.731 2.18-.026L45.596 52.72c.68.706.68 1.479-.051 2.211l-2.353 2.378c-.712.763-1.525.788-2.242.026l-7.503-7.528c-1.274-1.28-2.965-1.155-4.413.255-2.437 2.401-6.679 12.615-11.665 17.564-2.859 2.884-6.31 2.879-9.283-.063-2.916-2.948-2.953-6.435-.068-9.294 4.985-4.943 15.194-9.191 17.6-11.634 1.379-1.468 1.53-3.159.25-4.413l-7.56-7.54c-.711-.705-.711-1.493 0-2.205m-5.655 34.304c2.052 0 3.709-1.682 3.709-3.729a3.703 3.703 0 0 0-3.709-3.703c-2.046 0-3.728 1.657-3.728 3.703a3.745 3.745 0 0 0 3.728 3.729m37.082-13.117 15.103-15.097c3.62-3.626 3.547-7.991-.168-11.711L40.105 2.254c-3.551-3.551-9.537-2.107-10.76 3.28-3.019 13.052-3.048 14.119-7.942 20.958l4.128 4.117c5.806-7.465 6.139-11.005 9.128-22.015.356-1.391 1.499-1.752 2.438-.839l23.204 23.178c1.297 1.297 1.291 2.783.099 3.98L45.695 49.624zm-5.027-18.24c1.65 1.656 10.781-3.407 13.932-7.375l-6.87-6.844c-.594 5.531-3.469 9.531-6.969 13.031-.406.406-.375.912-.093 1.188" style="fill-rule:nonzero" transform="translate(6.02 2.76)scale(.7697)"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
7
src/assets/icons/designicon.svg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.7697,0,0,0.7697,6.02054,2.76086)">
|
||||||
|
<path d="M4.098,71.551C9.52,76.977 15.861,77.04 21.2,71.712C24.919,67.993 28.826,59.357 31.73,55.767L38.228,62.297C40.84,64.94 44.042,64.951 46.586,62.396L50.685,58.266C53.24,55.68 53.223,52.577 50.585,49.94L25.74,25.058C23.097,22.409 19.963,22.384 17.377,24.97L13.284,29.089C10.723,31.649 10.703,34.797 13.358,37.446L19.877,43.945C16.318,46.849 7.682,50.736 3.968,54.449C-1.36,59.788 -1.328,66.155 4.098,71.551ZM18.308,32.477L20.738,30.093C21.444,29.393 22.212,29.362 22.918,30.067L45.596,52.72C46.276,53.426 46.276,54.199 45.545,54.931L43.192,57.309C42.48,58.072 41.667,58.097 40.95,57.335L33.447,49.807C32.173,48.527 30.482,48.652 29.034,50.062C26.597,52.463 22.355,62.677 17.369,67.626C14.51,70.51 11.059,70.505 8.086,67.563C5.17,64.615 5.133,61.128 8.018,58.269C13.003,53.326 23.212,49.078 25.618,46.635C26.997,45.167 27.148,43.476 25.868,42.222L18.308,34.682C17.597,33.977 17.597,33.189 18.308,32.477ZM12.653,66.781C14.705,66.781 16.362,65.099 16.362,63.052C16.362,61.006 14.705,59.349 12.653,59.349C10.607,59.349 8.925,61.006 8.925,63.052C8.925,65.099 10.607,66.781 12.653,66.781ZM49.735,53.664L64.838,38.567C68.458,34.941 68.385,30.576 64.67,26.856L40.105,2.254C36.554,-1.297 30.568,0.147 29.345,5.534C26.326,18.586 26.297,19.653 21.403,26.492L25.531,30.609C31.337,23.144 31.67,19.604 34.659,8.594C35.015,7.203 36.158,6.842 37.097,7.755L60.301,30.933C61.598,32.23 61.592,33.716 60.4,34.913L45.695,49.624L49.735,53.664ZM44.708,35.424C46.358,37.08 55.489,32.017 58.64,28.049L51.77,21.205C51.176,26.736 48.301,30.736 44.801,34.236C44.395,34.642 44.426,35.148 44.708,35.424Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
BIN
src/assets/icons/documenticon.afdesign
Normal file
1
src/assets/icons/documenticon.min.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 64 64"><path d="M15.46 13.515c0-6.725 3.475-10.252 10.149-10.252h11.744c3.596 0 6.407.934 8.744 3.328l15.584 15.857c2.468 2.53 3.322 5.153 3.322 9.261v25.879c0 6.719-3.475 10.251-10.149 10.251h-6.939v-6.122h6.405c3.036 0 4.561-1.604 4.561-4.51V30.221H45.094c-3.841 0-5.878-2.011-5.878-5.878V9.386H26.118c-3.037 0-4.536 1.629-4.536 4.509v5.572a24 24 0 0 0-.543-.006H15.46zM44.326 23.57c0 1.071.443 1.54 1.515 1.54h11.446L44.326 11.945z" style="fill-rule:nonzero" transform="translate(7.224 .331)scale(.76231)"/><path d="M10.149 67.641h33.289c6.699 0 10.148-3.506 10.148-10.231V29.058c0-4.341-.558-6.333-3.278-9.104L33.894 3.284C31.282.616 29.111 0 25.212 0H10.149C3.481 0 0 3.532 0 10.257V57.41c0 6.751 3.455 10.231 10.149 10.231m.488-6.122c-3.016 0-4.515-1.578-4.515-4.49V10.637c0-2.885 1.499-4.515 4.541-4.515h13.321v17.139c0 4.472 2.183 6.634 6.635 6.634h16.845v27.134c0 2.912-1.499 4.49-4.535 4.49zm20.561-37.023c-1.295 0-1.814-.551-1.814-1.84V6.973l17.229 17.523z" style="fill-rule:nonzero" transform="translate(7.224 12.833)scale(.7121)"/><path d="M37.787 51.93c0-1.06-.86-1.921-1.92-1.921h-19.13a1.922 1.922 0 0 0 0 3.845h19.128c1.06 0 1.921-.862 1.921-1.923m.001-6.297c0-1.06-.86-1.922-1.92-1.922h-19.13a1.922 1.922 0 0 0 0 3.845h19.128c1.06 0 1.921-.861 1.921-1.923m.001-6.397c0-1.06-.86-1.922-1.92-1.922h-19.13a1.922 1.922 0 0 0 0 3.845h19.128c1.06 0 1.921-.861 1.921-1.923m-16.139-6.473c0-1.06-.861-1.922-1.922-1.922H16.74a1.923 1.923 0 0 0 0 3.845h2.986c1.06 0 1.922-.861 1.922-1.923"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
26
src/assets/icons/documenticon.svg
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?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.75929,0,0,0.75929,7.36648,3.81256)">
|
||||||
|
<g transform="matrix(1.00398,0,0,1.00398,-0.188198,-4.58479)">
|
||||||
|
<path d="M15.46,13.515C15.46,6.79 18.935,3.263 25.609,3.263L37.353,3.263C40.949,3.263 43.76,4.197 46.097,6.591L61.681,22.448C64.149,24.978 65.003,27.601 65.003,31.709L65.003,57.588C65.003,64.307 61.528,67.839 54.854,67.839L47.915,67.839L47.915,61.717L54.32,61.717C57.356,61.717 58.881,60.113 58.881,57.207L58.881,30.221L45.094,30.221C41.253,30.221 39.216,28.21 39.216,24.343L39.216,9.386L26.118,9.386C23.081,9.386 21.582,11.015 21.582,13.895L21.582,19.467C21.407,19.463 21.225,19.461 21.039,19.461L15.46,19.461L15.46,13.515ZM44.326,23.57C44.326,24.641 44.769,25.11 45.841,25.11L57.287,25.11L44.326,11.945L44.326,23.57Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.937843,0,0,0.937843,-0.188198,11.8804)">
|
||||||
|
<path d="M10.149,67.641L43.438,67.641C50.137,67.641 53.586,64.135 53.586,57.41L53.586,29.058C53.586,24.717 53.028,22.725 50.308,19.954L33.894,3.284C31.282,0.616 29.111,0 25.212,0L10.149,0C3.481,0 0,3.532 0,10.257L0,57.41C0,64.161 3.455,67.641 10.149,67.641ZM10.637,61.519C7.621,61.519 6.122,59.941 6.122,57.029L6.122,10.637C6.122,7.752 7.621,6.122 10.663,6.122L23.984,6.122L23.984,23.261C23.984,27.733 26.167,29.895 30.619,29.895L47.464,29.895L47.464,57.029C47.464,59.941 45.965,61.519 42.929,61.519L10.637,61.519ZM31.198,24.496C29.903,24.496 29.384,23.945 29.384,22.656L29.384,6.973L46.613,24.496L31.198,24.496Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.937843,0,0,0.937843,-0.188198,12.4057)">
|
||||||
|
<g transform="matrix(4.7745,0,0,1.46137,-127.467,5.67668)">
|
||||||
|
<path d="M35.687,33.303C35.687,32.284 35.434,31.456 35.122,31.456L29.496,31.456C29.184,31.456 28.931,32.284 28.931,33.303C28.931,34.323 29.184,35.151 29.496,35.151L35.122,35.151C35.434,35.151 35.687,34.323 35.687,33.303Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(4.7745,0,0,1.46137,-127.467,-3.16531)">
|
||||||
|
<path d="M35.687,33.303C35.687,32.284 35.434,31.456 35.122,31.456L29.496,31.456C29.184,31.456 28.931,32.284 28.931,33.303C28.931,34.323 29.184,35.151 29.496,35.151L35.122,35.151C35.434,35.151 35.687,34.323 35.687,33.303Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(4.7745,0,0,1.46137,-127.467,-12.1487)">
|
||||||
|
<path d="M35.687,33.303C35.687,32.284 35.434,31.456 35.122,31.456L29.496,31.456C29.184,31.456 28.931,32.284 28.931,33.303C28.931,34.323 29.184,35.151 29.496,35.151L35.122,35.151C35.434,35.151 35.687,34.323 35.687,33.303Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.41967,0,0,1.46137,-30.4087,-21.2388)">
|
||||||
|
<path d="M35.687,33.303C35.687,32.284 34.835,31.456 33.786,31.456L30.832,31.456C29.783,31.456 28.931,32.284 28.931,33.303C28.931,34.323 29.783,35.151 30.832,35.151L33.786,35.151C34.835,35.151 35.687,34.323 35.687,33.303Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
BIN
src/assets/icons/documentprintericon.afdesign
Normal file
1
src/assets/icons/documentprintericon.min.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 64 64"><path d="M58.753 9.703v1.679h-5.704V9.269c0-2.366-1.2-3.535-3.555-3.535H21.921c-2.329 0-3.555 1.169-3.555 3.535v2.113h-5.704V9.703c0-6.347 3.417-9.234 9.265-9.234h27.56c6.111 0 9.266 2.887 9.266 9.234m12.662 11.358v28.755c0 6.375-3.432 9.68-9.782 9.68h-3.245v-5.535h3.251c2.565 0 3.918-1.347 3.918-3.917V20.839c0-2.57-1.353-3.928-3.918-3.928H9.801c-2.59 0-3.917 1.358-3.917 3.928v29.205c0 2.57 1.327 3.917 3.917 3.917h3.226v5.535h-3.22C3.432 59.496 0 56.191 0 49.816V21.061c0-6.349 3.694-9.679 9.807-9.679h51.826c6.35 0 9.782 3.33 9.782 9.679m-12.861 3.172c0 2.235-1.843 4.052-4.027 4.052a4.057 4.057 0 0 1-4.053-4.052c0-2.184 1.818-4.027 4.053-4.027 2.184 0 4.027 1.843 4.027 4.027" style="fill-rule:nonzero" transform="translate(4 4.73)scale(.78415)"/><path d="M19.642 69.086h32.131c4.43 0 6.615-2.032 6.615-6.621V37.831c0-4.583-2.185-6.615-6.615-6.615H19.642c-4.27 0-6.615 2.032-6.615 6.615v24.634c0 4.589 2.186 6.621 6.615 6.621m1.316-5.393c-1.494 0-2.27-.745-2.27-2.27v-22.57c0-1.524.776-2.238 2.27-2.238h29.525c1.519 0 2.244.714 2.244 2.238v22.57c0 1.525-.725 2.27-2.244 2.27zm4.428-16.733h20.711c1.221 0 2.124-.928 2.124-2.154 0-1.164-.903-2.061-2.124-2.061H25.386c-1.232 0-2.155.897-2.155 2.061 0 1.226.928 2.154 2.155 2.154m0 10.636h20.711c1.221 0 2.124-.922 2.124-2.092 0-1.2-.903-2.128-2.124-2.128H25.386c-1.227 0-2.155.928-2.155 2.128 0 1.17.923 2.092 2.155 2.092" style="fill-rule:nonzero" transform="translate(4 4.73)scale(.78415)"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
15
src/assets/icons/documentprintericon.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?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.784149,0,0,0.784149,2.8024,2.29292)">
|
||||||
|
<g transform="matrix(1,0,0,1,1.52726,3.10699)">
|
||||||
|
<path d="M58.753,9.703L58.753,11.382L53.049,11.382L53.049,9.269C53.049,6.903 51.849,5.734 49.494,5.734L21.921,5.734C19.592,5.734 18.366,6.903 18.366,9.269L18.366,11.382L12.662,11.382L12.662,9.703C12.662,3.356 16.079,0.469 21.927,0.469L49.487,0.469C55.598,0.469 58.753,3.356 58.753,9.703Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,1.52726,3.10699)">
|
||||||
|
<path d="M71.415,21.061L71.415,49.816C71.415,56.191 67.983,59.496 61.633,59.496L58.388,59.496L58.388,53.961L61.639,53.961C64.204,53.961 65.557,52.614 65.557,50.044L65.557,20.839C65.557,18.269 64.204,16.911 61.639,16.911L9.801,16.911C7.211,16.911 5.884,18.269 5.884,20.839L5.884,50.044C5.884,52.614 7.211,53.961 9.801,53.961L13.027,53.961L13.027,59.496L9.807,59.496C3.432,59.496 0,56.191 0,49.816L0,21.061C0,14.712 3.694,11.382 9.807,11.382L61.633,11.382C67.983,11.382 71.415,14.712 71.415,21.061ZM58.554,24.233C58.554,26.468 56.711,28.285 54.527,28.285C52.292,28.285 50.474,26.468 50.474,24.233C50.474,22.049 52.292,20.206 54.527,20.206C56.711,20.206 58.554,22.049 58.554,24.233Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,1.52726,3.10699)">
|
||||||
|
<path d="M19.642,69.086L51.773,69.086C56.203,69.086 58.388,67.054 58.388,62.465L58.388,37.831C58.388,33.248 56.203,31.216 51.773,31.216L19.642,31.216C15.372,31.216 13.027,33.248 13.027,37.831L13.027,62.465C13.027,67.054 15.213,69.086 19.642,69.086ZM20.958,63.693C19.464,63.693 18.688,62.948 18.688,61.423L18.688,38.853C18.688,37.329 19.464,36.615 20.958,36.615L50.483,36.615C52.002,36.615 52.727,37.329 52.727,38.853L52.727,61.423C52.727,62.948 52.002,63.693 50.483,63.693L20.958,63.693ZM25.386,46.96L46.097,46.96C47.318,46.96 48.221,46.032 48.221,44.806C48.221,43.642 47.318,42.745 46.097,42.745L25.386,42.745C24.154,42.745 23.231,43.642 23.231,44.806C23.231,46.032 24.159,46.96 25.386,46.96ZM25.386,57.596L46.097,57.596C47.318,57.596 48.221,56.674 48.221,55.504C48.221,54.304 47.318,53.376 46.097,53.376L25.386,53.376C24.159,53.376 23.231,54.304 23.231,55.504C23.231,56.674 24.154,57.596 25.386,57.596Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
BIN
src/assets/icons/documentsizeicon.afdesign
Normal file
1
src/assets/icons/documentsizeicon.min.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 64 64"><path d="M52.109 26.338V23.46h-10.51c-2.928 0-4.481-1.534-4.481-4.481V7.577h-9.985c-2.314 0-3.457 1.242-3.457 3.437v4.248a18 18 0 0 0-.414-.005h-4.253v-4.533c0-5.126 2.649-7.814 7.736-7.814h8.953c2.742 0 4.884.711 6.666 2.536l11.88 12.088c1.881 1.929 2.532 3.928 2.532 7.06v1.744zM41.014 18.39c0 .816.338 1.174 1.155 1.174h8.725l-9.88-10.037z" style="fill-rule:nonzero"/><path d="M26.506 61.09H14.451c-4.767 0-7.227-2.478-7.227-7.285V20.228c0-4.789 2.478-7.304 7.227-7.304h10.726c2.776 0 4.322.438 6.182 2.338l10.906 11.076h-5.81l-8.307-8.449v10.824a7.1 7.1 0 0 0-1.81 4.738v.216c-1.363-.701-2.036-2.084-2.036-4.179V17.283h-9.485c-2.167 0-3.234 1.161-3.234 3.215v33.036c0 2.073 1.067 3.197 3.215 3.197h11.54v2.818q.002.795.168 1.541" style="fill-rule:nonzero"/><path d="M26.338 50.1H16.74a1.923 1.923 0 0 0 0 3.844h9.6zm0-6.297H16.74a1.923 1.923 0 0 0 0 3.845h9.6zm0-6.397H16.74a1.923 1.923 0 0 0 0 3.845h9.6zm-4.691-4.551c0-1.06-.861-1.922-1.922-1.922H16.74a1.923 1.923 0 0 0 0 3.845h2.986c1.06 0 1.922-.862 1.922-1.923"/><path d="m25.075 47.705-1.007-1.007-1.894-1.796-2.246-2.465.056 2.381.033 2.347c.032.471-.16.965-.496 1.3-.675.676-1.668.694-2.332.029-.384-.383-.524-.801-.488-1.331l.35-8.005c.031-.659.186-1.097.546-1.458.361-.36.799-.516 1.458-.546l8.005-.35c.53-.036.948.104 1.331.487.665.665.657 1.648-.031 2.336a1.76 1.76 0 0 1-1.298.493l-2.349-.03-2.379-.059 2.465 2.247 1.425 1.326 1.945 1.945 1.327 1.425 2.246 2.465-.059-2.379-.03-2.349a1.76 1.76 0 0 1 .493-1.298c.688-.688 1.671-.696 2.336-.031.383.383.523.801.487 1.331l-.35 8.005c-.03.659-.186 1.097-.546 1.458s-.799.515-1.458.546l-8.005.35c-.53.036-.947-.104-1.331-.488-.665-.664-.647-1.657.029-2.332a1.72 1.72 0 0 1 1.3-.496l2.347.033 2.381.056-2.465-2.246z" style="fill-rule:nonzero" transform="matrix(-1.06473 0 0 1.06473 74.043 -2.257)"/><path d="M11.031 59.75h37.688c7.14 0 11.031-3.859 11.031-10.953V10.969C59.75 3.891 55.859 0 48.719 0H11.031C3.906 0 0 3.891 0 10.969v37.828C0 55.891 3.906 59.75 11.031 59.75m.875-8.062c-2.515 0-3.843-1.219-3.843-3.875V11.969c0-2.656 1.328-3.891 3.843-3.891h35.938c2.5 0 3.844 1.235 3.844 3.891v35.844c0 2.656-1.344 3.875-3.844 3.875z" style="fill-rule:nonzero" transform="translate(29 29)scale(.58577)"/></svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
32
src/assets/icons/documentsizeicon.svg
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?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.75929,0,0,0.75929,7.36648,3.90301)">
|
||||||
|
<g transform="matrix(1.31702,0,0,1.31702,-9.70181,-5.14035)">
|
||||||
|
<path d="M52.109,26.338L52.109,23.46L41.599,23.46C38.671,23.46 37.118,21.926 37.118,18.979L37.118,7.577L27.133,7.577C24.819,7.577 23.676,8.819 23.676,11.014L23.676,15.262C23.543,15.259 23.404,15.257 23.262,15.257L19.009,15.257L19.009,10.724C19.009,5.598 21.658,2.91 26.745,2.91L35.698,2.91C38.44,2.91 40.582,3.621 42.364,5.446L54.244,17.534C56.125,19.463 56.776,21.462 56.776,24.594L56.776,26.338L52.109,26.338ZM41.014,18.39C41.014,19.206 41.352,19.564 42.169,19.564L50.894,19.564L41.014,9.527L41.014,18.39Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.31702,0,0,1.31702,-9.70181,-5.14035)">
|
||||||
|
<path d="M26.506,61.09L14.451,61.09C9.684,61.09 7.224,58.612 7.224,53.805L7.224,20.228C7.224,15.439 9.702,12.924 14.451,12.924L25.177,12.924C27.953,12.924 29.499,13.362 31.359,15.262L42.265,26.338L36.455,26.338L28.148,17.889L28.148,28.713C27.023,29.971 26.338,31.632 26.338,33.451L26.338,33.667C24.975,32.966 24.302,31.583 24.302,29.488L24.302,17.283L14.817,17.283C12.65,17.283 11.583,18.444 11.583,20.498L11.583,53.534C11.583,55.607 12.65,56.731 14.798,56.731L26.338,56.731L26.338,59.549C26.338,60.078 26.396,60.594 26.506,61.09Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.937843,0,0,0.937843,-0.188198,12.4057)">
|
||||||
|
<g transform="matrix(1.40431,0,0,1.40431,-10.1441,-18.7089)">
|
||||||
|
<path d="M26.338,50.099L16.739,50.099C15.678,50.099 14.817,50.961 14.817,52.022C14.817,53.083 15.678,53.944 16.739,53.944L26.338,53.944L26.338,50.099Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.40431,0,0,1.40431,-10.1441,-18.7089)">
|
||||||
|
<path d="M26.338,43.803L16.739,43.803C15.678,43.803 14.817,44.664 14.817,45.725C14.817,46.786 15.678,47.648 16.739,47.648L26.338,47.648L26.338,43.803Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.40431,0,0,1.40431,-10.1441,-18.7089)">
|
||||||
|
<path d="M26.338,37.406L16.739,37.406C15.678,37.406 14.817,38.267 14.817,39.328C14.817,40.389 15.678,41.251 16.739,41.251L26.338,41.251L26.338,37.406Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.41967,0,0,1.46137,-30.4087,-21.2388)">
|
||||||
|
<path d="M35.687,33.303C35.687,32.284 34.835,31.456 33.786,31.456L30.832,31.456C29.783,31.456 28.931,32.284 28.931,33.303C28.931,34.323 29.783,35.151 30.832,35.151L33.786,35.151C34.835,35.151 35.687,34.323 35.687,33.303Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(-1.40227,0,0,1.40227,87.814,-8.11344)">
|
||||||
|
<path d="M25.075,47.705C24.894,47.524 24.249,46.879 24.068,46.698L22.174,44.902L19.928,42.437L19.984,44.818L20.017,47.165C20.049,47.636 19.857,48.13 19.521,48.465C18.846,49.141 17.853,49.159 17.189,48.494C16.805,48.111 16.665,47.693 16.701,47.163L17.051,39.158C17.082,38.499 17.237,38.061 17.597,37.7C17.958,37.34 18.396,37.184 19.055,37.154L27.06,36.804C27.59,36.768 28.008,36.908 28.391,37.291C29.056,37.956 29.048,38.939 28.36,39.627C28.037,39.95 27.538,40.146 27.062,40.12L24.713,40.09L22.334,40.031L24.799,42.278L26.224,43.604L28.169,45.549L29.496,46.974L31.742,49.439L31.683,47.06L31.653,44.711C31.627,44.235 31.823,43.736 32.146,43.413C32.834,42.725 33.817,42.717 34.482,43.382C34.865,43.765 35.005,44.183 34.969,44.713L34.619,52.718C34.589,53.377 34.433,53.815 34.073,54.176C33.712,54.537 33.274,54.691 32.615,54.722L24.61,55.072C24.08,55.108 23.663,54.968 23.279,54.584C22.614,53.92 22.632,52.927 23.308,52.252C23.643,51.916 24.137,51.724 24.608,51.756L26.955,51.789L29.336,51.845L26.871,49.599L25.075,47.705Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.771471,0,0,0.771471,28.4918,33.0532)">
|
||||||
|
<path d="M11.031,59.75L48.719,59.75C55.859,59.75 59.75,55.891 59.75,48.797L59.75,10.969C59.75,3.891 55.859,-0 48.719,-0L11.031,-0C3.906,-0 -0,3.891 -0,10.969L-0,48.797C-0,55.891 3.906,59.75 11.031,59.75ZM11.906,51.688C9.391,51.688 8.063,50.469 8.063,47.813L8.063,11.969C8.063,9.313 9.391,8.078 11.906,8.078L47.844,8.078C50.344,8.078 51.688,9.313 51.688,11.969L51.688,47.813C51.688,50.469 50.344,51.688 47.844,51.688L11.906,51.688Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.6 KiB |
BIN
src/assets/icons/documenttemplateicon.afdesign
Normal file
1
src/assets/icons/documenttemplateicon.min.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 64 64"><path d="M47.11 47.181h-1.17a2.342 2.342 0 0 0 0 4.683h1.17a2.343 2.343 0 0 0 2.341-2.341 2.343 2.343 0 0 0-2.341-2.342m4.74-2.243a3 3 0 0 1-.338.548 2.343 2.343 0 0 0 3.698 2.873q.523-.672.885-1.443a2.342 2.342 0 0 0-4.245-1.978m.244-7.137v1.17a2.344 2.344 0 0 0 2.342 2.34 2.343 2.343 0 0 0 2.34-2.342V37.8a2.341 2.341 0 1 0-4.683.002m-.006-8.195.001 1.17a2.343 2.343 0 0 0 2.343 2.34 2.343 2.343 0 0 0 2.34-2.342v-1.17a2.342 2.342 0 0 0-4.684.002m-.418-7.282c.093.236.161.476.213.736a2.343 2.343 0 0 0 4.59-.932 9 9 0 0 0-.442-1.51 2.342 2.342 0 0 0-4.361 1.706m-32.62-5.133v.008q-.001.078.004.155l.001.03.006.048v.014c.053.584.284 1.005.581 1.31a2.34 2.34 0 0 0 1.749.785s2.341-.162 2.341-2.356V16.03a2.342 2.342 0 0 0-4.683 0zm28.155-.568.81.845a2.343 2.343 0 0 0 3.312.067 2.343 2.343 0 0 0 .067-3.31l-.81-.845a2.342 2.342 0 0 0-3.379 3.243m-5.657-5.908q.406.42.784.821a2.342 2.342 0 0 0 3.405-3.214c-.267-.282-.545-.576-.832-.871a2.343 2.343 0 0 0-3.312-.047 2.343 2.343 0 0 0-.045 3.311M23.83 9.66q.088-.308.243-.593a2.342 2.342 0 1 0-4.105-2.252 7.5 7.5 0 0 0-.643 1.568A2.341 2.341 0 0 0 23.83 9.66M37.143 3h-.874a2.342 2.342 0 0 0 0 4.683h.874q.056 0 .111.004a2.34 2.34 0 1 0 .37-4.667q-.24-.02-.481-.02m-9.07 4.683h1.171a2.343 2.343 0 0 0 2.342-2.342A2.343 2.343 0 0 0 29.244 3h-1.17a2.342 2.342 0 0 0 0 4.683M48.471 19.87h-1.17a2.342 2.342 0 0 0 0 4.684h1.17a2.342 2.342 0 0 0 0-4.683m-6.548-.195a2.4 2.4 0 0 1-.4-.415 2.343 2.343 0 0 0-3.275-.482 2.343 2.343 0 0 0-.482 3.276 7 7 0 0 0 1.22 1.267 2.342 2.342 0 0 0 2.937-3.646m-.553-5.897v-1.171a2.343 2.343 0 0 0-2.342-2.341 2.344 2.344 0 0 0-2.341 2.341v1.17a2.342 2.342 0 0 0 4.683 0"/><path d="M10.149 67.641h33.289c6.699 0 10.148-3.506 10.148-10.231V29.058c0-4.341-.558-6.333-3.278-9.104L33.894 3.284C31.282.616 29.111 0 25.212 0H10.149C3.481 0 0 3.532 0 10.257V57.41c0 6.751 3.455 10.231 10.149 10.231m.488-6.122c-3.016 0-4.515-1.578-4.515-4.49V10.637c0-2.885 1.499-4.515 4.541-4.515h13.321v17.139c0 4.472 2.183 6.634 6.635 6.634h16.845v27.134c0 2.912-1.499 4.49-4.535 4.49zm20.561-37.023c-1.295 0-1.814-.551-1.814-1.84V6.973l17.229 17.523z" style="fill-rule:nonzero" transform="translate(7.224 12.833)scale(.7121)"/><path d="M37.787 51.93c0-1.06-.86-1.921-1.92-1.921h-19.13a1.922 1.922 0 0 0 0 3.845h19.128c1.06 0 1.921-.862 1.921-1.923m.001-6.297c0-1.06-.86-1.922-1.92-1.922h-19.13a1.922 1.922 0 0 0 0 3.845h19.128c1.06 0 1.921-.861 1.921-1.923m.001-6.397c0-1.06-.86-1.922-1.92-1.922h-19.13a1.922 1.922 0 0 0 0 3.845h19.128c1.06 0 1.921-.861 1.921-1.923m-16.139-6.473c0-1.06-.861-1.922-1.922-1.922H16.74a1.923 1.923 0 0 0 0 3.845h2.986c1.06 0 1.922-.861 1.922-1.923"/></svg>
|
||||||
|
After Width: | Height: | Size: 2.7 KiB |
29
src/assets/icons/documenttemplateicon.svg
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?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.75929,0,0,0.75929,7.36648,3.81256)">
|
||||||
|
<g transform="matrix(1.37054,0,0,1.37054,-11.3557,-4.11161)">
|
||||||
|
<path d="M46.477,44.675L45.352,44.675C44.11,44.675 43.102,45.683 43.102,46.925C43.102,48.167 44.11,49.175 45.352,49.175L46.477,49.175C47.718,49.175 48.727,48.167 48.727,46.925C48.727,45.683 47.718,44.675 46.477,44.675ZM51.032,42.519C50.944,42.707 50.834,42.883 50.707,43.046C49.946,44.027 50.123,45.442 51.104,46.203C52.085,46.965 53.499,46.787 54.261,45.807C54.595,45.377 54.88,44.914 55.111,44.42C55.635,43.294 55.147,41.954 54.021,41.43C52.896,40.905 51.556,41.394 51.032,42.519ZM51.266,35.661C51.266,36.044 51.266,36.421 51.266,36.786C51.267,38.028 52.276,39.035 53.517,39.035C54.759,39.034 55.767,38.025 55.766,36.784C55.766,36.418 55.766,36.042 55.766,35.659C55.765,34.417 54.756,33.409 53.514,33.41C52.273,33.41 51.265,34.419 51.266,35.661ZM51.261,27.786C51.262,28.131 51.262,28.508 51.262,28.911C51.263,30.153 52.271,31.16 53.513,31.16C54.755,31.159 55.763,30.15 55.762,28.909C55.762,28.506 55.762,28.128 55.761,27.784C55.761,26.542 54.752,25.534 53.51,25.535C52.268,25.535 51.261,26.544 51.261,27.786ZM50.859,20.789C50.948,21.015 51.014,21.246 51.064,21.496C51.311,22.713 52.5,23.5 53.717,23.253C54.934,23.006 55.721,21.817 55.474,20.6C55.37,20.087 55.232,19.614 55.05,19.15C54.598,17.993 53.292,17.422 52.135,17.874C50.979,18.326 50.407,19.632 50.859,20.789ZM19.512,15.856L19.512,15.864C19.512,15.914 19.513,15.963 19.517,16.013L19.518,16.042L19.523,16.088L19.523,16.094L19.524,16.101C19.574,16.662 19.796,17.067 20.082,17.36C20.494,17.822 21.094,18.114 21.762,18.114C21.762,18.114 24.012,17.959 24.012,15.85L24.012,14.739C24.012,13.497 23.004,12.489 21.762,12.489C20.52,12.489 19.512,13.497 19.512,14.739L19.512,15.85L19.512,15.856ZM46.568,15.31C46.85,15.603 47.114,15.879 47.347,16.122C48.207,17.018 49.633,17.047 50.529,16.187C51.425,15.327 51.454,13.901 50.594,13.005C50.36,12.762 50.096,12.487 49.815,12.194C48.955,11.298 47.529,11.269 46.633,12.129C45.737,12.989 45.708,14.414 46.568,15.31ZM41.132,9.633C41.392,9.901 41.644,10.166 41.886,10.422C42.738,11.325 44.163,11.366 45.066,10.514C45.969,9.662 46.01,8.236 45.158,7.333C44.901,7.062 44.634,6.78 44.358,6.496C43.492,5.605 42.066,5.586 41.176,6.451C40.286,7.317 40.266,8.743 41.132,9.633ZM24.106,8.619C24.162,8.422 24.241,8.231 24.34,8.049C24.938,6.96 24.539,5.591 23.45,4.994C22.361,4.397 20.992,4.796 20.395,5.885C20.131,6.365 19.924,6.873 19.777,7.392C19.438,8.587 20.133,9.832 21.328,10.17C22.523,10.509 23.768,9.813 24.106,8.619ZM36.899,2.219L36.89,2.219L36.059,2.219C34.817,2.219 33.809,3.227 33.809,4.469C33.809,5.711 34.817,6.719 36.059,6.719L36.899,6.719C36.935,6.719 36.97,6.721 37.006,6.723C38.244,6.822 39.328,5.896 39.426,4.658C39.524,3.42 38.599,2.336 37.361,2.238C37.207,2.225 37.053,2.219 36.899,2.219L36.899,2.219ZM28.184,6.719L29.309,6.719C30.55,6.719 31.559,5.711 31.559,4.469C31.559,3.227 30.55,2.219 29.309,2.219L28.184,2.219C26.942,2.219 25.934,3.227 25.934,4.469C25.934,5.711 26.942,6.719 28.184,6.719Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.37054,0,0,1.37054,-11.3557,-4.11161)">
|
||||||
|
<path d="M47.785,18.431L46.66,18.431C45.418,18.431 44.41,19.439 44.41,20.681C44.41,21.923 45.418,22.931 46.66,22.931L47.785,22.931C49.027,22.931 50.035,21.923 50.035,20.681C50.035,19.439 49.027,18.431 47.785,18.431ZM41.493,18.243C41.35,18.127 41.218,17.99 41.109,17.844C40.368,16.848 38.958,16.64 37.961,17.381C36.965,18.122 36.757,19.533 37.498,20.529C37.83,20.976 38.233,21.395 38.671,21.747C39.638,22.526 41.055,22.373 41.834,21.406C42.613,20.439 42.46,19.022 41.493,18.243ZM40.961,12.576L40.961,11.451C40.961,10.21 39.953,9.201 38.711,9.201C37.47,9.201 36.461,10.21 36.461,11.451L36.461,12.576C36.461,13.818 37.47,14.826 38.711,14.826C39.953,14.826 40.961,13.818 40.961,12.576Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.937843,0,0,0.937843,-0.188198,11.8804)">
|
||||||
|
<path d="M10.149,67.641L43.438,67.641C50.137,67.641 53.586,64.135 53.586,57.41L53.586,29.058C53.586,24.717 53.028,22.725 50.308,19.954L33.894,3.284C31.282,0.616 29.111,0 25.212,0L10.149,0C3.481,0 0,3.532 0,10.257L0,57.41C0,64.161 3.455,67.641 10.149,67.641ZM10.637,61.519C7.621,61.519 6.122,59.941 6.122,57.029L6.122,10.637C6.122,7.752 7.621,6.122 10.663,6.122L23.984,6.122L23.984,23.261C23.984,27.733 26.167,29.895 30.619,29.895L47.464,29.895L47.464,57.029C47.464,59.941 45.965,61.519 42.929,61.519L10.637,61.519ZM31.198,24.496C29.903,24.496 29.384,23.945 29.384,22.656L29.384,6.973L46.613,24.496L31.198,24.496Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.937843,0,0,0.937843,-0.188198,12.4057)">
|
||||||
|
<g transform="matrix(4.7745,0,0,1.46137,-127.467,5.67668)">
|
||||||
|
<path d="M35.687,33.303C35.687,32.284 35.434,31.456 35.122,31.456L29.496,31.456C29.184,31.456 28.931,32.284 28.931,33.303C28.931,34.323 29.184,35.151 29.496,35.151L35.122,35.151C35.434,35.151 35.687,34.323 35.687,33.303Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(4.7745,0,0,1.46137,-127.467,-3.16531)">
|
||||||
|
<path d="M35.687,33.303C35.687,32.284 35.434,31.456 35.122,31.456L29.496,31.456C29.184,31.456 28.931,32.284 28.931,33.303C28.931,34.323 29.184,35.151 29.496,35.151L35.122,35.151C35.434,35.151 35.687,34.323 35.687,33.303Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(4.7745,0,0,1.46137,-127.467,-12.1487)">
|
||||||
|
<path d="M35.687,33.303C35.687,32.284 35.434,31.456 35.122,31.456L29.496,31.456C29.184,31.456 28.931,32.284 28.931,33.303C28.931,34.323 29.184,35.151 29.496,35.151L35.122,35.151C35.434,35.151 35.687,34.323 35.687,33.303Z"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.41967,0,0,1.46137,-30.4087,-21.2388)">
|
||||||
|
<path d="M35.687,33.303C35.687,32.284 34.835,31.456 33.786,31.456L30.832,31.456C29.783,31.456 28.931,32.284 28.931,33.303C28.931,34.323 29.783,35.151 30.832,35.151L33.786,35.151C34.835,35.151 35.687,34.323 35.687,33.303Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.3 KiB |
BIN
src/assets/icons/otpicon.afdesign
Normal file
1
src/assets/icons/otpicon.min.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2" viewBox="0 0 64 64"><path d="M39.064 13.281a16 16 0 0 0-1.552 2.283 16 16 0 0 0-1.102 2.424H11.319c-2.038 0-3.189 1.143-3.189 3.185V42.78c0 2.014 1.151 3.185 3.189 3.185h17.908c-.335 1.012-.407 2.18-.02 3.274l.532 1.47H10.622C5.731 50.709 3 48.064 3 43.165V20.784c0-4.878 2.731-7.503 7.622-7.503zm21.781 21.78v8.104c0 4.899-2.731 7.544-7.644 7.544h-5.695a4.8 4.8 0 0 0-.008-2.542l-.362-1.277 3.085-.865q.1-.029.197-.06h2.108c2.037 0 3.189-1.171 3.189-3.185v-5.062c1.92-.572 3.643-1.468 5.13-2.657" style="fill-rule:nonzero" transform="translate(-1.5)"/><path d="M18.888 77.914c1.551 1.303 3.64 1.459 5.099.032l9.646-9.657c1.437-1.448 1.375-3.661-.031-5.099l-4.446-4.435 6.674-6.653c1.408-1.406 1.385-3.65-.021-5.099l-6.024-6.046c8.385-4.254 13.079-11.304 13.079-19.577C42.864 9.551 33.292 0 21.443 0 9.51 0 0 9.498 0 21.38c0 8.455 4.793 16.002 12.406 19.504v29.212c0 1.161.372 2.517 1.378 3.399zm2.555-5.961-3.36-3.328V36.464C11.107 34.977 5.994 28.773 5.994 21.38c0-8.491 6.895-15.355 15.449-15.355 8.533 0 15.387 6.864 15.387 15.355 0 7.34-5.135 13.607-12.9 15.254v7.047l5.867 5.918-6.228 6.134v5.982l4.05 3.978zm0-51.321c2.771 0 5.042-2.282 5.042-5.064s-2.271-5.022-5.042-5.022c-2.814 0-5.043 2.231-5.043 5.022 0 2.782 2.25 5.064 5.043 5.064" style="fill-rule:nonzero" transform="rotate(29.234 11.74 88.091)scale(.60562)"/><path d="M34.933 27.241a4.697 4.697 0 0 0 4.694-4.694 4.697 4.697 0 0 0-4.694-4.694 4.7 4.7 0 0 0-4.695 4.694 4.7 4.7 0 0 0 4.695 4.694m-14.642 0c2.565 0 4.669-2.104 4.669-4.694s-2.13-4.694-4.669-4.694a4.697 4.697 0 0 0-4.694 4.694 4.697 4.697 0 0 0 4.694 4.694" style="fill-rule:nonzero" transform="translate(1.5 13.281)scale(.8287)"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
16
src/assets/icons/otpicon.svg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,-1.5,7.10543e-15)">
|
||||||
|
<path d="M39.064,13.281C38.491,13.982 37.971,14.744 37.512,15.564C37.071,16.351 36.704,17.161 36.41,17.988L11.319,17.988C9.281,17.988 8.13,19.131 8.13,21.173L8.13,42.78C8.13,44.794 9.281,45.965 11.319,45.965L29.227,45.965C28.892,46.977 28.82,48.145 29.207,49.239L29.739,50.709L10.622,50.709C5.731,50.709 3,48.064 3,43.165L3,20.784C3,15.906 5.731,13.281 10.622,13.281L39.064,13.281ZM60.845,35.061L60.845,43.165C60.845,48.064 58.114,50.709 53.201,50.709L47.506,50.709C47.725,49.911 47.734,49.038 47.498,48.167L47.136,46.89L50.221,46.025C50.287,46.006 50.353,45.986 50.418,45.965L52.526,45.965C54.563,45.965 55.715,44.794 55.715,42.78L55.715,37.718C57.635,37.146 59.358,36.25 60.845,35.061Z" style="fill-rule:nonzero;"/>
|
||||||
|
<g transform="matrix(0.528487,0.29577,-0.29577,0.528487,46.0168,5.48596)">
|
||||||
|
<path d="M18.888,77.914C20.439,79.217 22.528,79.373 23.987,77.946L33.633,68.289C35.07,66.841 35.008,64.628 33.602,63.19L29.156,58.755L35.83,52.102C37.238,50.696 37.215,48.452 35.809,47.003L29.785,40.957C38.17,36.703 42.864,29.653 42.864,21.38C42.864,9.551 33.292,0 21.443,0C9.51,0 0,9.498 0,21.38C0,29.835 4.793,37.382 12.406,40.884L12.406,70.096C12.406,71.257 12.778,72.613 13.784,73.495L18.888,77.914ZM21.443,71.953L18.083,68.625L18.083,36.464C11.107,34.977 5.994,28.773 5.994,21.38C5.994,12.889 12.889,6.025 21.443,6.025C29.976,6.025 36.83,12.889 36.83,21.38C36.83,28.72 31.695,34.987 23.93,36.634L23.93,43.681L29.797,49.599L23.569,55.733L23.569,61.715L27.619,65.693L21.443,71.953ZM21.443,20.632C24.214,20.632 26.485,18.35 26.485,15.568C26.485,12.787 24.214,10.546 21.443,10.546C18.629,10.546 16.4,12.777 16.4,15.568C16.4,18.35 18.65,20.632 21.443,20.632Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.828691,0,0,0.828691,3,13.2814)">
|
||||||
|
<path d="M34.933,27.241C37.523,27.241 39.627,25.137 39.627,22.547C39.627,19.957 37.523,17.853 34.933,17.853C32.343,17.853 30.238,19.957 30.238,22.547C30.238,25.137 32.343,27.241 34.933,27.241Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.828691,0,0,0.828691,3,13.2814)">
|
||||||
|
<path d="M20.291,27.241C22.856,27.241 24.96,25.137 24.96,22.547C24.96,19.957 22.83,17.853 20.291,17.853C17.701,17.853 15.597,19.957 15.597,22.547C15.597,25.137 17.701,27.241 20.291,27.241Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
@ -223,3 +223,7 @@ body {
|
|||||||
.objectTableDescritions >.ant-descriptions-view .ant-descriptions-row >.ant-descriptions-item-label {
|
.objectTableDescritions >.ant-descriptions-view .ant-descriptions-row >.ant-descriptions-item-label {
|
||||||
width: 35%;
|
width: 35%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.farmcontrol-splitter > .ant-splitter-bar {
|
||||||
|
margin: 8px
|
||||||
|
}
|
||||||
@ -19,7 +19,7 @@ import FilamentStockDisplay from '../../common/FilamentStockDisplay'
|
|||||||
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'
|
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'
|
||||||
|
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import PrinterState from '../../common/PrinterState'
|
import PrinterState from '../../common/StateDisplay'
|
||||||
|
|
||||||
const { Title } = Typography
|
const { Title } = Typography
|
||||||
|
|
||||||
|
|||||||
69
src/components/Dashboard/Management/DocumentPrinters.jsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import React, { useRef } from 'react'
|
||||||
|
import { Button, Flex, Space, Dropdown } from 'antd'
|
||||||
|
import ObjectTable from '../common/ObjectTable'
|
||||||
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
|
import GridIcon from '../../Icons/GridIcon'
|
||||||
|
import ListIcon from '../../Icons/ListIcon'
|
||||||
|
import useViewMode from '../hooks/useViewMode'
|
||||||
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
|
|
||||||
|
const DocumentPrinters = () => {
|
||||||
|
const tableRef = useRef()
|
||||||
|
|
||||||
|
const [viewMode, setViewMode] = useViewMode('documentPrinter')
|
||||||
|
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
useColumnVisibility('documentPrinter')
|
||||||
|
|
||||||
|
const actionItems = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'Reload List',
|
||||||
|
key: 'reloadList',
|
||||||
|
icon: <ReloadIcon />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onClick: ({ key }) => {
|
||||||
|
if (key === 'reloadList') {
|
||||||
|
tableRef.current?.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex vertical={'true'} gap='large'>
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='small'>
|
||||||
|
<Dropdown menu={actionItems}>
|
||||||
|
<Button>Actions</Button>
|
||||||
|
</Dropdown>
|
||||||
|
<ColumnViewButton
|
||||||
|
type='documentPrinter'
|
||||||
|
loading={false}
|
||||||
|
collapseState={columnVisibility}
|
||||||
|
updateCollapseState={setColumnVisibility}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||||
|
onClick={() =>
|
||||||
|
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ObjectTable
|
||||||
|
ref={tableRef}
|
||||||
|
visibleColumns={columnVisibility}
|
||||||
|
type='documentPrinter'
|
||||||
|
cards={viewMode === 'cards'}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocumentPrinters
|
||||||
@ -0,0 +1,201 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Space, Flex, Card } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
import loglevel from 'loglevel'
|
||||||
|
import config from '../../../../config.js'
|
||||||
|
import useCollapseState from '../../hooks/useCollapseState.js'
|
||||||
|
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||||
|
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||||
|
import ViewButton from '../../common/ViewButton.jsx'
|
||||||
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import EditObjectForm from '../../common/EditObjectForm.jsx'
|
||||||
|
import EditButtons from '../../common/EditButtons.jsx'
|
||||||
|
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'
|
||||||
|
|
||||||
|
const log = loglevel.getLogger('DocumentPrinterInfo')
|
||||||
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
|
const DocumentPrinterInfo = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const documentPrinterId = new URLSearchParams(location.search).get(
|
||||||
|
'documentPrinterId'
|
||||||
|
)
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState(
|
||||||
|
'DocumentPrinterInfo',
|
||||||
|
{
|
||||||
|
info: true,
|
||||||
|
notes: true,
|
||||||
|
auditLogs: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditObjectForm
|
||||||
|
id={documentPrinterId}
|
||||||
|
type='documentPrinter'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
>
|
||||||
|
{({
|
||||||
|
loading,
|
||||||
|
isEditing,
|
||||||
|
startEditing,
|
||||||
|
cancelEditing,
|
||||||
|
handleUpdate,
|
||||||
|
formValid,
|
||||||
|
objectData,
|
||||||
|
editLoading,
|
||||||
|
lock,
|
||||||
|
fetchObject
|
||||||
|
}) => {
|
||||||
|
// Define actions for ActionHandler
|
||||||
|
const actions = {
|
||||||
|
reload: () => {
|
||||||
|
fetchObject()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
edit: () => {
|
||||||
|
startEditing()
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
cancelEdit: () => {
|
||||||
|
cancelEditing()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
finishEdit: () => {
|
||||||
|
handleUpdate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionHandler actions={actions} loading={loading}>
|
||||||
|
{({ callAction }) => (
|
||||||
|
<Flex
|
||||||
|
gap='large'
|
||||||
|
vertical='true'
|
||||||
|
style={{
|
||||||
|
height: 'calc(var(--unit-100vh) - 155px)',
|
||||||
|
minHeight: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='middle'>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='documentPrinter'
|
||||||
|
id={documentPrinterId}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={loading}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'info',
|
||||||
|
label: 'DocumentPrinter Information'
|
||||||
|
},
|
||||||
|
{ key: 'stocks', label: 'DocumentPrinter Stocks' },
|
||||||
|
{ key: 'notes', label: 'Notes' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<LockIndicator lock={lock} />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<EditButtons
|
||||||
|
isEditing={isEditing}
|
||||||
|
handleUpdate={() => {
|
||||||
|
callAction('finishEdit')
|
||||||
|
}}
|
||||||
|
cancelEditing={() => {
|
||||||
|
callAction('cancelEdit')
|
||||||
|
}}
|
||||||
|
startEditing={() => {
|
||||||
|
callAction('edit')
|
||||||
|
}}
|
||||||
|
editLoading={editLoading}
|
||||||
|
formValid={formValid}
|
||||||
|
disabled={lock?.locked || loading}
|
||||||
|
loading={editLoading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Document Printer Information'
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
active={collapseState.info}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('info', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='info'
|
||||||
|
>
|
||||||
|
<ObjectInfo
|
||||||
|
loading={loading}
|
||||||
|
indicator={<LoadingOutlined />}
|
||||||
|
isEditing={isEditing}
|
||||||
|
type='documentPrinter'
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
|
||||||
|
<InfoCollapse
|
||||||
|
title='Notes'
|
||||||
|
icon={<NoteIcon />}
|
||||||
|
active={collapseState.notes}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('notes', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='notes'
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<NotesPanel
|
||||||
|
_id={documentPrinterId}
|
||||||
|
type='documentPrinter'
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</InfoCollapse>
|
||||||
|
|
||||||
|
<InfoCollapse
|
||||||
|
title='Audit Logs'
|
||||||
|
icon={<AuditLogIcon />}
|
||||||
|
active={collapseState.auditLogs}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('auditLogs', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='auditLogs'
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<InfoCollapsePlaceholder />
|
||||||
|
) : (
|
||||||
|
<ObjectTable
|
||||||
|
type='auditLog'
|
||||||
|
masterFilter={{ 'parent._id': documentPrinterId }}
|
||||||
|
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</ActionHandler>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</EditObjectForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocumentPrinterInfo
|
||||||
98
src/components/Dashboard/Management/DocumentSizes.jsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import React, { useState, useRef } from 'react'
|
||||||
|
import { Button, Flex, Space, Modal, Dropdown, message } from 'antd'
|
||||||
|
import NewDocumentSize from './DocumentSizes/NewDocumentSize'
|
||||||
|
import ObjectTable from '../common/ObjectTable'
|
||||||
|
import PlusIcon from '../../Icons/PlusIcon'
|
||||||
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
|
import GridIcon from '../../Icons/GridIcon'
|
||||||
|
import ListIcon from '../../Icons/ListIcon'
|
||||||
|
import useViewMode from '../hooks/useViewMode'
|
||||||
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
|
|
||||||
|
const DocumentSizes = () => {
|
||||||
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
const [newDocumentSizeOpen, setNewDocumentSizeOpen] = useState(false)
|
||||||
|
const tableRef = useRef()
|
||||||
|
|
||||||
|
const [viewMode, setViewMode] = useViewMode('documentSize')
|
||||||
|
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
useColumnVisibility('documentSize')
|
||||||
|
|
||||||
|
const actionItems = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'New Document Size',
|
||||||
|
key: 'newDocumentSize',
|
||||||
|
icon: <PlusIcon />
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
label: 'Reload List',
|
||||||
|
key: 'reloadList',
|
||||||
|
icon: <ReloadIcon />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onClick: ({ key }) => {
|
||||||
|
if (key === 'reloadList') {
|
||||||
|
tableRef.current?.reload()
|
||||||
|
} else if (key === 'newDocumentSize') {
|
||||||
|
setNewDocumentSizeOpen(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex vertical={'true'} gap='large'>
|
||||||
|
{contextHolder}
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='small'>
|
||||||
|
<Dropdown menu={actionItems}>
|
||||||
|
<Button>Actions</Button>
|
||||||
|
</Dropdown>
|
||||||
|
<ColumnViewButton
|
||||||
|
type='documentSize'
|
||||||
|
loading={false}
|
||||||
|
collapseState={columnVisibility}
|
||||||
|
updateCollapseState={setColumnVisibility}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||||
|
onClick={() =>
|
||||||
|
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ObjectTable
|
||||||
|
ref={tableRef}
|
||||||
|
visibleColumns={columnVisibility}
|
||||||
|
type='documentSize'
|
||||||
|
cards={viewMode === 'cards'}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Modal
|
||||||
|
open={newDocumentSizeOpen}
|
||||||
|
onCancel={() => setNewDocumentSizeOpen(false)}
|
||||||
|
footer={null}
|
||||||
|
destroyOnHidden={true}
|
||||||
|
width={700}
|
||||||
|
>
|
||||||
|
<NewDocumentSize
|
||||||
|
onOk={() => {
|
||||||
|
setNewDocumentSizeOpen(false)
|
||||||
|
messageApi.success('New note type created successfully.')
|
||||||
|
tableRef.current?.reload()
|
||||||
|
}}
|
||||||
|
reset={!newDocumentSizeOpen}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocumentSizes
|
||||||
@ -0,0 +1,195 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Space, Flex, Card } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
import loglevel from 'loglevel'
|
||||||
|
import config from '../../../../config.js'
|
||||||
|
import useCollapseState from '../../hooks/useCollapseState.js'
|
||||||
|
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||||
|
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||||
|
import ViewButton from '../../common/ViewButton.jsx'
|
||||||
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import EditObjectForm from '../../common/EditObjectForm.jsx'
|
||||||
|
import EditButtons from '../../common/EditButtons.jsx'
|
||||||
|
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'
|
||||||
|
|
||||||
|
const log = loglevel.getLogger('DocumentSizeInfo')
|
||||||
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
|
const DocumentSizeInfo = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const documentSizeId = new URLSearchParams(location.search).get(
|
||||||
|
'documentSizeId'
|
||||||
|
)
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState(
|
||||||
|
'DocumentSizeInfo',
|
||||||
|
{
|
||||||
|
info: true,
|
||||||
|
notes: true,
|
||||||
|
auditLogs: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditObjectForm
|
||||||
|
id={documentSizeId}
|
||||||
|
type='documentSize'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
>
|
||||||
|
{({
|
||||||
|
loading,
|
||||||
|
isEditing,
|
||||||
|
startEditing,
|
||||||
|
cancelEditing,
|
||||||
|
handleUpdate,
|
||||||
|
formValid,
|
||||||
|
objectData,
|
||||||
|
editLoading,
|
||||||
|
lock,
|
||||||
|
fetchObject
|
||||||
|
}) => {
|
||||||
|
// Define actions for ActionHandler
|
||||||
|
const actions = {
|
||||||
|
reload: () => {
|
||||||
|
fetchObject()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
edit: () => {
|
||||||
|
startEditing()
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
cancelEdit: () => {
|
||||||
|
cancelEditing()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
finishEdit: () => {
|
||||||
|
handleUpdate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ActionHandler actions={actions} loading={loading}>
|
||||||
|
{({ callAction }) => (
|
||||||
|
<Flex
|
||||||
|
gap='large'
|
||||||
|
vertical='true'
|
||||||
|
style={{
|
||||||
|
height: 'calc(var(--unit-100vh) - 155px)',
|
||||||
|
minHeight: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='middle'>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='documentSize'
|
||||||
|
id={documentSizeId}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={loading}
|
||||||
|
items={[
|
||||||
|
{ key: 'info', label: 'DocumentSize Information' },
|
||||||
|
{ key: 'stocks', label: 'DocumentSize Stocks' },
|
||||||
|
{ key: 'notes', label: 'Notes' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<LockIndicator lock={lock} />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<EditButtons
|
||||||
|
isEditing={isEditing}
|
||||||
|
handleUpdate={() => {
|
||||||
|
callAction('finishEdit')
|
||||||
|
}}
|
||||||
|
cancelEditing={() => {
|
||||||
|
callAction('cancelEdit')
|
||||||
|
}}
|
||||||
|
startEditing={() => {
|
||||||
|
callAction('edit')
|
||||||
|
}}
|
||||||
|
editLoading={editLoading}
|
||||||
|
formValid={formValid}
|
||||||
|
disabled={lock?.locked || loading}
|
||||||
|
loading={editLoading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Document Size Information'
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
active={collapseState.info}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('info', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='info'
|
||||||
|
>
|
||||||
|
<ObjectInfo
|
||||||
|
loading={loading}
|
||||||
|
indicator={<LoadingOutlined />}
|
||||||
|
isEditing={isEditing}
|
||||||
|
type='documentSize'
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
</InfoCollapse>
|
||||||
|
|
||||||
|
<InfoCollapse
|
||||||
|
title='Notes'
|
||||||
|
icon={<NoteIcon />}
|
||||||
|
active={collapseState.notes}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('notes', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='notes'
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<NotesPanel _id={documentSizeId} type='documentSize' />
|
||||||
|
</Card>
|
||||||
|
</InfoCollapse>
|
||||||
|
|
||||||
|
<InfoCollapse
|
||||||
|
title='Audit Logs'
|
||||||
|
icon={<AuditLogIcon />}
|
||||||
|
active={collapseState.auditLogs}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('auditLogs', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='auditLogs'
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<InfoCollapsePlaceholder />
|
||||||
|
) : (
|
||||||
|
<ObjectTable
|
||||||
|
type='auditLog'
|
||||||
|
masterFilter={{ 'parent._id': documentSizeId }}
|
||||||
|
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</ActionHandler>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</EditObjectForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocumentSizeInfo
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { useMediaQuery } from 'react-responsive'
|
||||||
|
import { Typography, Flex, Steps, Divider } from 'antd'
|
||||||
|
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
|
import NewObjectButtons from '../../common/NewObjectButtons'
|
||||||
|
|
||||||
|
const { Title } = Typography
|
||||||
|
|
||||||
|
const NewDocumentSize = ({ onOk }) => {
|
||||||
|
const [currentStep, setCurrentStep] = useState(0)
|
||||||
|
|
||||||
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NewObjectForm type={'documentSize'}>
|
||||||
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
title: 'Required',
|
||||||
|
key: 'required',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='documentSize'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={true}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Summary',
|
||||||
|
key: 'summary',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='documentSize'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
visibleProperties={{
|
||||||
|
_id: false,
|
||||||
|
createdAt: false,
|
||||||
|
updatedAt: false
|
||||||
|
}}
|
||||||
|
isEditing={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<Flex gap='middle'>
|
||||||
|
{!isMobile && (
|
||||||
|
<div style={{ minWidth: '160px' }}>
|
||||||
|
<Steps
|
||||||
|
current={currentStep}
|
||||||
|
items={steps}
|
||||||
|
direction='vertical'
|
||||||
|
style={{ width: 'fit-content' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isMobile && (
|
||||||
|
<Divider type='vertical' style={{ height: 'unset' }} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Flex vertical gap='middle' style={{ flexGrow: 1 }}>
|
||||||
|
<Title level={2} style={{ margin: 0 }}>
|
||||||
|
New Document Size
|
||||||
|
</Title>
|
||||||
|
<div style={{ minHeight: '260px', marginBottom: 8 }}>
|
||||||
|
{steps[currentStep].content}
|
||||||
|
</div>
|
||||||
|
<NewObjectButtons
|
||||||
|
currentStep={currentStep}
|
||||||
|
totalSteps={steps.length}
|
||||||
|
onPrevious={() => setCurrentStep((prev) => prev - 1)}
|
||||||
|
onNext={() => setCurrentStep((prev) => prev + 1)}
|
||||||
|
onSubmit={() => {
|
||||||
|
handleSubmit()
|
||||||
|
onOk()
|
||||||
|
}}
|
||||||
|
formValid={formValid}
|
||||||
|
submitLoading={submitLoading}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NewObjectForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
NewDocumentSize.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
reset: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewDocumentSize
|
||||||
98
src/components/Dashboard/Management/DocumentTemplates.jsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import React, { useState, useRef } from 'react'
|
||||||
|
import { Button, Flex, Space, Modal, Dropdown, message } from 'antd'
|
||||||
|
import NewDocumentTemplate from './DocumentTemplates/NewDocumentTemplate'
|
||||||
|
import ObjectTable from '../common/ObjectTable'
|
||||||
|
import PlusIcon from '../../Icons/PlusIcon'
|
||||||
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
|
import GridIcon from '../../Icons/GridIcon'
|
||||||
|
import ListIcon from '../../Icons/ListIcon'
|
||||||
|
import useViewMode from '../hooks/useViewMode'
|
||||||
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
|
|
||||||
|
const DocumentTemplates = () => {
|
||||||
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
const [newDocumentTemplateOpen, setNewDocumentTemplateOpen] = useState(false)
|
||||||
|
const tableRef = useRef()
|
||||||
|
|
||||||
|
const [viewMode, setViewMode] = useViewMode('documentTemplate')
|
||||||
|
|
||||||
|
const [columnVisibility, setColumnVisibility] =
|
||||||
|
useColumnVisibility('documentTemplate')
|
||||||
|
|
||||||
|
const actionItems = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'New Document Template',
|
||||||
|
key: 'newDocumentTemplate',
|
||||||
|
icon: <PlusIcon />
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
label: 'Reload List',
|
||||||
|
key: 'reloadList',
|
||||||
|
icon: <ReloadIcon />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onClick: ({ key }) => {
|
||||||
|
if (key === 'reloadList') {
|
||||||
|
tableRef.current?.reload()
|
||||||
|
} else if (key === 'newDocumentTemplate') {
|
||||||
|
setNewDocumentTemplateOpen(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex vertical={'true'} gap='large'>
|
||||||
|
{contextHolder}
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='small'>
|
||||||
|
<Dropdown menu={actionItems}>
|
||||||
|
<Button>Actions</Button>
|
||||||
|
</Dropdown>
|
||||||
|
<ColumnViewButton
|
||||||
|
type='documentTemplate'
|
||||||
|
loading={false}
|
||||||
|
collapseState={columnVisibility}
|
||||||
|
updateCollapseState={setColumnVisibility}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
icon={viewMode === 'cards' ? <ListIcon /> : <GridIcon />}
|
||||||
|
onClick={() =>
|
||||||
|
setViewMode(viewMode === 'cards' ? 'list' : 'cards')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
<ObjectTable
|
||||||
|
ref={tableRef}
|
||||||
|
visibleColumns={columnVisibility}
|
||||||
|
type='documentTemplate'
|
||||||
|
cards={viewMode === 'cards'}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Modal
|
||||||
|
open={newDocumentTemplateOpen}
|
||||||
|
onCancel={() => setNewDocumentTemplateOpen(false)}
|
||||||
|
footer={null}
|
||||||
|
destroyOnHidden={true}
|
||||||
|
width={700}
|
||||||
|
>
|
||||||
|
<NewDocumentTemplate
|
||||||
|
onOk={() => {
|
||||||
|
setNewDocumentTemplateOpen(false)
|
||||||
|
messageApi.success('New note type created successfully.')
|
||||||
|
tableRef.current?.reload()
|
||||||
|
}}
|
||||||
|
reset={!newDocumentTemplateOpen}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocumentTemplates
|
||||||
@ -0,0 +1,171 @@
|
|||||||
|
import React, { useState, useRef } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Space, Flex, Card } from 'antd'
|
||||||
|
import loglevel from 'loglevel'
|
||||||
|
import config from '../../../../config.js'
|
||||||
|
import useCollapseState from '../../hooks/useCollapseState.js'
|
||||||
|
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||||
|
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||||
|
import ViewButton from '../../common/ViewButton.jsx'
|
||||||
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
|
import EditObjectForm from '../../common/EditObjectForm.jsx'
|
||||||
|
import EditButtons from '../../common/EditButtons.jsx'
|
||||||
|
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||||
|
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||||
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
|
|
||||||
|
import TemplateEditor from '../../common/TemplateEditor.jsx'
|
||||||
|
|
||||||
|
const log = loglevel.getLogger('DocumentTemplateDesign')
|
||||||
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
|
const DocumentTemplateDesign = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const editFormRef = useRef(null)
|
||||||
|
const actionHandlerRef = useRef(null)
|
||||||
|
const documentTemplateId = new URLSearchParams(location.search).get(
|
||||||
|
'documentTemplateId'
|
||||||
|
)
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState(
|
||||||
|
'DocumentTemplateDesign',
|
||||||
|
{
|
||||||
|
preview: true,
|
||||||
|
editor: true,
|
||||||
|
notes: true,
|
||||||
|
auditLogs: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const [editFormState, setEditFormState] = useState({
|
||||||
|
isEditing: false,
|
||||||
|
editLoading: false,
|
||||||
|
formValid: false,
|
||||||
|
locked: false,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
reload: () => {
|
||||||
|
editFormRef?.current.handleFetchObject()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
edit: () => {
|
||||||
|
editFormRef?.current.startEditing()
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
cancelEdit: () => {
|
||||||
|
editFormRef?.current.cancelEditing()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
finishEdit: () => {
|
||||||
|
editFormRef?.current.handleUpdate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
gap='large'
|
||||||
|
vertical='true'
|
||||||
|
style={{
|
||||||
|
height: 'calc(var(--unit-100vh) - 155px)',
|
||||||
|
minHeight: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='middle'>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='documentTemplate'
|
||||||
|
id={documentTemplateId}
|
||||||
|
disabled={editFormState.loading}
|
||||||
|
visibleActions={{ edit: false }}
|
||||||
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={editFormState.loading}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'preview',
|
||||||
|
label: 'Preview'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'editor',
|
||||||
|
label: 'Editor'
|
||||||
|
},
|
||||||
|
{ key: 'notes', label: 'Notes' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<LockIndicator lock={editFormState.lock} />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<EditButtons
|
||||||
|
isEditing={editFormState.isEditing}
|
||||||
|
handleUpdate={() => {
|
||||||
|
actionHandlerRef.current.callAction('finishEdit')
|
||||||
|
}}
|
||||||
|
cancelEditing={() => {
|
||||||
|
actionHandlerRef.current.callAction('cancelEdit')
|
||||||
|
}}
|
||||||
|
startEditing={() => {
|
||||||
|
actionHandlerRef.current.callAction('edit')
|
||||||
|
}}
|
||||||
|
editLoading={editFormState.editLoading}
|
||||||
|
formValid={editFormState.formValid}
|
||||||
|
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||||
|
loading={editFormState.editLoading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<ActionHandler
|
||||||
|
actions={actions}
|
||||||
|
loading={editFormState.loading}
|
||||||
|
ref={actionHandlerRef}
|
||||||
|
>
|
||||||
|
<EditObjectForm
|
||||||
|
id={documentTemplateId}
|
||||||
|
type='documentTemplate'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
ref={editFormRef}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
console.log('Got edit form state change', state)
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading, isEditing, objectData }) => {
|
||||||
|
return (
|
||||||
|
<TemplateEditor
|
||||||
|
objectData={objectData}
|
||||||
|
loading={loading}
|
||||||
|
style={{ height: '72vh' }}
|
||||||
|
collapseState={collapseState}
|
||||||
|
isEditing={isEditing}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</EditObjectForm>
|
||||||
|
</ActionHandler>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Notes'
|
||||||
|
icon={<NoteIcon />}
|
||||||
|
active={collapseState.notes}
|
||||||
|
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||||
|
collapseKey='notes'
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<NotesPanel _id={documentTemplateId} type='documentTemplate' />
|
||||||
|
</Card>
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocumentTemplateDesign
|
||||||
@ -0,0 +1,202 @@
|
|||||||
|
import React, { useRef, useState } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
|
import { Space, Flex, Card } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
import loglevel from 'loglevel'
|
||||||
|
import config from '../../../../config.js'
|
||||||
|
import useCollapseState from '../../hooks/useCollapseState.js'
|
||||||
|
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||||
|
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||||
|
import ViewButton from '../../common/ViewButton.jsx'
|
||||||
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
|
import EditObjectForm from '../../common/EditObjectForm.jsx'
|
||||||
|
import EditButtons from '../../common/EditButtons.jsx'
|
||||||
|
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'
|
||||||
|
|
||||||
|
const log = loglevel.getLogger('DocumentTemplateInfo')
|
||||||
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
|
const DocumentTemplateInfo = () => {
|
||||||
|
const location = useLocation()
|
||||||
|
const editFormRef = useRef(null)
|
||||||
|
const actionHandlerRef = useRef(null)
|
||||||
|
const documentTemplateId = new URLSearchParams(location.search).get(
|
||||||
|
'documentTemplateId'
|
||||||
|
)
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState(
|
||||||
|
'DocumentTemplateInfo',
|
||||||
|
{
|
||||||
|
info: true,
|
||||||
|
stocks: true,
|
||||||
|
notes: true,
|
||||||
|
auditLogs: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const [editFormState, setEditFormState] = useState({
|
||||||
|
isEditing: false,
|
||||||
|
editLoading: false,
|
||||||
|
formValid: false,
|
||||||
|
locked: false,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
reload: () => {
|
||||||
|
editFormRef?.current.handleFetchObject()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
edit: () => {
|
||||||
|
editFormRef?.current.startEditing()
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
cancelEdit: () => {
|
||||||
|
editFormRef?.current.cancelEditing()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
finishEdit: () => {
|
||||||
|
editFormRef?.current.handleUpdate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex
|
||||||
|
gap='large'
|
||||||
|
vertical='true'
|
||||||
|
style={{
|
||||||
|
height: 'calc(var(--unit-100vh) - 155px)',
|
||||||
|
minHeight: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='middle'>
|
||||||
|
<Space size='small'>
|
||||||
|
<ObjectActions
|
||||||
|
type='documentTemplate'
|
||||||
|
id={documentTemplateId}
|
||||||
|
disabled={editFormState.loading}
|
||||||
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={editFormState.loading}
|
||||||
|
items={[
|
||||||
|
{ key: 'info', label: 'DocumentTemplate Information' },
|
||||||
|
{ key: 'notes', label: 'Notes' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<LockIndicator lock={editFormState.lock} />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<EditButtons
|
||||||
|
isEditing={editFormState.isEditing}
|
||||||
|
handleUpdate={() => {
|
||||||
|
actionHandlerRef.current.callAction('finishEdit')
|
||||||
|
}}
|
||||||
|
cancelEditing={() => {
|
||||||
|
actionHandlerRef.current.callAction('cancelEdit')
|
||||||
|
}}
|
||||||
|
startEditing={() => {
|
||||||
|
actionHandlerRef.current.callAction('edit')
|
||||||
|
}}
|
||||||
|
editLoading={editFormState.editLoading}
|
||||||
|
formValid={editFormState.formValid}
|
||||||
|
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||||
|
loading={editFormState.editLoading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<ActionHandler
|
||||||
|
actions={actions}
|
||||||
|
loading={editFormState.loading}
|
||||||
|
ref={actionHandlerRef}
|
||||||
|
>
|
||||||
|
<InfoCollapse
|
||||||
|
title='DocumentTemplate Information'
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
active={collapseState.info}
|
||||||
|
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||||
|
collapseKey='info'
|
||||||
|
>
|
||||||
|
<EditObjectForm
|
||||||
|
id={documentTemplateId}
|
||||||
|
type='documentTemplate'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
ref={editFormRef}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
console.log('Got edit form state change', state)
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading, isEditing, objectData }) => {
|
||||||
|
return (
|
||||||
|
<ObjectInfo
|
||||||
|
loading={loading}
|
||||||
|
indicator={<LoadingOutlined />}
|
||||||
|
isEditing={isEditing}
|
||||||
|
type='documentTemplate'
|
||||||
|
objectData={objectData}
|
||||||
|
visibleProperties={{
|
||||||
|
content: false,
|
||||||
|
testObject: false
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</EditObjectForm>
|
||||||
|
</InfoCollapse>
|
||||||
|
</ActionHandler>
|
||||||
|
|
||||||
|
<InfoCollapse
|
||||||
|
title='Notes'
|
||||||
|
icon={<NoteIcon />}
|
||||||
|
active={collapseState.notes}
|
||||||
|
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||||
|
collapseKey='notes'
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<NotesPanel _id={documentTemplateId} type='documentTemplate' />
|
||||||
|
</Card>
|
||||||
|
</InfoCollapse>
|
||||||
|
|
||||||
|
<InfoCollapse
|
||||||
|
title='Audit Logs'
|
||||||
|
icon={<AuditLogIcon />}
|
||||||
|
active={collapseState.auditLogs}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('auditLogs', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='auditLogs'
|
||||||
|
>
|
||||||
|
{editFormState.loading ? (
|
||||||
|
<InfoCollapsePlaceholder />
|
||||||
|
) : (
|
||||||
|
<ObjectTable
|
||||||
|
type='auditLog'
|
||||||
|
masterFilter={{ 'parent._id': documentTemplateId }}
|
||||||
|
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DocumentTemplateInfo
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { useMediaQuery } from 'react-responsive'
|
||||||
|
import { Typography, Flex, Steps, Divider } from 'antd'
|
||||||
|
|
||||||
|
import ObjectInfo from '../../common/ObjectInfo'
|
||||||
|
import NewObjectForm from '../../common/NewObjectForm'
|
||||||
|
import NewObjectButtons from '../../common/NewObjectButtons'
|
||||||
|
|
||||||
|
const { Title } = Typography
|
||||||
|
|
||||||
|
const NewDocumentTemplate = ({ onOk }) => {
|
||||||
|
const [currentStep, setCurrentStep] = useState(0)
|
||||||
|
|
||||||
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NewObjectForm
|
||||||
|
type={'documentTemplate'}
|
||||||
|
defaultValues={{ active: true, global: false }}
|
||||||
|
>
|
||||||
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
title: 'Required',
|
||||||
|
key: 'required',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='documentTemplate'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={true}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Optional',
|
||||||
|
key: 'optional',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='documentTemplate'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={false}
|
||||||
|
visibleProperties={{ content: false, testObject: false }}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Summary',
|
||||||
|
key: 'summary',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='documentTemplate'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
visibleProperties={{
|
||||||
|
_id: false,
|
||||||
|
createdAt: false,
|
||||||
|
updatedAt: false
|
||||||
|
}}
|
||||||
|
isEditing={false}
|
||||||
|
objectData={objectData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<Flex gap='middle'>
|
||||||
|
{!isMobile && (
|
||||||
|
<div style={{ minWidth: '160px' }}>
|
||||||
|
<Steps
|
||||||
|
current={currentStep}
|
||||||
|
items={steps}
|
||||||
|
direction='vertical'
|
||||||
|
style={{ width: 'fit-content' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isMobile && (
|
||||||
|
<Divider type='vertical' style={{ height: 'unset' }} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Flex vertical gap='middle' style={{ flexGrow: 1 }}>
|
||||||
|
<Title level={2} style={{ margin: 0 }}>
|
||||||
|
New Document Template
|
||||||
|
</Title>
|
||||||
|
<div style={{ minHeight: '260px', marginBottom: 8 }}>
|
||||||
|
{steps[currentStep].content}
|
||||||
|
</div>
|
||||||
|
<NewObjectButtons
|
||||||
|
currentStep={currentStep}
|
||||||
|
totalSteps={steps.length}
|
||||||
|
onPrevious={() => setCurrentStep((prev) => prev - 1)}
|
||||||
|
onNext={() => setCurrentStep((prev) => prev + 1)}
|
||||||
|
onSubmit={() => {
|
||||||
|
handleSubmit()
|
||||||
|
onOk()
|
||||||
|
}}
|
||||||
|
formValid={formValid}
|
||||||
|
submitLoading={submitLoading}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</NewObjectForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
NewDocumentTemplate.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
reset: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewDocumentTemplate
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React, { useRef, useState } from 'react'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
import { Space, Flex, Card } from 'antd'
|
import { Space, Flex, Card, Modal } from 'antd'
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import loglevel from 'loglevel'
|
import loglevel from 'loglevel'
|
||||||
import config from '../../../../config.js'
|
import config from '../../../../config.js'
|
||||||
@ -19,12 +19,15 @@ import ActionHandler from '../../common/ActionHandler.jsx'
|
|||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
|
import HostOTP from './HostOtp.jsx'
|
||||||
|
|
||||||
const log = loglevel.getLogger('HostInfo')
|
const log = loglevel.getLogger('HostInfo')
|
||||||
log.setLevel(config.logLevel)
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
const HostInfo = () => {
|
const HostInfo = () => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
const editFormRef = useRef(null)
|
||||||
|
const actionHandlerRef = useRef(null)
|
||||||
const hostId = new URLSearchParams(location.search).get('hostId')
|
const hostId = new URLSearchParams(location.search).get('hostId')
|
||||||
const [collapseState, updateCollapseState] = useCollapseState('HostInfo', {
|
const [collapseState, updateCollapseState] = useCollapseState('HostInfo', {
|
||||||
info: true,
|
info: true,
|
||||||
@ -33,43 +36,40 @@ const HostInfo = () => {
|
|||||||
auditLogs: true
|
auditLogs: true
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
const [hostOTPOpen, setHostOTPOpen] = useState(false)
|
||||||
<EditObjectForm id={hostId} type='host' style={{ height: '100%' }}>
|
const [editFormState, setEditFormState] = useState({
|
||||||
{({
|
isEditing: false,
|
||||||
loading,
|
editLoading: false,
|
||||||
isEditing,
|
formValid: false,
|
||||||
startEditing,
|
locked: false,
|
||||||
cancelEditing,
|
loading: false
|
||||||
handleUpdate,
|
})
|
||||||
formValid,
|
|
||||||
objectData,
|
|
||||||
editLoading,
|
|
||||||
lock,
|
|
||||||
fetchObject
|
|
||||||
}) => {
|
|
||||||
// Define actions for ActionHandler
|
|
||||||
const actions = {
|
const actions = {
|
||||||
reload: () => {
|
reload: () => {
|
||||||
fetchObject()
|
editFormRef?.current.handleFetchObject()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
hostOTP: () => {
|
||||||
|
setHostOTPOpen(true)
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
edit: () => {
|
edit: () => {
|
||||||
startEditing()
|
editFormRef?.current.startEditing()
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
cancelEdit: () => {
|
cancelEdit: () => {
|
||||||
cancelEditing()
|
editFormRef?.current.cancelEditing()
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
finishEdit: () => {
|
finishEdit: () => {
|
||||||
handleUpdate()
|
editFormRef?.current.handleUpdate()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionHandler actions={actions} loading={loading}>
|
<>
|
||||||
{({ callAction }) => (
|
|
||||||
<Flex
|
<Flex
|
||||||
gap='large'
|
gap='large'
|
||||||
vertical='true'
|
vertical='true'
|
||||||
@ -84,10 +84,10 @@ const HostInfo = () => {
|
|||||||
<ObjectActions
|
<ObjectActions
|
||||||
type='host'
|
type='host'
|
||||||
id={hostId}
|
id={hostId}
|
||||||
disabled={loading}
|
disabled={editFormState.loading}
|
||||||
/>
|
/>
|
||||||
<ViewButton
|
<ViewButton
|
||||||
disabled={loading}
|
disabled={editFormState.loading}
|
||||||
items={[
|
items={[
|
||||||
{ key: 'info', label: 'Host Information' },
|
{ key: 'info', label: 'Host Information' },
|
||||||
{ key: 'notes', label: 'Notes' },
|
{ key: 'notes', label: 'Notes' },
|
||||||
@ -97,39 +97,54 @@ const HostInfo = () => {
|
|||||||
updateVisibleState={updateCollapseState}
|
updateVisibleState={updateCollapseState}
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
<LockIndicator lock={lock} />
|
<LockIndicator lock={editFormState.lock} />
|
||||||
</Space>
|
</Space>
|
||||||
<Space>
|
<Space>
|
||||||
<EditButtons
|
<EditButtons
|
||||||
isEditing={isEditing}
|
isEditing={editFormState.isEditing}
|
||||||
handleUpdate={() => {
|
handleUpdate={() => {
|
||||||
callAction('finishEdit')
|
actionHandlerRef.current.callAction('finishEdit')
|
||||||
}}
|
}}
|
||||||
cancelEditing={() => {
|
cancelEditing={() => {
|
||||||
callAction('cancelEdit')
|
actionHandlerRef.current.callAction('cancelEdit')
|
||||||
}}
|
}}
|
||||||
startEditing={() => {
|
startEditing={() => {
|
||||||
callAction('edit')
|
actionHandlerRef.current.callAction('edit')
|
||||||
}}
|
}}
|
||||||
editLoading={editLoading}
|
editLoading={editFormState.editLoading}
|
||||||
formValid={formValid}
|
formValid={editFormState.formValid}
|
||||||
disabled={lock?.locked || loading}
|
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||||
loading={editLoading}
|
loading={editFormState.editLoading}
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
||||||
<Flex vertical gap={'large'}>
|
<Flex vertical gap={'large'}>
|
||||||
|
<ActionHandler
|
||||||
|
actions={actions}
|
||||||
|
loading={editFormState.loading}
|
||||||
|
ref={actionHandlerRef}
|
||||||
|
>
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Host Information'
|
title='Host Information'
|
||||||
icon={<InfoCircleIcon />}
|
icon={<InfoCircleIcon />}
|
||||||
active={collapseState.info}
|
active={collapseState.info}
|
||||||
onToggle={(expanded) =>
|
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||||
updateCollapseState('info', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='info'
|
collapseKey='info'
|
||||||
>
|
>
|
||||||
|
<EditObjectForm
|
||||||
|
id={hostId}
|
||||||
|
type='host'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
ref={editFormRef}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
console.log('Got edit form state change', state)
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading, isEditing, objectData }) => {
|
||||||
|
return (
|
||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
loading={loading}
|
loading={loading}
|
||||||
indicator={<LoadingOutlined />}
|
indicator={<LoadingOutlined />}
|
||||||
@ -137,15 +152,17 @@ const HostInfo = () => {
|
|||||||
type='host'
|
type='host'
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</EditObjectForm>
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
|
</ActionHandler>
|
||||||
|
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Notes'
|
title='Notes'
|
||||||
icon={<NoteIcon />}
|
icon={<NoteIcon />}
|
||||||
active={collapseState.notes}
|
active={collapseState.notes}
|
||||||
onToggle={(expanded) =>
|
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||||
updateCollapseState('notes', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='notes'
|
collapseKey='notes'
|
||||||
>
|
>
|
||||||
<Card>
|
<Card>
|
||||||
@ -162,7 +179,7 @@ const HostInfo = () => {
|
|||||||
}
|
}
|
||||||
collapseKey='auditLogs'
|
collapseKey='auditLogs'
|
||||||
>
|
>
|
||||||
{loading ? (
|
{editFormState.loading ? (
|
||||||
<InfoCollapsePlaceholder />
|
<InfoCollapsePlaceholder />
|
||||||
) : (
|
) : (
|
||||||
<ObjectTable
|
<ObjectTable
|
||||||
@ -175,11 +192,19 @@ const HostInfo = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
|
||||||
</ActionHandler>
|
<Modal
|
||||||
)
|
open={hostOTPOpen}
|
||||||
|
destroyOnHidden={true}
|
||||||
|
width={650}
|
||||||
|
onCancel={() => {
|
||||||
|
setHostOTPOpen(false)
|
||||||
}}
|
}}
|
||||||
</EditObjectForm>
|
footer={false}
|
||||||
|
>
|
||||||
|
<HostOTP id={hostId} />
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
138
src/components/Dashboard/Management/Hosts/HostOtp.jsx
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import React, { useContext, useEffect, useState, useRef } from 'react'
|
||||||
|
import { Input, Result, Typography, Flex, Progress, Button } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||||
|
import CopyButton from '../../common/CopyButton'
|
||||||
|
import OTPIcon from '../../../Icons/OTPIcon'
|
||||||
|
|
||||||
|
const { Text } = Typography
|
||||||
|
|
||||||
|
const HostOTP = ({ id }) => {
|
||||||
|
const { fetchHostOTP, sendObjectAction } = useContext(ApiServerContext)
|
||||||
|
const [hostObject, setHostObject] = useState(null)
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [timeRemaining, setTimeRemaining] = useState(0)
|
||||||
|
const [totalTime, setTotalTime] = useState(0)
|
||||||
|
const [initialized, setInitialized] = useState(false)
|
||||||
|
const intervalRef = useRef(null)
|
||||||
|
|
||||||
|
const fetchNewOTP = () => {
|
||||||
|
setLoading(true)
|
||||||
|
setHostObject(null) // Reset to show loading
|
||||||
|
fetchHostOTP(id, (hostOTPObject) => {
|
||||||
|
setHostObject(hostOTPObject)
|
||||||
|
setLoading(false)
|
||||||
|
|
||||||
|
if (hostOTPObject?.otpExpiresAt) {
|
||||||
|
const now = Date.now()
|
||||||
|
const expiresAt = new Date(hostOTPObject.otpExpiresAt).getTime()
|
||||||
|
const remaining = Math.max(0, expiresAt - now)
|
||||||
|
|
||||||
|
setTimeRemaining(remaining)
|
||||||
|
setTotalTime(remaining)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendTestAction = () => {
|
||||||
|
console.log('Sending test action...')
|
||||||
|
sendObjectAction(id, 'host', { method: 'testMethod' }, (result) => {
|
||||||
|
console.log('Got callback', result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hostObject === null && initialized == false) {
|
||||||
|
setInitialized(true)
|
||||||
|
fetchNewOTP()
|
||||||
|
}
|
||||||
|
}, [id])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hostObject && timeRemaining > 0) {
|
||||||
|
intervalRef.current = setInterval(() => {
|
||||||
|
setTimeRemaining((prev) => {
|
||||||
|
const newTime = prev - 1000
|
||||||
|
if (newTime <= 0) {
|
||||||
|
// OTP expired, fetch a new one
|
||||||
|
fetchNewOTP()
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return newTime
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (intervalRef.current) {
|
||||||
|
clearInterval(intervalRef.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [hostObject, timeRemaining])
|
||||||
|
|
||||||
|
// Clean up interval on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (intervalRef.current) {
|
||||||
|
clearInterval(intervalRef.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const progressPercent =
|
||||||
|
totalTime > 0 ? Math.max(0, (timeRemaining / totalTime) * 100) : 0
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex vertical align='center'>
|
||||||
|
<Button onClick={sendTestAction}>Test Action</Button>
|
||||||
|
<Result
|
||||||
|
title={'Connect a Host.'}
|
||||||
|
subTitle={<Text>Enter the following one time passcode.</Text>}
|
||||||
|
icon={<OTPIcon />}
|
||||||
|
>
|
||||||
|
<Flex justify='center'>
|
||||||
|
<Flex gap={'small'} align='center' justify='center'>
|
||||||
|
<CopyButton
|
||||||
|
size='default'
|
||||||
|
text={hostObject?.otp}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<Input.OTP
|
||||||
|
disabled={loading}
|
||||||
|
size='large'
|
||||||
|
value={hostObject?.otp}
|
||||||
|
onChange={(e) => e.preventDefault()} // prevent typing
|
||||||
|
onKeyDown={(e) => e.preventDefault()} // prevent key input
|
||||||
|
onPaste={(e) => e.preventDefault()} // prevent pasting
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ margin: '0 6px 0 8px', paddingBottom: '5px' }}>
|
||||||
|
{loading ? (
|
||||||
|
<Text>
|
||||||
|
<LoadingOutlined />
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<Progress
|
||||||
|
type='circle'
|
||||||
|
showInfo={false}
|
||||||
|
strokeColor='#32D74B'
|
||||||
|
size={14}
|
||||||
|
strokeWidth={14}
|
||||||
|
percent={progressPercent}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Result>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HostOTP.propTypes = {
|
||||||
|
id: PropTypes.string.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HostOTP
|
||||||
@ -12,6 +12,10 @@ import AuditLogIcon from '../../Icons/AuditLogIcon'
|
|||||||
import DeveloperIcon from '../../Icons/DeveloperIcon'
|
import DeveloperIcon from '../../Icons/DeveloperIcon'
|
||||||
import PersonIcon from '../../Icons/PersonIcon'
|
import PersonIcon from '../../Icons/PersonIcon'
|
||||||
import HostIcon from '../../Icons/HostIcon'
|
import HostIcon from '../../Icons/HostIcon'
|
||||||
|
import DocumentPrinterIcon from '../../Icons/DocumentPrinterIcon'
|
||||||
|
import DocumentTemplateIcon from '../../Icons/DocumentTemplateIcon'
|
||||||
|
import DocumentIcon from '../../Icons/DocumentIcon'
|
||||||
|
import DocumentSizeIcon from '../../Icons/DocumentSizeIcon'
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
@ -46,11 +50,37 @@ const items = [
|
|||||||
},
|
},
|
||||||
{ type: 'divider' },
|
{ type: 'divider' },
|
||||||
{
|
{
|
||||||
key: 'notetypes',
|
key: 'noteTypes',
|
||||||
icon: <NoteTypeIcon />,
|
icon: <NoteTypeIcon />,
|
||||||
label: 'Note Types',
|
label: 'Note Types',
|
||||||
path: '/dashboard/management/notetypes'
|
path: '/dashboard/management/notetypes'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'documents',
|
||||||
|
icon: <DocumentIcon />,
|
||||||
|
label: 'Documents',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'documentPrinters',
|
||||||
|
icon: <DocumentPrinterIcon />,
|
||||||
|
label: 'Document Printers',
|
||||||
|
path: '/dashboard/management/documentprinters'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'documentTemplates',
|
||||||
|
icon: <DocumentTemplateIcon />,
|
||||||
|
label: 'Document Templates',
|
||||||
|
path: '/dashboard/management/documenttemplates'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'documentSizes',
|
||||||
|
icon: <DocumentSizeIcon />,
|
||||||
|
label: 'Document Sizes',
|
||||||
|
path: '/dashboard/management/documentsizes'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'hosts',
|
key: 'hosts',
|
||||||
icon: <HostIcon />,
|
icon: <HostIcon />,
|
||||||
@ -70,7 +100,7 @@ const items = [
|
|||||||
path: '/dashboard/management/settings'
|
path: '/dashboard/management/settings'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'auditlogs',
|
key: 'auditLogs',
|
||||||
icon: <AuditLogIcon />,
|
icon: <AuditLogIcon />,
|
||||||
label: 'Audit Logs',
|
label: 'Audit Logs',
|
||||||
path: '/dashboard/management/auditlogs'
|
path: '/dashboard/management/auditlogs'
|
||||||
@ -96,10 +126,13 @@ const routeKeyMap = {
|
|||||||
'/dashboard/management/products': 'products',
|
'/dashboard/management/products': 'products',
|
||||||
'/dashboard/management/vendors': 'vendors',
|
'/dashboard/management/vendors': 'vendors',
|
||||||
'/dashboard/management/materials': 'materials',
|
'/dashboard/management/materials': 'materials',
|
||||||
'/dashboard/management/notetypes': 'notetypes',
|
'/dashboard/management/notetypes': 'noteTypes',
|
||||||
'/dashboard/management/settings': 'settings',
|
'/dashboard/management/settings': 'settings',
|
||||||
'/dashboard/management/auditlogs': 'auditlogs',
|
'/dashboard/management/auditlogs': 'auditLogs',
|
||||||
'/dashboard/management/hosts': 'hosts'
|
'/dashboard/management/hosts': 'hosts',
|
||||||
|
'/dashboard/management/documentsizes': 'documentSizes',
|
||||||
|
'/dashboard/management/documentprinters': 'documentPrinters',
|
||||||
|
'/dashboard/management/documenttemplates': 'documentTemplates'
|
||||||
}
|
}
|
||||||
|
|
||||||
const ManagementSidebar = (props) => {
|
const ManagementSidebar = (props) => {
|
||||||
|
|||||||
@ -28,7 +28,7 @@ import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'
|
|||||||
import PrinterPositionPanel from '../../common/PrinterPositionPanel'
|
import PrinterPositionPanel from '../../common/PrinterPositionPanel'
|
||||||
import PrinterMovementPanel from '../../common/PrinterMovementPanel'
|
import PrinterMovementPanel from '../../common/PrinterMovementPanel'
|
||||||
import PrinterMiscPanel from '../../common/PrinterMiscPanel'
|
import PrinterMiscPanel from '../../common/PrinterMiscPanel'
|
||||||
import PrinterState from '../../common/PrinterState'
|
import PrinterState from '../../common/StateDisplay'
|
||||||
import { AuthContext } from '../../context/AuthContext'
|
import { AuthContext } from '../../context/AuthContext'
|
||||||
import PrinterSubJobsTree from '../../common/PrinterJobsTree'
|
import PrinterSubJobsTree from '../../common/PrinterJobsTree'
|
||||||
import IdDisplay from '../../common/IdDisplay'
|
import IdDisplay from '../../common/IdDisplay'
|
||||||
|
|||||||
@ -1,81 +1,77 @@
|
|||||||
import React from 'react'
|
import React, { useRef, useState } from 'react'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
import { Space, Flex, Card } from 'antd'
|
import { Space, Flex, Card } from 'antd'
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import useCollapseState from '../../hooks/useCollapseState'
|
import loglevel from 'loglevel'
|
||||||
import NotesPanel from '../../common/NotesPanel'
|
import config from '../../../../config.js'
|
||||||
import InfoCollapse from '../../common/InfoCollapse'
|
import useCollapseState from '../../hooks/useCollapseState.js'
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||||
import ViewButton from '../../common/ViewButton'
|
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||||
import EditObjectForm from '../../common/EditObjectForm'
|
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||||
import EditButtons from '../../common/EditButtons'
|
import ViewButton from '../../common/ViewButton.jsx'
|
||||||
import LockIndicator from '../../common/LockIndicator.jsx'
|
|
||||||
import PrinterJobsTree from '../../common/PrinterJobsTree'
|
|
||||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||||
import PrinterIcon from '../../../Icons/PrinterIcon.jsx'
|
|
||||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||||
import ActionHandler from '../../common/ActionHandler'
|
import EditObjectForm from '../../common/EditObjectForm.jsx'
|
||||||
|
import EditButtons from '../../common/EditButtons.jsx'
|
||||||
|
import LockIndicator from '../../common/LockIndicator.jsx'
|
||||||
|
import ActionHandler from '../../common/ActionHandler.jsx'
|
||||||
import ObjectActions from '../../common/ObjectActions.jsx'
|
import ObjectActions from '../../common/ObjectActions.jsx'
|
||||||
import ObjectTable from '../../common/ObjectTable.jsx'
|
import ObjectTable from '../../common/ObjectTable.jsx'
|
||||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||||
|
|
||||||
|
const log = loglevel.getLogger('PrinterInfo')
|
||||||
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
const PrinterInfo = () => {
|
const PrinterInfo = () => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
const editFormRef = useRef(null)
|
||||||
|
const actionHandlerRef = useRef(null)
|
||||||
const printerId = new URLSearchParams(location.search).get('printerId')
|
const printerId = new URLSearchParams(location.search).get('printerId')
|
||||||
const [collapseState, updateCollapseState] = useCollapseState('PrinterInfo', {
|
const [collapseState, updateCollapseState] = useCollapseState('PrinterInfo', {
|
||||||
info: true,
|
info: true,
|
||||||
jobs: true,
|
stocks: true,
|
||||||
notes: true,
|
notes: true,
|
||||||
auditLogsParent: true,
|
auditLogs: true
|
||||||
auditLogsOwner: true
|
})
|
||||||
|
|
||||||
|
const [editFormState, setEditFormState] = useState({
|
||||||
|
isEditing: false,
|
||||||
|
editLoading: false,
|
||||||
|
formValid: false,
|
||||||
|
locked: false,
|
||||||
|
loading: false
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
|
||||||
<EditObjectForm
|
|
||||||
id={printerId}
|
|
||||||
type='printer'
|
|
||||||
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
|
|
||||||
>
|
|
||||||
{({
|
|
||||||
loading,
|
|
||||||
isEditing,
|
|
||||||
startEditing,
|
|
||||||
cancelEditing,
|
|
||||||
handleUpdate,
|
|
||||||
formValid,
|
|
||||||
objectData,
|
|
||||||
editLoading,
|
|
||||||
lock,
|
|
||||||
fetchObject
|
|
||||||
}) => {
|
|
||||||
// Define actions for ActionHandler
|
|
||||||
const actions = {
|
const actions = {
|
||||||
reload: () => {
|
reload: () => {
|
||||||
fetchObject()
|
editFormRef?.current.handleFetchObject()
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
edit: () => {
|
edit: () => {
|
||||||
startEditing()
|
editFormRef?.current.startEditing()
|
||||||
|
console.log('CALLING START EDITING')
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
cancelEdit: () => {
|
cancelEdit: () => {
|
||||||
cancelEditing()
|
editFormRef?.current.cancelEditing()
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
finishEdit: () => {
|
finishEdit: () => {
|
||||||
handleUpdate()
|
editFormRef?.current.handleUpdate()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionHandler actions={actions} loading={loading}>
|
<>
|
||||||
{({ callAction }) => (
|
|
||||||
<Flex
|
<Flex
|
||||||
gap='large'
|
gap='large'
|
||||||
vertical='true'
|
vertical='true'
|
||||||
style={{ height: '100%', minHeight: 0 }}
|
style={{
|
||||||
|
height: 'calc(var(--unit-100vh) - 155px)',
|
||||||
|
minHeight: 0
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Flex justify={'space-between'}>
|
<Flex justify={'space-between'}>
|
||||||
<Space size='middle'>
|
<Space size='middle'>
|
||||||
@ -83,60 +79,67 @@ const PrinterInfo = () => {
|
|||||||
<ObjectActions
|
<ObjectActions
|
||||||
type='printer'
|
type='printer'
|
||||||
id={printerId}
|
id={printerId}
|
||||||
disabled={loading}
|
disabled={editFormState.loading}
|
||||||
/>
|
/>
|
||||||
<ViewButton
|
<ViewButton
|
||||||
disabled={loading}
|
disabled={editFormState.loading}
|
||||||
items={[
|
items={[
|
||||||
{ key: 'info', label: 'Printer Information' },
|
{ key: 'info', label: 'Printer Information' },
|
||||||
{ key: 'jobs', label: 'Printer Jobs' },
|
|
||||||
{ key: 'notes', label: 'Notes' },
|
{ key: 'notes', label: 'Notes' },
|
||||||
{
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
key: 'auditLogsParent',
|
|
||||||
label: 'Audit Logs (By Parent)'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'auditLogsOwner',
|
|
||||||
label: 'Audit Logs (By Owner)'
|
|
||||||
}
|
|
||||||
]}
|
]}
|
||||||
visibleState={collapseState}
|
visibleState={collapseState}
|
||||||
updateVisibleState={updateCollapseState}
|
updateVisibleState={updateCollapseState}
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
<LockIndicator lock={lock} />
|
<LockIndicator lock={editFormState.lock} />
|
||||||
</Space>
|
</Space>
|
||||||
<Space>
|
<Space>
|
||||||
<EditButtons
|
<EditButtons
|
||||||
isEditing={isEditing}
|
isEditing={editFormState.isEditing}
|
||||||
handleUpdate={() => {
|
handleUpdate={() => {
|
||||||
callAction('finishEdit')
|
actionHandlerRef.current.callAction('finishEdit')
|
||||||
}}
|
}}
|
||||||
cancelEditing={() => {
|
cancelEditing={() => {
|
||||||
callAction('cancelEdit')
|
actionHandlerRef.current.callAction('cancelEdit')
|
||||||
}}
|
}}
|
||||||
startEditing={() => {
|
startEditing={() => {
|
||||||
callAction('edit')
|
actionHandlerRef.current.callAction('edit')
|
||||||
}}
|
}}
|
||||||
editLoading={editLoading}
|
editLoading={editFormState.editLoading}
|
||||||
formValid={formValid}
|
formValid={editFormState.formValid}
|
||||||
disabled={lock?.locked || loading}
|
disabled={editFormState.lock?.locked || editFormState.loading}
|
||||||
loading={editLoading}
|
loading={editFormState.editLoading}
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<div style={{ height: '100%', overflow: 'auto' }}>
|
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
||||||
<Flex vertical gap={'large'}>
|
<Flex vertical gap={'large'}>
|
||||||
|
<ActionHandler
|
||||||
|
actions={actions}
|
||||||
|
loading={editFormState.loading}
|
||||||
|
ref={actionHandlerRef}
|
||||||
|
>
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Printer Information'
|
title='Printer Information'
|
||||||
icon={<InfoCircleIcon />}
|
icon={<InfoCircleIcon />}
|
||||||
active={collapseState.info}
|
active={collapseState.info}
|
||||||
onToggle={(expanded) =>
|
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||||
updateCollapseState('info', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='info'
|
collapseKey='info'
|
||||||
>
|
>
|
||||||
|
<EditObjectForm
|
||||||
|
id={printerId}
|
||||||
|
type='printer'
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
ref={editFormRef}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
console.log('Got edit form state change', state)
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ loading, isEditing, objectData }) => {
|
||||||
|
return (
|
||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
loading={loading}
|
loading={loading}
|
||||||
indicator={<LoadingOutlined />}
|
indicator={<LoadingOutlined />}
|
||||||
@ -144,30 +147,17 @@ const PrinterInfo = () => {
|
|||||||
type='printer'
|
type='printer'
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</EditObjectForm>
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
|
</ActionHandler>
|
||||||
<InfoCollapse
|
|
||||||
title='Printer Jobs'
|
|
||||||
icon={<PrinterIcon />}
|
|
||||||
active={collapseState.jobs}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('jobs', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='jobs'
|
|
||||||
>
|
|
||||||
<PrinterJobsTree
|
|
||||||
subJobs={objectData?.subJobs}
|
|
||||||
loading={loading}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
|
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Notes'
|
title='Notes'
|
||||||
icon={<NoteIcon />}
|
icon={<NoteIcon />}
|
||||||
active={collapseState.notes}
|
active={collapseState.notes}
|
||||||
onToggle={(expanded) =>
|
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||||
updateCollapseState('notes', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='notes'
|
collapseKey='notes'
|
||||||
>
|
>
|
||||||
<Card>
|
<Card>
|
||||||
@ -176,15 +166,15 @@ const PrinterInfo = () => {
|
|||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
|
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Audit Logs (By Parent)'
|
title='Audit Logs'
|
||||||
icon={<AuditLogIcon />}
|
icon={<AuditLogIcon />}
|
||||||
active={collapseState.auditLogsParent}
|
active={collapseState.auditLogs}
|
||||||
onToggle={(expanded) =>
|
onToggle={(expanded) =>
|
||||||
updateCollapseState('auditLogsParent', expanded)
|
updateCollapseState('auditLogs', expanded)
|
||||||
}
|
}
|
||||||
collapseKey='auditLogs'
|
collapseKey='auditLogs'
|
||||||
>
|
>
|
||||||
{loading ? (
|
{editFormState.loading ? (
|
||||||
<InfoCollapsePlaceholder />
|
<InfoCollapsePlaceholder />
|
||||||
) : (
|
) : (
|
||||||
<ObjectTable
|
<ObjectTable
|
||||||
@ -194,33 +184,10 @@ const PrinterInfo = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
<InfoCollapse
|
|
||||||
title='Audit Logs (By Owner)'
|
|
||||||
icon={<AuditLogIcon />}
|
|
||||||
active={collapseState.auditLogsOwner}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('auditLogsOwner', expanded)
|
|
||||||
}
|
|
||||||
collapseKey='auditLogs'
|
|
||||||
>
|
|
||||||
{loading ? (
|
|
||||||
<InfoCollapsePlaceholder />
|
|
||||||
) : (
|
|
||||||
<ObjectTable
|
|
||||||
type='auditLog'
|
|
||||||
masterFilter={{ 'owner._id': printerId }}
|
|
||||||
visibleColumns={{ _id: false, 'owner._id': false }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</InfoCollapse>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
</>
|
||||||
</ActionHandler>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</EditObjectForm>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,19 @@
|
|||||||
import React, { useEffect, useRef } from 'react'
|
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
const ActionHandler = ({
|
const ActionHandler = forwardRef(
|
||||||
|
(
|
||||||
|
{
|
||||||
children,
|
children,
|
||||||
actions = {},
|
actions = {},
|
||||||
actionParam = 'action',
|
actionParam = 'action',
|
||||||
clearAfterExecute = true,
|
clearAfterExecute = true,
|
||||||
onAction,
|
onAction,
|
||||||
loading = true
|
loading = true
|
||||||
}) => {
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const action = new URLSearchParams(location.search).get(actionParam)
|
const action = new URLSearchParams(location.search).get(actionParam)
|
||||||
@ -36,6 +40,7 @@ const ActionHandler = ({
|
|||||||
) {
|
) {
|
||||||
// Execute the action
|
// Execute the action
|
||||||
const result = actions[action]()
|
const result = actions[action]()
|
||||||
|
|
||||||
// Mark this action as executed
|
// Mark this action as executed
|
||||||
lastExecutedAction.current = action
|
lastExecutedAction.current = action
|
||||||
// Call optional callback
|
// Call optional callback
|
||||||
@ -66,9 +71,16 @@ const ActionHandler = ({
|
|||||||
navigate
|
navigate
|
||||||
])
|
])
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
callAction
|
||||||
|
}))
|
||||||
|
|
||||||
// Return null as this is a utility component
|
// Return null as this is a utility component
|
||||||
return <>{children({ callAction })}</>
|
return children
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
ActionHandler.displayName = 'ActionHandler'
|
||||||
|
|
||||||
ActionHandler.propTypes = {
|
ActionHandler.propTypes = {
|
||||||
children: PropTypes.func,
|
children: PropTypes.func,
|
||||||
|
|||||||
@ -1,213 +0,0 @@
|
|||||||
import React, { forwardRef, useState } from 'react'
|
|
||||||
import { Typography, Space, Descriptions, Badge, Table } from 'antd'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import IdDisplay from './IdDisplay'
|
|
||||||
import { AuditOutlined, LoadingOutlined } from '@ant-design/icons'
|
|
||||||
import TimeDisplay from '../common/TimeDisplay'
|
|
||||||
import BoolDisplay from './BoolDisplay'
|
|
||||||
import StateTag from './StateTag'
|
|
||||||
|
|
||||||
const { Text } = Typography
|
|
||||||
|
|
||||||
const formatPropertyName = (name) => {
|
|
||||||
return name
|
|
||||||
.replace(/([A-Z])/g, ' $1')
|
|
||||||
.replace(/^./, (str) => str.toUpperCase())
|
|
||||||
}
|
|
||||||
|
|
||||||
const isObjectId = (value) => {
|
|
||||||
return typeof value === 'string' && /^[0-9a-fA-F]{24}$/.test(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatValue = (value, propertyName) => {
|
|
||||||
if (value === null || value === undefined || value === '') {
|
|
||||||
return (
|
|
||||||
<div style={{ maxWidth: 20 }}>
|
|
||||||
<Text type='secondary'>n/a</Text>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle colors specifically
|
|
||||||
if (propertyName === 'color') {
|
|
||||||
return <Badge color={value} text={value} />
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propertyName === 'state' && typeof value === 'object' && value.type) {
|
|
||||||
return <StateTag state={value.type} />
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the value is a timestamp (ISO date string)
|
|
||||||
if (
|
|
||||||
typeof value === 'string' &&
|
|
||||||
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)
|
|
||||||
) {
|
|
||||||
return <TimeDisplay dateTime={value} />
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === 'boolean' || value === true || value === false) {
|
|
||||||
return <BoolDisplay value={value} yesNo={true} />
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isObjectId(value)) {
|
|
||||||
return (
|
|
||||||
<IdDisplay
|
|
||||||
id={value}
|
|
||||||
type={propertyName.toLowerCase().replaceAll('current', '')}
|
|
||||||
longId={false}
|
|
||||||
showHyperlink={true}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === 'object') {
|
|
||||||
return <Text>{JSON.stringify(value)}</Text>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Text>{value}</Text>
|
|
||||||
}
|
|
||||||
|
|
||||||
const AuditLogTable = forwardRef(
|
|
||||||
(
|
|
||||||
{ items, loading = false, showTargetColumn = true, showOwnerColumn = true },
|
|
||||||
ref
|
|
||||||
) => {
|
|
||||||
const [sortedInfo, setSortedInfo] = useState({
|
|
||||||
columnKey: 'createdAt',
|
|
||||||
order: 'descend'
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleChange = (pagination, filters, sorter) => {
|
|
||||||
setSortedInfo(sorter)
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: '',
|
|
||||||
key: 'icon',
|
|
||||||
width: 50,
|
|
||||||
render: () => <AuditOutlined />
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'ID',
|
|
||||||
dataIndex: '_id',
|
|
||||||
key: 'id',
|
|
||||||
width: 180,
|
|
||||||
render: (text) => (
|
|
||||||
<IdDisplay id={text} type={'auditlog'} longId={false} />
|
|
||||||
),
|
|
||||||
sorter: (a, b) => a._id.localeCompare(b._id)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
if (showOwnerColumn) {
|
|
||||||
columns.push(
|
|
||||||
{
|
|
||||||
title: 'Owner Name',
|
|
||||||
dataIndex: ['owner', 'name'],
|
|
||||||
key: 'name',
|
|
||||||
width: 200,
|
|
||||||
sorter: (a, b) =>
|
|
||||||
(a.owner?.name || '').localeCompare(b.owner?.name || '')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Owner',
|
|
||||||
key: 'owner',
|
|
||||||
width: 180,
|
|
||||||
render: (record) => (
|
|
||||||
<IdDisplay
|
|
||||||
id={record.owner._id}
|
|
||||||
type={record.ownerModel.toLowerCase()}
|
|
||||||
longId={false}
|
|
||||||
showHyperlink={true}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showTargetColumn) {
|
|
||||||
columns.push({
|
|
||||||
title: 'Target',
|
|
||||||
key: 'target',
|
|
||||||
width: 180,
|
|
||||||
render: (record) => (
|
|
||||||
<IdDisplay
|
|
||||||
id={record.target}
|
|
||||||
type={record.targetModel.toLowerCase()}
|
|
||||||
longId={false}
|
|
||||||
showHyperlink={true}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
columns.push({
|
|
||||||
title: 'Properties',
|
|
||||||
dataIndex: 'type',
|
|
||||||
key: 'type',
|
|
||||||
width: 550,
|
|
||||||
render: (_, record) => {
|
|
||||||
const oldValue = record.oldValue || {}
|
|
||||||
const newValue = record.newValue || {}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Descriptions size='small' column={1}>
|
|
||||||
{Object.keys(newValue).map((key) => (
|
|
||||||
<Descriptions.Item key={key} label={formatPropertyName(key)}>
|
|
||||||
<Space>
|
|
||||||
{formatValue(oldValue[key], key)}
|
|
||||||
<Text type='secondary'>→</Text>
|
|
||||||
{formatValue(newValue[key], key)}
|
|
||||||
</Space>
|
|
||||||
</Descriptions.Item>
|
|
||||||
))}
|
|
||||||
</Descriptions>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.push({
|
|
||||||
title: 'Timestamp',
|
|
||||||
dataIndex: 'createdAt',
|
|
||||||
key: 'createdAt',
|
|
||||||
width: 180,
|
|
||||||
defaultSortOrder: 'descend',
|
|
||||||
sorter: (a, b) => new Date(a.createdAt) - new Date(b.createdAt),
|
|
||||||
render: (createdAt) => {
|
|
||||||
if (createdAt) {
|
|
||||||
return <TimeDisplay dateTime={createdAt} />
|
|
||||||
} else {
|
|
||||||
return 'n/a'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table
|
|
||||||
ref={ref}
|
|
||||||
columns={columns}
|
|
||||||
dataSource={items}
|
|
||||||
loading={{ spinning: loading, indicator: <LoadingOutlined spin /> }}
|
|
||||||
pagination={false}
|
|
||||||
scroll={{ x: 'max-content' }}
|
|
||||||
onChange={handleChange}
|
|
||||||
sortDirections={['ascend', 'descend']}
|
|
||||||
sortOrder={
|
|
||||||
sortedInfo.columnKey === 'createdAt' ? sortedInfo.order : null
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
AuditLogTable.displayName = 'AuditLogTable'
|
|
||||||
|
|
||||||
AuditLogTable.propTypes = {
|
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
||||||
loading: PropTypes.bool,
|
|
||||||
showTargetColumn: PropTypes.bool,
|
|
||||||
showOwnerColumn: PropTypes.bool
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AuditLogTable
|
|
||||||
192
src/components/Dashboard/common/CodeBlockEditor.jsx
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import React, { useMemo, useState } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import CodeMirror from '@uiw/react-codemirror'
|
||||||
|
import { javascript } from '@codemirror/lang-javascript'
|
||||||
|
import { python } from '@codemirror/lang-python'
|
||||||
|
import { json } from '@codemirror/lang-json'
|
||||||
|
import { css } from '@codemirror/lang-css'
|
||||||
|
import { html } from '@codemirror/lang-html'
|
||||||
|
import { markdown } from '@codemirror/lang-markdown'
|
||||||
|
import { sql } from '@codemirror/lang-sql'
|
||||||
|
import { java } from '@codemirror/lang-java'
|
||||||
|
import { cpp } from '@codemirror/lang-cpp'
|
||||||
|
import { rust } from '@codemirror/lang-rust'
|
||||||
|
import { go } from '@codemirror/lang-go'
|
||||||
|
import { php } from '@codemirror/lang-php'
|
||||||
|
import { yaml } from '@codemirror/lang-yaml'
|
||||||
|
import { xml } from '@codemirror/lang-xml'
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark'
|
||||||
|
import { useThemeContext } from '../context/ThemeContext'
|
||||||
|
import { Button, Modal, Typography } from 'antd'
|
||||||
|
|
||||||
|
const { Link } = Typography
|
||||||
|
|
||||||
|
export default function CodeBlockEditor({
|
||||||
|
code = '',
|
||||||
|
language = 'javascript',
|
||||||
|
style = {},
|
||||||
|
className = '',
|
||||||
|
readOnly = false,
|
||||||
|
onChange = null,
|
||||||
|
height = 'auto',
|
||||||
|
showLineNumbers = true,
|
||||||
|
disabled = false,
|
||||||
|
minimal = false
|
||||||
|
}) {
|
||||||
|
const { isDarkMode } = useThemeContext()
|
||||||
|
const [codeMirrorOpen, setCodeMirrorOpen] = useState(false)
|
||||||
|
var editorCode = code
|
||||||
|
|
||||||
|
if (typeof code == 'object' && language == 'json') {
|
||||||
|
editorCode = JSON.stringify(code, null, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(editorCode)
|
||||||
|
|
||||||
|
// Map language to CodeMirror extension
|
||||||
|
const languageExtension = useMemo(() => {
|
||||||
|
switch (language.toLowerCase()) {
|
||||||
|
case 'javascript':
|
||||||
|
case 'js':
|
||||||
|
return javascript()
|
||||||
|
case 'python':
|
||||||
|
case 'py':
|
||||||
|
return python()
|
||||||
|
case 'json':
|
||||||
|
return json()
|
||||||
|
case 'xml':
|
||||||
|
return xml()
|
||||||
|
case 'ejs':
|
||||||
|
return xml()
|
||||||
|
case 'html':
|
||||||
|
return html()
|
||||||
|
case 'css':
|
||||||
|
return css()
|
||||||
|
case 'markdown':
|
||||||
|
case 'md':
|
||||||
|
return markdown()
|
||||||
|
case 'sql':
|
||||||
|
return sql()
|
||||||
|
case 'java':
|
||||||
|
return java()
|
||||||
|
case 'cpp':
|
||||||
|
case 'c++':
|
||||||
|
case 'c':
|
||||||
|
return cpp()
|
||||||
|
case 'rust':
|
||||||
|
case 'rs':
|
||||||
|
return rust()
|
||||||
|
case 'go':
|
||||||
|
return go()
|
||||||
|
case 'php':
|
||||||
|
return php()
|
||||||
|
case 'yaml':
|
||||||
|
case 'yml':
|
||||||
|
return yaml()
|
||||||
|
default:
|
||||||
|
return javascript() // Default fallback
|
||||||
|
}
|
||||||
|
}, [language])
|
||||||
|
|
||||||
|
const handleOnChange = (value) => {
|
||||||
|
if (typeof code == 'object' && language == 'json') {
|
||||||
|
console.log('Parsing object', JSON.parse(value))
|
||||||
|
onChange(JSON.parse(value))
|
||||||
|
} else {
|
||||||
|
onChange(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeMirror = (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: '1px solid #85858541',
|
||||||
|
...style
|
||||||
|
}}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<CodeMirror
|
||||||
|
value={editorCode}
|
||||||
|
height={height}
|
||||||
|
theme={isDarkMode ? oneDark : 'light'}
|
||||||
|
extensions={[languageExtension]}
|
||||||
|
readOnly={readOnly || disabled}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
basicSetup={{
|
||||||
|
lineNumbers: showLineNumbers,
|
||||||
|
foldGutter: true,
|
||||||
|
dropCursor: false,
|
||||||
|
allowMultipleSelections: false,
|
||||||
|
indentOnInput: false,
|
||||||
|
syntaxHighlighting: true,
|
||||||
|
bracketMatching: true,
|
||||||
|
closeBrackets: true,
|
||||||
|
autocompletion: !readOnly,
|
||||||
|
rectangularSelection: false,
|
||||||
|
crosshairCursor: false,
|
||||||
|
highlightActiveLine: !readOnly,
|
||||||
|
highlightSelectionMatches: false,
|
||||||
|
closeBracketsKeymap: false,
|
||||||
|
searchKeymap: false,
|
||||||
|
foldKeymap: false,
|
||||||
|
completionKeymap: !readOnly,
|
||||||
|
lintKeymap: false
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
fontSize: '14px',
|
||||||
|
fontFamily: 'Monaco, Menlo, "Ubuntu Mono", monospace'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{minimal ? (
|
||||||
|
<div style={{ maxWidth: '300px' }}>
|
||||||
|
<Link
|
||||||
|
onClick={() => {
|
||||||
|
setCodeMirrorOpen(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<pre>{editorCode.slice(-64).trim()}...</pre>
|
||||||
|
</Link>
|
||||||
|
<Modal
|
||||||
|
closeIcon={false}
|
||||||
|
open={codeMirrorOpen}
|
||||||
|
width={800}
|
||||||
|
footer={
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setCodeMirrorOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{codeMirror}
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
codeMirror
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeBlockEditor.propTypes = {
|
||||||
|
code: PropTypes.string.isRequired,
|
||||||
|
language: PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.arrayOf(PropTypes.string)
|
||||||
|
]),
|
||||||
|
style: PropTypes.object,
|
||||||
|
className: PropTypes.string,
|
||||||
|
readOnly: PropTypes.bool,
|
||||||
|
onChange: PropTypes.func,
|
||||||
|
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
|
showLineNumbers: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
minimal: PropTypes.bool
|
||||||
|
}
|
||||||
@ -3,7 +3,12 @@ import PropTypes from 'prop-types'
|
|||||||
import { Button, Tooltip, message } from 'antd'
|
import { Button, Tooltip, message } from 'antd'
|
||||||
import CopyIcon from '../../Icons/CopyIcon'
|
import CopyIcon from '../../Icons/CopyIcon'
|
||||||
|
|
||||||
const CopyButton = ({ text, tooltip = 'Copy', size = 'small' }) => {
|
const CopyButton = ({
|
||||||
|
text,
|
||||||
|
tooltip = 'Copy',
|
||||||
|
size = 'small',
|
||||||
|
type = 'text'
|
||||||
|
}) => {
|
||||||
const [messageApi, contextHolder] = message.useMessage()
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
|
||||||
const doCopy = (copyText) => {
|
const doCopy = (copyText) => {
|
||||||
@ -49,7 +54,7 @@ const CopyButton = ({ text, tooltip = 'Copy', size = 'small' }) => {
|
|||||||
style={{ minWidth: 25 }}
|
style={{ minWidth: 25 }}
|
||||||
width={20}
|
width={20}
|
||||||
size={size}
|
size={size}
|
||||||
type={'text'}
|
type={type}
|
||||||
onClick={() => doCopy(text)}
|
onClick={() => doCopy(text)}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@ -61,7 +66,8 @@ CopyButton.propTypes = {
|
|||||||
text: PropTypes.string.isRequired,
|
text: PropTypes.string.isRequired,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
tooltip: PropTypes.string,
|
tooltip: PropTypes.string,
|
||||||
size: PropTypes.string
|
size: PropTypes.string,
|
||||||
|
type: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CopyButton
|
export default CopyButton
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Breadcrumb, Button, Flex, Space } from 'antd'
|
|||||||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||||||
import ArrowLeftIcon from '../../Icons/ArrowLeftIcon'
|
import ArrowLeftIcon from '../../Icons/ArrowLeftIcon'
|
||||||
import ArrowRightIcon from '../../Icons/ArrowRightIcon'
|
import ArrowRightIcon from '../../Icons/ArrowRightIcon'
|
||||||
|
import { getModelByName } from '../../../database/ObjectModels'
|
||||||
|
|
||||||
const breadcrumbNameMap = {
|
const breadcrumbNameMap = {
|
||||||
production: 'Production',
|
production: 'Production',
|
||||||
@ -11,30 +12,8 @@ const breadcrumbNameMap = {
|
|||||||
management: 'Management',
|
management: 'Management',
|
||||||
developer: 'Developer',
|
developer: 'Developer',
|
||||||
overview: 'Overview',
|
overview: 'Overview',
|
||||||
printers: 'Printers',
|
|
||||||
hosts: 'Hosts',
|
|
||||||
control: 'Control',
|
|
||||||
info: 'Info',
|
info: 'Info',
|
||||||
jobs: 'Jobs',
|
design: 'Design'
|
||||||
subjobs: 'Sub Jobs',
|
|
||||||
gcodefiles: 'G Code Files',
|
|
||||||
filaments: 'Filaments',
|
|
||||||
parts: 'Parts',
|
|
||||||
products: 'Products',
|
|
||||||
vendors: 'Vendors',
|
|
||||||
materials: 'Materials',
|
|
||||||
notetypes: 'Note Types',
|
|
||||||
users: 'Users',
|
|
||||||
settings: 'Settings',
|
|
||||||
auditlogs: 'Audit Logs',
|
|
||||||
filamentstocks: 'Filament Stocks',
|
|
||||||
partstocks: 'Part Stocks',
|
|
||||||
productstocks: 'Products',
|
|
||||||
stockevents: 'Stock Events',
|
|
||||||
stockaudits: 'Stock Audits',
|
|
||||||
sessionstorage: 'Session Storage',
|
|
||||||
authcontextdebug: 'Auth Context Debug',
|
|
||||||
printservercontextdebug: 'Print Server Context Debug'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainSections = ['production', 'inventory', 'management', 'developer']
|
const mainSections = ['production', 'inventory', 'management', 'developer']
|
||||||
@ -44,15 +23,39 @@ const DashboardBreadcrumb = () => {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const pathSnippets = location.pathname.split('/').filter((i) => i)
|
const pathSnippets = location.pathname.split('/').filter((i) => i)
|
||||||
|
|
||||||
|
function segmentToModel(segment) {
|
||||||
|
if (segment) {
|
||||||
|
// If segment ends with 's', remove it and get the model
|
||||||
|
if (segment.endsWith('s')) {
|
||||||
|
const singularSegment = segment.slice(0, -1)
|
||||||
|
return getModelByName(singularSegment, true)
|
||||||
|
}
|
||||||
|
// Otherwise, get the model as is
|
||||||
|
return getModelByName(segment, true)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const breadcrumbItems = pathSnippets.map((segment, index) => {
|
const breadcrumbItems = pathSnippets.map((segment, index) => {
|
||||||
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`
|
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`
|
||||||
if (segment !== 'dashboard') {
|
if (segment !== 'dashboard') {
|
||||||
const isMainSection = mainSections.includes(segment)
|
const isMainSection = mainSections.includes(segment)
|
||||||
const name = breadcrumbNameMap[segment] || segment
|
const model = segmentToModel(segment)
|
||||||
|
const modelLabelPlural = model?.label ? `${model.label}s` : null
|
||||||
|
const name = breadcrumbNameMap[segment] || modelLabelPlural || segment
|
||||||
|
if (isMainSection) {
|
||||||
return {
|
return {
|
||||||
title: isMainSection ? (
|
title: (
|
||||||
<span style={{ padding: '0 12px' }}>{name}</span>
|
<span style={{ padding: '0 12px' }} key={segment}>
|
||||||
) : (
|
{name}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
key: segment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: (
|
||||||
<Link to={url} style={{ padding: '0 12px' }}>
|
<Link to={url} style={{ padding: '0 12px' }}>
|
||||||
{name}
|
{name}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import {
|
|||||||
LoadingOutlined
|
LoadingOutlined
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import { AuthContext } from '../context/AuthContext'
|
import { AuthContext } from '../context/AuthContext'
|
||||||
import { PrintServerContext } from '../context/PrintServerContext'
|
|
||||||
import { SpotlightContext } from '../context/SpotlightContext'
|
import { SpotlightContext } from '../context/SpotlightContext'
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
import { useNavigate, useLocation } from 'react-router-dom'
|
import { useNavigate, useLocation } from 'react-router-dom'
|
||||||
@ -36,7 +35,6 @@ import BellIcon from '../../Icons/BellIcon'
|
|||||||
import SearchIcon from '../../Icons/SearchIcon'
|
import SearchIcon from '../../Icons/SearchIcon'
|
||||||
import SettingsIcon from '../../Icons/SettingsIcon'
|
import SettingsIcon from '../../Icons/SettingsIcon'
|
||||||
import DeveloperIcon from '../../Icons/DeveloperIcon'
|
import DeveloperIcon from '../../Icons/DeveloperIcon'
|
||||||
import PrinterIcon from '../../Icons/PrinterIcon'
|
|
||||||
import { ElectronContext } from '../context/ElectronContext'
|
import { ElectronContext } from '../context/ElectronContext'
|
||||||
import DashboardWindowButtons from './DashboardWindowButtons'
|
import DashboardWindowButtons from './DashboardWindowButtons'
|
||||||
|
|
||||||
@ -44,9 +42,7 @@ const DashboardNavigation = () => {
|
|||||||
const { logout, userProfile } = useContext(AuthContext)
|
const { logout, userProfile } = useContext(AuthContext)
|
||||||
const { showSpotlight } = useContext(SpotlightContext)
|
const { showSpotlight } = useContext(SpotlightContext)
|
||||||
const { toggleNotificationCenter, unreadCount } = useContext(ApiServerContext)
|
const { toggleNotificationCenter, unreadCount } = useContext(ApiServerContext)
|
||||||
const { printServer } = useContext(PrintServerContext)
|
const { connecting, connected } = useContext(ApiServerContext)
|
||||||
const { apiServer } = useContext(ApiServerContext)
|
|
||||||
const [printServerState, setPrintServerState] = useState('disconnected')
|
|
||||||
const [apiServerState, setApiServerState] = useState('disconnected')
|
const [apiServerState, setApiServerState] = useState('disconnected')
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
@ -62,24 +58,15 @@ const DashboardNavigation = () => {
|
|||||||
}, [location.pathname])
|
}, [location.pathname])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (printServer?.connecting) {
|
if (connecting == true) {
|
||||||
setPrintServerState('connecting')
|
|
||||||
} else if (printServer?.connected) {
|
|
||||||
setPrintServerState('connected')
|
|
||||||
} else {
|
|
||||||
setPrintServerState('disconnected')
|
|
||||||
}
|
|
||||||
}, [printServer?.connecting, printServer?.connected])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (apiServer?.connecting) {
|
|
||||||
setApiServerState('connecting')
|
setApiServerState('connecting')
|
||||||
} else if (apiServer?.connected) {
|
} else if (connected == true) {
|
||||||
setApiServerState('connected')
|
setApiServerState('connected')
|
||||||
} else {
|
} else {
|
||||||
setApiServerState('disconnected')
|
setApiServerState('disconnected')
|
||||||
}
|
}
|
||||||
}, [apiServer?.connecting, apiServer?.connected])
|
console.log('Connecting/connected', connecting, connected)
|
||||||
|
}, [connecting, connected])
|
||||||
|
|
||||||
const mainMenuItems = [
|
const mainMenuItems = [
|
||||||
{
|
{
|
||||||
@ -191,33 +178,6 @@ const DashboardNavigation = () => {
|
|||||||
|
|
||||||
{process.env.NODE_ENV === 'development' && (
|
{process.env.NODE_ENV === 'development' && (
|
||||||
<Space>
|
<Space>
|
||||||
{printServerState === 'connected' ? (
|
|
||||||
<Tooltip title='Connected to print server' arrow={false}>
|
|
||||||
<Tag
|
|
||||||
color='success'
|
|
||||||
style={{ marginRight: 0 }}
|
|
||||||
icon={<PrinterIcon />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
{printServerState === 'connecting' ? (
|
|
||||||
<Tooltip title='Connecting to print erver...' arrow={false}>
|
|
||||||
<Tag
|
|
||||||
color='default'
|
|
||||||
style={{ marginRight: 0 }}
|
|
||||||
icon={<LoadingOutlined />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
{printServerState === 'disconnected' ? (
|
|
||||||
<Tooltip title='Disconnected from print server' arrow={false}>
|
|
||||||
<Tag
|
|
||||||
color='error'
|
|
||||||
style={{ marginRight: 0 }}
|
|
||||||
icon={<PrinterIcon />}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
{apiServerState === 'connected' ? (
|
{apiServerState === 'connected' ? (
|
||||||
<Tooltip title='Connected to api server' arrow={false}>
|
<Tooltip title='Connected to api server' arrow={false}>
|
||||||
<Tag
|
<Tag
|
||||||
@ -230,7 +190,7 @@ const DashboardNavigation = () => {
|
|||||||
{apiServerState === 'connecting' ? (
|
{apiServerState === 'connecting' ? (
|
||||||
<Tooltip title='Connecting to api erver...' arrow={false}>
|
<Tooltip title='Connecting to api erver...' arrow={false}>
|
||||||
<Tag
|
<Tag
|
||||||
color='default'
|
color='warning'
|
||||||
style={{ marginRight: 0 }}
|
style={{ marginRight: 0 }}
|
||||||
icon={<LoadingOutlined />}
|
icon={<LoadingOutlined />}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -38,18 +38,35 @@ const DashboardSidebar = ({
|
|||||||
if (onCollapse) onCollapse(newCollapsed)
|
if (onCollapse) onCollapse(newCollapsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add onClick to each item
|
// Recursive function to map items and their children
|
||||||
const _items = items.map((item) => {
|
const mapItemsRecursively = (items) => {
|
||||||
if (item?.type == 'divider') {
|
return items.map((item) => {
|
||||||
|
if (item?.type === 'divider') {
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
|
const mappedItem = {
|
||||||
key: item.key,
|
key: item.key,
|
||||||
icon: item.icon,
|
icon: item.icon,
|
||||||
label: item.label,
|
label: item.label
|
||||||
onClick: () => navigate(item.path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add onClick for items with paths (leaf nodes)
|
||||||
|
if (item.path) {
|
||||||
|
mappedItem.onClick = () => navigate(item.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively map children if they exist
|
||||||
|
if (item?.children && Array.isArray(item.children)) {
|
||||||
|
mappedItem.children = mapItemsRecursively(item.children)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappedItem
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map items recursively
|
||||||
|
const _items = mapItemsRecursively(items)
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,4 +1,11 @@
|
|||||||
import React, { useState, useEffect, useContext, useCallback } from 'react'
|
import React, {
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useContext,
|
||||||
|
useCallback,
|
||||||
|
forwardRef,
|
||||||
|
useImperativeHandle
|
||||||
|
} from 'react'
|
||||||
import { Form, message } from 'antd'
|
import { Form, message } from 'antd'
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
import { AuthContext } from '../context/AuthContext'
|
import { AuthContext } from '../context/AuthContext'
|
||||||
@ -18,7 +25,8 @@ import merge from 'lodash/merge'
|
|||||||
* loading, isEditing, startEditing, cancelEditing, handleUpdate, form, formValid, objectData, setIsEditing, setObjectData
|
* loading, isEditing, startEditing, cancelEditing, handleUpdate, form, formValid, objectData, setIsEditing, setObjectData
|
||||||
* }) => ReactNode
|
* }) => ReactNode
|
||||||
*/
|
*/
|
||||||
const EditObjectForm = ({ id, type, style, children }) => {
|
const EditObjectForm = forwardRef(
|
||||||
|
({ id, type, style, children, onEdit, onStateChange }, ref) => {
|
||||||
const [objectData, setObjectData] = useState(null)
|
const [objectData, setObjectData] = useState(null)
|
||||||
const [serverObjectData, setServerObjectData] = useState(null)
|
const [serverObjectData, setServerObjectData] = useState(null)
|
||||||
const [fetchLoading, setFetchLoading] = useState(true)
|
const [fetchLoading, setFetchLoading] = useState(true)
|
||||||
@ -41,16 +49,22 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
fetchObjectLock,
|
fetchObjectLock,
|
||||||
showError,
|
showError,
|
||||||
connected,
|
connected,
|
||||||
subscribeToObject,
|
subscribeToObjectUpdates,
|
||||||
subscribeToLock
|
subscribeToObjectLock
|
||||||
} = useContext(ApiServerContext)
|
} = useContext(ApiServerContext)
|
||||||
const { token } = useContext(AuthContext)
|
const { token } = useContext(AuthContext)
|
||||||
// Validate form on change
|
// Validate form on change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form
|
form
|
||||||
.validateFields({ validateOnly: true })
|
.validateFields({ validateOnly: true })
|
||||||
.then(() => setFormValid(true))
|
.then(() => {
|
||||||
.catch(() => setFormValid(false))
|
setFormValid(true)
|
||||||
|
onStateChange({ formValid: true })
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setFormValid(false)
|
||||||
|
onStateChange({ formValid: true })
|
||||||
|
})
|
||||||
}, [form, formUpdateValues])
|
}, [form, formUpdateValues])
|
||||||
|
|
||||||
// Cleanup on unmount
|
// Cleanup on unmount
|
||||||
@ -65,13 +79,16 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
const handleFetchObject = useCallback(async () => {
|
const handleFetchObject = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setFetchLoading(true)
|
setFetchLoading(true)
|
||||||
|
onStateChange({ loading: true })
|
||||||
const data = await fetchObject(id, type)
|
const data = await fetchObject(id, type)
|
||||||
const lockEvent = await fetchObjectLock(id, type)
|
const lockEvent = await fetchObjectLock(id, type)
|
||||||
setLock(lockEvent)
|
setLock(lockEvent)
|
||||||
|
onStateChange({ lock: lockEvent })
|
||||||
setObjectData(data)
|
setObjectData(data)
|
||||||
setServerObjectData(data)
|
setServerObjectData(data)
|
||||||
form.setFieldsValue(data)
|
form.setFieldsValue(data)
|
||||||
setFetchLoading(false)
|
setFetchLoading(false)
|
||||||
|
onStateChange({ loading: false })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
messageApi.error('Failed to fetch object info')
|
messageApi.error('Failed to fetch object info')
|
||||||
showError(
|
showError(
|
||||||
@ -88,7 +105,10 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
|
|
||||||
// Update event handler
|
// Update event handler
|
||||||
const updateLockEventHandler = useCallback((value) => {
|
const updateLockEventHandler = useCallback((value) => {
|
||||||
setLock((prev) => ({ ...prev, ...value }))
|
setLock((prev) => {
|
||||||
|
onStateChange({ lock: { ...prev, ...value } })
|
||||||
|
return { ...prev, ...value }
|
||||||
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -100,22 +120,26 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (id && connected) {
|
if (id && connected) {
|
||||||
const objectUnsubscribe = subscribeToObject(
|
const objectUpdatesUnsubscribe = subscribeToObjectUpdates(
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
updateObjectEventHandler
|
updateObjectEventHandler
|
||||||
)
|
)
|
||||||
const lockUnsubscribe = subscribeToLock(id, type, updateLockEventHandler)
|
const lockUnsubscribe = subscribeToObjectLock(
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
updateLockEventHandler
|
||||||
|
)
|
||||||
return () => {
|
return () => {
|
||||||
if (objectUnsubscribe) objectUnsubscribe()
|
if (objectUpdatesUnsubscribe) objectUpdatesUnsubscribe()
|
||||||
if (lockUnsubscribe) lockUnsubscribe()
|
if (lockUnsubscribe) lockUnsubscribe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
subscribeToObject,
|
subscribeToObjectUpdates,
|
||||||
subscribeToLock,
|
subscribeToObjectLock,
|
||||||
updateObjectEventHandler,
|
updateObjectEventHandler,
|
||||||
connected,
|
connected,
|
||||||
updateLockEventHandler
|
updateLockEventHandler
|
||||||
@ -123,6 +147,7 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
|
|
||||||
const startEditing = () => {
|
const startEditing = () => {
|
||||||
setIsEditing(true)
|
setIsEditing(true)
|
||||||
|
onStateChange({ isEditing: true })
|
||||||
lockObject(id, type)
|
lockObject(id, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +157,7 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
setObjectData(serverObjectData)
|
setObjectData(serverObjectData)
|
||||||
}
|
}
|
||||||
setIsEditing(false)
|
setIsEditing(false)
|
||||||
|
onStateChange({ isEditing: false })
|
||||||
unlockObject(id, type)
|
unlockObject(id, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,9 +165,11 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
try {
|
try {
|
||||||
const value = await form.validateFields()
|
const value = await form.validateFields()
|
||||||
setEditLoading(true)
|
setEditLoading(true)
|
||||||
|
onStateChange({ editLoading: true })
|
||||||
await updateObject(id, type, value)
|
await updateObject(id, type, value)
|
||||||
setObjectData({ ...objectData, ...value })
|
setObjectData({ ...objectData, ...value })
|
||||||
setIsEditing(false)
|
setIsEditing(false)
|
||||||
|
onStateChange({ isEditing: false })
|
||||||
messageApi.success('Information updated successfully')
|
messageApi.success('Information updated successfully')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.errorFields) {
|
if (err.errorFields) {
|
||||||
@ -155,6 +183,7 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
} finally {
|
} finally {
|
||||||
handleFetchObject()
|
handleFetchObject()
|
||||||
setEditLoading(false)
|
setEditLoading(false)
|
||||||
|
onStateChange({ editLoading: false })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,6 +209,20 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
startEditing,
|
||||||
|
cancelEditing,
|
||||||
|
handleUpdate,
|
||||||
|
handleDelete,
|
||||||
|
confirmDelete,
|
||||||
|
handleFetchObject,
|
||||||
|
editLoading,
|
||||||
|
fetchLoading,
|
||||||
|
isEditing,
|
||||||
|
objectData,
|
||||||
|
lock
|
||||||
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DeleteObjectModal
|
<DeleteObjectModal
|
||||||
@ -195,7 +238,12 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
layout='vertical'
|
layout='vertical'
|
||||||
style={style}
|
style={style}
|
||||||
onValuesChange={(values) => {
|
onValuesChange={(values) => {
|
||||||
setObjectData((prev) => ({ ...prev, ...values }))
|
if (onEdit != undefined) {
|
||||||
|
onEdit(values)
|
||||||
|
}
|
||||||
|
setObjectData((prev) => {
|
||||||
|
return { ...prev, ...values }
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{contextHolder}
|
{contextHolder}
|
||||||
@ -219,12 +267,17 @@ const EditObjectForm = ({ id, type, style, children }) => {
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
EditObjectForm.displayName = 'EditObjectForm'
|
||||||
|
|
||||||
EditObjectForm.propTypes = {
|
EditObjectForm.propTypes = {
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
children: PropTypes.func.isRequired,
|
children: PropTypes.func.isRequired,
|
||||||
style: PropTypes.object
|
style: PropTypes.object,
|
||||||
|
onEdit: PropTypes.func,
|
||||||
|
onStateChange: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EditObjectForm
|
export default EditObjectForm
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import * as THREE from 'three'
|
import * as THREE from 'three'
|
||||||
|
|
||||||
function GCodePreviewUI(props, ref, initialGCode) {
|
function GCodePreviewUI(props, ref) {
|
||||||
const {
|
const {
|
||||||
topLayerColor = '',
|
topLayerColor = '',
|
||||||
lastSegmentColor = '',
|
lastSegmentColor = '',
|
||||||
|
|||||||
@ -4,6 +4,36 @@ import { getModelByName } from '../../../database/ObjectModels'
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { useNavigate, useLocation } from 'react-router-dom'
|
import { useNavigate, useLocation } from 'react-router-dom'
|
||||||
|
|
||||||
|
// Recursively filter actions based on visibleActions
|
||||||
|
function filterActionsByVisibility(actions, visibleActions) {
|
||||||
|
if (!visibleActions) return actions
|
||||||
|
|
||||||
|
return actions.filter((action) => {
|
||||||
|
if (action.type === 'divider') {
|
||||||
|
return true // Always show dividers
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionKey = action.key || action.name
|
||||||
|
const isVisible = visibleActions[actionKey] !== false
|
||||||
|
|
||||||
|
// If this action has children, filter them recursively
|
||||||
|
if (action.children && Array.isArray(action.children)) {
|
||||||
|
const filteredChildren = filterActionsByVisibility(
|
||||||
|
action.children,
|
||||||
|
visibleActions
|
||||||
|
)
|
||||||
|
action.children = filteredChildren
|
||||||
|
// Show parent if it has visible children or if it's explicitly visible
|
||||||
|
return (
|
||||||
|
isVisible &&
|
||||||
|
(filteredChildren.length > 0 || visibleActions[actionKey] === true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return isVisible
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Recursively map actions to AntD Dropdown items
|
// Recursively map actions to AntD Dropdown items
|
||||||
function mapActionsToMenuItems(actions, currentUrlWithActions, id) {
|
function mapActionsToMenuItems(actions, currentUrlWithActions, id) {
|
||||||
return actions.map((action) => {
|
return actions.map((action) => {
|
||||||
@ -41,6 +71,7 @@ const ObjectActions = ({
|
|||||||
id,
|
id,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
buttonProps = {},
|
buttonProps = {},
|
||||||
|
visibleActions = {},
|
||||||
...dropdownProps
|
...dropdownProps
|
||||||
}) => {
|
}) => {
|
||||||
const model = getModelByName(type)
|
const model = getModelByName(type)
|
||||||
@ -54,7 +85,13 @@ const ObjectActions = ({
|
|||||||
location.search
|
location.search
|
||||||
)
|
)
|
||||||
|
|
||||||
const filteredActions = actions.filter(
|
// First filter by visibility, then by current URL
|
||||||
|
const visibilityFilteredActions = filterActionsByVisibility(
|
||||||
|
actions,
|
||||||
|
visibleActions
|
||||||
|
)
|
||||||
|
|
||||||
|
const filteredActions = visibilityFilteredActions.filter(
|
||||||
(action) =>
|
(action) =>
|
||||||
typeof action.url !== 'function' ||
|
typeof action.url !== 'function' ||
|
||||||
action.url(id) !== currentUrlWithoutActions
|
action.url(id) !== currentUrlWithoutActions
|
||||||
@ -98,7 +135,8 @@ ObjectActions.propTypes = {
|
|||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
buttonProps: PropTypes.object,
|
buttonProps: PropTypes.object,
|
||||||
buttonLabel: PropTypes.string
|
buttonLabel: PropTypes.string,
|
||||||
|
visibleActions: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectActions
|
export default ObjectActions
|
||||||
|
|||||||
@ -20,14 +20,10 @@ import CountrySelect from './CountrySelect'
|
|||||||
import TagsDisplay from './TagsDisplay'
|
import TagsDisplay from './TagsDisplay'
|
||||||
import TagsInput from './TagsInput'
|
import TagsInput from './TagsInput'
|
||||||
import BoolDisplay from './BoolDisplay'
|
import BoolDisplay from './BoolDisplay'
|
||||||
import PrinterState from './PrinterState'
|
|
||||||
import SubJobState from './SubJobState'
|
|
||||||
import JobState from './JobState'
|
|
||||||
import ColorSelector from './ColorSelector'
|
import ColorSelector from './ColorSelector'
|
||||||
import SecretDisplay from './SecretDisplay'
|
import SecretDisplay from './SecretDisplay'
|
||||||
import EyeIcon from '../../Icons/EyeIcon'
|
import EyeIcon from '../../Icons/EyeIcon'
|
||||||
import EyeSlashIcon from '../../Icons/EyeSlashIcon'
|
import EyeSlashIcon from '../../Icons/EyeSlashIcon'
|
||||||
import FilamentStockState from './FilamentStockState'
|
|
||||||
import { getPropertyValue } from '../../../database/ObjectModels'
|
import { getPropertyValue } from '../../../database/ObjectModels'
|
||||||
import PropertyChanges from './PropertyChanges'
|
import PropertyChanges from './PropertyChanges'
|
||||||
import NetGrossDisplay from './NetGrossDisplay'
|
import NetGrossDisplay from './NetGrossDisplay'
|
||||||
@ -38,6 +34,10 @@ import OperationDisplay from './OperationDisplay'
|
|||||||
import MarkdownDisplay from './MarkdownDisplay'
|
import MarkdownDisplay from './MarkdownDisplay'
|
||||||
import ObjectSelect from './ObjectSelect'
|
import ObjectSelect from './ObjectSelect'
|
||||||
import ObjectDisplay from './ObjectDisplay'
|
import ObjectDisplay from './ObjectDisplay'
|
||||||
|
import ObjectTypeSelect from './ObjectTypeSelect'
|
||||||
|
import ObjectTypeDisplay from './ObjectTypeDisplay'
|
||||||
|
import CodeBlockEditor from './CodeBlockEditor'
|
||||||
|
import StateDisplay from './StateDisplay'
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
@ -65,11 +65,16 @@ const ObjectProperty = ({
|
|||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
showLabel = false,
|
showLabel = false,
|
||||||
|
masterFilter = {},
|
||||||
|
language = '',
|
||||||
objectData = null,
|
objectData = null,
|
||||||
objectType = 'unknown',
|
objectType = 'unknown',
|
||||||
readOnly = false,
|
readOnly = false,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
empty = false,
|
||||||
initial = false,
|
initial = false,
|
||||||
|
height = 'auto',
|
||||||
|
minimal = false,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
if (value && typeof value == 'function' && objectData) {
|
if (value && typeof value == 'function' && objectData) {
|
||||||
@ -84,6 +89,10 @@ const ObjectProperty = ({
|
|||||||
disabled = disabled(objectData)
|
disabled = disabled(objectData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (empty && typeof empty == 'function' && objectData) {
|
||||||
|
empty = empty(objectData)
|
||||||
|
}
|
||||||
|
|
||||||
if (difference && typeof difference == 'function' && objectData) {
|
if (difference && typeof difference == 'function' && objectData) {
|
||||||
difference = difference(objectData)
|
difference = difference(objectData)
|
||||||
}
|
}
|
||||||
@ -114,6 +123,9 @@ const ObjectProperty = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const renderProperty = () => {
|
const renderProperty = () => {
|
||||||
|
if (empty == true) {
|
||||||
|
return <Text type='secondary'>n/a</Text>
|
||||||
|
}
|
||||||
if (!isEditing || (readOnly && !initial)) {
|
if (!isEditing || (readOnly && !initial)) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'netGross':
|
case 'netGross':
|
||||||
@ -256,6 +268,24 @@ const ObjectProperty = ({
|
|||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
case 'codeBlock':
|
||||||
|
if (value != null && value != '') {
|
||||||
|
return (
|
||||||
|
<CodeBlockEditor
|
||||||
|
code={value}
|
||||||
|
language={language}
|
||||||
|
height={height}
|
||||||
|
readOnly={true}
|
||||||
|
minimal={minimal}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Text type='secondary' {...textParams}>
|
||||||
|
n/a
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
case 'markdown':
|
case 'markdown':
|
||||||
if (value != null && value != '') {
|
if (value != null && value != '') {
|
||||||
return <MarkdownDisplay content={value} />
|
return <MarkdownDisplay content={value} />
|
||||||
@ -297,27 +327,23 @@ const ObjectProperty = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 'objectType': {
|
||||||
|
if (value) {
|
||||||
|
return <ObjectTypeDisplay objectType={value} />
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Text type='secondary' {...textParams}>
|
||||||
|
n/a
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
case 'objectList': {
|
case 'objectList': {
|
||||||
return <ObjectList value={value} objectType={objectType} />
|
return <ObjectList value={value} objectType={objectType} />
|
||||||
}
|
}
|
||||||
case 'state': {
|
case 'state': {
|
||||||
if (value && value?.type) {
|
if (value && value?.type) {
|
||||||
switch (objectType) {
|
return <StateDisplay {...rest} state={value} />
|
||||||
case 'printer':
|
|
||||||
return <PrinterState state={value} {...rest} />
|
|
||||||
case 'job':
|
|
||||||
return <JobState state={value} {...rest} />
|
|
||||||
case 'subJob':
|
|
||||||
return <SubJobState state={value} {...rest} />
|
|
||||||
case 'filamentStock':
|
|
||||||
return <FilamentStockState state={value} {...rest} />
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Text type='secondary' {...textParams}>
|
|
||||||
No Object Type Specified
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Text type='secondary' {...textParams}>
|
<Text type='secondary' {...textParams}>
|
||||||
@ -566,6 +592,18 @@ const ObjectProperty = ({
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)
|
||||||
|
case 'codeBlock':
|
||||||
|
return (
|
||||||
|
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||||
|
<CodeBlockEditor
|
||||||
|
code={value}
|
||||||
|
language={language}
|
||||||
|
disabled={disabled}
|
||||||
|
height={height}
|
||||||
|
minimal={minimal}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
case 'material':
|
case 'material':
|
||||||
return (
|
return (
|
||||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||||
@ -590,13 +628,23 @@ const ObjectProperty = ({
|
|||||||
case 'object':
|
case 'object':
|
||||||
return (
|
return (
|
||||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||||
<ObjectSelect type={objectType} />
|
<ObjectSelect
|
||||||
|
type={objectType}
|
||||||
|
disabled={disabled}
|
||||||
|
masterFilter={masterFilter}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
case 'objectType':
|
||||||
|
return (
|
||||||
|
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||||
|
<ObjectTypeSelect disabled={disabled} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)
|
||||||
case 'objectList':
|
case 'objectList':
|
||||||
return (
|
return (
|
||||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||||
<ObjectSelect type={objectType} multiple />
|
<ObjectSelect type={objectType} multiple disabled={disabled} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)
|
||||||
case 'tags':
|
case 'tags':
|
||||||
@ -624,8 +672,10 @@ ObjectProperty.propTypes = {
|
|||||||
value: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
|
value: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
|
||||||
isEditing: PropTypes.bool,
|
isEditing: PropTypes.bool,
|
||||||
formItemProps: PropTypes.object,
|
formItemProps: PropTypes.object,
|
||||||
|
masterFilter: PropTypes.object,
|
||||||
required: PropTypes.bool,
|
required: PropTypes.bool,
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
|
language: PropTypes.string,
|
||||||
prefix: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
prefix: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
||||||
suffix: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
suffix: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
||||||
min: PropTypes.number,
|
min: PropTypes.number,
|
||||||
@ -635,8 +685,10 @@ ObjectProperty.propTypes = {
|
|||||||
objectType: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
objectType: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
||||||
readOnly: PropTypes.bool,
|
readOnly: PropTypes.bool,
|
||||||
disabled: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
|
disabled: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
|
||||||
|
empty: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
|
||||||
difference: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
|
difference: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
|
||||||
objectData: PropTypes.object
|
objectData: PropTypes.object,
|
||||||
|
height: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectProperty
|
export default ObjectProperty
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import PropTypes from 'prop-types'
|
|||||||
import { TreeSelect, Space, Button, Input } from 'antd'
|
import { TreeSelect, Space, Button, Input } from 'antd'
|
||||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
|
import { AuthContext } from '../context/AuthContext'
|
||||||
import ObjectProperty from './ObjectProperty'
|
import ObjectProperty from './ObjectProperty'
|
||||||
import { getModelByName } from '../../../database/ObjectModels'
|
import { getModelByName } from '../../../database/ObjectModels'
|
||||||
import merge from 'lodash/merge'
|
import merge from 'lodash/merge'
|
||||||
@ -20,10 +21,13 @@ const ObjectSelect = ({
|
|||||||
multiple = false,
|
multiple = false,
|
||||||
treeSelectProps = {},
|
treeSelectProps = {},
|
||||||
filter = {},
|
filter = {},
|
||||||
|
masterFilter = {},
|
||||||
value,
|
value,
|
||||||
|
disabled = false,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const { fetchObjectsByProperty } = useContext(ApiServerContext)
|
const { fetchObjectsByProperty } = useContext(ApiServerContext)
|
||||||
|
const { token } = useContext(AuthContext)
|
||||||
// --- State ---
|
// --- State ---
|
||||||
const [treeData, setTreeData] = useState([])
|
const [treeData, setTreeData] = useState([])
|
||||||
const [objectPropertiesTree, setObjectPropertiesTree] = useState({})
|
const [objectPropertiesTree, setObjectPropertiesTree] = useState({})
|
||||||
@ -40,9 +44,14 @@ const ObjectSelect = ({
|
|||||||
try {
|
try {
|
||||||
const data = await fetchObjectsByProperty(type, {
|
const data = await fetchObjectsByProperty(type, {
|
||||||
properties: properties,
|
properties: properties,
|
||||||
filter: customFilter
|
filter: customFilter,
|
||||||
|
masterFilter
|
||||||
})
|
})
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
setObjectPropertiesTree((prev) => merge([], prev, data))
|
||||||
|
} else {
|
||||||
setObjectPropertiesTree((prev) => merge({}, prev, data))
|
setObjectPropertiesTree((prev) => merge({}, prev, data))
|
||||||
|
}
|
||||||
setInitialLoading(false)
|
setInitialLoading(false)
|
||||||
setError(false)
|
setError(false)
|
||||||
return data
|
return data
|
||||||
@ -97,7 +106,7 @@ const ObjectSelect = ({
|
|||||||
value: key,
|
value: key,
|
||||||
key: parentKeys.concat(key).join(':'),
|
key: parentKeys.concat(key).join(':'),
|
||||||
property,
|
property,
|
||||||
parentKeys: parentKeys.concat(key),
|
parentKeys: parentKeys.concat(key || ':'),
|
||||||
filterPath: newFilterPath,
|
filterPath: newFilterPath,
|
||||||
selectable: false,
|
selectable: false,
|
||||||
children: buildTreeData(
|
children: buildTreeData(
|
||||||
@ -131,9 +140,15 @@ const ObjectSelect = ({
|
|||||||
// Fetch children for this node
|
// Fetch children for this node
|
||||||
const data = await handleFetchObjectsProperties(customFilter)
|
const data = await handleFetchObjectsProperties(customFilter)
|
||||||
if (!data) return
|
if (!data) return
|
||||||
// Build new children
|
// Extract only the children for the specific node that was expanded
|
||||||
|
let nodeSpecificData = data
|
||||||
|
if (typeof data === 'object' && !Array.isArray(data)) {
|
||||||
|
// If the API returns an object with multiple keys, get only the data for this node
|
||||||
|
nodeSpecificData = data[node.value] || {}
|
||||||
|
}
|
||||||
|
// Build new children only for this specific node
|
||||||
const children = buildTreeData(
|
const children = buildTreeData(
|
||||||
data,
|
nodeSpecificData,
|
||||||
properties.indexOf(node.property) + 1,
|
properties.indexOf(node.property) + 1,
|
||||||
node.parentKeys || [],
|
node.parentKeys || [],
|
||||||
(node.filterPath || []).concat({
|
(node.filterPath || []).concat({
|
||||||
@ -141,7 +156,7 @@ const ObjectSelect = ({
|
|||||||
value: node.value
|
value: node.value
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
// Update treeData with new children for this node
|
// Update treeData with new children for this node only
|
||||||
setTreeData((prevTreeData) => {
|
setTreeData((prevTreeData) => {
|
||||||
// Helper to recursively update the correct node
|
// Helper to recursively update the correct node
|
||||||
const updateNode = (nodes) =>
|
const updateNode = (nodes) =>
|
||||||
@ -195,11 +210,12 @@ const ObjectSelect = ({
|
|||||||
// Build a new filter from value's properties that are in the properties list
|
// Build a new filter from value's properties that are in the properties list
|
||||||
const valueFilter = { ...filter }
|
const valueFilter = { ...filter }
|
||||||
properties.forEach((prop) => {
|
properties.forEach((prop) => {
|
||||||
console.log('prop', prop)
|
|
||||||
if (Object.prototype.hasOwnProperty.call(value, prop)) {
|
if (Object.prototype.hasOwnProperty.call(value, prop)) {
|
||||||
const filterValue = value[prop]
|
const filterValue = value[prop]
|
||||||
if (filterValue?.name) {
|
if (filterValue?.name) {
|
||||||
valueFilter[prop] = filterValue.name
|
valueFilter[prop] = filterValue.name
|
||||||
|
} else if (Array.isArray(filterValue)) {
|
||||||
|
valueFilter[prop] = filterValue.join(',')
|
||||||
} else {
|
} else {
|
||||||
valueFilter[prop] = filterValue
|
valueFilter[prop] = filterValue
|
||||||
}
|
}
|
||||||
@ -211,7 +227,7 @@ const ObjectSelect = ({
|
|||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!initialized) {
|
if (!initialized && token != null) {
|
||||||
handleFetchObjectsProperties()
|
handleFetchObjectsProperties()
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
}
|
}
|
||||||
@ -254,6 +270,7 @@ const ObjectSelect = ({
|
|||||||
{...rest}
|
{...rest}
|
||||||
value={treeSelectValue}
|
value={treeSelectValue}
|
||||||
onChange={onTreeSelectChange}
|
onChange={onTreeSelectChange}
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -261,13 +278,15 @@ const ObjectSelect = ({
|
|||||||
ObjectSelect.propTypes = {
|
ObjectSelect.propTypes = {
|
||||||
properties: PropTypes.arrayOf(PropTypes.string).isRequired,
|
properties: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||||
filter: PropTypes.object,
|
filter: PropTypes.object,
|
||||||
|
masterFilter: PropTypes.object,
|
||||||
useFilter: PropTypes.bool,
|
useFilter: PropTypes.bool,
|
||||||
value: PropTypes.any,
|
value: PropTypes.any,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
showSearch: PropTypes.bool,
|
showSearch: PropTypes.bool,
|
||||||
multiple: PropTypes.bool,
|
multiple: PropTypes.bool,
|
||||||
treeSelectProps: PropTypes.object,
|
treeSelectProps: PropTypes.object,
|
||||||
type: PropTypes.string.isRequired
|
type: PropTypes.string.isRequired,
|
||||||
|
disabled: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectSelect
|
export default ObjectSelect
|
||||||
|
|||||||
@ -57,8 +57,12 @@ const ObjectTable = forwardRef(
|
|||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const { token } = useContext(AuthContext)
|
const { token } = useContext(AuthContext)
|
||||||
const { fetchObjects, connected, subscribeToObject, subscribeToType } =
|
const {
|
||||||
useContext(ApiServerContext)
|
fetchObjects,
|
||||||
|
connected,
|
||||||
|
subscribeToObjectUpdates,
|
||||||
|
subscribeToObjectTypeUpdates
|
||||||
|
} = useContext(ApiServerContext)
|
||||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
var adjustedScrollHeight = scrollHeight
|
var adjustedScrollHeight = scrollHeight
|
||||||
@ -286,14 +290,9 @@ const ObjectTable = forwardRef(
|
|||||||
)
|
)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const newEventHandler = useCallback(
|
const newEventHandler = useCallback(() => {
|
||||||
(newData) => {
|
|
||||||
if (newData.type == type) {
|
|
||||||
reload()
|
reload()
|
||||||
}
|
}, [reload, type])
|
||||||
},
|
|
||||||
[reload, type]
|
|
||||||
)
|
|
||||||
|
|
||||||
// Subscribe to real-time updates for all items
|
// Subscribe to real-time updates for all items
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -305,7 +304,7 @@ const ObjectTable = forwardRef(
|
|||||||
if (page?.items && page?.items?.length > 0) {
|
if (page?.items && page?.items?.length > 0) {
|
||||||
page.items.forEach((item) => {
|
page.items.forEach((item) => {
|
||||||
if (!item.isSkeleton) {
|
if (!item.isSkeleton) {
|
||||||
const unsubscribe = subscribeToObject(
|
const unsubscribe = subscribeToObjectUpdates(
|
||||||
item._id,
|
item._id,
|
||||||
type,
|
type,
|
||||||
updateEventHandler
|
updateEventHandler
|
||||||
@ -325,18 +324,18 @@ const ObjectTable = forwardRef(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [pages, type, subscribeToObject, updateEventHandler, connected])
|
}, [pages, type, subscribeToObjectUpdates, updateEventHandler, connected])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (connected == true) {
|
if (connected == true) {
|
||||||
const unsubscribe = subscribeToType(type, newEventHandler)
|
const unsubscribe = subscribeToObjectTypeUpdates(type, newEventHandler)
|
||||||
return () => {
|
return () => {
|
||||||
if (unsubscribe) {
|
if (unsubscribe) {
|
||||||
unsubscribe()
|
unsubscribe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [type, subscribeToType, connected, newEventHandler])
|
}, [type, subscribeToObjectTypeUpdates, connected, newEventHandler])
|
||||||
|
|
||||||
const updateData = useCallback(
|
const updateData = useCallback(
|
||||||
(_id, updatedData) => {
|
(_id, updatedData) => {
|
||||||
|
|||||||
47
src/components/Dashboard/common/ObjectTypeDisplay.jsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { Tag, Typography } from 'antd'
|
||||||
|
import { getModelByName } from '../../../database/ObjectModels'
|
||||||
|
|
||||||
|
const { Text } = Typography
|
||||||
|
|
||||||
|
const ObjectTypeDisplay = ({
|
||||||
|
objectType,
|
||||||
|
showIcon = true,
|
||||||
|
showLabel = true,
|
||||||
|
style,
|
||||||
|
color = 'default'
|
||||||
|
}) => {
|
||||||
|
if (!objectType) {
|
||||||
|
return <Text type='secondary'>n/a</Text>
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = getModelByName(objectType)
|
||||||
|
|
||||||
|
if (!model || model.name === 'unknown') {
|
||||||
|
return <Text type='secondary'>Unknown type</Text>
|
||||||
|
}
|
||||||
|
|
||||||
|
const Icon = model.icon
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
color={color}
|
||||||
|
style={{ margin: 0, ...style }}
|
||||||
|
icon={showIcon && Icon ? <Icon /> : undefined}
|
||||||
|
>
|
||||||
|
{showLabel && model.label && model.label}
|
||||||
|
</Tag>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectTypeDisplay.propTypes = {
|
||||||
|
objectType: PropTypes.string,
|
||||||
|
showIcon: PropTypes.bool,
|
||||||
|
showLabel: PropTypes.bool,
|
||||||
|
showPrefix: PropTypes.bool,
|
||||||
|
style: PropTypes.object,
|
||||||
|
color: PropTypes.string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ObjectTypeDisplay
|
||||||
56
src/components/Dashboard/common/ObjectTypeSelect.jsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Select } from 'antd'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { objectModels } from '../../../database/ObjectModels'
|
||||||
|
import ObjectTypeDisplay from './ObjectTypeDisplay'
|
||||||
|
|
||||||
|
const ObjectTypeSelect = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
style,
|
||||||
|
placeholder = 'Select object type...',
|
||||||
|
showSearch = true,
|
||||||
|
allowClear = true,
|
||||||
|
disabled = false
|
||||||
|
}) => {
|
||||||
|
// Create options from object models
|
||||||
|
const options = objectModels
|
||||||
|
.sort((a, b) => a.label.localeCompare(b.label))
|
||||||
|
.map((model) => ({
|
||||||
|
value: model.name,
|
||||||
|
label: <ObjectTypeDisplay objectType={model.name} />
|
||||||
|
}))
|
||||||
|
|
||||||
|
console.log('VALUE', value)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
showSearch={showSearch}
|
||||||
|
style={{ width: '100%', ...style }}
|
||||||
|
placeholder={placeholder}
|
||||||
|
optionFilterProp='children'
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
allowClear={allowClear}
|
||||||
|
disabled={disabled}
|
||||||
|
filterOption={(input, option) =>
|
||||||
|
option.label.props.children[1].props.children
|
||||||
|
.toLowerCase()
|
||||||
|
.indexOf(input.toLowerCase()) >= 0
|
||||||
|
}
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectTypeSelect.propTypes = {
|
||||||
|
value: PropTypes.string,
|
||||||
|
onChange: PropTypes.func,
|
||||||
|
style: PropTypes.object,
|
||||||
|
placeholder: PropTypes.string,
|
||||||
|
showSearch: PropTypes.bool,
|
||||||
|
allowClear: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ObjectTypeSelect
|
||||||
@ -3,15 +3,14 @@ import { Card, Tree, Spin, Space, Button, message } from 'antd'
|
|||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import React, { useState, useEffect, useContext } from 'react'
|
import React, { useState, useEffect, useContext } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import SubJobState from './SubJobState'
|
|
||||||
import { PrintServerContext } from '../context/PrintServerContext'
|
import { PrintServerContext } from '../context/PrintServerContext'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import JobState from './JobState'
|
|
||||||
import JobIcon from '../../Icons/JobIcon'
|
import JobIcon from '../../Icons/JobIcon'
|
||||||
import SubJobIcon from '../../Icons/SubJobIcon'
|
import SubJobIcon from '../../Icons/SubJobIcon'
|
||||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
|
|
||||||
import config from '../../../config'
|
import config from '../../../config'
|
||||||
|
import StateDisplay from './StateDisplay'
|
||||||
|
|
||||||
const PrinterJobsTree = ({
|
const PrinterJobsTree = ({
|
||||||
subJobs: initialSubJobs,
|
subJobs: initialSubJobs,
|
||||||
@ -62,7 +61,7 @@ const PrinterJobsTree = ({
|
|||||||
<Space size={5}>
|
<Space size={5}>
|
||||||
<JobIcon />
|
<JobIcon />
|
||||||
{'Job'}
|
{'Job'}
|
||||||
<JobState state={job?.state} />
|
<StateDisplay state={job?.state} />
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
key: `job-${job._id}`,
|
key: `job-${job._id}`,
|
||||||
@ -71,8 +70,8 @@ const PrinterJobsTree = ({
|
|||||||
<Space>
|
<Space>
|
||||||
<SubJobIcon />
|
<SubJobIcon />
|
||||||
{'Sub Job'}
|
{'Sub Job'}
|
||||||
<SubJobState
|
<StateDisplay
|
||||||
subJob={subJob}
|
state={subJob?.state}
|
||||||
showProgress={false}
|
showProgress={false}
|
||||||
showControls={false}
|
showControls={false}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -57,6 +57,7 @@ const PropertyChanges = ({ type, value }) => {
|
|||||||
<ObjectProperty
|
<ObjectProperty
|
||||||
{...changeProperty}
|
{...changeProperty}
|
||||||
longId={false}
|
longId={false}
|
||||||
|
minimal={true}
|
||||||
objectData={value?.old}
|
objectData={value?.old}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
@ -69,6 +70,7 @@ const PropertyChanges = ({ type, value }) => {
|
|||||||
<ObjectProperty
|
<ObjectProperty
|
||||||
{...changeProperty}
|
{...changeProperty}
|
||||||
longId={false}
|
longId={false}
|
||||||
|
minimal={true}
|
||||||
objectData={value?.new}
|
objectData={value?.new}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { Progress, Flex, Space } from 'antd'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import StateTag from './StateTag'
|
import StateTag from './StateTag'
|
||||||
|
|
||||||
const PrinterState = ({ state, showProgress = true, showState = true }) => {
|
const StateDisplay = ({ state, showProgress = true, showState = true }) => {
|
||||||
const currentState = state || {
|
const currentState = state || {
|
||||||
type: 'unknown',
|
type: 'unknown',
|
||||||
progress: 0
|
progress: 0
|
||||||
@ -17,9 +17,7 @@ const PrinterState = ({ state, showProgress = true, showState = true }) => {
|
|||||||
<StateTag state={currentState.type} />
|
<StateTag state={currentState.type} />
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
{showProgress &&
|
{showProgress && currentState?.progress && currentState?.progress > 0 ? (
|
||||||
(currentState.type === 'printing' ||
|
|
||||||
currentState.type === 'deploying') ? (
|
|
||||||
<Progress
|
<Progress
|
||||||
percent={Math.round(currentState.progress * 100)}
|
percent={Math.round(currentState.progress * 100)}
|
||||||
status='active'
|
status='active'
|
||||||
@ -30,10 +28,10 @@ const PrinterState = ({ state, showProgress = true, showState = true }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PrinterState.propTypes = {
|
StateDisplay.propTypes = {
|
||||||
state: PropTypes.object,
|
state: PropTypes.object,
|
||||||
showProgress: PropTypes.bool,
|
showProgress: PropTypes.bool,
|
||||||
showState: PropTypes.bool
|
showState: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PrinterState
|
export default StateDisplay
|
||||||
@ -1,147 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import { Progress, Flex, Button, Space, Tooltip } from 'antd' // eslint-disable-line
|
|
||||||
import { CaretLeftOutlined } from '@ant-design/icons' // eslint-disable-line
|
|
||||||
import React, { useContext } from 'react'
|
|
||||||
import { PrintServerContext } from '../context/PrintServerContext'
|
|
||||||
import IdDisplay from './IdDisplay'
|
|
||||||
import StateTag from './StateTag'
|
|
||||||
import XMarkIcon from '../../Icons/XMarkIcon'
|
|
||||||
import PauseIcon from '../../Icons/PauseIcon'
|
|
||||||
import BinIcon from '../../Icons/BinIcon'
|
|
||||||
import config from '../../../config'
|
|
||||||
import loglevel from 'loglevel'
|
|
||||||
const logger = loglevel.getLogger('SubJobState')
|
|
||||||
logger.setLevel(config.logLevel)
|
|
||||||
|
|
||||||
const SubJobState = ({
|
|
||||||
state,
|
|
||||||
showStatus = true,
|
|
||||||
showId = true,
|
|
||||||
showProgress = true,
|
|
||||||
showControls = true //eslint-disable-line
|
|
||||||
}) => {
|
|
||||||
const { printServer } = useContext(PrintServerContext)
|
|
||||||
const currentState = state || {
|
|
||||||
type: 'unknown',
|
|
||||||
progress: 0
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Flex gap='small' align={'center'}>
|
|
||||||
{showId && state?._id && (
|
|
||||||
<IdDisplay
|
|
||||||
id={state._id}
|
|
||||||
showCopy={false}
|
|
||||||
type='subjob'
|
|
||||||
longId={false}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{showStatus && (
|
|
||||||
<Space>
|
|
||||||
<StateTag state={currentState?.type} />
|
|
||||||
</Space>
|
|
||||||
)}
|
|
||||||
{showProgress &&
|
|
||||||
(currentState.type === 'printing' ||
|
|
||||||
currentState.type === 'processing') ? (
|
|
||||||
<Progress
|
|
||||||
percent={Math.round(currentState.progress * 100)}
|
|
||||||
status='active'
|
|
||||||
style={{ width: '150px', marginBottom: '2px' }}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{showControls &&
|
|
||||||
(currentState.type === 'printing' || currentState.type === 'paused') &&
|
|
||||||
state?.printer ? (
|
|
||||||
<Space.Compact>
|
|
||||||
<Tooltip
|
|
||||||
title={currentState.type === 'printing' ? 'Pause' : 'Resume'}
|
|
||||||
arrow={false}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
if (currentState.type === 'printing') {
|
|
||||||
printServer.emit('printer.print.pause', {
|
|
||||||
printerId: state.printer
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
printServer.emit('printer.print.resume', {
|
|
||||||
printerId: state.printer
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{ height: '22px' }}
|
|
||||||
type='text'
|
|
||||||
icon={
|
|
||||||
currentState.type === 'printing' ? (
|
|
||||||
<PauseIcon
|
|
||||||
style={{ fontSize: '12px', marginBottom: '3px' }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<CaretLeftOutlined
|
|
||||||
style={{ fontSize: '10px', marginBottom: '3px' }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
></Button>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title='Cancel' arrow={false}>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
printServer.emit('printer.print.cancel', {
|
|
||||||
printerId: state.printer
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
type='text'
|
|
||||||
style={{ height: '22px' }}
|
|
||||||
icon={
|
|
||||||
<XMarkIcon style={{ fontSize: '12px', marginBottom: '3px' }} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Space.Compact>
|
|
||||||
) : null}
|
|
||||||
{showControls && currentState.type === 'queued' && state?._id ? (
|
|
||||||
<Tooltip title='Cancel' arrow={false}>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
printServer.emit('server.job_queue.cancel', {
|
|
||||||
subJobId: state._id
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
style={{ height: '22px' }}
|
|
||||||
type='text'
|
|
||||||
icon={
|
|
||||||
<XMarkIcon style={{ fontSize: '12px', marginBottom: '3px' }} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
{showControls && currentState.type === 'draft' ? (
|
|
||||||
<Space>
|
|
||||||
<Tooltip title='Delete' arrow={false}>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
logger.debug('Hello')
|
|
||||||
}}
|
|
||||||
type='text'
|
|
||||||
style={{ height: 'unset' }}
|
|
||||||
icon={
|
|
||||||
<BinIcon style={{ fontSize: '14px', marginBottom: '2px' }} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Space>
|
|
||||||
) : null}
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
SubJobState.propTypes = {
|
|
||||||
state: PropTypes.object,
|
|
||||||
showProgress: PropTypes.bool,
|
|
||||||
showControls: PropTypes.bool,
|
|
||||||
showId: PropTypes.bool,
|
|
||||||
showStatus: PropTypes.bool
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SubJobState
|
|
||||||
211
src/components/Dashboard/common/TemplateEditor.jsx
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
import React, { useState, useContext, useEffect, useRef } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { Flex, Alert, Card, Spin, Splitter, Button, Modal, Input } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
import PlusIcon from '../../Icons/PlusIcon.jsx'
|
||||||
|
import MinusIcon from '../../Icons/MinusIcon.jsx'
|
||||||
|
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon.jsx'
|
||||||
|
import CheckCircleIcon from '../../Icons/CheckCircleIcon.jsx'
|
||||||
|
import ObjectProperty from '../common/ObjectProperty.jsx'
|
||||||
|
import { ApiServerContext } from '../context/ApiServerContext.js'
|
||||||
|
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
|
||||||
|
|
||||||
|
const TemplateEditor = ({
|
||||||
|
objectData,
|
||||||
|
loading,
|
||||||
|
collapseState,
|
||||||
|
isEditing,
|
||||||
|
style
|
||||||
|
}) => {
|
||||||
|
const iframeRef = useRef(null)
|
||||||
|
const { fetchTemplatePreview } = useContext(ApiServerContext)
|
||||||
|
const [testObjectOpen, setTestObjectOpen] = useState(false)
|
||||||
|
const [previewMessage, setPreviewMessage] = useState('No issues found.')
|
||||||
|
const [previewError, setPreviewError] = useState(false)
|
||||||
|
const [previewContent, setPreviewContent] = useState('')
|
||||||
|
const [reloadLoading, setReloadLoading] = useState(false)
|
||||||
|
const [previewScale, setPreviewScale] = useState(1)
|
||||||
|
|
||||||
|
const updatePreviewContent = (html) => {
|
||||||
|
if (iframeRef.current) {
|
||||||
|
// Save current scroll position
|
||||||
|
const scrollY = iframeRef.current.contentWindow.scrollY
|
||||||
|
const scrollX = iframeRef.current.contentWindow.scrollX
|
||||||
|
|
||||||
|
// Update srcDoc
|
||||||
|
setPreviewContent(html)
|
||||||
|
|
||||||
|
// Restore scroll position after iframe loads new content
|
||||||
|
const handleLoad = () => {
|
||||||
|
iframeRef.current.contentWindow.scrollTo(scrollX, scrollY)
|
||||||
|
iframeRef.current.removeEventListener('load', handleLoad)
|
||||||
|
}
|
||||||
|
iframeRef.current.addEventListener('load', handleLoad)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadPreview(content, testObject = {}, scale = 1) {
|
||||||
|
fetchTemplatePreview(
|
||||||
|
objectData._id,
|
||||||
|
content,
|
||||||
|
testObject,
|
||||||
|
scale,
|
||||||
|
(result) => {
|
||||||
|
setReloadLoading(false)
|
||||||
|
if (result?.error) {
|
||||||
|
setPreviewError(true)
|
||||||
|
setPreviewMessage(result.error)
|
||||||
|
} else {
|
||||||
|
setPreviewError(false)
|
||||||
|
updatePreviewContent(result.html)
|
||||||
|
setPreviewMessage('No issues found.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move useEffect to component level and use state to track objectData changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (objectData) {
|
||||||
|
console.log('PreviewScale', previewScale)
|
||||||
|
reloadPreview(objectData.content, objectData.testObject, previewScale)
|
||||||
|
}
|
||||||
|
}, [objectData, previewScale])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Splitter className={'farmcontrol-splitter'}>
|
||||||
|
{collapseState.preview == true && (
|
||||||
|
<Splitter.Panel style={{ height: '100%' }}>
|
||||||
|
<Spin
|
||||||
|
spinning={loading || reloadLoading}
|
||||||
|
indicator={<LoadingOutlined />}
|
||||||
|
>
|
||||||
|
<Card style={style} styles={{ body: { height: '100%' } }}>
|
||||||
|
<Flex vertical gap={'middle'} style={{ height: '100%' }}>
|
||||||
|
<Flex gap={'small'}>
|
||||||
|
{objectData?.objectType ? (
|
||||||
|
<ObjectProperty
|
||||||
|
objectType={objectData?.objectType}
|
||||||
|
name={'testObject'}
|
||||||
|
isEditing={true}
|
||||||
|
objectData={objectData}
|
||||||
|
disabled={!isEditing || objectData?.global}
|
||||||
|
type={'object'}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={{ flexGrow: 1 }}>
|
||||||
|
<Input disabled={true} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
onClick={() => {
|
||||||
|
setTestObjectOpen(true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={<PlusIcon />}
|
||||||
|
onClick={() => {
|
||||||
|
setPreviewScale((prev) => prev + 0.1)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
icon={<MinusIcon />}
|
||||||
|
onClick={() => {
|
||||||
|
setPreviewScale((prev) => prev - 0.1)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
ref={iframeRef}
|
||||||
|
srcDoc={previewContent}
|
||||||
|
frameBorder='0'
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
flexGrow: 1,
|
||||||
|
border: '1px solid #85858541',
|
||||||
|
overflow: 'auto'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
</Spin>
|
||||||
|
</Splitter.Panel>
|
||||||
|
)}
|
||||||
|
{collapseState.editor == true && (
|
||||||
|
<Splitter.Panel>
|
||||||
|
<Spin spinning={loading} indicator={<LoadingOutlined />}>
|
||||||
|
<Card style={style} styles={{ body: { height: '100%' } }}>
|
||||||
|
<Flex vertical gap={'middle'} style={{ height: '100%' }}>
|
||||||
|
<Alert
|
||||||
|
message={previewMessage}
|
||||||
|
showIcon
|
||||||
|
style={{ padding: '4px 8px' }}
|
||||||
|
icon={
|
||||||
|
previewError ? (
|
||||||
|
<ExclamationOctagonIcon />
|
||||||
|
) : (
|
||||||
|
<CheckCircleIcon />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
type={previewError ? 'error' : 'success'}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
overflowY: 'scroll',
|
||||||
|
height: '100%',
|
||||||
|
flex: '1 1 auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ObjectProperty
|
||||||
|
name={'content'}
|
||||||
|
height='100%'
|
||||||
|
type='codeBlock'
|
||||||
|
language={'xml'}
|
||||||
|
objectData={objectData}
|
||||||
|
isEditing={isEditing}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
</Spin>
|
||||||
|
</Splitter.Panel>
|
||||||
|
)}
|
||||||
|
</Splitter>
|
||||||
|
<Modal
|
||||||
|
open={testObjectOpen}
|
||||||
|
closeIcon={null}
|
||||||
|
footer={
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setTestObjectOpen(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ObjectProperty
|
||||||
|
type={'codeBlock'}
|
||||||
|
name='testObject'
|
||||||
|
language='json'
|
||||||
|
objectData={objectData}
|
||||||
|
isEditing={true}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateEditor.propTypes = {
|
||||||
|
loading: PropTypes.bool,
|
||||||
|
objectData: PropTypes.object,
|
||||||
|
collapseState: PropTypes.object,
|
||||||
|
isEditing: PropTypes.bool,
|
||||||
|
style: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TemplateEditor
|
||||||
@ -77,20 +77,23 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
const newSocket = io(config.apiServerUrl, {
|
const newSocket = io(config.apiServerUrl, {
|
||||||
reconnectionAttempts: 3,
|
reconnectionAttempts: 3,
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
auth: { token: token }
|
auth: { type: 'user' }
|
||||||
})
|
})
|
||||||
|
|
||||||
setConnecting(true)
|
setConnecting(true)
|
||||||
|
|
||||||
newSocket.on('connect', () => {
|
newSocket.on('connect', () => {
|
||||||
logger.debug('Api Server connected')
|
logger.debug('Api Server connected')
|
||||||
|
newSocket.emit('authenticate', { token: token }, (result) => {
|
||||||
|
console.log('Auth result', result)
|
||||||
setConnecting(false)
|
setConnecting(false)
|
||||||
setConnected(true)
|
setConnected(true)
|
||||||
setError(null)
|
setError(null)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
newSocket.on('notify_object_update', notifyObjectUpdate)
|
newSocket.on('objectUpdate', handleObjectUpdate)
|
||||||
newSocket.on('notify_object_new', notifyObjectNew)
|
newSocket.on('objectNew', handleObjectNew)
|
||||||
newSocket.on('notify_lock_update', notifyLockUpdate)
|
newSocket.on('notify_lock_update', notifyLockUpdate)
|
||||||
|
|
||||||
newSocket.on('disconnect', () => {
|
newSocket.on('disconnect', () => {
|
||||||
@ -181,34 +184,41 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyObjectUpdate = async (object) => {
|
const handleObjectUpdate = async (data) => {
|
||||||
logger.debug('Notifying object update:', object)
|
logger.debug('Notifying object update:', data)
|
||||||
const objectId = object._id || object.id
|
const id = data._id
|
||||||
|
const objectType = data.objectType
|
||||||
|
|
||||||
if (objectId && subscribedCallbacksRef.current.has(objectId)) {
|
const callbacksRefKey = `${objectType}:${id}`
|
||||||
const callbacks = subscribedCallbacksRef.current.get(objectId)
|
|
||||||
|
if (
|
||||||
|
id &&
|
||||||
|
objectType &&
|
||||||
|
subscribedCallbacksRef.current.has(callbacksRefKey)
|
||||||
|
) {
|
||||||
|
const callbacks = subscribedCallbacksRef.current.get(callbacksRefKey)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Calling ${callbacks.length} callbacks for object:`,
|
`Calling ${callbacks.length} callbacks for object:`,
|
||||||
objectId
|
callbacksRefKey
|
||||||
)
|
)
|
||||||
callbacks.forEach((callback) => {
|
callbacks.forEach((callback) => {
|
||||||
try {
|
try {
|
||||||
callback(object)
|
callback(data.object)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error in object update callback:', error)
|
logger.error('Error in object update callback:', error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`No callbacks found for object: ${objectId}, subscribed callbacks:`,
|
`No callbacks found for object: ${callbacksRefKey}, subscribed callbacks:`,
|
||||||
Array.from(subscribedCallbacksRef.current.keys())
|
Array.from(subscribedCallbacksRef.current.keys())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyObjectNew = async (object) => {
|
const handleObjectNew = async (data) => {
|
||||||
logger.debug('Notifying object new:', object)
|
logger.debug('Notifying object new:', data)
|
||||||
const objectType = object.type || 'unknown'
|
const objectType = data.objectType || 'unknown'
|
||||||
|
|
||||||
if (objectType && subscribedCallbacksRef.current.has(objectType)) {
|
if (objectType && subscribedCallbacksRef.current.has(objectType)) {
|
||||||
const callbacks = subscribedCallbacksRef.current.get(objectType)
|
const callbacks = subscribedCallbacksRef.current.get(objectType)
|
||||||
@ -218,7 +228,7 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
)
|
)
|
||||||
callbacks.forEach((callback) => {
|
callbacks.forEach((callback) => {
|
||||||
try {
|
try {
|
||||||
callback(object)
|
callback(data.object)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error in object new callback:', error)
|
logger.error('Error in object new callback:', error)
|
||||||
}
|
}
|
||||||
@ -231,7 +241,7 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const offUpdateEvent = useCallback((id, type, callback) => {
|
const offObjectUpdatesEvent = useCallback((id, objectType, callback) => {
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
// Remove callback from the subscribed callbacks map
|
// Remove callback from the subscribed callbacks map
|
||||||
if (subscribedCallbacksRef.current.has(id)) {
|
if (subscribedCallbacksRef.current.has(id)) {
|
||||||
@ -240,7 +250,7 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
.filter((cb) => cb !== callback)
|
.filter((cb) => cb !== callback)
|
||||||
if (callbacks.length === 0) {
|
if (callbacks.length === 0) {
|
||||||
subscribedCallbacksRef.current.delete(id)
|
subscribedCallbacksRef.current.delete(id)
|
||||||
socketRef.current.emit('unsubscribe', { id: id, type: type })
|
socketRef.current.emit('unsubscribe', { id: id, type: objectType })
|
||||||
} else {
|
} else {
|
||||||
subscribedCallbacksRef.current.set(id, callbacks)
|
subscribedCallbacksRef.current.set(id, callbacks)
|
||||||
}
|
}
|
||||||
@ -248,62 +258,82 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const offTypeEvent = useCallback((type, callback) => {
|
const offObjectTypeUpdatesEvent = useCallback((objectType, callback) => {
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
// Remove callback from the subscribed callbacks map
|
// Remove callback from the subscribed callbacks map
|
||||||
if (subscribedCallbacksRef.current.has(type)) {
|
if (subscribedCallbacksRef.current.has(objectType)) {
|
||||||
const callbacks = subscribedCallbacksRef.current
|
const callbacks = subscribedCallbacksRef.current
|
||||||
.get(type)
|
.get(objectType)
|
||||||
.filter((cb) => cb !== callback)
|
.filter((cb) => cb !== callback)
|
||||||
if (callbacks.length === 0) {
|
if (callbacks.length === 0) {
|
||||||
subscribedCallbacksRef.current.delete(type)
|
subscribedCallbacksRef.current.delete(objectType)
|
||||||
socketRef.current.emit('unsubscribe', { type: type })
|
socketRef.current.emit('unsubscribe', { objectType: objectType })
|
||||||
} else {
|
} else {
|
||||||
subscribedCallbacksRef.current.set(type, callbacks)
|
subscribedCallbacksRef.current.set(objectType, callbacks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const subscribeToObject = useCallback(
|
const subscribeToObjectUpdates = useCallback(
|
||||||
(id, type, callback) => {
|
(id, objectType, callback) => {
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
|
const callbacksRefKey = `${objectType}:${id}`
|
||||||
// Add callback to the subscribed callbacks map immediately
|
// Add callback to the subscribed callbacks map immediately
|
||||||
if (!subscribedCallbacksRef.current.has(id)) {
|
if (!subscribedCallbacksRef.current.has(callbacksRefKey)) {
|
||||||
subscribedCallbacksRef.current.set(id, [])
|
subscribedCallbacksRef.current.set(callbacksRefKey, [])
|
||||||
}
|
}
|
||||||
subscribedCallbacksRef.current.get(id).push(callback)
|
subscribedCallbacksRef.current.get(callbacksRefKey).push(callback)
|
||||||
|
|
||||||
socketRef.current.emit('subscribe', { id: id, type: type })
|
socketRef.current.emit(
|
||||||
|
'subscribeToObjectUpdate',
|
||||||
// Return cleanup function
|
{
|
||||||
return () => offUpdateEvent(id, type, callback)
|
_id: id,
|
||||||
}
|
objectType: objectType
|
||||||
},
|
},
|
||||||
[offUpdateEvent]
|
(result) => {
|
||||||
|
if (result.success) {
|
||||||
|
logger.info('Subscribed to id:', id, 'objectType:', objectType)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const subscribeToType = useCallback(
|
// Return cleanup function
|
||||||
(type, callback) => {
|
return () => offObjectUpdatesEvent(id, objectType, callback)
|
||||||
logger.debug('Subscribing to type:', type)
|
}
|
||||||
|
},
|
||||||
|
[offObjectUpdatesEvent]
|
||||||
|
)
|
||||||
|
|
||||||
|
const subscribeToObjectTypeUpdates = useCallback(
|
||||||
|
(objectType, callback) => {
|
||||||
|
logger.debug('Subscribing to type updates:', objectType)
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
// Add callback to the subscribed callbacks map immediately
|
// Add callback to the subscribed callbacks map immediately
|
||||||
if (!subscribedCallbacksRef.current.has(type)) {
|
if (!subscribedCallbacksRef.current.has(objectType)) {
|
||||||
subscribedCallbacksRef.current.set(type, [])
|
subscribedCallbacksRef.current.set(objectType, [])
|
||||||
}
|
}
|
||||||
subscribedCallbacksRef.current.get(type).push(callback)
|
subscribedCallbacksRef.current.get(objectType).push(callback)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Added callback for type ${type}, total callbacks: ${subscribedCallbacksRef.current.get(type).length}`
|
`Added callback for type ${objectType}, total callbacks: ${subscribedCallbacksRef.current.get(objectType).length}`
|
||||||
)
|
)
|
||||||
|
|
||||||
socketRef.current.emit('subscribe', { type: type })
|
socketRef.current.emit(
|
||||||
logger.debug('Registered update event listener for object:', type)
|
'subscribeToObjectTypeUpdate',
|
||||||
|
{ objectType: objectType },
|
||||||
|
(result) => {
|
||||||
|
if (result.success) {
|
||||||
|
logger.info('Subscribed to objectType:', objectType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
logger.debug('Registered update event listener for object:', objectType)
|
||||||
|
|
||||||
// Return cleanup function
|
// Return cleanup function
|
||||||
return () => offTypeEvent(type, callback)
|
return () => offObjectTypeUpdatesEvent(objectType, callback)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[offTypeEvent]
|
[offObjectTypeUpdatesEvent]
|
||||||
)
|
)
|
||||||
|
|
||||||
const offLockEvent = useCallback((id, callback) => {
|
const offLockEvent = useCallback((id, callback) => {
|
||||||
@ -324,7 +354,7 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const subscribeToLock = useCallback(
|
const subscribeToObjectLock = useCallback(
|
||||||
(id, type, callback) => {
|
(id, type, callback) => {
|
||||||
logger.debug('Subscribing to lock for object:', id, 'type:', type)
|
logger.debug('Subscribing to lock for object:', id, 'type:', type)
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
@ -453,7 +483,7 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
|
|
||||||
// Fetch table data with pagination, filtering, and sorting
|
// Fetch table data with pagination, filtering, and sorting
|
||||||
const fetchObjectsByProperty = async (type, params = {}) => {
|
const fetchObjectsByProperty = async (type, params = {}) => {
|
||||||
const { filter = {}, properties = [] } = params
|
const { filter = {}, properties = [], masterFilter = {} } = params
|
||||||
|
|
||||||
logger.debug('Fetching property object data from:', type, {
|
logger.debug('Fetching property object data from:', type, {
|
||||||
properties,
|
properties,
|
||||||
@ -466,7 +496,8 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
...filter,
|
...filter,
|
||||||
properties: properties.join(',') // Convert array to comma-separated string
|
properties: properties.join(','), // Convert array to comma-separated string
|
||||||
|
masterFilter: JSON.stringify(masterFilter)
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
@ -636,6 +667,56 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchTemplatePreview = async (
|
||||||
|
id,
|
||||||
|
content,
|
||||||
|
testObject,
|
||||||
|
scale,
|
||||||
|
callback
|
||||||
|
) => {
|
||||||
|
logger.debug('Fetching preview...')
|
||||||
|
if (socketRef.current && socketRef.current.connected) {
|
||||||
|
return socketRef.current.emit(
|
||||||
|
'previewTemplate',
|
||||||
|
{
|
||||||
|
_id: id,
|
||||||
|
content: content,
|
||||||
|
testObject: testObject,
|
||||||
|
scale: scale
|
||||||
|
},
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchHostOTP = async (id, callback) => {
|
||||||
|
logger.debug('Fetching host OTP...')
|
||||||
|
if (socketRef.current && socketRef.current.connected) {
|
||||||
|
return socketRef.current.emit(
|
||||||
|
'generateHostOtp',
|
||||||
|
{
|
||||||
|
_id: id
|
||||||
|
},
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendObjectAction = async (id, objectType, action, callback) => {
|
||||||
|
logger.debug('Sending object action...')
|
||||||
|
if (socketRef.current && socketRef.current.connected) {
|
||||||
|
return socketRef.current.emit(
|
||||||
|
'objectAction',
|
||||||
|
{
|
||||||
|
_id: id,
|
||||||
|
objectType: objectType,
|
||||||
|
action: action
|
||||||
|
},
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApiServerContext.Provider
|
<ApiServerContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@ -649,10 +730,9 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
updateObject,
|
updateObject,
|
||||||
createObject,
|
createObject,
|
||||||
deleteObject,
|
deleteObject,
|
||||||
subscribeToObject,
|
subscribeToObjectUpdates,
|
||||||
subscribeToType,
|
subscribeToObjectTypeUpdates,
|
||||||
subscribeToLock,
|
subscribeToObjectLock,
|
||||||
offUpdateEvent,
|
|
||||||
fetchObject,
|
fetchObject,
|
||||||
fetchObjects,
|
fetchObjects,
|
||||||
fetchObjectsByProperty,
|
fetchObjectsByProperty,
|
||||||
@ -660,7 +740,10 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
fetchLoading,
|
fetchLoading,
|
||||||
showError,
|
showError,
|
||||||
fetchObjectContent,
|
fetchObjectContent,
|
||||||
fetchNotes
|
fetchTemplatePreview,
|
||||||
|
fetchNotes,
|
||||||
|
fetchHostOTP,
|
||||||
|
sendObjectAction
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{contextHolder}
|
{contextHolder}
|
||||||
|
|||||||
@ -54,13 +54,17 @@ const AuthProvider = ({ children }) => {
|
|||||||
// Read token from session storage if present
|
// Read token from session storage if present
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const storedToken = sessionStorage.getItem('authToken')
|
const storedToken = sessionStorage.getItem('authToken')
|
||||||
|
const storedUser = sessionStorage.getItem('user')
|
||||||
const storedExpiresAt = sessionStorage.getItem('authExpiresAt')
|
const storedExpiresAt = sessionStorage.getItem('authExpiresAt')
|
||||||
if (storedToken && storedExpiresAt) {
|
console.log('stored user', storedUser, storedToken)
|
||||||
|
if (storedToken && storedExpiresAt && storedUser) {
|
||||||
setToken(storedToken)
|
setToken(storedToken)
|
||||||
|
setUserProfile(storedUser)
|
||||||
setExpiresAt(storedExpiresAt)
|
setExpiresAt(storedExpiresAt)
|
||||||
setAuthenticated(true)
|
setAuthenticated(true)
|
||||||
} else {
|
} else {
|
||||||
setAuthenticated(false)
|
setAuthenticated(false)
|
||||||
|
setUserProfile(null)
|
||||||
setShowUnauthorizedModal(true)
|
setShowUnauthorizedModal(true)
|
||||||
}
|
}
|
||||||
setRetreivedTokenFromSession(true)
|
setRetreivedTokenFromSession(true)
|
||||||
@ -73,6 +77,7 @@ const AuthProvider = ({ children }) => {
|
|||||||
setUserProfile(null)
|
setUserProfile(null)
|
||||||
sessionStorage.removeItem('authToken')
|
sessionStorage.removeItem('authToken')
|
||||||
sessionStorage.removeItem('authExpiresAt')
|
sessionStorage.removeItem('authExpiresAt')
|
||||||
|
sessionStorage.removeItem('user')
|
||||||
window.location.href = `${config.backendUrl}/auth/logout?redirect_uri=${encodeURIComponent(redirectUri)}`
|
window.location.href = `${config.backendUrl}/auth/logout?redirect_uri=${encodeURIComponent(redirectUri)}`
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@ -112,6 +117,16 @@ const AuthProvider = ({ children }) => {
|
|||||||
setUserProfile(response.data)
|
setUserProfile(response.data)
|
||||||
sessionStorage.setItem('authToken', response.data.access_token)
|
sessionStorage.setItem('authToken', response.data.access_token)
|
||||||
sessionStorage.setItem('authExpiresAt', response.data.expires_at)
|
sessionStorage.setItem('authExpiresAt', response.data.expires_at)
|
||||||
|
console.log('SETTING USER', {
|
||||||
|
...response.data,
|
||||||
|
// eslint-disable-next-line
|
||||||
|
access_token: 'redacted',
|
||||||
|
// eslint-disable-next-line
|
||||||
|
refresh_token: 'redacted',
|
||||||
|
// eslint-disable-next-line
|
||||||
|
id_token: 'redacted'
|
||||||
|
})
|
||||||
|
sessionStorage.setItem('user', response.data)
|
||||||
const searchParams = new URLSearchParams(location.search)
|
const searchParams = new URLSearchParams(location.search)
|
||||||
searchParams.delete('authCode')
|
searchParams.delete('authCode')
|
||||||
const newSearch = searchParams.toString()
|
const newSearch = searchParams.toString()
|
||||||
@ -154,6 +169,7 @@ const AuthProvider = ({ children }) => {
|
|||||||
setUserProfile(response.data)
|
setUserProfile(response.data)
|
||||||
sessionStorage.setItem('authToken', response.data.access_token)
|
sessionStorage.setItem('authToken', response.data.access_token)
|
||||||
sessionStorage.setItem('authExpiresAt', response.data.expires_at)
|
sessionStorage.setItem('authExpiresAt', response.data.expires_at)
|
||||||
|
sessionStorage.setItem('user', response.data)
|
||||||
} else {
|
} else {
|
||||||
setAuthenticated(false)
|
setAuthenticated(false)
|
||||||
setAuthError('Failed to authenticate user.')
|
setAuthError('Failed to authenticate user.')
|
||||||
|
|||||||
@ -14,8 +14,7 @@ import axios from 'axios'
|
|||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import PrinterState from '../common/PrinterState'
|
import StateDisplay from '../common/StateDisplay'
|
||||||
import JobState from '../common/JobState'
|
|
||||||
import IdDisplay from '../common/IdDisplay'
|
import IdDisplay from '../common/IdDisplay'
|
||||||
|
|
||||||
import config from '../../../config'
|
import config from '../../../config'
|
||||||
@ -24,8 +23,6 @@ import {
|
|||||||
getModelByPrefix
|
getModelByPrefix
|
||||||
} from '../../../database/ObjectModels'
|
} from '../../../database/ObjectModels'
|
||||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
||||||
import FilamentStockState from '../common/FilamentStockState'
|
|
||||||
import SubJobState from '../common/SubJobState'
|
|
||||||
|
|
||||||
const SpotlightContext = createContext()
|
const SpotlightContext = createContext()
|
||||||
|
|
||||||
@ -469,38 +466,14 @@ const SpotlightProvider = ({ children }) => {
|
|||||||
{item.name}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
) : null}
|
) : null}
|
||||||
{model.type == 'printer' ? (
|
{item?.state ? (
|
||||||
<PrinterState
|
<StateDisplay
|
||||||
printer={item}
|
state={item.state}
|
||||||
showName={false}
|
showName={false}
|
||||||
showProgress={false}
|
showProgress={false}
|
||||||
showId={false}
|
showId={false}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{model.type == 'job' ? (
|
|
||||||
<JobState
|
|
||||||
job={item}
|
|
||||||
showQuantity={false}
|
|
||||||
showProgress={false}
|
|
||||||
showId={false}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{model.type == 'subjob' ? (
|
|
||||||
<SubJobState
|
|
||||||
subJob={item}
|
|
||||||
showProgress={false}
|
|
||||||
showId={false}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{model.type == 'filamentstock' ? (
|
|
||||||
<Flex gap={'small'}>
|
|
||||||
<FilamentStockState
|
|
||||||
filamentStock={item}
|
|
||||||
showId={false}
|
|
||||||
showProgress={false}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
) : null}
|
|
||||||
<IdDisplay id={item._id} type={type} longId={false} />
|
<IdDisplay id={item._id} type={type} longId={false} />
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex gap={'small'}>
|
<Flex gap={'small'}>
|
||||||
|
|||||||
7
src/components/Icons/DesignIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/designicon.min.svg'
|
||||||
|
|
||||||
|
const DesignIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default DesignIcon
|
||||||
7
src/components/Icons/DocumentIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/documenticon.min.svg'
|
||||||
|
|
||||||
|
const DocumentIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default DocumentIcon
|
||||||
9
src/components/Icons/DocumentPrinterIcon.jsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/documentprintericon.min.svg'
|
||||||
|
|
||||||
|
const DocumentPrinterIcon = (props) => (
|
||||||
|
<Icon component={CustomIconSvg} {...props} />
|
||||||
|
)
|
||||||
|
|
||||||
|
export default DocumentPrinterIcon
|
||||||
9
src/components/Icons/DocumentSizeIcon.jsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/documentsizeicon.min.svg'
|
||||||
|
|
||||||
|
const DocumentSizeIcon = (props) => (
|
||||||
|
<Icon component={CustomIconSvg} {...props} />
|
||||||
|
)
|
||||||
|
|
||||||
|
export default DocumentSizeIcon
|
||||||
9
src/components/Icons/DocumentTemplateIcon.jsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/documenttemplateicon.min.svg'
|
||||||
|
|
||||||
|
const DocumentTemplateIcon = (props) => (
|
||||||
|
<Icon component={CustomIconSvg} {...props} />
|
||||||
|
)
|
||||||
|
|
||||||
|
export default DocumentTemplateIcon
|
||||||
7
src/components/Icons/OTPIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/otpicon.min.svg'
|
||||||
|
|
||||||
|
const OTPIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default OTPIcon
|
||||||
@ -18,6 +18,9 @@ import { AuditLog } from './models/AuditLog'
|
|||||||
import { User } from './models/User'
|
import { User } from './models/User'
|
||||||
import { NoteType } from './models/NoteType'
|
import { NoteType } from './models/NoteType'
|
||||||
import { Note } from './models/Note'
|
import { Note } from './models/Note'
|
||||||
|
import { DocumentSize } from './models/DocumentSize.js'
|
||||||
|
import { DocumentTemplate } from './models/DocumentTemplate.js'
|
||||||
|
import { DocumentPrinter } from './models/DocumentPrinter.js'
|
||||||
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
|
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
|
||||||
|
|
||||||
export const objectModels = [
|
export const objectModels = [
|
||||||
@ -40,7 +43,10 @@ export const objectModels = [
|
|||||||
AuditLog,
|
AuditLog,
|
||||||
User,
|
User,
|
||||||
NoteType,
|
NoteType,
|
||||||
Note
|
Note,
|
||||||
|
DocumentSize,
|
||||||
|
DocumentTemplate,
|
||||||
|
DocumentPrinter
|
||||||
]
|
]
|
||||||
|
|
||||||
// Re-export individual models for direct access
|
// Re-export individual models for direct access
|
||||||
@ -64,12 +70,21 @@ export {
|
|||||||
AuditLog,
|
AuditLog,
|
||||||
User,
|
User,
|
||||||
NoteType,
|
NoteType,
|
||||||
Note
|
Note,
|
||||||
|
DocumentSize,
|
||||||
|
DocumentTemplate,
|
||||||
|
DocumentPrinter
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getModelByName(name) {
|
export function getModelByName(name, ignoreCase = false) {
|
||||||
|
function formatName(formattedName) {
|
||||||
|
if (ignoreCase == true) {
|
||||||
|
formattedName = formattedName.toUpperCase()
|
||||||
|
}
|
||||||
|
return formattedName
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
objectModels.find((meta) => meta.name === name) || {
|
objectModels.find((meta) => formatName(meta.name) === formatName(name)) || {
|
||||||
name: 'unknown',
|
name: 'unknown',
|
||||||
label: 'Unknown',
|
label: 'Unknown',
|
||||||
prefix: 'UNK',
|
prefix: 'UNK',
|
||||||
|
|||||||
125
src/database/models/DocumentPrinter.js
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import DocumentPrinterIcon from '../../components/Icons/DocumentPrinterIcon'
|
||||||
|
|
||||||
|
export const DocumentPrinter = {
|
||||||
|
name: 'documentPrinter',
|
||||||
|
label: 'Document Printer',
|
||||||
|
prefix: 'DPR',
|
||||||
|
icon: DocumentPrinterIcon,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
label: 'Info',
|
||||||
|
default: true,
|
||||||
|
row: true,
|
||||||
|
icon: InfoCircleIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/documentprinters/info?documentPrinterId=${_id}`
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'reload',
|
||||||
|
label: 'Reload',
|
||||||
|
icon: ReloadIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/documentprinters/info?documentPrinterId=${_id}&action=reload`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/documentprinters/info?documentPrinterId=${_id}&action=edit`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
'name',
|
||||||
|
'_id',
|
||||||
|
'documentSize',
|
||||||
|
'documentSize._id',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt'
|
||||||
|
],
|
||||||
|
filters: ['name', '_id'],
|
||||||
|
sorters: ['name', 'documentSize', 'createdAt', 'updatedAt'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: '_id',
|
||||||
|
label: 'ID',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'documentPrinter',
|
||||||
|
showCopy: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
label: 'Created At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
required: true,
|
||||||
|
type: 'text',
|
||||||
|
columnWidth: 200,
|
||||||
|
columnFixed: 'left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'documentSize',
|
||||||
|
label: 'Document Size',
|
||||||
|
required: true,
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'documentSize'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'documentSize._id',
|
||||||
|
label: 'Document Size ID',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'documentSize',
|
||||||
|
showCopy: true,
|
||||||
|
showHyperlink: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'active',
|
||||||
|
label: 'Active',
|
||||||
|
required: true,
|
||||||
|
type: 'bool'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tags',
|
||||||
|
label: 'Tags',
|
||||||
|
required: false,
|
||||||
|
type: 'tags'
|
||||||
|
},
|
||||||
|
{ name: 'global', label: 'Global', required: false, type: 'bool' },
|
||||||
|
{
|
||||||
|
name: 'parent',
|
||||||
|
label: 'Parent',
|
||||||
|
required: false,
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'documentPrinter',
|
||||||
|
disabled: (documentPrinter) => {
|
||||||
|
if (documentPrinter.global == true) {
|
||||||
|
documentPrinter.parent = null
|
||||||
|
}
|
||||||
|
return documentPrinter.global
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent._id',
|
||||||
|
label: 'Parent ID',
|
||||||
|
required: false,
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'documentPrinter'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
86
src/database/models/DocumentSize.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import DocumentSizeIcon from '../../components/Icons/DocumentSizeIcon'
|
||||||
|
|
||||||
|
export const DocumentSize = {
|
||||||
|
name: 'documentSize',
|
||||||
|
label: 'Document Size',
|
||||||
|
prefix: 'DSZ',
|
||||||
|
icon: DocumentSizeIcon,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
label: 'Info',
|
||||||
|
default: true,
|
||||||
|
row: true,
|
||||||
|
icon: InfoCircleIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/documentsizes/info?documentSizeId=${_id}`
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'reload',
|
||||||
|
label: 'Reload',
|
||||||
|
icon: ReloadIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/documentsizes/info?documentSizeId=${_id}&action=reload`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/documentsizes/info?documentSizeId=${_id}&action=edit`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: ['name', '_id', 'width', 'height', 'createdAt', 'updatedAt'],
|
||||||
|
filters: ['name', '_id', 'width', 'height'],
|
||||||
|
sorters: ['name', 'width', 'height', 'createdAt', 'updatedAt'],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: '_id',
|
||||||
|
label: 'ID',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'documentSize',
|
||||||
|
showCopy: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
label: 'Created At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
required: true,
|
||||||
|
type: 'text',
|
||||||
|
columnWidth: 200,
|
||||||
|
columnFixed: 'left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'width',
|
||||||
|
label: 'Width',
|
||||||
|
required: true,
|
||||||
|
columnWidth: 150,
|
||||||
|
type: 'number',
|
||||||
|
suffix: 'mm'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'height',
|
||||||
|
label: 'Height',
|
||||||
|
required: true,
|
||||||
|
columnWidth: 150,
|
||||||
|
type: 'number',
|
||||||
|
suffix: 'mm'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
189
src/database/models/DocumentTemplate.js
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import DesignIcon from '../../components/Icons/DesignIcon'
|
||||||
|
import DocumentTemplateIcon from '../../components/Icons/DocumentTemplateIcon'
|
||||||
|
|
||||||
|
export const DocumentTemplate = {
|
||||||
|
name: 'documentTemplate',
|
||||||
|
label: 'Document Template',
|
||||||
|
prefix: 'DTP',
|
||||||
|
icon: DocumentTemplateIcon,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
name: 'design',
|
||||||
|
label: 'Design',
|
||||||
|
row: true,
|
||||||
|
icon: DesignIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/documenttemplates/design?documentTemplateId=${_id}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'info',
|
||||||
|
label: 'Info',
|
||||||
|
default: true,
|
||||||
|
row: true,
|
||||||
|
icon: InfoCircleIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/documenttemplates/info?documentTemplateId=${_id}`
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'reload',
|
||||||
|
label: 'Reload',
|
||||||
|
icon: ReloadIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/documenttemplates/info?documentTemplateId=${_id}&action=reload`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/documenttemplates/info?documentTemplateId=${_id}&action=edit`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
columns: [
|
||||||
|
'name',
|
||||||
|
'_id',
|
||||||
|
'active',
|
||||||
|
'global',
|
||||||
|
'objectType',
|
||||||
|
'tags',
|
||||||
|
'parent',
|
||||||
|
'parent._id',
|
||||||
|
'documentSize',
|
||||||
|
'documentSize._id',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt'
|
||||||
|
],
|
||||||
|
filters: ['name', '_id', 'active', 'tags', 'objectType'],
|
||||||
|
group: ['documentSize', 'tags'],
|
||||||
|
sorters: [
|
||||||
|
'name',
|
||||||
|
'parent',
|
||||||
|
'documentSize',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
'active',
|
||||||
|
'global',
|
||||||
|
'objectType'
|
||||||
|
],
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: '_id',
|
||||||
|
label: 'ID',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'documentTemplate',
|
||||||
|
showCopy: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'createdAt',
|
||||||
|
label: 'Created At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
required: true,
|
||||||
|
type: 'text',
|
||||||
|
columnWidth: 200,
|
||||||
|
columnFixed: 'left'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'objectType',
|
||||||
|
label: 'Object Type',
|
||||||
|
required: false,
|
||||||
|
type: 'objectType',
|
||||||
|
empty: (documentTemplate) => {
|
||||||
|
return documentTemplate.global
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'active',
|
||||||
|
label: 'Active',
|
||||||
|
required: true,
|
||||||
|
type: 'bool',
|
||||||
|
columnWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tags',
|
||||||
|
label: 'Tags',
|
||||||
|
required: false,
|
||||||
|
type: 'tags'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'global',
|
||||||
|
label: 'Global',
|
||||||
|
required: true,
|
||||||
|
type: 'bool',
|
||||||
|
columnWidth: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'documentSize',
|
||||||
|
label: 'Document Size',
|
||||||
|
required: true,
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'documentSize'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'documentSize._id',
|
||||||
|
label: 'Document Size ID',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'documentSize',
|
||||||
|
showCopy: true,
|
||||||
|
showHyperlink: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent',
|
||||||
|
label: 'Parent',
|
||||||
|
required: false,
|
||||||
|
type: 'object',
|
||||||
|
masterFilter: { global: true, active: true },
|
||||||
|
objectType: 'documentTemplate',
|
||||||
|
empty: (documentTemplate) => {
|
||||||
|
return documentTemplate.global
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent._id',
|
||||||
|
label: 'Parent ID',
|
||||||
|
required: false,
|
||||||
|
type: 'id',
|
||||||
|
showHyperlink: true,
|
||||||
|
objectType: 'documentTemplate',
|
||||||
|
empty: (documentTemplate) => {
|
||||||
|
return documentTemplate.global
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'documentPrinters',
|
||||||
|
label: 'Document Printers',
|
||||||
|
required: false,
|
||||||
|
type: 'objectList',
|
||||||
|
objectType: 'documentPrinter'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'content',
|
||||||
|
label: 'Content',
|
||||||
|
required: false,
|
||||||
|
type: 'codeBlock',
|
||||||
|
language: 'ejs'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'testObject',
|
||||||
|
label: 'Test Object',
|
||||||
|
required: false,
|
||||||
|
type: 'codeBlock',
|
||||||
|
language: 'json'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -47,7 +47,15 @@ export const Filament = {
|
|||||||
'updatedAt'
|
'updatedAt'
|
||||||
],
|
],
|
||||||
filters: ['_id', 'name', 'type', 'color', 'cost', 'vendor', 'vendor._id'],
|
filters: ['_id', 'name', 'type', 'color', 'cost', 'vendor', 'vendor._id'],
|
||||||
sorters: ['name', 'createdAt', 'type', 'vendor', 'cost', 'updatedAt'],
|
sorters: [
|
||||||
|
'name',
|
||||||
|
'createdAt',
|
||||||
|
'type',
|
||||||
|
'vendor',
|
||||||
|
'cost',
|
||||||
|
'updatedAt',
|
||||||
|
'createdAt'
|
||||||
|
],
|
||||||
group: ['diameter', 'type', 'vendor'],
|
group: ['diameter', 'type', 'vendor'],
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import HostIcon from '../../components/Icons/HostIcon'
|
|||||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
import EditIcon from '../../components/Icons/EditIcon'
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
import OTPIcon from '../../components/Icons/OTPIcon'
|
||||||
|
|
||||||
export const Host = {
|
export const Host = {
|
||||||
name: 'host',
|
name: 'host',
|
||||||
@ -15,7 +16,7 @@ export const Host = {
|
|||||||
default: true,
|
default: true,
|
||||||
row: true,
|
row: true,
|
||||||
icon: InfoCircleIcon,
|
icon: InfoCircleIcon,
|
||||||
url: (_id) => `/dashboard/production/hosts/info?hostId=${_id}`
|
url: (_id) => `/dashboard/management/hosts/info?hostId=${_id}`
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -23,14 +24,21 @@ export const Host = {
|
|||||||
label: 'Reload',
|
label: 'Reload',
|
||||||
icon: ReloadIcon,
|
icon: ReloadIcon,
|
||||||
url: (_id) =>
|
url: (_id) =>
|
||||||
`/dashboard/production/hosts/info?hostId=${_id}&action=reload`
|
`/dashboard/management/hosts/info?hostId=${_id}&action=reload`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'connect',
|
||||||
|
label: 'Connect',
|
||||||
|
icon: OTPIcon,
|
||||||
|
url: (_id) =>
|
||||||
|
`/dashboard/management/hosts/info?hostId=${_id}&action=hostOTP`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'edit',
|
name: 'edit',
|
||||||
label: 'Edit',
|
label: 'Edit',
|
||||||
row: true,
|
row: true,
|
||||||
icon: EditIcon,
|
icon: EditIcon,
|
||||||
url: (_id) => `/dashboard/production/hosts/info?hostId=${_id}&action=edit`
|
url: (_id) => `/dashboard/management/hosts/info?hostId=${_id}&action=edit`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
columns: ['name', '_id', 'state', 'tags', 'connectedAt'],
|
columns: ['name', '_id', 'state', 'tags', 'connectedAt'],
|
||||||
@ -61,30 +69,95 @@ export const Host = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'state',
|
name: 'state',
|
||||||
label: 'Status',
|
label: 'State',
|
||||||
type: 'state',
|
type: 'state',
|
||||||
objectType: 'host',
|
objectType: 'host',
|
||||||
showName: false,
|
showName: false,
|
||||||
readOnly: true
|
readOnly: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'host',
|
name: 'active',
|
||||||
label: 'Host',
|
label: 'Active',
|
||||||
type: 'text',
|
type: 'bool',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'online',
|
||||||
|
label: 'Online',
|
||||||
|
type: 'bool',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deviceInfo.os',
|
||||||
|
label: 'Operating System',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
readOnly: true,
|
||||||
|
value: (objectData) => {
|
||||||
|
if (
|
||||||
|
objectData.deviceInfo?.os?.type &&
|
||||||
|
objectData.deviceInfo?.os?.release &&
|
||||||
|
objectData.deviceInfo?.os?.arch
|
||||||
|
) {
|
||||||
|
return `${objectData.deviceInfo.os.type} ${objectData.deviceInfo.os.release} (${objectData.deviceInfo.os.arch})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deviceInfo.os.hostname',
|
||||||
|
label: 'Hostname',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deviceInfo.cpu.model',
|
||||||
|
label: 'CPU Model',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deviceInfo.cpu',
|
||||||
|
label: 'CPU Info',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
readOnly: true,
|
||||||
|
value: (objectData) => {
|
||||||
|
if (
|
||||||
|
objectData.deviceInfo?.cpu?.cores &&
|
||||||
|
objectData.deviceInfo?.cpu?.speedMHz
|
||||||
|
) {
|
||||||
|
return `Cores: ${objectData.deviceInfo.cpu.cores}, Speed: ${objectData.deviceInfo.cpu.speedMHz} MHz`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deviceInfo.user.username',
|
||||||
|
label: 'User',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deviceInfo.user.homedir',
|
||||||
|
label: 'User Home',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deviceInfo.process.nodeVersion',
|
||||||
|
label: 'NodeJS Version',
|
||||||
|
type: 'text',
|
||||||
|
required: false,
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'tags',
|
name: 'tags',
|
||||||
label: 'Tags',
|
label: 'Tags',
|
||||||
type: 'tags',
|
type: 'tags',
|
||||||
required: false
|
required: false
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'operatingSystem',
|
|
||||||
label: 'Operating System',
|
|
||||||
type: 'text',
|
|
||||||
required: false,
|
|
||||||
readOnly: true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,8 +92,23 @@ export const Printer = {
|
|||||||
readOnly: true
|
readOnly: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'moonraker.host',
|
name: 'host',
|
||||||
label: 'Host',
|
label: 'Host',
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'host',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'host._id',
|
||||||
|
label: 'Host ID',
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'host',
|
||||||
|
showHyperlink: true,
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'moonraker.host',
|
||||||
|
label: 'Hostname',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
|||||||
25
src/routes/DeveloperRoutes.jsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Route } from 'react-router-dom'
|
||||||
|
import SessionStorage from '../components/Dashboard/Developer/SessionStorage.jsx'
|
||||||
|
import AuthContextDebug from '../components/Dashboard/Developer/AuthContextDebug.jsx'
|
||||||
|
import PrintServerContextDebug from '../components/Dashboard/Developer/PrintServerContextDebug.jsx'
|
||||||
|
|
||||||
|
const DeveloperRoutes = [
|
||||||
|
<Route
|
||||||
|
key='sessionstorage'
|
||||||
|
path='developer/sessionstorage'
|
||||||
|
element={<SessionStorage />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='authcontextdebug'
|
||||||
|
path='developer/authcontextdebug'
|
||||||
|
element={<AuthContextDebug />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='printservercontextdebug'
|
||||||
|
path='developer/printservercontextdebug'
|
||||||
|
element={<PrintServerContextDebug />}
|
||||||
|
/>
|
||||||
|
]
|
||||||
|
|
||||||
|
export default DeveloperRoutes
|
||||||
44
src/routes/InventoryRoutes.jsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Route } from 'react-router-dom'
|
||||||
|
|
||||||
|
import FilamentStocks from '../components/Dashboard/Inventory/FilamentStocks.jsx'
|
||||||
|
import FilamentStockInfo from '../components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx'
|
||||||
|
import PartStocks from '../components/Dashboard/Inventory/PartStocks.jsx'
|
||||||
|
import StockEvents from '../components/Dashboard/Inventory/StockEvents.jsx'
|
||||||
|
import StockAudits from '../components/Dashboard/Inventory/StockAudits.jsx'
|
||||||
|
import StockAuditInfo from '../components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx'
|
||||||
|
|
||||||
|
const InventoryRoutes = [
|
||||||
|
<Route
|
||||||
|
key='filamentstocks'
|
||||||
|
path='inventory/filamentstocks'
|
||||||
|
element={<FilamentStocks />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='filamentstocks-info'
|
||||||
|
path='inventory/filamentstocks/info'
|
||||||
|
element={<FilamentStockInfo />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='partstocks'
|
||||||
|
path='inventory/partstocks'
|
||||||
|
element={<PartStocks />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='stockevents'
|
||||||
|
path='inventory/stockevents'
|
||||||
|
element={<StockEvents />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='stockaudits'
|
||||||
|
path='inventory/stockaudits'
|
||||||
|
element={<StockAudits />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='stockaudits-info'
|
||||||
|
path='inventory/stockaudits/info'
|
||||||
|
element={<StockAuditInfo />}
|
||||||
|
/>
|
||||||
|
]
|
||||||
|
|
||||||
|
export default InventoryRoutes
|
||||||
112
src/routes/ManagementRoutes.jsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Route } from 'react-router-dom'
|
||||||
|
|
||||||
|
import Filaments from '../components/Dashboard/Management/Filaments'
|
||||||
|
import FilamentInfo from '../components/Dashboard/Management/Filaments/FilamentInfo.jsx'
|
||||||
|
import Parts from '../components/Dashboard/Management/Parts.jsx'
|
||||||
|
import PartInfo from '../components/Dashboard/Management/Parts/PartInfo.jsx'
|
||||||
|
import Products from '../components/Dashboard/Management/Products.jsx'
|
||||||
|
import ProductInfo from '../components/Dashboard/Management/Products/ProductInfo.jsx'
|
||||||
|
import Vendors from '../components/Dashboard/Management/Vendors'
|
||||||
|
import VendorInfo from '../components/Dashboard/Management/Vendors/VendorInfo'
|
||||||
|
import Materials from '../components/Dashboard/Management/Materials'
|
||||||
|
import Settings from '../components/Dashboard/Management/Settings'
|
||||||
|
import AuditLogs from '../components/Dashboard/Management/AuditLogs.jsx'
|
||||||
|
import NoteTypes from '../components/Dashboard/Management/NoteTypes.jsx'
|
||||||
|
import NoteTypeInfo from '../components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx'
|
||||||
|
import Users from '../components/Dashboard/Management/Users.jsx'
|
||||||
|
import UserInfo from '../components/Dashboard/Management/Users/UserInfo.jsx'
|
||||||
|
import Hosts from '../components/Dashboard/Management/Hosts.jsx'
|
||||||
|
import HostInfo from '../components/Dashboard/Management/Hosts/HostInfo.jsx'
|
||||||
|
import DocumentSizes from '../components/Dashboard/Management/DocumentSizes.jsx'
|
||||||
|
import DocumentSizeInfo from '../components/Dashboard/Management/DocumentSizes/DocumentSizeInfo.jsx'
|
||||||
|
import DocumentTemplates from '../components/Dashboard/Management/DocumentTemplates.jsx'
|
||||||
|
import DocumentTemplateInfo from '../components/Dashboard/Management/DocumentTemplates/DocumentTemplateInfo.jsx'
|
||||||
|
import DocumentPrinters from '../components/Dashboard/Management/DocumentPrinters.jsx'
|
||||||
|
import DocumentPrinterInfo from '../components/Dashboard/Management/DocumentPrinters/DocumentPrinterInfo.jsx'
|
||||||
|
import DocumentTemplateDesign from '../components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx'
|
||||||
|
|
||||||
|
const ManagementRoutes = [
|
||||||
|
<Route key='filaments' path='management/filaments' element={<Filaments />} />,
|
||||||
|
<Route
|
||||||
|
key='filaments-info'
|
||||||
|
path='management/filaments/info'
|
||||||
|
element={<FilamentInfo />}
|
||||||
|
/>,
|
||||||
|
<Route key='parts' path='management/parts' element={<Parts />} />,
|
||||||
|
<Route
|
||||||
|
key='parts-info'
|
||||||
|
path='management/parts/info'
|
||||||
|
element={<PartInfo />}
|
||||||
|
/>,
|
||||||
|
<Route key='products' path='management/products' element={<Products />} />,
|
||||||
|
<Route
|
||||||
|
key='products-info'
|
||||||
|
path='management/products/info'
|
||||||
|
element={<ProductInfo />}
|
||||||
|
/>,
|
||||||
|
<Route key='vendors' path='management/vendors' element={<Vendors />} />,
|
||||||
|
<Route key='hosts' path='management/hosts' element={<Hosts />} />,
|
||||||
|
<Route
|
||||||
|
key='hosts-info'
|
||||||
|
path='management/hosts/info'
|
||||||
|
element={<HostInfo />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='users-info'
|
||||||
|
path='management/users/info'
|
||||||
|
element={<UserInfo />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='vendors-info'
|
||||||
|
path='management/vendors/info'
|
||||||
|
element={<VendorInfo />}
|
||||||
|
/>,
|
||||||
|
<Route key='materials' path='management/materials' element={<Materials />} />,
|
||||||
|
<Route key='notetypes' path='management/notetypes' element={<NoteTypes />} />,
|
||||||
|
<Route
|
||||||
|
key='notetypes-info'
|
||||||
|
path='management/notetypes/info'
|
||||||
|
element={<NoteTypeInfo />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='documentsizes'
|
||||||
|
path='management/documentsizes'
|
||||||
|
element={<DocumentSizes />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='documentsizes-info'
|
||||||
|
path='management/documentsizes/info'
|
||||||
|
element={<DocumentSizeInfo />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='documenttemplates'
|
||||||
|
path='management/documenttemplates'
|
||||||
|
element={<DocumentTemplates />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='documenttemplates-info'
|
||||||
|
path='management/documenttemplates/info'
|
||||||
|
element={<DocumentTemplateInfo />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='documentprinters'
|
||||||
|
path='management/documentprinters'
|
||||||
|
element={<DocumentPrinters />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='documentprinters-info'
|
||||||
|
path='management/documentprinters/info'
|
||||||
|
element={<DocumentPrinterInfo />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='documenttemplates-design'
|
||||||
|
path='management/documenttemplates/design'
|
||||||
|
element={<DocumentTemplateDesign />}
|
||||||
|
/>,
|
||||||
|
<Route key='users' path='management/users' element={<Users />} />,
|
||||||
|
<Route key='settings' path='management/settings' element={<Settings />} />,
|
||||||
|
<Route key='auditlogs' path='management/auditlogs' element={<AuditLogs />} />
|
||||||
|
]
|
||||||
|
|
||||||
|
export default ManagementRoutes
|
||||||
46
src/routes/ProductionRoutes.jsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Route } from 'react-router-dom'
|
||||||
|
|
||||||
|
import ProductionOverview from '../components/Dashboard/Production/ProductionOverview'
|
||||||
|
import Printers from '../components/Dashboard/Production/Printers'
|
||||||
|
import ControlPrinter from '../components/Dashboard/Production/Printers/ControlPrinter.jsx'
|
||||||
|
import PrinterInfo from '../components/Dashboard/Production/Printers/PrinterInfo.jsx'
|
||||||
|
import Jobs from '../components/Dashboard/Production/Jobs.jsx'
|
||||||
|
import JobInfo from '../components/Dashboard/Production/Jobs/JobInfo.jsx'
|
||||||
|
import SubJobs from '../components/Dashboard/Production/SubJobs.jsx'
|
||||||
|
import GCodeFiles from '../components/Dashboard/Production/GCodeFiles'
|
||||||
|
import GCodeFileInfo from '../components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx'
|
||||||
|
|
||||||
|
const ProductionRoutes = [
|
||||||
|
<Route
|
||||||
|
key='production-overview'
|
||||||
|
path='production/overview'
|
||||||
|
element={<ProductionOverview />}
|
||||||
|
/>,
|
||||||
|
<Route key='printers' path='production/printers' element={<Printers />} />,
|
||||||
|
<Route
|
||||||
|
key='printers-control'
|
||||||
|
path='production/printers/control'
|
||||||
|
element={<ControlPrinter />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='printers-info'
|
||||||
|
path='production/printers/info'
|
||||||
|
element={<PrinterInfo />}
|
||||||
|
/>,
|
||||||
|
<Route key='jobs' path='production/jobs' element={<Jobs />} />,
|
||||||
|
<Route key='subjobs' path='production/subjobs' element={<SubJobs />} />,
|
||||||
|
<Route key='jobs-info' path='production/jobs/info' element={<JobInfo />} />,
|
||||||
|
<Route
|
||||||
|
key='gcodefiles'
|
||||||
|
path='production/gcodefiles'
|
||||||
|
element={<GCodeFiles />}
|
||||||
|
/>,
|
||||||
|
<Route
|
||||||
|
key='gcodefiles-info'
|
||||||
|
path='production/gcodefiles/info'
|
||||||
|
element={<GCodeFileInfo />}
|
||||||
|
/>
|
||||||
|
]
|
||||||
|
|
||||||
|
export default ProductionRoutes
|
||||||
4
src/routes/index.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export { default as ProductionRoutes } from './ProductionRoutes'
|
||||||
|
export { default as InventoryRoutes } from './InventoryRoutes'
|
||||||
|
export { default as ManagementRoutes } from './ManagementRoutes'
|
||||||
|
export { default as DeveloperRoutes } from './DeveloperRoutes'
|
||||||