Added more icons, ui tweaks etc.
55
Jenkinsfile
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
node {
|
||||||
|
env.NODE_ENV = 'production'
|
||||||
|
|
||||||
|
try {
|
||||||
|
stage('Checkout') {
|
||||||
|
checkout scm
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Setup Node.js') {
|
||||||
|
nodejs(nodeJSInstallationName: 'Node23') {
|
||||||
|
sh 'node -v'
|
||||||
|
sh 'npm -v'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Install Dependencies') {
|
||||||
|
nodejs(nodeJSInstallationName: 'Node23') {
|
||||||
|
sh 'npm ci --include=dev'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Build') {
|
||||||
|
nodejs(nodeJSInstallationName: 'Node23') {
|
||||||
|
sh 'npm run build'
|
||||||
|
sh 'ls -la build || echo "Build directory not found"'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Verify Build') {
|
||||||
|
sh 'test -d build || (echo "Build directory does not exist" && exit 1)'
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Deploy to printer1') {
|
||||||
|
def remote = [:]
|
||||||
|
remote.name = 'farmcontrolserver'
|
||||||
|
remote.host = 'farmcontrol.tombutcher.local'
|
||||||
|
remote.user = 'ci'
|
||||||
|
remote.password = 'ci'
|
||||||
|
remote.allowAnyHosts = true
|
||||||
|
|
||||||
|
// Copy the build directory to the remote server
|
||||||
|
sshPut remote: remote, from: 'build/*', into: '/srv/farmcontrol-server/'
|
||||||
|
|
||||||
|
// Restart the service using sudo
|
||||||
|
sshCommand remote: remote, command: 'sudo /bin/systemctl restart farmcontrol-server.service'
|
||||||
|
}
|
||||||
|
|
||||||
|
echo 'Pipeline completed successfully!'
|
||||||
|
} catch (Exception e) {
|
||||||
|
echo 'Pipeline failed!'
|
||||||
|
throw e
|
||||||
|
} finally {
|
||||||
|
cleanWs()
|
||||||
|
}
|
||||||
|
}
|
||||||
7662
package-lock.json
generated
51
package.json
@ -3,30 +3,44 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@simplewebauthn/browser": "^10.0.0",
|
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
|
||||||
|
"@simplewebauthn/browser": "^13.1.0",
|
||||||
"@tsparticles/react": "^3.0.0",
|
"@tsparticles/react": "^3.0.0",
|
||||||
"@tsparticles/slim": "^3.5.0",
|
"@tsparticles/slim": "^3.8.1",
|
||||||
"antd": "^5.25.1",
|
"antd": "^5.25.4",
|
||||||
"antd-style": "^3.7.1",
|
"antd-style": "^3.7.1",
|
||||||
"axios": "*",
|
"axios": "^1.9.0",
|
||||||
"country-list": "^2.3.0",
|
"country-list": "^2.3.0",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"gcode-preview": "^2.17.0",
|
"eslint": "^8.57.0",
|
||||||
"keycloak-js": "^26.1.5",
|
"eslint-config-prettier": "^10.1.5",
|
||||||
|
"eslint-plugin-prettier": "^5.4.1",
|
||||||
|
"eslint-plugin-react": "^7.37.5",
|
||||||
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
|
"gcode-preview": "^2.18.0",
|
||||||
|
"keycloak-js": "^26.2.0",
|
||||||
|
"log4js": "^6.9.1",
|
||||||
"moment": "*",
|
"moment": "*",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
"prettier-eslint": "^16.4.2",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "*",
|
"react": "^18.3.1",
|
||||||
"react-country-flag": "^3.1.0",
|
"react-country-flag": "^3.1.0",
|
||||||
"react-dom": "*",
|
"react-dom": "^18.3.1",
|
||||||
"react-router-dom": "*",
|
"react-router-dom": "*",
|
||||||
"react-scripts": "*",
|
"react-scripts": "*",
|
||||||
"react-stl-viewer": "^2.5.0",
|
"react-stl-viewer": "^2.5.0",
|
||||||
"socket.io-client": "*",
|
"socket.io-client": "*",
|
||||||
|
"standard": "^17.1.2",
|
||||||
"styled-components": "*",
|
"styled-components": "*",
|
||||||
"three": "^0.166.1",
|
"svgo": "^3.3.2",
|
||||||
"tsparticles": "^3.5.0",
|
"svgo-loader": "^4.0.0",
|
||||||
|
"three": "^0.177.0",
|
||||||
|
"tsparticles": "^3.8.1",
|
||||||
"virtualizedtableforantd4": "^1.3.1",
|
"virtualizedtableforantd4": "^1.3.1",
|
||||||
"web-vitals": "*"
|
"web-vitals": "*",
|
||||||
|
"webpack": "^5.99.9",
|
||||||
|
"webpack-cli": "^6.0.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "react-scripts start",
|
"dev": "react-scripts start",
|
||||||
@ -54,14 +68,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^10.1.5",
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
"eslint-plugin-prettier": "^5.4.1",
|
||||||
"eslint-plugin-react": "^7.35.0",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"eslint-plugin-react-hooks": "^4.6.2",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-eslint": "^16.3.0",
|
"prettier-eslint": "^16.4.2",
|
||||||
"standard": "^17.1.0",
|
"standard": "^17.1.2",
|
||||||
"svgo": "^3.3.2",
|
"svgo": "^3.3.2",
|
||||||
"svgo-loader": "^4.0.0",
|
"svgo-loader": "^4.0.0",
|
||||||
"webpack": "^5.99.9",
|
"webpack": "^5.99.9",
|
||||||
|
|||||||
50
src/App.jsx
@ -5,7 +5,7 @@ import {
|
|||||||
Route,
|
Route,
|
||||||
Navigate
|
Navigate
|
||||||
} from 'react-router-dom'
|
} from 'react-router-dom'
|
||||||
import { App, ConfigProvider, theme } from 'antd'
|
import { App, ConfigProvider } from 'antd'
|
||||||
import AuthLayout from './components/Auth/AuthLayout.jsx'
|
import AuthLayout from './components/Auth/AuthLayout.jsx'
|
||||||
import ProductionOverview from './components/Dashboard/Production/ProductionOverview'
|
import ProductionOverview from './components/Dashboard/Production/ProductionOverview'
|
||||||
|
|
||||||
@ -36,6 +36,8 @@ import Materials from './components/Dashboard/Management/Materials'
|
|||||||
import FilamentStocks from './components/Dashboard/Inventory/FilamentStocks.jsx'
|
import FilamentStocks from './components/Dashboard/Inventory/FilamentStocks.jsx'
|
||||||
import FilamentStockInfo from './components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.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 StockAudits from './components/Dashboard/Inventory/StockAudits.jsx'
|
||||||
import StockAuditInfo from './components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx'
|
import StockAuditInfo from './components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx'
|
||||||
|
|
||||||
@ -46,27 +48,18 @@ import './App.css'
|
|||||||
import { SocketProvider } from './components/Dashboard/context/SocketContext.js'
|
import { SocketProvider } from './components/Dashboard/context/SocketContext.js'
|
||||||
import { AuthProvider } from './components/Auth/AuthContext.js'
|
import { AuthProvider } from './components/Auth/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 {
|
||||||
|
ThemeProvider,
|
||||||
|
useThemeContext
|
||||||
|
} from './components/Dashboard/context/ThemeContext'
|
||||||
|
|
||||||
|
const AppContent = () => {
|
||||||
|
const { themeConfig } = useThemeContext()
|
||||||
|
|
||||||
const FarmControlApp = () => {
|
|
||||||
return (
|
return (
|
||||||
<ConfigProvider
|
<ConfigProvider theme={themeConfig}>
|
||||||
theme={{
|
|
||||||
algorithm: theme.darkAlgorithm,
|
|
||||||
token: {
|
|
||||||
colorPrimary: '#007AFF',
|
|
||||||
colorSuccess: '#32D74B',
|
|
||||||
colorWarning: '#FF9F0A',
|
|
||||||
colorInfo: '#007AFF',
|
|
||||||
colorLink: '#5AC8F5',
|
|
||||||
borderRadius: '10px'
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Layout: {
|
|
||||||
headerBg: '#141414'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<App>
|
<App>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<SocketProvider>
|
<SocketProvider>
|
||||||
@ -135,6 +128,14 @@ const FarmControlApp = () => {
|
|||||||
path='inventory/filamentstocks/info'
|
path='inventory/filamentstocks/info'
|
||||||
element={<FilamentStockInfo />}
|
element={<FilamentStockInfo />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path='inventory/partstocks'
|
||||||
|
element={<PartStocks />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path='inventory/stockevents'
|
||||||
|
element={<StockEvents />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path='inventory/stockaudits'
|
path='inventory/stockaudits'
|
||||||
element={<StockAudits />}
|
element={<StockAudits />}
|
||||||
@ -172,6 +173,7 @@ const FarmControlApp = () => {
|
|||||||
path='management/materials'
|
path='management/materials'
|
||||||
element={<Materials />}
|
element={<Materials />}
|
||||||
/>
|
/>
|
||||||
|
<Route path='management/settings' element={<Settings />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</Router>
|
||||||
@ -183,4 +185,12 @@ const FarmControlApp = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FarmControlApp = () => {
|
||||||
|
return (
|
||||||
|
<ThemeProvider>
|
||||||
|
<AppContent />
|
||||||
|
</ThemeProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default FarmControlApp
|
export default FarmControlApp
|
||||||
|
|||||||
BIN
src/assets/icons/arrowdownicon.afdesign
Normal file
1
src/assets/icons/arrowdownicon.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="M0 0h48.569v59.324H0z" style="fill-opacity:0" transform="translate(8.257 3)scale(.97769)"/><path d="M24.188 0c-2.013 0-3.411 1.401-3.411 3.453v38.822l.374 10.184 2.054-1.024-10.428-11.629-7.086-7c-.592-.618-1.455-.986-2.424-.986C1.384 31.82 0 33.227 0 35.129c0 .913.354 1.733 1.066 2.47l20.578 20.635a3.515 3.515 0 0 0 5.088 0l20.604-20.635c.717-.737 1.046-1.557 1.046-2.47 0-1.902-1.384-3.309-3.267-3.309-.969 0-1.812.368-2.424.986l-7.097 7-10.448 11.629 2.073 1.024.375-10.184V3.453C27.594 1.401 26.201 0 24.188 0" style="fill-rule:nonzero" transform="translate(8.349 3)scale(.97769)"/></svg>
|
||||||
|
After Width: | Height: | Size: 770 B |
10
src/assets/icons/arrowdownicon.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.977687,0,0,0.977687,8.25722,3)">
|
||||||
|
<rect x="0" y="0" width="48.569" height="59.324" style="fill-opacity:0;"/>
|
||||||
|
<g transform="matrix(1,0,0,1,0.09375,0)">
|
||||||
|
<path d="M24.188,0C22.175,0 20.777,1.401 20.777,3.453L20.777,42.275L21.151,52.459L23.205,51.435L12.777,39.806L5.691,32.806C5.099,32.188 4.236,31.82 3.267,31.82C1.384,31.82 0,33.227 0,35.129C0,36.042 0.354,36.862 1.066,37.599L21.644,58.234C22.323,58.944 23.244,59.324 24.188,59.324C25.133,59.324 26.048,58.944 26.732,58.234L47.336,37.599C48.053,36.862 48.382,36.042 48.382,35.129C48.382,33.227 46.998,31.82 45.115,31.82C44.146,31.82 43.303,32.188 42.691,32.806L35.594,39.806L25.146,51.435L27.219,52.459L27.594,42.275L27.594,3.453C27.594,1.401 26.201,0 24.188,0Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/icons/arrowlefticon.afdesign
Normal file
1
src/assets/icons/arrowlefticon.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="M0 24.194c0 .944.411 1.86 1.121 2.544L21.73 47.31c.738.718 1.557 1.072 2.436 1.072 1.937 0 3.369-1.384 3.369-3.267 0-.969-.394-1.837-1.012-2.424l-7-7.086L7.879 24.971l-1.112 1.998 10.811.636h38.018c2.053 0 3.448-1.398 3.448-3.411s-1.395-3.411-3.448-3.411H17.578l-10.811.636 1.112 2.029 11.644-10.665 7-7.092a3.4 3.4 0 0 0 1.012-2.424C27.535 1.384 26.103 0 24.166 0c-.879 0-1.698.329-2.487 1.123L1.121 21.65C.411 22.334 0 23.249 0 24.194" style="fill-rule:nonzero" transform="translate(3 8.237)scale(.98232)"/></svg>
|
||||||
|
After Width: | Height: | Size: 691 B |
7
src/assets/icons/arrowlefticon.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.982323,0,0,0.982323,3,8.23672)">
|
||||||
|
<path d="M0,24.194C0,25.138 0.411,26.054 1.121,26.738L21.73,47.31C22.468,48.028 23.287,48.382 24.166,48.382C26.103,48.382 27.535,46.998 27.535,45.115C27.535,44.146 27.141,43.278 26.523,42.691L19.523,35.605L7.879,24.971L6.767,26.969L17.578,27.605L55.596,27.605C57.649,27.605 59.044,26.207 59.044,24.194C59.044,22.181 57.649,20.783 55.596,20.783L17.578,20.783L6.767,21.419L7.879,23.448L19.523,12.783L26.523,5.691C27.141,5.073 27.535,4.236 27.535,3.267C27.535,1.384 26.103,0 24.166,0C23.287,0 22.468,0.329 21.679,1.123L1.121,21.65C0.411,22.334 0,23.249 0,24.194Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/icons/arrowrighticon.afdesign
Normal file
1
src/assets/icons/arrowrighticon.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="M0 24.194c0 .944.411 1.86 1.121 2.544L21.73 47.31c.738.718 1.557 1.072 2.436 1.072 1.937 0 3.369-1.384 3.369-3.267 0-.969-.394-1.837-1.012-2.424l-7-7.086L7.879 24.971l-1.112 1.998 10.811.636h38.018c2.053 0 3.448-1.398 3.448-3.411s-1.395-3.411-3.448-3.411H17.578l-10.811.636 1.112 2.029 11.644-10.665 7-7.092a3.4 3.4 0 0 0 1.012-2.424C27.535 1.384 26.103 0 24.166 0c-.879 0-1.698.329-2.487 1.123L1.121 21.65C.411 22.334 0 23.249 0 24.194" style="fill-rule:nonzero" transform="matrix(-.98232 0 0 .98232 61 8.237)"/></svg>
|
||||||
|
After Width: | Height: | Size: 695 B |
7
src/assets/icons/arrowrighticon.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.982323,0,0,0.982323,61,8.23672)">
|
||||||
|
<path d="M0,24.194C0,25.138 0.411,26.054 1.121,26.738L21.73,47.31C22.468,48.028 23.287,48.382 24.166,48.382C26.103,48.382 27.535,46.998 27.535,45.115C27.535,44.146 27.141,43.278 26.523,42.691L19.523,35.605L7.879,24.971L6.767,26.969L17.578,27.605L55.596,27.605C57.649,27.605 59.044,26.207 59.044,24.194C59.044,22.181 57.649,20.783 55.596,20.783L17.578,20.783L6.767,21.419L7.879,23.448L19.523,12.783L26.523,5.691C27.141,5.073 27.535,4.236 27.535,3.267C27.535,1.384 26.103,0 24.166,0C23.287,0 22.468,0.329 21.679,1.123L1.121,21.65C0.411,22.334 0,23.249 0,24.194Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/icons/arrowupicon.afdesign
Normal file
1
src/assets/icons/arrowupicon.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="M0 0h48.569v59.324H0z" style="fill-opacity:0" transform="translate(8.257 3)scale(.97769)"/><path d="M24.188 0c-2.013 0-3.411 1.401-3.411 3.453v38.822l.374 10.184 2.054-1.024-10.428-11.629-7.086-7c-.592-.618-1.455-.986-2.424-.986C1.384 31.82 0 33.227 0 35.129c0 .913.354 1.733 1.066 2.47l20.578 20.635a3.515 3.515 0 0 0 5.088 0l20.604-20.635c.717-.737 1.046-1.557 1.046-2.47 0-1.902-1.384-3.309-3.267-3.309-.969 0-1.812.368-2.424.986l-7.097 7-10.448 11.629 2.073 1.024.375-10.184V3.453C27.594 1.401 26.201 0 24.188 0" style="fill-rule:nonzero" transform="matrix(.97769 0 0 -.97769 8.349 61)"/></svg>
|
||||||
|
After Width: | Height: | Size: 774 B |
10
src/assets/icons/arrowupicon.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.977687,0,0,0.977687,8.25722,3)">
|
||||||
|
<rect x="0" y="0" width="48.569" height="59.324" style="fill-opacity:0;"/>
|
||||||
|
<g transform="matrix(1,0,0,-1,0.09375,59.3237)">
|
||||||
|
<path d="M24.188,0C22.175,0 20.777,1.401 20.777,3.453L20.777,42.275L21.151,52.459L23.205,51.435L12.777,39.806L5.691,32.806C5.099,32.188 4.236,31.82 3.267,31.82C1.384,31.82 0,33.227 0,35.129C0,36.042 0.354,36.862 1.066,37.599L21.644,58.234C22.323,58.944 23.244,59.324 24.188,59.324C25.133,59.324 26.048,58.944 26.732,58.234L47.336,37.599C48.053,36.862 48.382,36.042 48.382,35.129C48.382,33.227 46.998,31.82 45.115,31.82C44.146,31.82 43.303,32.188 42.691,32.806L35.594,39.806L25.146,51.435L27.219,52.459L27.594,42.275L27.594,3.453C27.594,1.401 26.201,0 24.188,0Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/icons/checkcircleicon.afdesign
Normal file
1
src/assets/icons/checkcircleicon.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="M32.28 64.56c17.824 0 32.286-14.461 32.286-32.28S50.104 0 32.28 0C14.461 0 0 14.461 0 32.28s14.461 32.28 32.28 32.28m0-6.359c-14.333 0-25.921-11.588-25.921-25.921S17.947 6.359 32.28 6.359 58.207 17.947 58.207 32.28 46.613 58.201 32.28 58.201" style="fill-rule:nonzero" transform="scale(.99133)"/><path d="M28.899 47.141c1.121 0 2.097-.557 2.762-1.587l13.977-21.833c.395-.676.773-1.403.773-2.107 0-1.564-1.385-2.605-2.855-2.605-.917 0-1.765.545-2.427 1.575L28.791 40.406 22.983 33c-.767-.989-1.505-1.295-2.447-1.295-1.507 0-2.722 1.223-2.722 2.756 0 .755.293 1.433.809 2.112l7.389 9.003c.837 1.065 1.754 1.565 2.887 1.565" style="fill-rule:nonzero" transform="scale(.99133)"/></svg>
|
||||||
|
After Width: | Height: | Size: 857 B |
8
src/assets/icons/checkcircleicon.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.991326,0,0,0.991326,0,0)">
|
||||||
|
<path d="M32.28,64.56C50.104,64.56 64.566,50.099 64.566,32.28C64.566,14.461 50.104,0 32.28,0C14.461,0 0,14.461 0,32.28C0,50.099 14.461,64.56 32.28,64.56ZM32.28,58.201C17.947,58.201 6.359,46.613 6.359,32.28C6.359,17.947 17.947,6.359 32.28,6.359C46.613,6.359 58.207,17.947 58.207,32.28C58.207,46.613 46.613,58.201 32.28,58.201Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M28.899,47.141C30.02,47.141 30.996,46.584 31.661,45.554L45.638,23.721C46.033,23.045 46.411,22.318 46.411,21.614C46.411,20.05 45.026,19.009 43.556,19.009C42.639,19.009 41.791,19.554 41.129,20.584L28.791,40.406L22.983,33C22.216,32.011 21.478,31.705 20.536,31.705C19.029,31.705 17.814,32.928 17.814,34.461C17.814,35.216 18.107,35.894 18.623,36.573L26.012,45.576C26.849,46.641 27.766,47.141 28.899,47.141Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@ -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="M21.134 55.708c1.625 0 2.907-.679 3.798-2.037L54.306 7.879c.658-1.03.921-1.892.921-2.74 0-2.152-1.513-3.636-3.687-3.636-1.542 0-2.438.518-3.374 1.987L21.009 46.634 7.015 28.64c-.911-1.206-1.866-1.711-3.216-1.711C1.567 26.929 0 28.491 0 30.648c0 .927.346 1.863 1.126 2.81l16.19 20.257c1.078 1.348 2.244 1.993 3.818 1.993" style="fill-rule:nonzero" transform="translate(0 -1.149)scale(1.15885)"/></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="M21.134 55.708c1.625 0 2.907-.679 3.798-2.037L54.306 7.879c.658-1.03.921-1.892.921-2.74 0-2.152-1.513-3.636-3.687-3.636-1.542 0-2.438.518-3.374 1.987L21.009 46.634 7.015 28.64c-.911-1.206-1.866-1.711-3.216-1.711C1.567 26.929 0 28.491 0 30.648c0 .927.346 1.863 1.126 2.81l16.19 20.257c1.078 1.348 2.244 1.993 3.818 1.993" style="fill-rule:nonzero" transform="translate(3 1.959)scale(1.0502)"/></svg>
|
||||||
|
Before Width: | Height: | Size: 576 B After Width: | Height: | Size: 574 B |
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?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">
|
<!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;">
|
<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.15885,0,0,1.15885,4.88498e-15,-1.14877)">
|
<g transform="matrix(1.0502,0,0,1.0502,3,1.95893)">
|
||||||
<path d="M21.134,55.708C22.759,55.708 24.041,55.029 24.932,53.671L54.306,7.879C54.964,6.849 55.227,5.987 55.227,5.139C55.227,2.987 53.714,1.503 51.54,1.503C49.998,1.503 49.102,2.021 48.166,3.49L21.009,46.634L7.015,28.64C6.104,27.434 5.149,26.929 3.799,26.929C1.567,26.929 0,28.491 0,30.648C0,31.575 0.346,32.511 1.126,33.458L17.316,53.715C18.394,55.063 19.56,55.708 21.134,55.708Z" style="fill-rule:nonzero;"/>
|
<path d="M21.134,55.708C22.759,55.708 24.041,55.029 24.932,53.671L54.306,7.879C54.964,6.849 55.227,5.987 55.227,5.139C55.227,2.987 53.714,1.503 51.54,1.503C49.998,1.503 49.102,2.021 48.166,3.49L21.009,46.634L7.015,28.64C6.104,27.434 5.149,26.929 3.799,26.929C1.567,26.929 0,28.491 0,30.648C0,31.575 0.346,32.511 1.126,33.458L17.316,53.715C18.394,55.063 19.56,55.708 21.134,55.708Z" style="fill-rule:nonzero;"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 947 B After Width: | Height: | Size: 934 B |
BIN
src/assets/icons/exclamationoctagonicon.afdesign
Normal file
1
src/assets/icons/exclamationoctagonicon.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="M23.142 62.87h19.121c4.042 0 6.204-1.354 8.373-3.731l12.046-13.425c2.144-2.408 2.724-4.012 2.724-7.291v-13.95c0-3.305-.681-4.805-2.858-7.291L50.636 3.756C48.484 1.363 46.305 0 42.263 0H23.142c-4.041 0-6.229 1.38-8.367 3.756L2.729 17.182C.554 19.589 0 21.168 0 24.473v13.95c0 3.279.554 4.883 2.729 7.291l12.046 13.425c2.138 2.377 4.326 3.731 8.367 3.731m1.519-6.035c-2.978 0-3.947-.693-5.575-2.489L8.249 42.396c-1.213-1.381-1.506-2.23-1.506-4.522V25.016c0-2.292.293-3.141 1.506-4.516L19.086 8.55c1.628-1.822 2.597-2.489 5.575-2.489H40.75c2.973 0 3.916.667 5.57 2.489L57.131 20.5c1.245 1.375 1.532 2.224 1.532 4.516v12.858c0 2.292-.287 3.141-1.532 4.522L46.32 54.346c-1.654 1.796-2.597 2.489-5.57 2.489z" style="fill-rule:nonzero" transform="translate(0 1.24)scale(.9785)"/><path d="M32.701 36.638c1.692 0 2.679-.945 2.733-2.75l.46-15.2c.059-1.814-1.326-3.125-3.224-3.125-1.929 0-3.257 1.286-3.198 3.099l.454 15.237c.054 1.768 1.053 2.739 2.775 2.739m0 10.353c2.004 0 3.651-1.454 3.651-3.424 0-1.964-1.621-3.43-3.651-3.43-2.003 0-3.642 1.486-3.642 3.43s1.664 3.424 3.642 3.424" style="fill-rule:nonzero" transform="translate(0 1.24)scale(.9785)"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
8
src/assets/icons/exclamationoctagonicon.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.978509,0,0,0.978509,0,1.24055)">
|
||||||
|
<path d="M23.142,62.87L42.263,62.87C46.305,62.87 48.467,61.516 50.636,59.139L62.682,45.714C64.826,43.306 65.406,41.702 65.406,38.423L65.406,24.473C65.406,21.168 64.725,19.668 62.548,17.182L50.636,3.756C48.484,1.363 46.305,0 42.263,0L23.142,0C19.101,0 16.913,1.38 14.775,3.756L2.729,17.182C0.554,19.589 0,21.168 0,24.473L0,38.423C0,41.702 0.554,43.306 2.729,45.714L14.775,59.139C16.913,61.516 19.101,62.87 23.142,62.87ZM24.661,56.835C21.683,56.835 20.714,56.142 19.086,54.346L8.249,42.396C7.036,41.015 6.743,40.166 6.743,37.874L6.743,25.016C6.743,22.724 7.036,21.875 8.249,20.5L19.086,8.55C20.714,6.728 21.683,6.061 24.661,6.061L40.75,6.061C43.723,6.061 44.666,6.728 46.32,8.55L57.131,20.5C58.376,21.875 58.663,22.724 58.663,25.016L58.663,37.874C58.663,40.166 58.376,41.015 57.131,42.396L46.32,54.346C44.666,56.142 43.723,56.835 40.75,56.835L24.661,56.835Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M32.701,36.638C34.393,36.638 35.38,35.693 35.434,33.888L35.894,18.688C35.953,16.874 34.568,15.563 32.67,15.563C30.741,15.563 29.413,16.849 29.472,18.662L29.926,33.899C29.98,35.667 30.979,36.638 32.701,36.638ZM32.701,46.991C34.705,46.991 36.352,45.537 36.352,43.567C36.352,41.603 34.731,40.137 32.701,40.137C30.698,40.137 29.059,41.623 29.059,43.567C29.059,45.511 30.723,46.991 32.701,46.991Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/assets/icons/homeicon.afdesign
Normal file
1
src/assets/icons/homeicon.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="M0 0h76.494v67.007H0z" style="fill-opacity:0" transform="translate(3 6.597)scale(.75823)"/><path d="M29.122 62.416h18.063V42.667c0-1.46-.949-2.409-2.409-2.409H31.562c-1.46 0-2.44.949-2.44 2.409zM3.194 34.517c.97 0 1.835-.497 2.598-1.129l31.04-26.062c.422-.349.896-.539 1.319-.539.453 0 .902.19 1.324.539L70.54 33.388c.738.632 1.603 1.129 2.573 1.129 1.98 0 3.194-1.376 3.194-2.989 0-.871-.383-1.79-1.225-2.465L42.519 1.725C41.157.579 39.651 0 38.151 0c-1.495 0-3.002.579-4.363 1.725L1.225 29.063C.408 29.738 0 30.657 0 31.528c0 1.613 1.214 2.989 3.194 2.989m54.794-18.118 8.828 7.424V9.155c0-1.409-.897-2.306-2.3-2.306H60.3c-1.377 0-2.312.897-2.312 2.306zM17.026 66.945h42.281c4.73 0 7.509-2.728 7.509-7.328V24.823l-6.122-4.165v37.113c0 1.975-1.077 3.052-3.001 3.052H18.639c-1.949 0-3.026-1.077-3.026-3.052V20.683l-6.122 4.14v34.794c0 4.626 2.779 7.328 7.535 7.328" style="fill-rule:nonzero" transform="translate(3 6.597)scale(.75823)"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
8
src/assets/icons/homeicon.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.758226,0,0,0.758226,3,6.59657)">
|
||||||
|
<rect x="0" y="0" width="76.494" height="67.007" style="fill-opacity:0;"/>
|
||||||
|
<path d="M29.122,62.416L47.185,62.416L47.185,42.667C47.185,41.207 46.236,40.258 44.776,40.258L31.562,40.258C30.102,40.258 29.122,41.207 29.122,42.667L29.122,62.416ZM3.194,34.517C4.164,34.517 5.029,34.02 5.792,33.388L36.832,7.326C37.254,6.977 37.728,6.787 38.151,6.787C38.604,6.787 39.053,6.977 39.475,7.326L70.54,33.388C71.278,34.02 72.143,34.517 73.113,34.517C75.093,34.517 76.307,33.141 76.307,31.528C76.307,30.657 75.924,29.738 75.082,29.063L42.519,1.725C41.157,0.579 39.651,0 38.151,0C36.656,0 35.149,0.579 33.788,1.725L1.225,29.063C0.408,29.738 0,30.657 0,31.528C0,33.141 1.214,34.517 3.194,34.517ZM57.988,16.399L66.816,23.823L66.816,9.155C66.816,7.746 65.919,6.849 64.516,6.849L60.3,6.849C58.923,6.849 57.988,7.746 57.988,9.155L57.988,16.399ZM17.026,66.945L59.307,66.945C64.037,66.945 66.816,64.217 66.816,59.617L66.816,24.823L60.694,20.658L60.694,57.771C60.694,59.746 59.617,60.823 57.693,60.823L18.639,60.823C16.69,60.823 15.613,59.746 15.613,57.771L15.613,20.683L9.491,24.823L9.491,59.617C9.491,64.243 12.27,66.945 17.026,66.945Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
BIN
src/assets/icons/pausecircleicon.afdesign
Normal file
1
src/assets/icons/pausecircleicon.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="M32.28 64.56c17.824 0 32.286-14.461 32.286-32.28S50.104 0 32.28 0C14.461 0 0 14.461 0 32.28s14.461 32.28 32.28 32.28m0-6.359c-14.333 0-25.921-11.588-25.921-25.921S17.947 6.359 32.28 6.359 58.207 17.947 58.207 32.28 46.613 58.201 32.28 58.201" style="fill-rule:nonzero" transform="scale(.99124)"/><path d="M23.358 44.569h3.231c1.607 0 2.388-.889 2.388-2.198V22.138c0-1.278-.781-2.172-2.388-2.172h-3.231c-1.588 0-2.363.894-2.363 2.172v20.233c0 1.309.775 2.198 2.363 2.198m14.619 0h3.231c1.556 0 2.337-.889 2.337-2.198V22.138c0-1.278-.781-2.172-2.337-2.172h-3.231c-1.613 0-2.388.894-2.388 2.172v20.233c0 1.309.775 2.198 2.388 2.198" style="fill-rule:nonzero" transform="scale(.99124)"/></svg>
|
||||||
|
After Width: | Height: | Size: 865 B |
8
src/assets/icons/pausecircleicon.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.99124,0,0,0.99124,0,0)">
|
||||||
|
<path d="M32.28,64.56C50.104,64.56 64.566,50.099 64.566,32.28C64.566,14.461 50.104,0 32.28,0C14.461,0 0,14.461 0,32.28C0,50.099 14.461,64.56 32.28,64.56ZM32.28,58.201C17.947,58.201 6.359,46.613 6.359,32.28C6.359,17.947 17.947,6.359 32.28,6.359C46.613,6.359 58.207,17.947 58.207,32.28C58.207,46.613 46.613,58.201 32.28,58.201Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M23.358,44.569L26.589,44.569C28.196,44.569 28.977,43.68 28.977,42.371L28.977,22.138C28.977,20.86 28.196,19.966 26.589,19.966L23.358,19.966C21.77,19.966 20.995,20.86 20.995,22.138L20.995,42.371C20.995,43.68 21.77,44.569 23.358,44.569ZM37.977,44.569L41.208,44.569C42.764,44.569 43.545,43.68 43.545,42.371L43.545,22.138C43.545,20.86 42.764,19.966 41.208,19.966L37.977,19.966C36.364,19.966 35.589,20.86 35.589,22.138L35.589,42.371C35.589,43.68 36.364,44.569 37.977,44.569Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
BIN
src/assets/icons/playcircleicon.afdesign
Normal file
1
src/assets/icons/playcircleicon.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="M32.28 64.56c17.824 0 32.286-14.461 32.286-32.28S50.104 0 32.28 0C14.461 0 0 14.461 0 32.28s14.461 32.28 32.28 32.28m0-6.359c-14.333 0-25.921-11.588-25.921-25.921S17.947 6.359 32.28 6.359 58.207 17.947 58.207 32.28 46.613 58.201 32.28 58.201" style="fill-rule:nonzero" transform="scale(.99133)"/><path d="M26.779 44.527 44.128 34.26c1.548-.896 1.517-3.035 0-3.951L26.779 20.042c-1.602-.933-3.625-.182-3.625 1.664v21.151c0 1.826 1.932 2.68 3.625 1.67" style="fill-rule:nonzero" transform="scale(.99133)"/></svg>
|
||||||
|
After Width: | Height: | Size: 686 B |
8
src/assets/icons/playcircleicon.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.991326,0,0,0.991326,0,0)">
|
||||||
|
<path d="M32.28,64.56C50.104,64.56 64.566,50.099 64.566,32.28C64.566,14.461 50.104,0 32.28,0C14.461,0 0,14.461 0,32.28C0,50.099 14.461,64.56 32.28,64.56ZM32.28,58.201C17.947,58.201 6.359,46.613 6.359,32.28C6.359,17.947 17.947,6.359 32.28,6.359C46.613,6.359 58.207,17.947 58.207,32.28C58.207,46.613 46.613,58.201 32.28,58.201Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M26.779,44.527L44.128,34.26C45.676,33.364 45.645,31.225 44.128,30.309L26.779,20.042C25.177,19.109 23.154,19.86 23.154,21.706L23.154,42.857C23.154,44.683 25.086,45.537 26.779,44.527Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/icons/questioncircleicon.afdesign
Normal file
1
src/assets/icons/questioncircleicon.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="M32.28 64.56c17.824 0 32.286-14.461 32.286-32.28S50.104 0 32.28 0C14.461 0 0 14.461 0 32.28s14.461 32.28 32.28 32.28m0-6.359c-14.333 0-25.921-11.588-25.921-25.921S17.947 6.359 32.28 6.359 58.207 17.947 58.207 32.28 46.613 58.201 32.28 58.201" style="fill-rule:nonzero" transform="scale(.99124)"/><path d="M31.573 38.518c1.703 0 2.818-1.011 2.897-2.275.006-.119.011-.275.023-.369.084-1.621 1.168-2.712 3.21-4.043 3.093-2.011 5.08-3.801 5.08-7.444 0-5.193-4.681-8.175-10.23-8.175-5.358 0-8.985 2.462-9.941 5.387a4.8 4.8 0 0 0-.289 1.622c0 1.507 1.166 2.437 2.428 2.437 1.136 0 1.919-.489 2.532-1.3l.49-.655c1.06-1.694 2.538-2.616 4.424-2.616 2.534 0 4.22 1.501 4.22 3.617 0 1.99-1.306 2.954-3.946 4.793-2.228 1.551-3.859 3.157-3.859 5.994v.345c0 1.765 1.069 2.682 2.961 2.682m-.045 9.566c2.003 0 3.65-1.46 3.65-3.43 0-1.965-1.621-3.43-3.65-3.43s-3.668 1.485-3.668 3.43c0 1.944 1.664 3.43 3.668 3.43" style="fill-rule:nonzero" transform="scale(.99124)"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
8
src/assets/icons/questioncircleicon.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.99124,0,0,0.99124,0,0)">
|
||||||
|
<path d="M32.28,64.56C50.104,64.56 64.566,50.099 64.566,32.28C64.566,14.461 50.104,0 32.28,0C14.461,0 0,14.461 0,32.28C0,50.099 14.461,64.56 32.28,64.56ZM32.28,58.201C17.947,58.201 6.359,46.613 6.359,32.28C6.359,17.947 17.947,6.359 32.28,6.359C46.613,6.359 58.207,17.947 58.207,32.28C58.207,46.613 46.613,58.201 32.28,58.201Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M31.573,38.518C33.276,38.518 34.391,37.507 34.47,36.243C34.476,36.124 34.481,35.968 34.493,35.874C34.577,34.253 35.661,33.162 37.703,31.831C40.796,29.82 42.783,28.03 42.783,24.387C42.783,19.194 38.102,16.212 32.553,16.212C27.195,16.212 23.568,18.674 22.612,21.599C22.427,22.128 22.323,22.656 22.323,23.221C22.323,24.728 23.489,25.658 24.751,25.658C25.887,25.658 26.67,25.169 27.283,24.358L27.773,23.703C28.833,22.009 30.311,21.087 32.197,21.087C34.731,21.087 36.417,22.588 36.417,24.704C36.417,26.694 35.111,27.658 32.471,29.497C30.243,31.048 28.612,32.654 28.612,35.491L28.612,35.836C28.612,37.601 29.681,38.518 31.573,38.518ZM31.528,48.084C33.531,48.084 35.178,46.624 35.178,44.654C35.178,42.689 33.557,41.224 31.528,41.224C29.498,41.224 27.86,42.709 27.86,44.654C27.86,46.598 29.524,48.084 31.528,48.084Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
@ -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="M26.391 65.119c.55 0 1.397-.216 2.189-.648 17.973-9.811 24.201-14.3 24.201-25.7V15.146c0-3.703-1.434-5.01-4.535-6.332-3.473-1.447-15.183-5.565-18.589-6.728-1.035-.332-2.217-.548-3.266-.548-1.05 0-2.232.273-3.241.548-3.406.972-15.141 5.307-18.615 6.728C1.46 10.11 0 11.443 0 15.146v23.625c0 11.4 6.254 15.863 24.201 25.7.818.432 1.639.648 2.19.648m0-6.562c-.45 0-.893-.165-1.803-.716C10.303 49.13 5.869 46.787 5.869 37.603V16.142c0-1.101.21-1.541 1.096-1.891 4.626-1.835 13.609-4.834 17.793-6.478.703-.264 1.188-.354 1.633-.354s.924.116 1.632.354c4.185 1.644 13.116 4.822 17.819 6.478.855.324 1.071.79 1.071 1.891v21.461c0 9.235-4.592 11.715-18.72 20.238-.884.545-1.353.716-1.802.716" style="fill-rule:nonzero" transform="translate(7.926 1.597)scale(.91223)"/><path d="M23.061 46.514c1.121 0 2.097-.556 2.741-1.555L39.8 23.094c.394-.644.773-1.376.773-2.106 0-1.533-1.38-2.58-2.855-2.58-.937 0-1.791.519-2.427 1.575L22.958 39.806l-5.839-7.407c-.741-.988-1.499-1.315-2.416-1.315-1.538 0-2.721 1.218-2.721 2.776 0 .729.287 1.434.803 2.112l7.369 9.004c.857 1.07 1.749 1.538 2.907 1.538" style="fill-rule:nonzero" transform="translate(7.926 1.597)scale(.91223)"/></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="M32.468 59.712a.82.82 0 0 1-.383 1.454c-.938.098-1.869-.093-2.785-.611L8.262 48.803c-2.533-1.425-3.821-2.924-3.821-6.676V20.65c0-2.836 1.048-4.63 3.436-5.958L26.541 4.215c3.287-1.848 6.635-1.848 9.922 0l18.686 10.477c2.366 1.328 3.414 3.122 3.414 5.958a1.722 1.722 0 0 1-2.285 1.627 547 547 0 0 0-6.455-2.182l-.03-.009a.598.598 0 0 1-.12-1.093l.001-.001a.966.966 0 0 0 0-1.685L34.365 8.675c-1.958-1.119-3.778-1.088-5.709 0l-4.399 2.49 14.745 8.254a1.171 1.171 0 0 1-.195 2.129c-.808.278-1.655.572-2.494.868a2.8 2.8 0 0 1-2.292-.199l-14.747-8.283-7.418 4.216 14.428 8.09c.513.288.722.92.481 1.456-.321.751-.458 1.673-.458 2.836a.836.836 0 0 1-1.247.728L9.169 22.284v19.38c0 1.395.512 2.255 1.968 3.058l14.946 8.44a6.4 6.4 0 0 1 2.319 2.24c.927 1.528 2.279 2.914 4.066 4.31" style="fill-rule:nonzero"/><path d="M27.141 65.719c.64 0 1.547-.266 2.359-.688 18.313-9.531 24.781-14.5 24.781-25.89V16.016c0-4.453-1.484-6.25-5.375-7.922-3.093-1.297-14.453-5.125-17.359-6.078-1.375-.422-3.047-.688-4.406-.688-1.36 0-3.032.313-4.391.688-2.906.812-14.281 4.797-17.375 6.078C1.5 9.75 0 11.563 0 16.016v23.125c0 11.39 6.484 16.343 24.781 25.89.828.422 1.719.688 2.36.688m0-8.172c-.36 0-.703-.125-1.563-.656C11.563 48.25 7.469 46.297 7.469 37.953v-20.89c0-1.172.25-1.672 1.156-2.032 4.406-1.765 12.969-4.593 16.203-5.828 1.063-.344 1.688-.484 2.313-.484s1.234.156 2.312.484c3.235 1.235 11.766 4.172 16.219 5.828.875.344 1.141.86 1.141 2.032v20.89c0 8.485-4.532 10.875-18.11 18.938-.844.515-1.203.656-1.562.656" style="fill-rule:nonzero" transform="translate(28.594 21.134)scale(.65227)"/><path d="M23.891 46.484c1.281 0 2.437-.656 3.172-1.765L40.5 23.984c.484-.734.813-1.546.813-2.296 0-1.813-1.61-3.11-3.375-3.11-1.157 0-2.141.61-2.907 1.875L23.828 38.516l-5.219-6.407c-.781-.968-1.609-1.375-2.656-1.375-1.828 0-3.281 1.438-3.281 3.266 0 .859.297 1.594.953 2.422l6.969 8.344c.937 1.14 1.969 1.718 3.297 1.718" style="fill-rule:nonzero" transform="translate(28.594 21.134)scale(.65227)"/></svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 2.1 KiB |
@ -1,8 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?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">
|
<!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;">
|
<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.912228,0,0,0.912228,7.92575,1.59687)">
|
<path d="M32.468,59.712C32.725,59.914 32.837,60.249 32.754,60.565C32.671,60.881 32.408,61.117 32.085,61.166C31.147,61.264 30.216,61.073 29.3,60.555L8.262,48.803C5.729,47.378 4.441,45.879 4.441,42.127L4.441,20.65C4.441,17.814 5.489,16.02 7.877,14.692L26.541,4.215C29.828,2.367 33.176,2.367 36.463,4.215L55.149,14.692C57.515,16.02 58.563,17.814 58.563,20.65L58.563,20.65C58.563,21.207 58.294,21.73 57.84,22.053C57.386,22.376 56.804,22.46 56.278,22.277C53.589,21.35 50.911,20.452 49.823,20.095C49.813,20.092 49.803,20.089 49.793,20.086C49.564,20.017 49.398,19.817 49.372,19.579C49.346,19.341 49.464,19.11 49.673,18.993C49.673,18.992 49.674,18.992 49.674,18.992C49.978,18.821 50.166,18.499 50.166,18.15C50.167,17.801 49.979,17.479 49.674,17.307C45.559,14.986 34.365,8.675 34.365,8.675C32.407,7.556 30.587,7.587 28.656,8.675L24.257,11.165C24.257,11.165 34.347,16.813 39.002,19.419C39.405,19.645 39.637,20.087 39.595,20.547C39.553,21.007 39.244,21.399 38.807,21.548C37.999,21.826 37.152,22.12 36.313,22.416C35.556,22.682 34.72,22.61 34.021,22.217C30.425,20.197 19.274,13.934 19.274,13.934L11.856,18.15C11.856,18.15 22.923,24.355 26.284,26.24C26.797,26.528 27.006,27.16 26.765,27.696C26.444,28.447 26.307,29.369 26.307,30.532L26.307,30.532C26.307,30.829 26.149,31.104 25.892,31.254C25.635,31.404 25.318,31.406 25.06,31.26C21.115,29.032 9.169,22.284 9.169,22.284L9.169,41.664C9.169,43.059 9.681,43.919 11.137,44.722C11.137,44.722 22.059,50.89 26.083,53.162C27.034,53.7 27.832,54.471 28.402,55.402C29.329,56.93 30.681,58.316 32.468,59.712Z" style="fill-rule:nonzero;"/>
|
||||||
<path d="M26.391,65.119C26.941,65.119 27.788,64.903 28.58,64.471C46.553,54.66 52.781,50.171 52.781,38.771L52.781,15.146C52.781,11.443 51.347,10.136 48.246,8.814C44.773,7.367 33.063,3.249 29.657,2.086C28.622,1.754 27.44,1.538 26.391,1.538C25.341,1.538 24.159,1.811 23.15,2.086C19.744,3.058 8.009,7.393 4.535,8.814C1.46,10.11 0,11.443 0,15.146L0,38.771C0,50.171 6.254,54.634 24.201,64.471C25.019,64.903 25.84,65.119 26.391,65.119ZM26.391,58.557C25.941,58.557 25.498,58.392 24.588,57.841C10.303,49.13 5.869,46.787 5.869,37.603L5.869,16.142C5.869,15.041 6.079,14.601 6.965,14.251C11.591,12.416 20.574,9.417 24.758,7.773C25.461,7.509 25.946,7.419 26.391,7.419C26.836,7.419 27.315,7.535 28.023,7.773C32.208,9.417 41.139,12.595 45.842,14.251C46.697,14.575 46.913,15.041 46.913,16.142L46.913,37.603C46.913,46.838 42.321,49.318 28.193,57.841C27.309,58.386 26.84,58.557 26.391,58.557Z" style="fill-rule:nonzero;"/>
|
<g transform="matrix(0.652268,0,0,0.652268,28.5941,21.1337)">
|
||||||
<path d="M23.061,46.514C24.182,46.514 25.158,45.958 25.802,44.959L39.8,23.094C40.194,22.45 40.573,21.718 40.573,20.988C40.573,19.455 39.193,18.408 37.718,18.408C36.781,18.408 35.927,18.927 35.291,19.983L22.958,39.806L17.119,32.399C16.378,31.411 15.62,31.084 14.703,31.084C13.165,31.084 11.982,32.302 11.982,33.86C11.982,34.589 12.269,35.294 12.785,35.972L20.154,44.976C21.011,46.046 21.903,46.514 23.061,46.514Z" style="fill-rule:nonzero;"/>
|
<path d="M27.141,65.719C27.781,65.719 28.688,65.453 29.5,65.031C47.813,55.5 54.281,50.531 54.281,39.141L54.281,16.016C54.281,11.563 52.797,9.766 48.906,8.094C45.813,6.797 34.453,2.969 31.547,2.016C30.172,1.594 28.5,1.328 27.141,1.328C25.781,1.328 24.109,1.641 22.75,2.016C19.844,2.828 8.469,6.813 5.375,8.094C1.5,9.75 0,11.563 0,16.016L0,39.141C0,50.531 6.484,55.484 24.781,65.031C25.609,65.453 26.5,65.719 27.141,65.719ZM27.141,57.547C26.781,57.547 26.438,57.422 25.578,56.891C11.563,48.25 7.469,46.297 7.469,37.953L7.469,17.063C7.469,15.891 7.719,15.391 8.625,15.031C13.031,13.266 21.594,10.438 24.828,9.203C25.891,8.859 26.516,8.719 27.141,8.719C27.766,8.719 28.375,8.875 29.453,9.203C32.688,10.438 41.219,13.375 45.672,15.031C46.547,15.375 46.813,15.891 46.813,17.063L46.813,37.953C46.813,46.438 42.281,48.828 28.703,56.891C27.859,57.406 27.5,57.547 27.141,57.547Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M23.891,46.484C25.172,46.484 26.328,45.828 27.063,44.719L40.5,23.984C40.984,23.25 41.313,22.438 41.313,21.688C41.313,19.875 39.703,18.578 37.938,18.578C36.781,18.578 35.797,19.188 35.031,20.453L23.828,38.516L18.609,32.109C17.828,31.141 17,30.734 15.953,30.734C14.125,30.734 12.672,32.172 12.672,34C12.672,34.859 12.969,35.594 13.625,36.422L20.594,44.766C21.531,45.906 22.563,46.484 23.891,46.484Z" style="fill-rule:nonzero;"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 3.4 KiB |
BIN
src/assets/icons/stockeventicon.afdesign
Normal file
1
src/assets/icons/stockeventicon.min.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" clip-rule="evenodd" viewBox="0 0 64 64"><path d="M25 54.028a2.42 2.42 0 0 1-3.602 2.113L8.262 48.803c-2.533-1.425-3.821-2.924-3.821-6.676V20.65c0-2.836 1.048-4.63 3.436-5.958L26.541 4.215c3.287-1.848 6.635-1.848 9.922 0l18.686 10.477c2.366 1.328 3.414 3.122 3.414 5.958v1.987A2.363 2.363 0 0 1 56.2 25h-.002a2.363 2.363 0 0 1-2.363-2.363v-.353l-3.45 1.948a5.93 5.93 0 0 1-2.92.768h-6.99a5.93 5.93 0 0 1-2.915-.765L19.274 13.934l-7.418 4.216 13.894 7.79a2.01 2.01 0 0 1 .684 2.868 3.2 3.2 0 0 0-.421.629 1.97 1.97 0 0 1-2.707.789c-4.313-2.393-14.137-7.942-14.137-7.942v19.38c0 1.395.512 2.255 1.968 3.058l12.633 7.134A2.42 2.42 0 0 1 25 53.964zm18.972-31.826L24.257 11.165l4.399-2.49c1.931-1.088 3.751-1.119 5.709 0L51.17 18.15z"/><g fill-rule="nonzero"><path d="M35.462 64h22.076C61.721 64 64 61.74 64 57.584V35.425C64 31.28 61.72 29 57.538 29H35.462C31.288 29 29 31.28 29 35.425v22.159C29 61.739 31.288 64 35.462 64m.512-4.723c-1.473 0-2.25-.714-2.25-2.27V36.011c0-1.556.777-2.28 2.25-2.28h21.052c1.464 0 2.251.724 2.251 2.28v20.996c0 1.556-.787 2.27-2.251 2.27z"/><path d="m40.185 38.134-4.339 4.476c-.247.256-.42.632-.42.961 0 .77.576 1.336 1.354 1.336.357 0 .677-.128.933-.384l1.767-1.849.366-.393-.019.96V53.96c0 .76.586 1.337 1.355 1.337.788 0 1.373-.577 1.373-1.337V43.232l-.027-.951.375.393 1.757 1.858a1.3 1.3 0 0 0 .943.375c.778 0 1.354-.558 1.354-1.336 0-.348-.192-.723-.43-.961l-4.347-4.476a1.38 1.38 0 0 0-1.995 0m12.658 16.74 4.338-4.466c.238-.256.412-.623.412-.97 0-.778-.55-1.337-1.337-1.337-.374 0-.704.129-.942.385l-1.776 1.84-.366.393.019-.942V39.049c0-.768-.586-1.345-1.355-1.345-.778 0-1.364.577-1.364 1.345v10.736l.028.944-.367-.403-1.766-1.84a1.28 1.28 0 0 0-.952-.385c-.787 0-1.327.56-1.327 1.337 0 .339.156.705.403.97l4.347 4.467c.522.522 1.437.576 2.005 0"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
9
src/assets/icons/stockeventicon.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
|
||||||
|
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" version="1.1" viewBox="0 0 64 64" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m25 54.028c0 0.859-0.455 1.653-1.196 2.088-0.74 0.434-1.656 0.444-2.406 0.025-4.919-2.748-13.136-7.338-13.136-7.338-2.533-1.425-3.821-2.924-3.821-6.676v-21.477c0-2.836 1.048-4.63 3.436-5.958l18.664-10.477c3.287-1.848 6.635-1.848 9.922 0l18.686 10.477c2.366 1.328 3.414 3.122 3.414 5.958v1.987c0 1.305-1.058 2.363-2.363 2.363h-2e-3c-0.627 0-1.228-0.249-1.671-0.692s-0.692-1.044-0.692-1.671v-0.353s-2.028 1.145-3.45 1.948c-0.891 0.504-1.897 0.768-2.92 0.768h-6.99c-1.021 0-2.025-0.263-2.915-0.765-4.445-2.504-18.286-10.301-18.286-10.301l-7.418 4.216s9.552 5.355 13.894 7.79c0.488 0.274 0.84 0.74 0.97 1.284 0.129 0.545 0.026 1.119-0.286 1.584-0.17 0.189-0.301 0.406-0.421 0.629-0.25 0.469-0.678 0.817-1.187 0.965-0.51 0.149-1.058 0.085-1.52-0.176-4.313-2.393-14.137-7.942-14.137-7.942v19.38c0 1.395 0.512 2.255 1.968 3.058 0 0 9.24 5.218 12.633 7.134 0.76 0.429 1.23 1.235 1.23 2.108v0.064zm18.972-31.826-19.715-11.037 4.399-2.49c1.931-1.088 3.751-1.119 5.709 0l16.805 9.475-7.198 4.052z"/>
|
||||||
|
<g transform="matrix(.58577 0 0 .58577 29 29)" fill-rule="nonzero">
|
||||||
|
<path d="m11.031 59.75h37.688c7.14 0 11.031-3.859 11.031-10.953v-37.828c0-7.078-3.891-10.969-11.031-10.969h-37.688c-7.125 0-11.031 3.891-11.031 10.969v37.828c0 7.094 3.906 10.953 11.031 10.953zm0.875-8.062c-2.515 0-3.843-1.219-3.843-3.875v-35.844c0-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.875h-35.938z"/>
|
||||||
|
<path d="m19.094 15.594-7.406 7.64c-0.422 0.438-0.719 1.079-0.719 1.641 0 1.313 0.984 2.281 2.312 2.281 0.61 0 1.157-0.218 1.594-0.656l3.016-3.156 0.625-0.672-0.032 1.641v18.296c0 1.297 1 2.282 2.313 2.282 1.344 0 2.344-0.985 2.344-2.282v-18.312l-0.047-1.625 0.64 0.672 3 3.172c0.422 0.437 1.016 0.64 1.61 0.64 1.328 0 2.312-0.953 2.312-2.281 0-0.594-0.328-1.234-0.734-1.641l-7.422-7.64c-0.906-0.922-2.406-1.016-3.406 0zm21.609 28.578 7.406-7.625c0.407-0.438 0.704-1.063 0.704-1.656 0-1.328-0.938-2.282-2.282-2.282-0.64 0-1.203 0.219-1.609 0.657l-3.031 3.14-0.625 0.672 0.031-1.609v-18.313c0-1.312-1-2.297-2.313-2.297-1.328 0-2.328 0.985-2.328 2.297v18.328l0.047 1.61-0.625-0.688-3.015-3.14c-0.438-0.453-1-0.657-1.625-0.657-1.344 0-2.266 0.954-2.266 2.282 0 0.578 0.266 1.203 0.687 1.656l7.422 7.625c0.891 0.891 2.453 0.984 3.422 0z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
BIN
src/assets/icons/stopcircleicon.afdesign
Normal file
1
src/assets/icons/stopcircleicon.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="M32.28 64.56c17.824 0 32.286-14.461 32.286-32.28S50.104 0 32.28 0C14.461 0 0 14.461 0 32.28s14.461 32.28 32.28 32.28m0-6.359c-14.333 0-25.921-11.588-25.921-25.921S17.947 6.359 32.28 6.359 58.207 17.947 58.207 32.28 46.613 58.201 32.28 58.201" style="fill-rule:nonzero" transform="scale(.99133)"/><path d="M24.123 43.633h16.258c2.013 0 3.231-1.19 3.231-3.119V24.041c0-1.955-1.218-3.119-3.231-3.119H24.123c-1.989 0-3.227 1.164-3.227 3.119v16.473c0 1.929 1.238 3.119 3.227 3.119" style="fill-rule:nonzero" transform="scale(.99133)"/></svg>
|
||||||
|
After Width: | Height: | Size: 712 B |
8
src/assets/icons/stopcircleicon.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.991326,0,0,0.991326,0,0)">
|
||||||
|
<path d="M32.28,64.56C50.104,64.56 64.566,50.099 64.566,32.28C64.566,14.461 50.104,0 32.28,0C14.461,0 0,14.461 0,32.28C0,50.099 14.461,64.56 32.28,64.56ZM32.28,58.201C17.947,58.201 6.359,46.613 6.359,32.28C6.359,17.947 17.947,6.359 32.28,6.359C46.613,6.359 58.207,17.947 58.207,32.28C58.207,46.613 46.613,58.201 32.28,58.201Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M24.123,43.633L40.381,43.633C42.394,43.633 43.612,42.443 43.612,40.514L43.612,24.041C43.612,22.086 42.394,20.922 40.381,20.922L24.123,20.922C22.134,20.922 20.896,22.086 20.896,24.041L20.896,40.514C20.896,42.443 22.134,43.633 24.123,43.633Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/icons/xmarkcircleicon.afdesign
Normal file
1
src/assets/icons/xmarkcircleicon.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="M32.28 64.56c17.824 0 32.286-14.461 32.286-32.28S50.104 0 32.28 0C14.461 0 0 14.461 0 32.28s14.461 32.28 32.28 32.28m0-6.359c-14.333 0-25.921-11.588-25.921-25.921S17.947 6.359 32.28 6.359 58.207 17.947 58.207 32.28 46.613 58.201 32.28 58.201" style="fill-rule:nonzero" transform="scale(.99124)"/><path d="M21.981 45.414c.825 0 1.518-.303 2.054-.865l8.219-8.24 8.282 8.24c.537.536 1.203.865 2.029.865 1.561 0 2.815-1.254 2.815-2.846 0-.78-.289-1.427-.846-1.984l-8.261-8.267 8.287-8.319c.582-.582.846-1.203.846-1.958a2.804 2.804 0 0 0-2.815-2.815c-.769 0-1.39.273-1.978.86l-8.359 8.276-8.27-8.245c-.536-.567-1.178-.84-2.003-.84-1.561 0-2.815 1.223-2.815 2.79 0 .755.315 1.433.845 1.958l8.268 8.293-8.268 8.293c-.53.531-.845 1.209-.845 1.958 0 1.592 1.254 2.846 2.815 2.846" style="fill-rule:nonzero" transform="scale(.99124)"/></svg>
|
||||||
|
After Width: | Height: | Size: 1007 B |
8
src/assets/icons/xmarkcircleicon.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(0.99124,0,0,0.99124,0,0)">
|
||||||
|
<path d="M32.28,64.56C50.104,64.56 64.566,50.099 64.566,32.28C64.566,14.461 50.104,0 32.28,0C14.461,0 0,14.461 0,32.28C0,50.099 14.461,64.56 32.28,64.56ZM32.28,58.201C17.947,58.201 6.359,46.613 6.359,32.28C6.359,17.947 17.947,6.359 32.28,6.359C46.613,6.359 58.207,17.947 58.207,32.28C58.207,46.613 46.613,58.201 32.28,58.201Z" style="fill-rule:nonzero;"/>
|
||||||
|
<path d="M21.981,45.414C22.806,45.414 23.499,45.111 24.035,44.549L32.254,36.309L40.536,44.549C41.073,45.085 41.739,45.414 42.565,45.414C44.126,45.414 45.38,44.16 45.38,42.568C45.38,41.788 45.091,41.141 44.534,40.584L36.273,32.317L44.56,23.998C45.142,23.416 45.406,22.795 45.406,22.04C45.406,20.474 44.151,19.225 42.591,19.225C41.822,19.225 41.201,19.498 40.613,20.085L32.254,28.361L23.984,20.116C23.448,19.549 22.806,19.276 21.981,19.276C20.42,19.276 19.166,20.499 19.166,22.066C19.166,22.821 19.481,23.499 20.011,24.024L28.279,32.317L20.011,40.61C19.481,41.141 19.166,41.819 19.166,42.568C19.166,44.16 20.42,45.414 21.981,45.414Z" style="fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@ -3,7 +3,7 @@ import React, { createContext, useState, useCallback, useEffect } from 'react'
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { message, Modal, notification, Progress, Button, Space } from 'antd'
|
import { message, Modal, notification, Progress, Button, Space } from 'antd'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { ExclamationCircleOutlined } from '@ant-design/icons'
|
import ExclamationOctogonIcon from '../Icons/ExclamationOctagonIcon'
|
||||||
import InfoCircleIcon from '../Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../Icons/InfoCircleIcon'
|
||||||
import config from '../../config'
|
import config from '../../config'
|
||||||
|
|
||||||
@ -224,7 +224,7 @@ const AuthProvider = ({ children }) => {
|
|||||||
<Modal
|
<Modal
|
||||||
title={
|
title={
|
||||||
<Space size={'middle'}>
|
<Space size={'middle'}>
|
||||||
<ExclamationCircleOutlined />
|
<ExclamationOctogonIcon />
|
||||||
Please log in to continue
|
Please log in to continue
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
|
|||||||
265
src/components/Dashboard/Inventory/PartStocks.jsx
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
// src/partStocks.js
|
||||||
|
|
||||||
|
import React, { useEffect, useState, useContext, useCallback } from 'react'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import axios from 'axios'
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
Space,
|
||||||
|
Modal,
|
||||||
|
message,
|
||||||
|
Dropdown,
|
||||||
|
Typography
|
||||||
|
} from 'antd'
|
||||||
|
import { createStyles } from 'antd-style'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
|
import { AuthContext } from '../../Auth/AuthContext'
|
||||||
|
|
||||||
|
import NewPartStock from './PartStocks/NewPartStock'
|
||||||
|
import IdText from '../common/IdText'
|
||||||
|
import PartStockIcon from '../../Icons/PartStockIcon'
|
||||||
|
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
||||||
|
import PlusIcon from '../../Icons/PlusIcon'
|
||||||
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
|
import PartStockState from '../common/PartStockState'
|
||||||
|
import TimeDisplay from '../common/TimeDisplay'
|
||||||
|
|
||||||
|
import config from '../../../config'
|
||||||
|
|
||||||
|
const { Text } = Typography
|
||||||
|
|
||||||
|
const useStyle = createStyles(({ css, token }) => {
|
||||||
|
const { antCls } = token
|
||||||
|
return {
|
||||||
|
customTable: css`
|
||||||
|
${antCls}-table {
|
||||||
|
${antCls}-table-container {
|
||||||
|
${antCls}-table-body,
|
||||||
|
${antCls}-table-content {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #eaeaea transparent;
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const PartStocks = () => {
|
||||||
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const { styles } = useStyle()
|
||||||
|
|
||||||
|
const [partStocksData, setPartStocksData] = useState([])
|
||||||
|
|
||||||
|
const [newPartStockOpen, setNewPartStockOpen] = useState(false)
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
const { authenticated } = useContext(AuthContext)
|
||||||
|
|
||||||
|
const fetchPartStocksData = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${config.backendUrl}/partstocks`, {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
|
},
|
||||||
|
withCredentials: true // Important for including cookies
|
||||||
|
})
|
||||||
|
setPartStocksData(response.data)
|
||||||
|
setLoading(false)
|
||||||
|
} catch (err) {
|
||||||
|
messageApi.info(err)
|
||||||
|
}
|
||||||
|
}, [messageApi])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Fetch initial data
|
||||||
|
if (authenticated) {
|
||||||
|
fetchPartStocksData()
|
||||||
|
}
|
||||||
|
}, [authenticated, fetchPartStocksData])
|
||||||
|
|
||||||
|
const getPartStockActionItems = (id) => {
|
||||||
|
return {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'Info',
|
||||||
|
key: 'info',
|
||||||
|
icon: <InfoCircleIcon />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onClick: ({ key }) => {
|
||||||
|
if (key === 'info') {
|
||||||
|
navigate(`/dashboard/inventory/partstocks/info?partStockId=${id}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Column definitions
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
dataIndex: '',
|
||||||
|
key: 'icon',
|
||||||
|
width: 40,
|
||||||
|
fixed: 'left',
|
||||||
|
render: () => <PartStockIcon></PartStockIcon>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Part Name',
|
||||||
|
dataIndex: 'part',
|
||||||
|
key: 'name',
|
||||||
|
width: 200,
|
||||||
|
fixed: 'left',
|
||||||
|
render: (part) => <Text ellipsis>{part.name}</Text>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: '_id',
|
||||||
|
key: 'id',
|
||||||
|
width: 165,
|
||||||
|
render: (text) => <IdText id={text} type={'partstock'} longId={false} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'State',
|
||||||
|
key: 'state',
|
||||||
|
width: 350,
|
||||||
|
render: (record) => <PartStockState partStock={record} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Current Quantity',
|
||||||
|
dataIndex: 'currentQuantity',
|
||||||
|
key: 'currentQuantity',
|
||||||
|
width: 160,
|
||||||
|
render: (currentQuantity) => <Text ellipsis>{currentQuantity}</Text>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Starting Quantity',
|
||||||
|
dataIndex: 'startingQuantity',
|
||||||
|
key: 'startingQuantity',
|
||||||
|
width: 160,
|
||||||
|
render: (startingQuantity) => <Text ellipsis>{startingQuantity}</Text>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Created At',
|
||||||
|
dataIndex: 'createdAt',
|
||||||
|
key: 'createdAt',
|
||||||
|
width: 180,
|
||||||
|
render: (createdAt) => {
|
||||||
|
if (createdAt) {
|
||||||
|
return <TimeDisplay dateTime={createdAt} />
|
||||||
|
} else {
|
||||||
|
return 'n/a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Updated At',
|
||||||
|
dataIndex: 'updatedAt',
|
||||||
|
key: 'updatedAt',
|
||||||
|
width: 180,
|
||||||
|
render: (updatedAt) => {
|
||||||
|
if (updatedAt) {
|
||||||
|
return <TimeDisplay dateTime={updatedAt} />
|
||||||
|
} else {
|
||||||
|
return 'n/a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Actions',
|
||||||
|
key: 'actions',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 150,
|
||||||
|
render: (text, record) => {
|
||||||
|
return (
|
||||||
|
<Space gap='small'>
|
||||||
|
<Button
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
onClick={() =>
|
||||||
|
navigate(
|
||||||
|
`/dashboard/inventory/partstocks/info?partStockId=${record._id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Dropdown menu={getPartStockActionItems(record._id)}>
|
||||||
|
<Button>Actions</Button>
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const actionItems = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'New Part Stock',
|
||||||
|
key: 'newPartStock',
|
||||||
|
icon: <PlusIcon />
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
label: 'Reload List',
|
||||||
|
key: 'reloadList',
|
||||||
|
icon: <ReloadIcon />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onClick: ({ key }) => {
|
||||||
|
if (key === 'reloadList') {
|
||||||
|
fetchPartStocksData()
|
||||||
|
} else if (key === 'newPartStock') {
|
||||||
|
setNewPartStockOpen(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex vertical={'true'} gap='large'>
|
||||||
|
{contextHolder}
|
||||||
|
<Space>
|
||||||
|
<Dropdown menu={actionItems}>
|
||||||
|
<Button>Actions</Button>
|
||||||
|
</Dropdown>
|
||||||
|
</Space>
|
||||||
|
<Table
|
||||||
|
dataSource={partStocksData}
|
||||||
|
className={styles.customTable}
|
||||||
|
columns={columns}
|
||||||
|
pagination={false}
|
||||||
|
rowKey='_id'
|
||||||
|
loading={{ spinning: loading, indicator: <LoadingOutlined spin /> }}
|
||||||
|
scroll={{ y: 'calc(100vh - 270px)' }}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Modal
|
||||||
|
open={newPartStockOpen}
|
||||||
|
styles={{ content: { paddingBottom: '24px' } }}
|
||||||
|
footer={null}
|
||||||
|
width={700}
|
||||||
|
onCancel={() => {
|
||||||
|
setNewPartStockOpen(false)
|
||||||
|
}}
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<NewPartStock
|
||||||
|
onOk={() => {
|
||||||
|
setNewPartStockOpen(false)
|
||||||
|
messageApi.success('New part stock created successfully.')
|
||||||
|
fetchPartStocksData()
|
||||||
|
}}
|
||||||
|
reset={newPartStockOpen}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PartStocks
|
||||||
129
src/components/Dashboard/Inventory/PartStocks/NewPartStock.jsx
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import { Form, Input, Button, Space, Select, InputNumber } from 'antd'
|
||||||
|
import axios from 'axios'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import config from '../../../../config'
|
||||||
|
|
||||||
|
const NewPartStock = ({ onOk, reset }) => {
|
||||||
|
const [form] = Form.useForm()
|
||||||
|
const [parts, setParts] = useState([])
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Reset form when reset prop changes
|
||||||
|
if (reset) {
|
||||||
|
form.resetFields()
|
||||||
|
}
|
||||||
|
}, [reset, form])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Fetch parts for the select dropdown
|
||||||
|
const fetchParts = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${config.backendUrl}/parts`, {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
|
},
|
||||||
|
withCredentials: true
|
||||||
|
})
|
||||||
|
setParts(response.data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching parts:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchParts()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const onFinish = async (values) => {
|
||||||
|
setLoading(true)
|
||||||
|
try {
|
||||||
|
await axios.post(
|
||||||
|
`${config.backendUrl}/partstocks`,
|
||||||
|
{
|
||||||
|
part: values.part,
|
||||||
|
startingLots: values.startingLots,
|
||||||
|
currentLots: values.startingLots, // Initially current lots equals starting lots
|
||||||
|
notes: values.notes
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
withCredentials: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
onOk()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating part stock:', error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
layout='vertical'
|
||||||
|
onFinish={onFinish}
|
||||||
|
style={{ maxWidth: '100%' }}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name='part'
|
||||||
|
label='Part'
|
||||||
|
rules={[{ required: true, message: 'Please select a part' }]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
placeholder='Select a part'
|
||||||
|
options={parts.map((part) => ({
|
||||||
|
value: part._id,
|
||||||
|
label: part.name
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name='startingLots'
|
||||||
|
label='Starting Lots'
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: 'Please enter the starting lots' },
|
||||||
|
{ type: 'number', min: 1, message: 'Lots must be at least 1' }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
placeholder='Enter starting lots'
|
||||||
|
min={1}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name='notes' label='Notes'>
|
||||||
|
<Input.TextArea
|
||||||
|
placeholder='Enter any additional notes'
|
||||||
|
autoSize={{ minRows: 3, maxRows: 6 }}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item>
|
||||||
|
<Space>
|
||||||
|
<Button type='primary' htmlType='submit' loading={loading}>
|
||||||
|
Create Part Stock
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => form.resetFields()}>Reset</Button>
|
||||||
|
</Space>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
NewPartStock.propTypes = {
|
||||||
|
onOk: PropTypes.func.isRequired,
|
||||||
|
reset: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
NewPartStock.defaultProps = {
|
||||||
|
reset: false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewPartStock
|
||||||
@ -14,8 +14,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
ArrowLeftOutlined,
|
ArrowLeftOutlined,
|
||||||
LoadingOutlined,
|
LoadingOutlined,
|
||||||
CheckCircleOutlined,
|
|
||||||
CloseCircleOutlined,
|
|
||||||
ClockCircleOutlined
|
ClockCircleOutlined
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
|
|
||||||
@ -24,6 +22,8 @@ import IdText from '../../common/IdText'
|
|||||||
import TimeDisplay from '../../common/TimeDisplay'
|
import TimeDisplay from '../../common/TimeDisplay'
|
||||||
|
|
||||||
import config from '../../../../config'
|
import config from '../../../../config'
|
||||||
|
import XMarkCircleIcon from '../../../Icons/XMarkCircleIcon'
|
||||||
|
import CheckCircleIcon from '../../../Icons/CheckCircleIcon'
|
||||||
|
|
||||||
const { Text, Title } = Typography
|
const { Text, Title } = Typography
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ const StockAuditInfo = () => {
|
|||||||
switch (status?.toLowerCase()) {
|
switch (status?.toLowerCase()) {
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return (
|
return (
|
||||||
<Tag icon={<CheckCircleOutlined />} color='success'>
|
<Tag icon={<CheckCircleIcon />} color='success'>
|
||||||
Completed
|
Completed
|
||||||
</Tag>
|
</Tag>
|
||||||
)
|
)
|
||||||
@ -85,7 +85,7 @@ const StockAuditInfo = () => {
|
|||||||
)
|
)
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return (
|
return (
|
||||||
<Tag icon={<CloseCircleOutlined />} color='error'>
|
<Tag icon={<XMarkCircleIcon />} color='error'>
|
||||||
Failed
|
Failed
|
||||||
</Tag>
|
</Tag>
|
||||||
)
|
)
|
||||||
|
|||||||
422
src/components/Dashboard/Inventory/StockEvents.jsx
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
import React, { useEffect, useState, useContext, useCallback } from 'react'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
Space,
|
||||||
|
message,
|
||||||
|
Spin,
|
||||||
|
Popover,
|
||||||
|
Checkbox,
|
||||||
|
Dropdown,
|
||||||
|
Table,
|
||||||
|
Typography
|
||||||
|
} from 'antd'
|
||||||
|
import { createStyles } from 'antd-style'
|
||||||
|
import { LoadingOutlined, AuditOutlined } from '@ant-design/icons'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
import { AuthContext } from '../../Auth/AuthContext'
|
||||||
|
import { SocketContext } from '../context/SocketContext'
|
||||||
|
import IdText from '../common/IdText'
|
||||||
|
import TimeDisplay from '../common/TimeDisplay'
|
||||||
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
|
import PlusMinusIcon from '../../Icons/PlusMinusIcon'
|
||||||
|
import SubJobIcon from '../../Icons/SubJobIcon'
|
||||||
|
import PlayCircleIcon from '../../Icons/PlayCircleIcon'
|
||||||
|
|
||||||
|
import config from '../../../config'
|
||||||
|
|
||||||
|
const { Text } = Typography
|
||||||
|
|
||||||
|
const useStyle = createStyles(({ css, token }) => {
|
||||||
|
const { antCls } = token
|
||||||
|
return {
|
||||||
|
customTable: css`
|
||||||
|
${antCls}-table {
|
||||||
|
${antCls}-table-container {
|
||||||
|
${antCls}-table-body,
|
||||||
|
${antCls}-table-content {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #eaeaea transparent;
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const StockEvents = () => {
|
||||||
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
const { styles } = useStyle()
|
||||||
|
const { socket } = useContext(SocketContext)
|
||||||
|
const [initialized, setInitialized] = useState(false)
|
||||||
|
|
||||||
|
const [stockEventsData, setStockEventsData] = useState([])
|
||||||
|
const [page, setPage] = useState(1)
|
||||||
|
const [hasMore, setHasMore] = useState(true)
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [lazyLoading, setLazyLoading] = useState(false)
|
||||||
|
|
||||||
|
const [filters, setFilters] = useState({})
|
||||||
|
const [sorter, setSorter] = useState({})
|
||||||
|
|
||||||
|
// Column definitions for visibility
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
key: 'icon',
|
||||||
|
width: 50,
|
||||||
|
render: (record) => {
|
||||||
|
switch (record.type.toLowerCase()) {
|
||||||
|
case 'subjob':
|
||||||
|
return <SubJobIcon />
|
||||||
|
case 'audit':
|
||||||
|
return <AuditOutlined />
|
||||||
|
case 'initial':
|
||||||
|
return <PlayCircleIcon />
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Type',
|
||||||
|
dataIndex: 'type',
|
||||||
|
key: 'type',
|
||||||
|
width: 200,
|
||||||
|
sorter: (a, b) => a.type.localeCompare(b.type),
|
||||||
|
filters: [
|
||||||
|
{ text: 'Sub Job', value: 'Sub Job' },
|
||||||
|
{ text: 'Audit Adjustment', value: 'Audit Adjustment' },
|
||||||
|
{ text: 'Initial', value: 'Initial' }
|
||||||
|
],
|
||||||
|
onFilter: (value, record) => {
|
||||||
|
const recordType = record.type.toLowerCase()
|
||||||
|
if (recordType === 'subjob') {
|
||||||
|
return value === 'Sub Job'
|
||||||
|
} else if (recordType === 'audit') {
|
||||||
|
return value === 'Audit Adjustment'
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
value === recordType.charAt(0).toUpperCase() + recordType.slice(1)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
render: (type) => {
|
||||||
|
switch (type.toLowerCase()) {
|
||||||
|
case 'subjob':
|
||||||
|
return 'Sub Job'
|
||||||
|
case 'audit':
|
||||||
|
return 'Audit Adjustment'
|
||||||
|
default:
|
||||||
|
return type.charAt(0).toUpperCase() + type.slice(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: <PlusMinusIcon />,
|
||||||
|
dataIndex: 'value',
|
||||||
|
key: 'value',
|
||||||
|
width: 100,
|
||||||
|
sorter: (a, b) => a.value - b.value,
|
||||||
|
render: (value, record) => {
|
||||||
|
const formattedValue = value.toFixed(2) + record.unit
|
||||||
|
return (
|
||||||
|
<Text type={value < 0 ? 'danger' : 'success'}>
|
||||||
|
{value > 0 ? '+' + formattedValue : formattedValue}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Linked ID',
|
||||||
|
key: 'linkedId',
|
||||||
|
width: 100,
|
||||||
|
render: (record) => {
|
||||||
|
if (record.subJob?.number) {
|
||||||
|
return (
|
||||||
|
<IdText
|
||||||
|
id={record.subJob.number.toString().padStart(6, '0')}
|
||||||
|
longId={false}
|
||||||
|
type={'subjob'}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (record.stockAudit) {
|
||||||
|
return (
|
||||||
|
<IdText
|
||||||
|
id={record.stockAudit._id}
|
||||||
|
longId={false}
|
||||||
|
type={'stockaudit'}
|
||||||
|
showHyperlink={true}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return 'n/a'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Job ID',
|
||||||
|
key: 'jobId',
|
||||||
|
width: 100,
|
||||||
|
render: (record) => {
|
||||||
|
if (record.subJob) {
|
||||||
|
return (
|
||||||
|
<IdText
|
||||||
|
id={record.job}
|
||||||
|
longId={false}
|
||||||
|
type={'job'}
|
||||||
|
showHyperlink={true}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return 'n/a'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Created At',
|
||||||
|
dataIndex: 'createdAt',
|
||||||
|
key: 'createdAt',
|
||||||
|
width: 180,
|
||||||
|
defaultSortOrder: 'descend',
|
||||||
|
sorter: (a, b) => moment(a.createdAt).unix() - moment(b.createdAt).unix(),
|
||||||
|
render: (createdAt) => {
|
||||||
|
if (createdAt) {
|
||||||
|
return <TimeDisplay dateTime={createdAt} />
|
||||||
|
} else {
|
||||||
|
return 'n/a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Updated At',
|
||||||
|
dataIndex: 'updatedAt',
|
||||||
|
key: 'updatedAt',
|
||||||
|
width: 180,
|
||||||
|
sorter: (a, b) => moment(a.updatedAt).unix() - moment(b.updatedAt).unix(),
|
||||||
|
render: (updatedAt) => {
|
||||||
|
if (updatedAt) {
|
||||||
|
return <TimeDisplay dateTime={updatedAt} />
|
||||||
|
} else {
|
||||||
|
return 'n/a'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const [columnVisibility, setColumnVisibility] = useState(
|
||||||
|
columns.reduce((acc, col) => {
|
||||||
|
if (col.key) {
|
||||||
|
acc[col.key] = true
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
)
|
||||||
|
|
||||||
|
const { authenticated } = useContext(AuthContext)
|
||||||
|
|
||||||
|
const fetchStockEventsData = useCallback(
|
||||||
|
async (pageNum = 1, append = false) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${config.backendUrl}/stockevents`, {
|
||||||
|
params: {
|
||||||
|
page: pageNum,
|
||||||
|
limit: 25,
|
||||||
|
...filters,
|
||||||
|
sort: sorter.field,
|
||||||
|
order: sorter.order
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
|
},
|
||||||
|
withCredentials: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const newData = response.data
|
||||||
|
setHasMore(newData.length === 25)
|
||||||
|
|
||||||
|
if (append) {
|
||||||
|
setStockEventsData((prev) => [...prev, ...newData])
|
||||||
|
} else {
|
||||||
|
setStockEventsData(newData)
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false)
|
||||||
|
setLazyLoading(false)
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response) {
|
||||||
|
messageApi.error(
|
||||||
|
'Error fetching stock events:',
|
||||||
|
error.response.status
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
messageApi.error(
|
||||||
|
'An unexpected error occurred. Please try again later.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
setLoading(false)
|
||||||
|
setLazyLoading(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[messageApi, filters, sorter]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (authenticated) {
|
||||||
|
fetchStockEventsData()
|
||||||
|
}
|
||||||
|
}, [authenticated, fetchStockEventsData])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Add WebSocket event listener for real-time updates
|
||||||
|
if (socket && !initialized) {
|
||||||
|
setInitialized(true)
|
||||||
|
socket.on('notify_stockevent_update', (updateData) => {
|
||||||
|
console.log('Received stock event update:', updateData)
|
||||||
|
setStockEventsData((prevData) => {
|
||||||
|
return prevData.map((stockEvent) => {
|
||||||
|
if (stockEvent?._id) {
|
||||||
|
if (stockEvent._id === updateData._id) {
|
||||||
|
return {
|
||||||
|
...stockEvent,
|
||||||
|
...updateData
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return stockEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (socket && initialized) {
|
||||||
|
console.log('Deregistering stock event update listener')
|
||||||
|
socket.off('notify_stockevent_update')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [socket, initialized])
|
||||||
|
|
||||||
|
const handleScroll = useCallback(
|
||||||
|
(e) => {
|
||||||
|
const { target } = e
|
||||||
|
const scrollHeight = target.scrollHeight
|
||||||
|
const scrollTop = target.scrollTop
|
||||||
|
const clientHeight = target.clientHeight
|
||||||
|
|
||||||
|
if (
|
||||||
|
scrollHeight - scrollTop - clientHeight < 100 &&
|
||||||
|
!lazyLoading &&
|
||||||
|
hasMore
|
||||||
|
) {
|
||||||
|
setLazyLoading(true)
|
||||||
|
const nextPage = page + 1
|
||||||
|
setPage(nextPage)
|
||||||
|
fetchStockEventsData(nextPage, true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[page, lazyLoading, hasMore, fetchStockEventsData]
|
||||||
|
)
|
||||||
|
|
||||||
|
const actionItems = {
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'Reload List',
|
||||||
|
key: 'reloadList',
|
||||||
|
icon: <ReloadIcon />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
onClick: ({ key }) => {
|
||||||
|
if (key === 'reloadList') {
|
||||||
|
setPage(1)
|
||||||
|
fetchStockEventsData(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
const newFilters = {}
|
||||||
|
Object.entries(filters).forEach(([key, value]) => {
|
||||||
|
if (value && value.length > 0) {
|
||||||
|
newFilters[key] = value[0]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setPage(1)
|
||||||
|
setFilters(newFilters)
|
||||||
|
setSorter({
|
||||||
|
field: sorter.field,
|
||||||
|
order: sorter.order
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getViewDropdownItems = () => {
|
||||||
|
const columnItems = columns
|
||||||
|
.filter((col) => col.key && col.title !== '')
|
||||||
|
.map((col) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={columnVisibility[col.key]}
|
||||||
|
key={col.key}
|
||||||
|
onChange={(e) => {
|
||||||
|
setColumnVisibility((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[col.key]: e.target.checked
|
||||||
|
}))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{col.title}
|
||||||
|
</Checkbox>
|
||||||
|
))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex vertical>
|
||||||
|
<Flex vertical gap='middle' style={{ margin: '4px 8px' }}>
|
||||||
|
{columnItems}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const visibleColumns = columns.filter(
|
||||||
|
(col) => !col.key || columnVisibility[col.key]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex vertical={'true'} gap='large'>
|
||||||
|
{contextHolder}
|
||||||
|
<Flex justify={'space-between'}>
|
||||||
|
<Space size='small'>
|
||||||
|
<Dropdown menu={actionItems}>
|
||||||
|
<Button>Actions</Button>
|
||||||
|
</Dropdown>
|
||||||
|
<Popover
|
||||||
|
content={getViewDropdownItems()}
|
||||||
|
placement='bottomLeft'
|
||||||
|
arrow={false}
|
||||||
|
>
|
||||||
|
<Button>View</Button>
|
||||||
|
</Popover>
|
||||||
|
</Space>
|
||||||
|
{lazyLoading && <Spin indicator={<LoadingOutlined />} />}
|
||||||
|
</Flex>
|
||||||
|
<Table
|
||||||
|
dataSource={stockEventsData}
|
||||||
|
columns={visibleColumns}
|
||||||
|
className={styles.customTable}
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ y: 'calc(100vh - 270px)' }}
|
||||||
|
rowKey='_id'
|
||||||
|
loading={{ spinning: loading, indicator: <LoadingOutlined spin /> }}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
showSorterTooltip={false}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StockEvents
|
||||||
99
src/components/Dashboard/Management/Settings.jsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Select, Typography, Descriptions, Collapse, Flex } from 'antd'
|
||||||
|
import { CaretRightOutlined } from '@ant-design/icons'
|
||||||
|
import { useThemeContext } from '../context/ThemeContext'
|
||||||
|
import useCollapseState from '../hooks/useCollapseState'
|
||||||
|
|
||||||
|
const { Title } = Typography
|
||||||
|
const { Option } = Select
|
||||||
|
|
||||||
|
const Settings = () => {
|
||||||
|
const { isDarkMode, toggleTheme, isCompact, toggleCompact } =
|
||||||
|
useThemeContext()
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState('Settings', {
|
||||||
|
appearance: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleThemeChange = (value) => {
|
||||||
|
if (value === 'dark' && !isDarkMode) {
|
||||||
|
toggleTheme()
|
||||||
|
} else if (value === 'light' && isDarkMode) {
|
||||||
|
toggleTheme()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCompactChange = (value) => {
|
||||||
|
if (value === 'compact' && !isCompact) {
|
||||||
|
toggleCompact()
|
||||||
|
} else if (value === 'comfortable' && isCompact) {
|
||||||
|
toggleCompact()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%', minHeight: 0, overflowY: 'auto' }}>
|
||||||
|
<Collapse
|
||||||
|
ghost
|
||||||
|
collapsible='icon'
|
||||||
|
activeKey={collapseState.appearance ? ['1'] : []}
|
||||||
|
onChange={(keys) => updateCollapseState('appearance', keys.length > 0)}
|
||||||
|
expandIcon={({ isActive }) => (
|
||||||
|
<CaretRightOutlined
|
||||||
|
rotate={isActive ? 90 : 0}
|
||||||
|
style={{ paddingTop: '9px' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Collapse.Panel
|
||||||
|
header={
|
||||||
|
<Flex
|
||||||
|
align='center'
|
||||||
|
justify='space-between'
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
>
|
||||||
|
<Title level={5} style={{ margin: 0 }}>
|
||||||
|
Appearance Settings
|
||||||
|
</Title>
|
||||||
|
</Flex>
|
||||||
|
}
|
||||||
|
key='1'
|
||||||
|
>
|
||||||
|
<Descriptions
|
||||||
|
bordered
|
||||||
|
column={{
|
||||||
|
xs: 1,
|
||||||
|
sm: 1,
|
||||||
|
md: 1,
|
||||||
|
lg: 2,
|
||||||
|
xl: 2,
|
||||||
|
xxl: 2
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Descriptions.Item label='Theme'>
|
||||||
|
<Select
|
||||||
|
value={isDarkMode ? 'dark' : 'light'}
|
||||||
|
onChange={handleThemeChange}
|
||||||
|
style={{ width: '200px' }}
|
||||||
|
>
|
||||||
|
<Option value='light'>Light</Option>
|
||||||
|
<Option value='dark'>Dark</Option>
|
||||||
|
</Select>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='UI Density'>
|
||||||
|
<Select
|
||||||
|
value={isCompact ? 'compact' : 'comfortable'}
|
||||||
|
onChange={handleCompactChange}
|
||||||
|
style={{ width: '200px' }}
|
||||||
|
>
|
||||||
|
<Option value='comfortable'>Comfortable</Option>
|
||||||
|
<Option value='compact'>Compact</Option>
|
||||||
|
</Select>
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
</Collapse.Panel>
|
||||||
|
</Collapse>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Settings
|
||||||
@ -233,7 +233,7 @@ const VendorInfo = () => {
|
|||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
) : (
|
) : (
|
||||||
vendorData.names
|
vendorData.name
|
||||||
)}
|
)}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
|
||||||
|
|||||||
@ -19,14 +19,7 @@ import {
|
|||||||
Spin
|
Spin
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import { createStyles } from 'antd-style'
|
import { createStyles } from 'antd-style'
|
||||||
import {
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
LoadingOutlined,
|
|
||||||
PlayCircleOutlined,
|
|
||||||
CheckCircleOutlined,
|
|
||||||
CloseCircleOutlined,
|
|
||||||
PauseCircleOutlined,
|
|
||||||
QuestionCircleOutlined
|
|
||||||
} from '@ant-design/icons'
|
|
||||||
|
|
||||||
import { AuthContext } from '../../Auth/AuthContext'
|
import { AuthContext } from '../../Auth/AuthContext'
|
||||||
import { SocketContext } from '../context/SocketContext'
|
import { SocketContext } from '../context/SocketContext'
|
||||||
@ -43,8 +36,13 @@ import ReloadIcon from '../../Icons/ReloadIcon'
|
|||||||
import EditIcon from '../../Icons/EditIcon.jsx'
|
import EditIcon from '../../Icons/EditIcon.jsx'
|
||||||
import XMarkIcon from '../../Icons/XMarkIcon.jsx'
|
import XMarkIcon from '../../Icons/XMarkIcon.jsx'
|
||||||
import CheckIcon from '../../Icons/CheckIcon.jsx'
|
import CheckIcon from '../../Icons/CheckIcon.jsx'
|
||||||
|
import PlayCircleIcon from '../../Icons/PlayCircleIcon.jsx'
|
||||||
|
|
||||||
import config from '../../../config.js'
|
import config from '../../../config.js'
|
||||||
|
import CheckCircleIcon from '../../Icons/CheckCircleIcon.jsx'
|
||||||
|
import PauseCircleIcon from '../../Icons/PauseCircleIcon.jsx'
|
||||||
|
import XMarkCircleIcon from '../../Icons/XMarkCircleIcon.jsx'
|
||||||
|
import QuestionCircleIcon from '../../Icons/QuestionCircleIcon.jsx'
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
@ -198,7 +196,7 @@ const PrintJobs = () => {
|
|||||||
record.state.type.toLowerCase().includes(value.toLowerCase())
|
record.state.type.toLowerCase().includes(value.toLowerCase())
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: <CheckCircleOutlined />,
|
title: <CheckCircleIcon />,
|
||||||
key: 'complete',
|
key: 'complete',
|
||||||
width: 70,
|
width: 70,
|
||||||
render: (record) => {
|
render: (record) => {
|
||||||
@ -206,7 +204,7 @@ const PrintJobs = () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: <PauseCircleOutlined />,
|
title: <PauseCircleIcon />,
|
||||||
key: 'queued',
|
key: 'queued',
|
||||||
width: 70,
|
width: 70,
|
||||||
render: (record) => {
|
render: (record) => {
|
||||||
@ -214,7 +212,7 @@ const PrintJobs = () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: <CloseCircleOutlined />,
|
title: <XMarkCircleIcon />,
|
||||||
key: 'failed',
|
key: 'failed',
|
||||||
width: 70,
|
width: 70,
|
||||||
render: (record) => {
|
render: (record) => {
|
||||||
@ -222,7 +220,7 @@ const PrintJobs = () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: <QuestionCircleOutlined />,
|
title: <QuestionCircleIcon />,
|
||||||
key: 'draft',
|
key: 'draft',
|
||||||
width: 70,
|
width: 70,
|
||||||
render: (record) => {
|
render: (record) => {
|
||||||
@ -267,7 +265,7 @@ const PrintJobs = () => {
|
|||||||
<Space size='small'>
|
<Space size='small'>
|
||||||
{record.state.type === 'draft' ? (
|
{record.state.type === 'draft' ? (
|
||||||
<Button
|
<Button
|
||||||
icon={<PlayCircleOutlined />}
|
icon={<PlayCircleIcon />}
|
||||||
onClick={() => handleDeployPrintJob(record.id)}
|
onClick={() => handleDeployPrintJob(record.id)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -16,19 +16,15 @@ import {
|
|||||||
Badge,
|
Badge,
|
||||||
Alert,
|
Alert,
|
||||||
Popover,
|
Popover,
|
||||||
Checkbox
|
Checkbox,
|
||||||
|
Collapse
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import {
|
import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons'
|
||||||
LoadingOutlined,
|
|
||||||
PlayCircleOutlined,
|
|
||||||
ExclamationCircleOutlined,
|
|
||||||
PauseCircleOutlined,
|
|
||||||
CloseCircleOutlined
|
|
||||||
} from '@ant-design/icons'
|
|
||||||
|
|
||||||
import { SocketContext } from '../../context/SocketContext'
|
import { SocketContext } from '../../context/SocketContext'
|
||||||
|
|
||||||
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'
|
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'
|
||||||
|
import PrinterPositionPanel from '../../common/PrinterPositionPanel'
|
||||||
import PrinterMovementPanel from '../../common/PrinterMovementPanel'
|
import PrinterMovementPanel from '../../common/PrinterMovementPanel'
|
||||||
import PrinterState from '../../common/PrinterState'
|
import PrinterState from '../../common/PrinterState'
|
||||||
import { AuthContext } from '../../../Auth/AuthContext'
|
import { AuthContext } from '../../../Auth/AuthContext'
|
||||||
@ -44,10 +40,15 @@ import GCodeFileIcon from '../../../Icons/GCodeFileIcon'
|
|||||||
import LoadFilamentStock from '../../Inventory/FilamentStocks/LoadFilamentStock'
|
import LoadFilamentStock from '../../Inventory/FilamentStocks/LoadFilamentStock'
|
||||||
import UnloadFilamentStock from '../../Inventory/FilamentStocks/UnloadFilamentStock'
|
import UnloadFilamentStock from '../../Inventory/FilamentStocks/UnloadFilamentStock'
|
||||||
import FilamentStockState from '../../common/FilamentStockState'
|
import FilamentStockState from '../../common/FilamentStockState'
|
||||||
|
import useCollapseState from '../../hooks/useCollapseState'
|
||||||
|
|
||||||
import config from '../../../../config'
|
import config from '../../../../config'
|
||||||
|
import PlayCircleIcon from '../../../Icons/PlayCircleIcon'
|
||||||
|
import XMarkCircleIcon from '../../../Icons/XMarkCircleIcon'
|
||||||
|
import PauseCircleIcon from '../../../Icons/PauseCircleIcon'
|
||||||
|
import ExclamationOctagonIcon from '../../../Icons/ExclamationOctagonIcon'
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text, Title } = Typography
|
||||||
|
|
||||||
// Helper function to parse query parameters
|
// Helper function to parse query parameters
|
||||||
const useQuery = () => {
|
const useQuery = () => {
|
||||||
@ -69,6 +70,16 @@ const ControlPrinter = () => {
|
|||||||
const [klippyErrorMessage, setKlippyErrorMessage] = useState('')
|
const [klippyErrorMessage, setKlippyErrorMessage] = useState('')
|
||||||
const [klippyStartupMessage, setKlippyStartupMessage] = useState('')
|
const [klippyStartupMessage, setKlippyStartupMessage] = useState('')
|
||||||
|
|
||||||
|
const [collapseState, updateCollapseState] = useCollapseState(
|
||||||
|
'ControlPrinter',
|
||||||
|
{
|
||||||
|
job: true,
|
||||||
|
filament: true,
|
||||||
|
gcodefile: true,
|
||||||
|
jobs: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Load visibility preferences from sessionStorage on component mount
|
// Load visibility preferences from sessionStorage on component mount
|
||||||
const [componentVisibility, setComponentVisibility] = useState(() => {
|
const [componentVisibility, setComponentVisibility] = useState(() => {
|
||||||
const savedVisibility = sessionStorage.getItem('printerControlVisibility')
|
const savedVisibility = sessionStorage.getItem('printerControlVisibility')
|
||||||
@ -216,19 +227,19 @@ const ControlPrinter = () => {
|
|||||||
{
|
{
|
||||||
label: 'Resume Print',
|
label: 'Resume Print',
|
||||||
key: 'resumePrint',
|
key: 'resumePrint',
|
||||||
icon: <PlayCircleOutlined />,
|
icon: <PlayCircleIcon />,
|
||||||
disabled: printerData?.state?.type !== 'paused'
|
disabled: printerData?.state?.type !== 'paused'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Pause Print',
|
label: 'Pause Print',
|
||||||
key: 'pausePrint',
|
key: 'pausePrint',
|
||||||
icon: <PauseCircleOutlined />,
|
icon: <PauseCircleIcon />,
|
||||||
disabled: printerData?.state?.type !== 'printing'
|
disabled: printerData?.state?.type !== 'printing'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Cancel Print',
|
label: 'Cancel Print',
|
||||||
key: 'cancelPrint',
|
key: 'cancelPrint',
|
||||||
icon: <CloseCircleOutlined />,
|
icon: <XMarkCircleIcon />,
|
||||||
disabled: !(
|
disabled: !(
|
||||||
printerData?.state?.type === 'printing' ||
|
printerData?.state?.type === 'printing' ||
|
||||||
printerData?.state?.type === 'paused'
|
printerData?.state?.type === 'paused'
|
||||||
@ -250,12 +261,12 @@ const ControlPrinter = () => {
|
|||||||
printerData?.state?.type === 'paused' ||
|
printerData?.state?.type === 'paused' ||
|
||||||
printerData?.state?.type === 'error',
|
printerData?.state?.type === 'error',
|
||||||
|
|
||||||
icon: <PlayCircleOutlined />
|
icon: <PlayCircleIcon />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Pause Queue',
|
label: 'Pause Queue',
|
||||||
key: 'pauseQueue',
|
key: 'pauseQueue',
|
||||||
icon: <PauseCircleOutlined />
|
icon: <PauseCircleIcon />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -346,6 +357,17 @@ const ControlPrinter = () => {
|
|||||||
>
|
>
|
||||||
Temperature Panel
|
Temperature Panel
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
<Checkbox
|
||||||
|
checked={componentVisibility.position}
|
||||||
|
onChange={(e) => {
|
||||||
|
setComponentVisibility((prev) => ({
|
||||||
|
...prev,
|
||||||
|
position: e.target.checked
|
||||||
|
}))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Position Panel
|
||||||
|
</Checkbox>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={componentVisibility.movement}
|
checked={componentVisibility.movement}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@ -407,16 +429,16 @@ const ControlPrinter = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
<Space size='small'>
|
<Space size='small'>
|
||||||
<Button
|
<Button
|
||||||
icon={<ExclamationCircleOutlined />}
|
icon={<ExclamationOctagonIcon />}
|
||||||
danger
|
danger
|
||||||
onClick={handleEmergencyStop}
|
onClick={handleEmergencyStop}
|
||||||
></Button>
|
></Button>
|
||||||
<Button
|
<Button
|
||||||
icon={
|
icon={
|
||||||
printerData?.state?.type === 'paused' ? (
|
printerData?.state?.type === 'paused' ? (
|
||||||
<PlayCircleOutlined />
|
<PlayCircleIcon />
|
||||||
) : (
|
) : (
|
||||||
<PauseCircleOutlined />
|
<PauseCircleIcon />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
disabled={
|
disabled={
|
||||||
@ -434,7 +456,7 @@ const ControlPrinter = () => {
|
|||||||
}}
|
}}
|
||||||
></Button>
|
></Button>
|
||||||
<Button
|
<Button
|
||||||
icon={<PlayCircleOutlined />}
|
icon={<PlayCircleIcon />}
|
||||||
disabled={
|
disabled={
|
||||||
printerData?.state?.type === 'printing' ||
|
printerData?.state?.type === 'printing' ||
|
||||||
printerData?.state?.type === 'deploying' ||
|
printerData?.state?.type === 'deploying' ||
|
||||||
@ -449,211 +471,321 @@ const ControlPrinter = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
<div style={{ height: '100%', overflow: 'auto' }}>
|
<div style={{ height: '100%', overflow: 'auto' }}>
|
||||||
{printerData ? (
|
{printerData ? (
|
||||||
<Flex gap={16}>
|
<Flex>
|
||||||
<Flex gap={16} vertical style={{ flexGrow: 1 }}>
|
<Flex vertical style={{ flexGrow: 1 }}>
|
||||||
{printerData?.alerts?.some(
|
<Flex gap={16} vertical style={{ flexGrow: 1 }}>
|
||||||
(alert) => alert.type === 'klippyError'
|
{printerData?.alerts?.some(
|
||||||
) && <Alert message={klippyErrorMessage} type='error' />}
|
(alert) => alert.type === 'klippyError'
|
||||||
{printerData?.alerts?.some(
|
) && <Alert message={klippyErrorMessage} type='error' />}
|
||||||
(alert) => alert.type === 'klippyStartup'
|
{printerData?.alerts?.some(
|
||||||
) && <Alert message={klippyStartupMessage} type='warning' />}
|
(alert) => alert.type === 'klippyStartup'
|
||||||
<Descriptions
|
) && <Alert message={klippyStartupMessage} type='warning' />}
|
||||||
bordered
|
</Flex>
|
||||||
column={{
|
<Collapse
|
||||||
xs: 1,
|
ghost
|
||||||
sm: 1,
|
collapsible='icon'
|
||||||
md: 1,
|
activeKey={collapseState.job ? ['1'] : []}
|
||||||
lg: 2,
|
onChange={(keys) =>
|
||||||
xl: 2,
|
updateCollapseState('job', keys.length > 0)
|
||||||
xxl: 2
|
}
|
||||||
}}
|
expandIcon={({ isActive }) => (
|
||||||
|
<CaretRightOutlined
|
||||||
|
rotate={isActive ? 90 : 0}
|
||||||
|
style={{ paddingTop: '2px' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<Descriptions.Item label='Printer Name'>
|
<Collapse.Panel
|
||||||
{printerData.name}
|
header={
|
||||||
</Descriptions.Item>
|
<Flex
|
||||||
<Descriptions.Item label='Printer ID'>
|
align='center'
|
||||||
{printerData._id ? (
|
justify='space-between'
|
||||||
<IdText
|
style={{ width: '100%' }}
|
||||||
id={printerData._id}
|
>
|
||||||
type='printer'
|
<Title level={5} style={{ margin: 0 }}>
|
||||||
longId={false}
|
Current Job
|
||||||
showHyperlink={true}
|
</Title>
|
||||||
/>
|
</Flex>
|
||||||
) : (
|
|
||||||
'n/a'
|
|
||||||
)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label='Print Job ID'>
|
|
||||||
{printerData.currentJob?.id ? (
|
|
||||||
<IdText
|
|
||||||
id={printerData.currentJob.id}
|
|
||||||
type='job'
|
|
||||||
longId={false}
|
|
||||||
showHyperlink={true}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
'n/a'
|
|
||||||
)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label='Sub Job ID'>
|
|
||||||
{printerData.currentSubJob?.id ? (
|
|
||||||
<IdText
|
|
||||||
id={printerData.currentSubJob.number
|
|
||||||
.toString()
|
|
||||||
.padStart(6, '0')}
|
|
||||||
type='subjob'
|
|
||||||
longId={false}
|
|
||||||
showHyperlink={false}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
'n/a'
|
|
||||||
)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label='GCode File Name'>
|
|
||||||
{
|
|
||||||
<Space>
|
|
||||||
<GCodeFileIcon />
|
|
||||||
<Text ellipsis style={{ maxWidth: 200 }}>
|
|
||||||
{printerData.currentJob?.gcodeFile?.name || 'n/a'}
|
|
||||||
</Text>
|
|
||||||
</Space>
|
|
||||||
}
|
}
|
||||||
</Descriptions.Item>
|
key='1'
|
||||||
<Descriptions.Item label='GCode File ID'>
|
>
|
||||||
{printerData.currentJob?.gcodeFile ? (
|
<Descriptions
|
||||||
<IdText
|
bordered
|
||||||
id={printerData.currentJob.gcodeFile.id}
|
column={{
|
||||||
type='gcodeFile'
|
xs: 1,
|
||||||
longId={false}
|
sm: 1,
|
||||||
showHyperlink={true}
|
md: 1,
|
||||||
/>
|
lg: 1,
|
||||||
) : (
|
xl: 2,
|
||||||
'n/a'
|
xxl: 2
|
||||||
)}
|
}}
|
||||||
</Descriptions.Item>
|
>
|
||||||
|
<Descriptions.Item label='Printer Name'>
|
||||||
|
{printerData.name}
|
||||||
|
</Descriptions.Item>
|
||||||
|
|
||||||
{printerData?.currentFilamentStock && (
|
<Descriptions.Item label='Printer ID'>
|
||||||
<Descriptions.Item label='Filament Stock' span={2}>
|
{printerData._id ? (
|
||||||
<FilamentStockState
|
<IdText
|
||||||
filamentStock={printerData?.currentFilamentStock}
|
id={printerData._id}
|
||||||
/>
|
type='printer'
|
||||||
</Descriptions.Item>
|
longId={false}
|
||||||
)}
|
showHyperlink={true}
|
||||||
|
/>
|
||||||
<Descriptions.Item label='Filament Stock Weight'>
|
) : (
|
||||||
{printerData.currentFilamentStock?.currentNetWeight ? (
|
'n/a'
|
||||||
<Descriptions style={{ width: '250px' }} column={2}>
|
|
||||||
<Descriptions.Item label='Net'>
|
|
||||||
{printerData.currentFilamentStock.currentNetWeight.toFixed(
|
|
||||||
2
|
|
||||||
) + 'g'}
|
|
||||||
</Descriptions.Item>
|
|
||||||
<Descriptions.Item label='Gross'>
|
|
||||||
{printerData.currentFilamentStock.currentGrossWeight.toFixed(
|
|
||||||
2
|
|
||||||
) + 'g'}
|
|
||||||
</Descriptions.Item>
|
|
||||||
</Descriptions>
|
|
||||||
) : (
|
|
||||||
'n/a'
|
|
||||||
)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
|
|
||||||
<Descriptions.Item label='Filament Stock ID'>
|
|
||||||
{printerData.currentFilamentStock ? (
|
|
||||||
<IdText
|
|
||||||
id={printerData.currentFilamentStock._id}
|
|
||||||
type='filamentstock'
|
|
||||||
longId={false}
|
|
||||||
showHyperlink={true}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
'n/a'
|
|
||||||
)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
|
|
||||||
<Descriptions.Item label='Filament Name'>
|
|
||||||
{printerData.currentFilamentStock?.filament?.name ? (
|
|
||||||
<Space>
|
|
||||||
<FilamentIcon />
|
|
||||||
<Badge
|
|
||||||
text={printerData.currentFilamentStock.filament.name}
|
|
||||||
color={
|
|
||||||
printerData.currentFilamentStock.filament.color
|
|
||||||
}
|
|
||||||
></Badge>
|
|
||||||
</Space>
|
|
||||||
) : (
|
|
||||||
'n/a'
|
|
||||||
)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
|
|
||||||
<Descriptions.Item label='Filament ID'>
|
|
||||||
{printerData?.currentFilamentStock?.filament ? (
|
|
||||||
<IdText
|
|
||||||
id={printerData.currentFilamentStock.filament._id}
|
|
||||||
type='filament'
|
|
||||||
longId={false}
|
|
||||||
showHyperlink={true}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
'n/a'
|
|
||||||
)}
|
|
||||||
</Descriptions.Item>
|
|
||||||
|
|
||||||
{printerData?.state.type === 'printing' && (
|
|
||||||
<Descriptions.Item label='Progress' span={2}>
|
|
||||||
<Progress
|
|
||||||
percent={Math.round(
|
|
||||||
(printerData.state.progress || 0) * 100
|
|
||||||
)}
|
)}
|
||||||
status='active'
|
</Descriptions.Item>
|
||||||
/>
|
|
||||||
</Descriptions.Item>
|
<Descriptions.Item label='GCode File Name'>
|
||||||
|
{
|
||||||
|
<Space>
|
||||||
|
<GCodeFileIcon />
|
||||||
|
<Text ellipsis style={{ maxWidth: 200 }}>
|
||||||
|
{printerData.currentJob?.gcodeFile?.name || 'n/a'}
|
||||||
|
</Text>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='GCode File ID'>
|
||||||
|
{printerData.currentJob?.gcodeFile ? (
|
||||||
|
<IdText
|
||||||
|
id={printerData.currentJob.gcodeFile.id}
|
||||||
|
type='gcodeFile'
|
||||||
|
longId={false}
|
||||||
|
showHyperlink={true}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Descriptions.Item>
|
||||||
|
|
||||||
|
<Descriptions.Item label='Print Job ID'>
|
||||||
|
{printerData.currentJob?.id ? (
|
||||||
|
<IdText
|
||||||
|
id={printerData.currentJob.id}
|
||||||
|
type='job'
|
||||||
|
longId={false}
|
||||||
|
showHyperlink={true}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Descriptions.Item>
|
||||||
|
|
||||||
|
<Descriptions.Item label='Sub Job ID'>
|
||||||
|
{printerData.currentSubJob?.id ? (
|
||||||
|
<IdText
|
||||||
|
id={printerData.currentSubJob.number
|
||||||
|
.toString()
|
||||||
|
.padStart(6, '0')}
|
||||||
|
type='subjob'
|
||||||
|
longId={false}
|
||||||
|
showHyperlink={false}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Descriptions.Item>
|
||||||
|
|
||||||
|
{printerData?.state.type === 'printing' && (
|
||||||
|
<>
|
||||||
|
<Descriptions.Item label='Progress' span={1}>
|
||||||
|
<Progress
|
||||||
|
percent={Math.round(
|
||||||
|
(printerData.state.progress || 0) * 100
|
||||||
|
)}
|
||||||
|
status='active'
|
||||||
|
/>
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Started At' span={1}>
|
||||||
|
{printerData.name}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Descriptions.Item label='Print Profile'>
|
||||||
|
{(() => {
|
||||||
|
if (
|
||||||
|
printerData?.currentJob?.gcodeFile.gcodeFileInfo
|
||||||
|
.printSettingsId
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Text ellipsis style={{ maxWidth: 200 }}>
|
||||||
|
{printerData.currentJob.gcodeFile.gcodeFileInfo.printSettingsId.replaceAll(
|
||||||
|
'"',
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return 'n/a'
|
||||||
|
}
|
||||||
|
})()}
|
||||||
|
</Descriptions.Item>
|
||||||
|
|
||||||
|
<Descriptions.Item label='Est. Print Time'>
|
||||||
|
{(() => {
|
||||||
|
if (
|
||||||
|
printerData.currentJob?.gcodeFile?.gcodeFileInfo
|
||||||
|
.estimatedPrintingTimeNormalMode
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Text ellipsis>
|
||||||
|
{
|
||||||
|
printerData.currentJob.gcodeFile.gcodeFileInfo
|
||||||
|
.estimatedPrintingTimeNormalMode
|
||||||
|
}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return 'n/a'
|
||||||
|
})()}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
</Collapse.Panel>
|
||||||
|
</Collapse>
|
||||||
|
<Collapse
|
||||||
|
ghost
|
||||||
|
collapsible='icon'
|
||||||
|
activeKey={collapseState.filament ? ['1'] : []}
|
||||||
|
onChange={(keys) =>
|
||||||
|
updateCollapseState('filament', keys.length > 0)
|
||||||
|
}
|
||||||
|
expandIcon={({ isActive }) => (
|
||||||
|
<CaretRightOutlined
|
||||||
|
rotate={isActive ? 90 : 0}
|
||||||
|
style={{ paddingTop: '2px' }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
>
|
||||||
<Descriptions.Item label='Print Profile'>
|
<Collapse.Panel
|
||||||
{(() => {
|
header={
|
||||||
if (
|
<Flex
|
||||||
printerData?.currentJob?.gcodeFile.gcodeFileInfo
|
align='center'
|
||||||
.printSettingsId
|
justify='space-between'
|
||||||
) {
|
style={{ width: '100%' }}
|
||||||
return (
|
>
|
||||||
<Text ellipsis style={{ maxWidth: 200 }}>
|
<Title level={5} style={{ margin: 0 }}>
|
||||||
{printerData.currentJob.gcodeFile.gcodeFileInfo.printSettingsId.replaceAll(
|
Loaded Filament Stock
|
||||||
'"',
|
</Title>
|
||||||
''
|
</Flex>
|
||||||
)}
|
}
|
||||||
</Text>
|
key='1'
|
||||||
)
|
>
|
||||||
} else {
|
<Descriptions
|
||||||
return 'n/a'
|
bordered
|
||||||
}
|
column={{
|
||||||
})()}
|
xs: 1,
|
||||||
</Descriptions.Item>
|
sm: 1,
|
||||||
|
md: 1,
|
||||||
<Descriptions.Item label='Est. Print Time'>
|
lg: 1,
|
||||||
{(() => {
|
xl: 2,
|
||||||
if (
|
xxl: 2
|
||||||
printerData.currentJob?.gcodeFile?.gcodeFileInfo
|
}}
|
||||||
.estimatedPrintingTimeNormalMode
|
>
|
||||||
) {
|
<Descriptions.Item label='Filament Stock' span={1}>
|
||||||
return (
|
{printerData?.currentFilamentStock ? (
|
||||||
<Text ellipsis>
|
<FilamentStockState
|
||||||
{
|
filamentStock={printerData?.currentFilamentStock}
|
||||||
printerData.currentJob.gcodeFile.gcodeFileInfo
|
/>
|
||||||
.estimatedPrintingTimeNormalMode
|
) : (
|
||||||
}
|
'n/a'
|
||||||
</Text>
|
)}
|
||||||
)
|
</Descriptions.Item>
|
||||||
}
|
<Descriptions.Item label='Filament Stock ID'>
|
||||||
return 'n/a'
|
{printerData.currentFilamentStock ? (
|
||||||
})()}
|
<IdText
|
||||||
</Descriptions.Item>
|
id={printerData.currentFilamentStock._id}
|
||||||
</Descriptions>
|
type='filamentstock'
|
||||||
{componentVisibility.subjobs && (
|
longId={false}
|
||||||
<PrinterSubJobsTree subJobs={printerData.subJobs} />
|
showHyperlink={true}
|
||||||
)}
|
/>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Filament Name'>
|
||||||
|
{printerData.currentFilamentStock?.filament?.name ? (
|
||||||
|
<Space>
|
||||||
|
<FilamentIcon />
|
||||||
|
<Badge
|
||||||
|
text={
|
||||||
|
printerData.currentFilamentStock.filament.name
|
||||||
|
}
|
||||||
|
color={
|
||||||
|
printerData.currentFilamentStock.filament.color
|
||||||
|
}
|
||||||
|
></Badge>
|
||||||
|
</Space>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Filament ID'>
|
||||||
|
{printerData?.currentFilamentStock?.filament ? (
|
||||||
|
<IdText
|
||||||
|
id={printerData.currentFilamentStock.filament._id}
|
||||||
|
type='filament'
|
||||||
|
longId={false}
|
||||||
|
showHyperlink={true}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Weight'>
|
||||||
|
{printerData.currentFilamentStock?.currentNetWeight ? (
|
||||||
|
<Descriptions style={{ width: '250px' }} column={2}>
|
||||||
|
<Descriptions.Item label='Net'>
|
||||||
|
{printerData.currentFilamentStock.currentNetWeight.toFixed(
|
||||||
|
2
|
||||||
|
) + 'g'}
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Gross'>
|
||||||
|
{printerData.currentFilamentStock.currentGrossWeight.toFixed(
|
||||||
|
2
|
||||||
|
) + 'g'}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
</Collapse.Panel>
|
||||||
|
</Collapse>
|
||||||
|
<Collapse
|
||||||
|
ghost
|
||||||
|
collapsible='icon'
|
||||||
|
activeKey={collapseState.jobs ? ['1'] : []}
|
||||||
|
onChange={(keys) =>
|
||||||
|
updateCollapseState('jobs', keys.length > 0)
|
||||||
|
}
|
||||||
|
expandIcon={({ isActive }) => (
|
||||||
|
<CaretRightOutlined
|
||||||
|
rotate={isActive ? 90 : 0}
|
||||||
|
style={{ paddingTop: '2px' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Collapse.Panel
|
||||||
|
header={
|
||||||
|
<Flex
|
||||||
|
align='center'
|
||||||
|
justify='space-between'
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
>
|
||||||
|
<Title level={5} style={{ margin: 0 }}>
|
||||||
|
Printer Jobs
|
||||||
|
</Title>
|
||||||
|
</Flex>
|
||||||
|
}
|
||||||
|
key='1'
|
||||||
|
>
|
||||||
|
<PrinterSubJobsTree subJobs={printerData.subJobs} />
|
||||||
|
</Collapse.Panel>
|
||||||
|
</Collapse>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex gap={16} vertical>
|
<Flex gap={16} vertical>
|
||||||
{componentVisibility.temperature && (
|
{componentVisibility.temperature && (
|
||||||
@ -665,6 +797,16 @@ const ControlPrinter = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{componentVisibility.position && (
|
||||||
|
<Card>
|
||||||
|
<PrinterPositionPanel
|
||||||
|
printerId={printerId}
|
||||||
|
showControls={true}
|
||||||
|
showMoreInfo={true}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{componentVisibility.movement && (
|
{componentVisibility.movement && (
|
||||||
<Card>
|
<Card>
|
||||||
<PrinterMovementPanel
|
<PrinterMovementPanel
|
||||||
@ -718,7 +860,7 @@ const ControlPrinter = () => {
|
|||||||
open={klippyErrorModalOpen}
|
open={klippyErrorModalOpen}
|
||||||
title={
|
title={
|
||||||
<Space size={'middle'}>
|
<Space size={'middle'}>
|
||||||
<ExclamationCircleOutlined />
|
<ExclamationOctagonIcon />
|
||||||
Klipper Error
|
Klipper Error
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Breadcrumb, Button, Flex, Space } from 'antd'
|
import { Breadcrumb, Button, Flex, Space } from 'antd'
|
||||||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { ArrowLeftOutlined, ArrowRightOutlined } from '@ant-design/icons'
|
import ArrowLeftIcon from '../../Icons/ArrowLeftIcon'
|
||||||
|
import ArrowRightIcon from '../../Icons/ArrowRightIcon'
|
||||||
|
|
||||||
const breadcrumbNameMap = {
|
const breadcrumbNameMap = {
|
||||||
'/dashboard/production': 'Production',
|
'/dashboard/production': 'Production',
|
||||||
@ -26,12 +27,15 @@ const breadcrumbNameMap = {
|
|||||||
'/dashboard/management/vendors/info': 'Info',
|
'/dashboard/management/vendors/info': 'Info',
|
||||||
'/dashboard/management/materials': 'Materials',
|
'/dashboard/management/materials': 'Materials',
|
||||||
'/dashboard/management/materials/info': 'Info',
|
'/dashboard/management/materials/info': 'Info',
|
||||||
|
'/dashboard/management/settings': 'Settings',
|
||||||
'/dashboard/inventory/filamentstocks': 'Filament Stocks',
|
'/dashboard/inventory/filamentstocks': 'Filament Stocks',
|
||||||
'/dashboard/inventory/filamentstocks/info': 'Info',
|
'/dashboard/inventory/filamentstocks/info': 'Info',
|
||||||
'/dashboard/inventory/partstocks': 'Parts',
|
'/dashboard/inventory/partstocks': 'Part Stocks',
|
||||||
'/dashboard/inventory/partstocks/info': 'Info',
|
'/dashboard/inventory/partstocks/info': 'Info',
|
||||||
'/dashboard/inventory/productstocks': 'Products',
|
'/dashboard/inventory/productstocks': 'Products',
|
||||||
'/dashboard/inventory/productstocks/info': 'Info',
|
'/dashboard/inventory/productstocks/info': 'Info',
|
||||||
|
'/dashboard/inventory/stockevents': 'Stock Events',
|
||||||
|
'/dashboard/inventory/stockevents/info': 'Info',
|
||||||
'/dashboard/inventory/stockaudits': 'Stock Audits',
|
'/dashboard/inventory/stockaudits': 'Stock Audits',
|
||||||
'/dashboard/inventory/stockaudits/info': 'Info'
|
'/dashboard/inventory/stockaudits/info': 'Info'
|
||||||
}
|
}
|
||||||
@ -71,13 +75,13 @@ const DashboardBreadcrumb = () => {
|
|||||||
<Space.Compact>
|
<Space.Compact>
|
||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
icon={<ArrowLeftOutlined style={{ fontSize: '14px' }} />}
|
icon={<ArrowLeftIcon style={{ fontSize: '14px' }} />}
|
||||||
onClick={() => navigate(-1)}
|
onClick={() => navigate(-1)}
|
||||||
style={{ padding: '0 2px', height: '22px' }}
|
style={{ padding: '0 2px', height: '22px' }}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
icon={<ArrowRightOutlined style={{ fontSize: '14px' }} />}
|
icon={<ArrowRightIcon style={{ fontSize: '14px' }} />}
|
||||||
onClick={() => navigate(1)}
|
onClick={() => navigate(1)}
|
||||||
style={{ padding: '0 2px', height: '22px' }}
|
style={{ padding: '0 2px', height: '22px' }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import {
|
|||||||
Dropdown,
|
Dropdown,
|
||||||
Button,
|
Button,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography
|
Typography,
|
||||||
|
Divider
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import {
|
import {
|
||||||
LogoutOutlined,
|
LogoutOutlined,
|
||||||
@ -124,7 +125,12 @@ const DashboardNavigation = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Header
|
<Header
|
||||||
style={{ width: '100vw', padding: 0, background: 'unset' }}
|
style={{
|
||||||
|
width: '100vw',
|
||||||
|
padding: 0,
|
||||||
|
marginBottom: '0.1px',
|
||||||
|
background: 'unset'
|
||||||
|
}}
|
||||||
theme='light'
|
theme='light'
|
||||||
className='ant-menu-horizontal'
|
className='ant-menu-horizontal'
|
||||||
>
|
>
|
||||||
@ -178,11 +184,13 @@ const DashboardNavigation = () => {
|
|||||||
</Tag>
|
</Tag>
|
||||||
</Space>
|
</Space>
|
||||||
) : null}
|
) : null}
|
||||||
<Space>
|
{process.env.NODE_ENV === 'development' && (
|
||||||
<Tag color='yellow' style={{ marginRight: 0 }}>
|
<Space>
|
||||||
Dev
|
<Tag color='yellow' style={{ marginRight: 0 }}>
|
||||||
</Tag>
|
Dev
|
||||||
</Space>
|
</Tag>
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
{userProfile ? (
|
{userProfile ? (
|
||||||
<Space>
|
<Space>
|
||||||
<Dropdown menu={userMenuItems} placement='bottomRight'>
|
<Dropdown menu={userMenuItems} placement='bottomRight'>
|
||||||
@ -194,6 +202,7 @@ const DashboardNavigation = () => {
|
|||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Divider style={{ margin: 0 }} />
|
||||||
</Header>
|
</Header>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,11 +60,11 @@ const FilamentStockState = ({
|
|||||||
break
|
break
|
||||||
case 'partiallyconsumed':
|
case 'partiallyconsumed':
|
||||||
setBadgeStatus('warning')
|
setBadgeStatus('warning')
|
||||||
setBadgeText('Partially Consumed')
|
setBadgeText('Partial')
|
||||||
break
|
break
|
||||||
case 'fullyconsumed':
|
case 'fullyconsumed':
|
||||||
setBadgeStatus('error')
|
setBadgeStatus('error')
|
||||||
setBadgeText('Fully Consumed')
|
setBadgeText('Consumed')
|
||||||
break
|
break
|
||||||
case 'error':
|
case 'error':
|
||||||
setBadgeStatus('error')
|
setBadgeStatus('error')
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const propertyOrder = [
|
|||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
const GCodeFileSelect = ({ onChange, filter, useFilter }) => {
|
const GCodeFileSelect = ({ onChange, filter, useFilter, style }) => {
|
||||||
const [gcodeFilesTreeData, setGCodeFilesTreeData] = useState(null)
|
const [gcodeFilesTreeData, setGCodeFilesTreeData] = useState(null)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [searchValue, setSearchValue] = useState('')
|
const [searchValue, setSearchValue] = useState('')
|
||||||
@ -205,6 +205,7 @@ const GCodeFileSelect = ({ onChange, filter, useFilter }) => {
|
|||||||
onSearch={handleGCodeFilesSearch}
|
onSearch={handleGCodeFilesSearch}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
placeholder='Select GCode File'
|
placeholder='Select GCode File'
|
||||||
|
style={style}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -212,7 +213,8 @@ const GCodeFileSelect = ({ onChange, filter, useFilter }) => {
|
|||||||
GCodeFileSelect.propTypes = {
|
GCodeFileSelect.propTypes = {
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
filter: PropTypes.string.isRequired,
|
filter: PropTypes.string.isRequired,
|
||||||
useFilter: PropTypes.bool.isRequired
|
useFilter: PropTypes.bool.isRequired,
|
||||||
|
style: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GCodeFileSelect
|
export default GCodeFileSelect
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import FilamentStockIcon from '../../Icons/FilamentStockIcon'
|
|||||||
import PartStockIcon from '../../Icons/PartStockIcon'
|
import PartStockIcon from '../../Icons/PartStockIcon'
|
||||||
import ProductStockIcon from '../../Icons/ProductStockIcon'
|
import ProductStockIcon from '../../Icons/ProductStockIcon'
|
||||||
import StockAuditIcon from '../../Icons/StockAuditIcon'
|
import StockAuditIcon from '../../Icons/StockAuditIcon'
|
||||||
|
import StockEventIcon from '../../Icons/StockEventIcon'
|
||||||
import CollapseSidebarIcon from '../../Icons/CollapseSidebarIcon'
|
import CollapseSidebarIcon from '../../Icons/CollapseSidebarIcon'
|
||||||
import ExpandSidebarIcon from '../../Icons/ExpandSidebarIcon'
|
import ExpandSidebarIcon from '../../Icons/ExpandSidebarIcon'
|
||||||
|
|
||||||
@ -60,6 +61,11 @@ const InventorySidebar = () => {
|
|||||||
icon: <ProductStockIcon />
|
icon: <ProductStockIcon />
|
||||||
},
|
},
|
||||||
{ type: 'divider' },
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
key: 'stockevents',
|
||||||
|
label: <Link to='/dashboard/inventory/stockevents'>Stock Events</Link>,
|
||||||
|
icon: <StockEventIcon />
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'stockaudits',
|
key: 'stockaudits',
|
||||||
label: <Link to='/dashboard/inventory/stockaudits'>Stock Audits</Link>,
|
label: <Link to='/dashboard/inventory/stockaudits'>Stock Audits</Link>,
|
||||||
|
|||||||
112
src/components/Dashboard/common/PartStockState.jsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { Badge, Progress, Flex, Space, Tag, Typography } from 'antd'
|
||||||
|
import { green } from '@ant-design/colors'
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
const { Text } = Typography
|
||||||
|
|
||||||
|
const getProgressColor = (percent) => {
|
||||||
|
if (percent <= 50) {
|
||||||
|
return green[5]
|
||||||
|
} else if (percent <= 80) {
|
||||||
|
// Interpolate between green and yellow
|
||||||
|
const ratio = (percent - 50) / 30
|
||||||
|
return `rgb(${Math.round(255 * ratio)}, ${Math.round(255 * (1 - ratio))}, 0)`
|
||||||
|
} else {
|
||||||
|
// Interpolate between yellow and red
|
||||||
|
const ratio = (percent - 80) / 20
|
||||||
|
return `rgb(255, ${Math.round(255 * (1 - ratio))}, 0)`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PartStockState = ({
|
||||||
|
partStock,
|
||||||
|
showProgress = true,
|
||||||
|
showStatus = true
|
||||||
|
}) => {
|
||||||
|
const [badgeStatus, setBadgeStatus] = useState('unknown')
|
||||||
|
const [badgeText, setBadgeText] = useState('Unknown')
|
||||||
|
const [currentState] = useState(
|
||||||
|
partStock?.state || {
|
||||||
|
type: 'unknown',
|
||||||
|
progress: 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
switch (currentState.type) {
|
||||||
|
case 'unused':
|
||||||
|
setBadgeStatus('success')
|
||||||
|
setBadgeText('Unused')
|
||||||
|
break
|
||||||
|
case 'partiallyused':
|
||||||
|
setBadgeStatus('warning')
|
||||||
|
setBadgeText('Partial')
|
||||||
|
break
|
||||||
|
case 'fullyused':
|
||||||
|
setBadgeStatus('error')
|
||||||
|
setBadgeText('Used')
|
||||||
|
break
|
||||||
|
case 'error':
|
||||||
|
setBadgeStatus('error')
|
||||||
|
setBadgeText('Error')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
setBadgeStatus('default')
|
||||||
|
setBadgeText(currentState.type)
|
||||||
|
}
|
||||||
|
}, [currentState])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex gap='middle' align={'center'}>
|
||||||
|
{showStatus && (
|
||||||
|
<Space>
|
||||||
|
<Tag color={badgeStatus} style={{ marginRight: 0 }}>
|
||||||
|
<Flex gap={6}>
|
||||||
|
<Badge status={badgeStatus} />
|
||||||
|
{badgeText}
|
||||||
|
</Flex>
|
||||||
|
</Tag>
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
{showProgress && currentState.type === 'partiallyused' ? (
|
||||||
|
<Flex style={{ width: '150px' }} gap={'small'}>
|
||||||
|
<div style={{ flexGrow: '1' }}>
|
||||||
|
<Progress
|
||||||
|
percent={Math.round(currentState.percent * 100)}
|
||||||
|
style={{ marginBottom: '2px', width: '100%' }}
|
||||||
|
strokeColor={getProgressColor(
|
||||||
|
Math.round(currentState.percent * 100)
|
||||||
|
)}
|
||||||
|
showInfo={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Text style={{ marginTop: '1px' }}>
|
||||||
|
{Math.round(currentState.percent * 100) + '%'}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
) : null}
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
PartStockState.propTypes = {
|
||||||
|
partStock: PropTypes.shape({
|
||||||
|
_id: PropTypes.string,
|
||||||
|
name: PropTypes.string,
|
||||||
|
state: PropTypes.shape({
|
||||||
|
type: PropTypes.oneOf([
|
||||||
|
'unused',
|
||||||
|
'partiallyused',
|
||||||
|
'fullyused',
|
||||||
|
'error',
|
||||||
|
'unknown'
|
||||||
|
]),
|
||||||
|
progress: PropTypes.number
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
showProgress: PropTypes.bool,
|
||||||
|
showStatus: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PartStockState
|
||||||
@ -10,17 +10,14 @@ import {
|
|||||||
Card,
|
Card,
|
||||||
message // eslint-disable-line
|
message // eslint-disable-line
|
||||||
} from 'antd'
|
} from 'antd'
|
||||||
import {
|
|
||||||
ArrowUpOutlined,
|
|
||||||
ArrowLeftOutlined,
|
|
||||||
HomeOutlined,
|
|
||||||
ArrowRightOutlined,
|
|
||||||
ArrowDownOutlined
|
|
||||||
} from '@ant-design/icons'
|
|
||||||
import { SocketContext } from '../context/SocketContext'
|
import { SocketContext } from '../context/SocketContext'
|
||||||
import UnloadIcon from '../../Icons/UnloadIcon'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import LevelBedIcon from '../../Icons/LevelBedIcon'
|
import LevelBedIcon from '../../Icons/LevelBedIcon'
|
||||||
|
import ArrowLeftIcon from '../../Icons/ArrowLeftIcon'
|
||||||
|
import ArrowRightIcon from '../../Icons/ArrowRightIcon'
|
||||||
|
import ArrowUpIcon from '../../Icons/ArrowUpIcon'
|
||||||
|
import ArrowDownIcon from '../../Icons/ArrowDownIcon'
|
||||||
|
import HomeIcon from '../../Icons/HomeIcon'
|
||||||
|
|
||||||
const PrinterMovementPanel = ({ printerId }) => {
|
const PrinterMovementPanel = ({ printerId }) => {
|
||||||
const [posValue, setPosValue] = useState(10)
|
const [posValue, setPosValue] = useState(10)
|
||||||
@ -68,15 +65,6 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
//sendCommand('levelBed')
|
//sendCommand('levelBed')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUnloadFilamentClick = () => {
|
|
||||||
if (socket) {
|
|
||||||
socket.emit('printer.gcode.script', {
|
|
||||||
printerId,
|
|
||||||
script: `UNLOAD_FILAMENT TEMP=`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const homeAxisButtonItems = [
|
const homeAxisButtonItems = [
|
||||||
{
|
{
|
||||||
key: 'homeXYZ',
|
key: 'homeXYZ',
|
||||||
@ -115,17 +103,17 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
align='center'
|
align='center'
|
||||||
justify='center'
|
justify='center'
|
||||||
gap='small'
|
gap='small'
|
||||||
style={{ height: '100%' }}
|
style={{ height: '100%', minHeight: '120px' }}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
icon={<ArrowUpOutlined />}
|
icon={<ArrowUpIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleMoveAxisClick('Y', false)
|
handleMoveAxisClick('Y', false)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Space>
|
<Space>
|
||||||
<Button
|
<Button
|
||||||
icon={<ArrowLeftOutlined />}
|
icon={<ArrowLeftIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleMoveAxisClick('X', false)
|
handleMoveAxisClick('X', false)
|
||||||
}}
|
}}
|
||||||
@ -134,17 +122,17 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
menu={{ items: homeAxisButtonItems }}
|
menu={{ items: homeAxisButtonItems }}
|
||||||
placement='bottom'
|
placement='bottom'
|
||||||
>
|
>
|
||||||
<Button icon={<HomeOutlined />}></Button>
|
<Button icon={<HomeIcon />}></Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Button
|
<Button
|
||||||
icon={<ArrowRightOutlined />}
|
icon={<ArrowRightIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleMoveAxisClick('X', true)
|
handleMoveAxisClick('X', true)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
<Button
|
<Button
|
||||||
icon={<ArrowDownOutlined />}
|
icon={<ArrowDownIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleMoveAxisClick('Y', true)
|
handleMoveAxisClick('Y', true)
|
||||||
}}
|
}}
|
||||||
@ -157,10 +145,10 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
align='center'
|
align='center'
|
||||||
justify='center'
|
justify='center'
|
||||||
gap='small'
|
gap='small'
|
||||||
style={{ height: '100%' }}
|
style={{ height: '100%', minHeight: '120px' }}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
icon={<ArrowUpOutlined />}
|
icon={<ArrowUpIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleMoveAxisClick('Z', true)
|
handleMoveAxisClick('Z', true)
|
||||||
}}
|
}}
|
||||||
@ -172,7 +160,7 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon={<ArrowDownOutlined />}
|
icon={<ArrowDownIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleMoveAxisClick('Z', false)
|
handleMoveAxisClick('Z', false)
|
||||||
}}
|
}}
|
||||||
@ -180,21 +168,21 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</Card>
|
||||||
<Card size='small' title='E'>
|
<Card size='small' title='E'>
|
||||||
<Flex vertical align='center' justify='center' gap='small'>
|
<Flex
|
||||||
|
vertical
|
||||||
|
align='center'
|
||||||
|
justify='center'
|
||||||
|
gap='small'
|
||||||
|
style={{ height: '100%', minHeight: '120px' }}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
icon={<ArrowUpOutlined />}
|
icon={<ArrowUpIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleMoveAxisClick('E', true)
|
handleMoveAxisClick('E', true)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon={<UnloadIcon />}
|
icon={<ArrowDownIcon />}
|
||||||
onClick={() => {
|
|
||||||
handleUnloadFilamentClick()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
icon={<ArrowDownOutlined />}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleMoveAxisClick('E', false)
|
handleMoveAxisClick('E', false)
|
||||||
}}
|
}}
|
||||||
@ -202,6 +190,7 @@ const PrinterMovementPanel = ({ printerId }) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</Card>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex vertical gap='small'>
|
<Flex vertical gap='small'>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
onChange={handlePosRadioChange}
|
onChange={handlePosRadioChange}
|
||||||
|
|||||||
256
src/components/Dashboard/common/PrinterPositionPanel.jsx
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
import React, { useContext, useState, useEffect } from 'react'
|
||||||
|
import {
|
||||||
|
Typography,
|
||||||
|
Spin,
|
||||||
|
Flex,
|
||||||
|
Space,
|
||||||
|
Collapse,
|
||||||
|
InputNumber,
|
||||||
|
Switch,
|
||||||
|
Descriptions
|
||||||
|
} from 'antd'
|
||||||
|
import { LoadingOutlined, CaretRightOutlined } from '@ant-design/icons'
|
||||||
|
import { SocketContext } from '../context/SocketContext'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
const { Text } = Typography
|
||||||
|
|
||||||
|
const CustomCollapse = styled(Collapse)`
|
||||||
|
.ant-collapse-header {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
.ant-collapse-content-box {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const PrinterPositionPanel = ({
|
||||||
|
printerId,
|
||||||
|
showControls = true,
|
||||||
|
showMoreInfo = true,
|
||||||
|
shouldUnsubscribe = true
|
||||||
|
}) => {
|
||||||
|
const [positionData, setPositionData] = useState({
|
||||||
|
speed_factor: 1.0, // eslint-disable-line
|
||||||
|
speed: 100,
|
||||||
|
extrude_factor: 1.0, // eslint-disable-line
|
||||||
|
absolute_foordinates: true, // eslint-disable-line
|
||||||
|
absolute_extrude: false, // eslint-disable-line
|
||||||
|
homing_origin: [0.0, 0.0, 0.0, 0.0], // eslint-disable-line
|
||||||
|
position: [0.0, 0.0, 0.0, 0.0],
|
||||||
|
gcode_position: [0.0, 0.0, 0.0, 0.0] // eslint-disable-line
|
||||||
|
})
|
||||||
|
|
||||||
|
const [initialized, setInitialized] = useState(false)
|
||||||
|
const { socket } = useContext(SocketContext)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const params = {
|
||||||
|
printerId,
|
||||||
|
objects: {
|
||||||
|
toolhead: null,
|
||||||
|
gcode_move: null // eslint-disable-line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notifyPositionStatusUpdate = (statusUpdate) => {
|
||||||
|
console.log(statusUpdate)
|
||||||
|
if (statusUpdate?.toolhead) {
|
||||||
|
setPositionData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
...statusUpdate.toolhead
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
if (statusUpdate?.gcode_move) {
|
||||||
|
setPositionData((prevData) => ({
|
||||||
|
...prevData,
|
||||||
|
...statusUpdate.gcode_move
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initialized && socket) {
|
||||||
|
setInitialized(true)
|
||||||
|
|
||||||
|
socket.on('connect', () => {
|
||||||
|
console.log('Connected to socket!')
|
||||||
|
socket.emit('printer.objects.subscribe', params)
|
||||||
|
socket.emit('printer.objects.query', params)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('Subscribing to position data')
|
||||||
|
socket.emit('printer.objects.subscribe', params)
|
||||||
|
socket.emit('printer.objects.query', params)
|
||||||
|
socket.on('notify_status_update', notifyPositionStatusUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (socket && initialized && shouldUnsubscribe) {
|
||||||
|
console.log('Unsubscribing...')
|
||||||
|
socket.off('notify_status_update', notifyPositionStatusUpdate)
|
||||||
|
socket.emit('printer.objects.unsubscribe', params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [socket, initialized, printerId, shouldUnsubscribe])
|
||||||
|
|
||||||
|
const handleSetSpeedFactor = (value) => {
|
||||||
|
if (socket) {
|
||||||
|
socket.emit('printer.gcode.script', {
|
||||||
|
printerId,
|
||||||
|
script: `M220 S${value * 100}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSetExtrudeFactor = (value) => {
|
||||||
|
if (socket) {
|
||||||
|
socket.emit('printer.gcode.script', {
|
||||||
|
printerId,
|
||||||
|
script: `M221 S${value * 100}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const moreInfoItems = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: 'More Position Data',
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<Flex vertical gap={'middle'}>
|
||||||
|
<Text>GCode Position:</Text>
|
||||||
|
<Descriptions column={1} size='small' bordered>
|
||||||
|
<Descriptions.Item label='X'>
|
||||||
|
{positionData.gcode_position[0].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Y'>
|
||||||
|
{positionData.gcode_position[1].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Z'>
|
||||||
|
{positionData.gcode_position[2].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='E'>
|
||||||
|
{positionData.gcode_position[3].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
<Text>Homing Origin:</Text>
|
||||||
|
<Descriptions
|
||||||
|
column={1}
|
||||||
|
size='small'
|
||||||
|
bordered
|
||||||
|
style={{ flexGrow: 1 }}
|
||||||
|
>
|
||||||
|
<Descriptions.Item label='X' span={1}>
|
||||||
|
{positionData.homing_origin[0].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Y' span={1}>
|
||||||
|
{positionData.homing_origin[1].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Z' span={1}>
|
||||||
|
{positionData.homing_origin[2].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='E'>
|
||||||
|
{positionData.homing_origin[3].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ minWidth: 190 }}>
|
||||||
|
{positionData ? (
|
||||||
|
<Flex vertical gap='middle'>
|
||||||
|
<Flex vertical gap={'middle'}>
|
||||||
|
<Descriptions column={1} size='small' bordered>
|
||||||
|
<Descriptions.Item label='X'>
|
||||||
|
{positionData.position[0].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Y'>
|
||||||
|
{positionData.position[1].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='Z'>
|
||||||
|
{positionData.position[2].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
<Descriptions.Item label='E'>
|
||||||
|
{positionData.position[3].toFixed(2)}mm
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
<Descriptions column={1} size='small' bordered>
|
||||||
|
<Descriptions.Item label='Current Speed'>
|
||||||
|
{positionData.speed}mm/s
|
||||||
|
</Descriptions.Item>
|
||||||
|
</Descriptions>
|
||||||
|
|
||||||
|
{showControls && (
|
||||||
|
<>
|
||||||
|
<Space direction='vertical' style={{ width: '100%' }}>
|
||||||
|
<Space direction='horizontal'>
|
||||||
|
<Text>Speed Factor:</Text>
|
||||||
|
<InputNumber
|
||||||
|
value={positionData.speed_factor}
|
||||||
|
min={0.1}
|
||||||
|
max={2}
|
||||||
|
step={0.1}
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
onChange={(value) => handleSetSpeedFactor(value)}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Space direction='horizontal'>
|
||||||
|
<Text>Extrude Factor:</Text>
|
||||||
|
<InputNumber
|
||||||
|
value={positionData.extrude_factor}
|
||||||
|
min={0.1}
|
||||||
|
max={2}
|
||||||
|
step={0.1}
|
||||||
|
style={{ width: '100px' }}
|
||||||
|
onChange={(value) => handleSetExtrudeFactor(value)}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Space direction='horizontal'>
|
||||||
|
<Text>Absolute Coordinates:</Text>
|
||||||
|
<Switch
|
||||||
|
checked={positionData.absolute_coordinates}
|
||||||
|
disabled={true}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Space>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{showMoreInfo && (
|
||||||
|
<CustomCollapse
|
||||||
|
ghost
|
||||||
|
size='small'
|
||||||
|
items={moreInfoItems}
|
||||||
|
expandIcon={({ isActive }) => (
|
||||||
|
<CaretRightOutlined rotate={isActive ? 90 : 0} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
) : (
|
||||||
|
<Flex justify='centre'>
|
||||||
|
<Spin indicator={<LoadingOutlined spin />} size='large' />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
PrinterPositionPanel.propTypes = {
|
||||||
|
printerId: PropTypes.string.isRequired,
|
||||||
|
showControls: PropTypes.bool,
|
||||||
|
showMoreInfo: PropTypes.bool,
|
||||||
|
shouldUnsubscribe: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrinterPositionPanel
|
||||||
@ -1,6 +1,15 @@
|
|||||||
// PrinterSelect.js
|
// PrinterSelect.js
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Badge, Progress, Flex, Space, Tag, Typography, Button } from 'antd'
|
import {
|
||||||
|
Badge,
|
||||||
|
Progress,
|
||||||
|
Flex,
|
||||||
|
Space,
|
||||||
|
Tag,
|
||||||
|
Typography,
|
||||||
|
Button,
|
||||||
|
Tooltip
|
||||||
|
} from 'antd'
|
||||||
import React, { useState, useContext, useEffect } from 'react'
|
import React, { useState, useContext, useEffect } from 'react'
|
||||||
import { SocketContext } from '../context/SocketContext'
|
import { SocketContext } from '../context/SocketContext'
|
||||||
import { CaretRightOutlined } from '@ant-design/icons'
|
import { CaretRightOutlined } from '@ant-design/icons'
|
||||||
@ -130,42 +139,51 @@ const PrinterState = ({
|
|||||||
) : null}
|
) : null}
|
||||||
{showControls && currentState.type === 'printing' ? (
|
{showControls && currentState.type === 'printing' ? (
|
||||||
<Space.Compact>
|
<Space.Compact>
|
||||||
<Button
|
<Tooltip
|
||||||
onClick={() => {
|
title={currentState.type === 'printing' ? 'Pause' : 'Resume'}
|
||||||
if (currentState.type === 'printing') {
|
arrow={false}
|
||||||
socket.emit('printer.print.pause', {
|
>
|
||||||
printerId: printer.id
|
<Button
|
||||||
})
|
onClick={() => {
|
||||||
} else {
|
if (currentState.type === 'printing') {
|
||||||
socket.emit('printer.print.resume', {
|
socket.emit('printer.print.pause', {
|
||||||
printerId: printer.id
|
printerId: printer.id
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
socket.emit('printer.print.resume', {
|
||||||
|
printerId: printer.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{ height: '22px' }}
|
||||||
|
type='text'
|
||||||
|
icon={
|
||||||
|
currentState.type === 'printing' ? (
|
||||||
|
<PauseIcon
|
||||||
|
style={{ fontSize: '12px', marginBottom: '3px' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CaretRightOutlined
|
||||||
|
style={{ fontSize: '10px', marginBottom: '3px' }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}}
|
></Button>
|
||||||
style={{ height: '22px' }}
|
</Tooltip>
|
||||||
type='text'
|
<Tooltip title='Cancel' arrow={false}>
|
||||||
icon={
|
<Button
|
||||||
currentState.type === 'printing' ? (
|
onClick={() => {
|
||||||
<PauseIcon style={{ fontSize: '12px', marginBottom: '3px' }} />
|
socket.emit('printer.print.cancel', {
|
||||||
) : (
|
printerId: printer.id
|
||||||
<CaretRightOutlined
|
})
|
||||||
style={{ fontSize: '10px', marginBottom: '3px' }}
|
}}
|
||||||
/>
|
type='text'
|
||||||
)
|
style={{ height: '22px' }}
|
||||||
}
|
icon={
|
||||||
></Button>
|
<XMarkIcon style={{ fontSize: '12px', marginBottom: '3px' }} />
|
||||||
<Button
|
}
|
||||||
onClick={() => {
|
/>
|
||||||
socket.emit('printer.print.cancel', {
|
</Tooltip>
|
||||||
printerId: printer.id
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
type='text'
|
|
||||||
style={{ height: '22px' }}
|
|
||||||
icon={
|
|
||||||
<XMarkIcon style={{ fontSize: '12px', marginBottom: '3px' }} />
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
// PrinterTemperaturePanel.js
|
// PrinterTemperaturePanel.js
|
||||||
import React, { useContext, useState, useEffect } from 'react'
|
import React, { useContext, useState, useEffect, useCallback } from 'react'
|
||||||
import {
|
import {
|
||||||
Progress,
|
Progress,
|
||||||
Typography,
|
Typography,
|
||||||
@ -52,19 +52,10 @@ const PrinterTemperaturePanel = ({
|
|||||||
)
|
)
|
||||||
const { socket } = useContext(SocketContext)
|
const { socket } = useContext(SocketContext)
|
||||||
|
|
||||||
const [initialized, setInitialized] = useState(false)
|
const notifyTemperatureStatusUpdate = useCallback((statusUpdate) => {
|
||||||
|
setTemperatureData((prev) => {
|
||||||
useEffect(() => {
|
const temperatureObject = {
|
||||||
const params = {
|
...prev
|
||||||
printerId,
|
|
||||||
objects: {
|
|
||||||
extruder: null,
|
|
||||||
heater_bed: null // eslint-disable-line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const notifyStatusUpdate = (statusUpdate) => {
|
|
||||||
var temperatureObject = {
|
|
||||||
...temperatureData
|
|
||||||
}
|
}
|
||||||
if (statusUpdate?.extruder?.temperature !== undefined) {
|
if (statusUpdate?.extruder?.temperature !== undefined) {
|
||||||
temperatureObject.hotEnd.current = statusUpdate?.extruder?.temperature
|
temperatureObject.hotEnd.current = statusUpdate?.extruder?.temperature
|
||||||
@ -93,35 +84,38 @@ const PrinterTemperaturePanel = ({
|
|||||||
temperatureObject.heatedBed.power = statusUpdate?.heater_bed?.power
|
temperatureObject.heatedBed.power = statusUpdate?.heater_bed?.power
|
||||||
}
|
}
|
||||||
|
|
||||||
setTemperatureData(temperatureObject)
|
return temperatureObject
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const params = {
|
||||||
|
printerId,
|
||||||
|
objects: {
|
||||||
|
extruder: null,
|
||||||
|
heater_bed: null // eslint-disable-line
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!initialized && socket) {
|
if (socket.connected == true) {
|
||||||
setInitialized(true)
|
console.log('Printer Temperature Panel is subscribing...')
|
||||||
|
|
||||||
socket.on('connect', () => {
|
|
||||||
console.log('Connected to socket!')
|
|
||||||
socket.emit('printer.objects.subscribe', params)
|
|
||||||
socket.emit('printer.objects.query', params)
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('Subscribing to temperature data')
|
|
||||||
socket.emit('printer.objects.subscribe', params)
|
socket.emit('printer.objects.subscribe', params)
|
||||||
socket.emit('printer.objects.query', params)
|
socket.emit('printer.objects.query', params)
|
||||||
socket.on('notify_status_update', notifyStatusUpdate)
|
socket.on('notify_status_update', notifyTemperatureStatusUpdate)
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
if (socket && initialized && shouldUnsubscribe == true) {
|
if (socket && shouldUnsubscribe == true) {
|
||||||
console.log('Unsubscribing...')
|
console.log('Printer Temperature Panel is unsubscribing...')
|
||||||
socket.off('notify_status_update', notifyStatusUpdate)
|
socket.off('notify_status_update', notifyTemperatureStatusUpdate)
|
||||||
socket.emit('printer.objects.unsubscribe', params)
|
socket.emit('printer.objects.unsubscribe', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup code here, like:
|
|
||||||
// - Removing event listeners
|
|
||||||
// - Clearing timers
|
|
||||||
// - Closing sockets
|
|
||||||
}
|
}
|
||||||
}, [socket, initialized, printerId])
|
}, [
|
||||||
|
socket,
|
||||||
|
socket.connected,
|
||||||
|
printerId,
|
||||||
|
notifyTemperatureStatusUpdate,
|
||||||
|
shouldUnsubscribe
|
||||||
|
])
|
||||||
|
|
||||||
const handleSetTemperatureClick = (target, value) => {
|
const handleSetTemperatureClick = (target, value) => {
|
||||||
if (socket) {
|
if (socket) {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import moment from 'moment'
|
|||||||
import TimeDisplay from '../common/TimeDisplay'
|
import TimeDisplay from '../common/TimeDisplay'
|
||||||
import PlusMinusIcon from '../../Icons/PlusMinusIcon'
|
import PlusMinusIcon from '../../Icons/PlusMinusIcon'
|
||||||
import SubJobIcon from '../../Icons/SubJobIcon'
|
import SubJobIcon from '../../Icons/SubJobIcon'
|
||||||
|
import PlayCircleIcon from '../../Icons/PlayCircleIcon'
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
@ -87,6 +88,8 @@ const StockEventTable = ({ stockEvents }) => {
|
|||||||
return <SubJobIcon />
|
return <SubJobIcon />
|
||||||
case 'audit':
|
case 'audit':
|
||||||
return <AuditOutlined />
|
return <AuditOutlined />
|
||||||
|
case 'initial':
|
||||||
|
return <PlayCircleIcon />
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -116,8 +119,8 @@ const StockEventTable = ({ stockEvents }) => {
|
|||||||
key: 'value',
|
key: 'value',
|
||||||
width: 100,
|
width: 100,
|
||||||
sorter: (a, b) => a.value - b.value,
|
sorter: (a, b) => a.value - b.value,
|
||||||
render: (value) => {
|
render: (value, record) => {
|
||||||
const formattedValue = value.toFixed(2) + 'g'
|
const formattedValue = value.toFixed(2) + record.unit
|
||||||
return (
|
return (
|
||||||
<Text type={value < 0 ? 'danger' : 'success'}>
|
<Text type={value < 0 ? 'danger' : 'success'}>
|
||||||
{value > 0 ? '+' + formattedValue : formattedValue}
|
{value > 0 ? '+' + formattedValue : formattedValue}
|
||||||
@ -126,7 +129,7 @@ const StockEventTable = ({ stockEvents }) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'ID',
|
title: 'Linked ID',
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (record) => {
|
render: (record) => {
|
||||||
if (record.subJob) {
|
if (record.subJob) {
|
||||||
@ -205,6 +208,7 @@ const StockEventTable = ({ stockEvents }) => {
|
|||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey={(record) => record._id}
|
rowKey={(record) => record._id}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
|
scroll={{ x: 'max-content' }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,11 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Typography, Tag } from 'antd' // eslint-disable-line
|
import { Typography, Tag } from 'antd' // eslint-disable-line
|
||||||
import {
|
|
||||||
CheckCircleOutlined,
|
|
||||||
PauseCircleOutlined,
|
|
||||||
QuestionCircleOutlined,
|
|
||||||
PlayCircleOutlined,
|
|
||||||
CloseCircleOutlined
|
|
||||||
} from '@ant-design/icons' // eslint-disable-line
|
|
||||||
import React, { useState, useContext, useEffect } from 'react'
|
import React, { useState, useContext, useEffect } from 'react'
|
||||||
import { SocketContext } from '../context/SocketContext'
|
import { SocketContext } from '../context/SocketContext'
|
||||||
|
import QuestionCircleIcon from '../../Icons/QuestionCircleIcon'
|
||||||
|
import PauseCircleIcon from '../../Icons/PauseCircleIcon'
|
||||||
|
import XMarkCircleIcon from '../../Icons/XMarkCircleIcon'
|
||||||
|
import CheckCircleIcon from '../../Icons/CheckCircleIcon'
|
||||||
|
|
||||||
const SubJobCounter = ({
|
const SubJobCounter = ({
|
||||||
job,
|
job,
|
||||||
@ -18,7 +15,7 @@ const SubJobCounter = ({
|
|||||||
const { socket } = useContext(SocketContext)
|
const { socket } = useContext(SocketContext)
|
||||||
const [initialized, setInitialized] = useState(false)
|
const [initialized, setInitialized] = useState(false)
|
||||||
var badgeStatus = 'unknown'
|
var badgeStatus = 'unknown'
|
||||||
var badgeIcon = <QuestionCircleOutlined />
|
var badgeIcon = <QuestionCircleIcon />
|
||||||
const [subJobs, setSubJobs] = useState(job.subJobs)
|
const [subJobs, setSubJobs] = useState(job.subJobs)
|
||||||
|
|
||||||
const [count, setCount] = useState(0)
|
const [count, setCount] = useState(0)
|
||||||
@ -47,30 +44,30 @@ const SubJobCounter = ({
|
|||||||
switch (state.type) {
|
switch (state.type) {
|
||||||
case 'draft':
|
case 'draft':
|
||||||
badgeStatus = 'default'
|
badgeStatus = 'default'
|
||||||
badgeIcon = <QuestionCircleOutlined />
|
badgeIcon = <QuestionCircleIcon />
|
||||||
break
|
break
|
||||||
case 'printing':
|
case 'printing':
|
||||||
badgeStatus = 'processing'
|
badgeStatus = 'processing'
|
||||||
badgeIcon = <PlayCircleOutlined />
|
badgeIcon = <CheckCircleIcon />
|
||||||
break
|
break
|
||||||
case 'complete':
|
case 'complete':
|
||||||
badgeStatus = 'success'
|
badgeStatus = 'success'
|
||||||
badgeIcon = <CheckCircleOutlined />
|
badgeIcon = <CheckCircleIcon />
|
||||||
break
|
break
|
||||||
case 'failed':
|
case 'failed':
|
||||||
badgeStatus = 'error'
|
badgeStatus = 'error'
|
||||||
badgeIcon = <CloseCircleOutlined />
|
badgeIcon = <XMarkCircleIcon />
|
||||||
break
|
break
|
||||||
case 'queued':
|
case 'queued':
|
||||||
badgeStatus = 'warning'
|
badgeStatus = 'warning'
|
||||||
badgeIcon = <PauseCircleOutlined />
|
badgeIcon = <PauseCircleIcon />
|
||||||
break
|
break
|
||||||
case 'paused':
|
case 'paused':
|
||||||
badgeStatus = 'warning'
|
badgeStatus = 'warning'
|
||||||
badgeIcon = <PauseCircleOutlined />
|
badgeIcon = <PauseCircleIcon />
|
||||||
break
|
break
|
||||||
case 'cancelled':
|
case 'cancelled':
|
||||||
badgeIcon = <CloseCircleOutlined />
|
badgeIcon = <XMarkCircleIcon />
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
badgeStatus = 'default'
|
badgeStatus = 'default'
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Badge, Progress, Flex, Button, Space, Tag } from 'antd' // eslint-disable-line
|
import { Badge, Progress, Flex, Button, Space, Tag, Tooltip } from 'antd' // eslint-disable-line
|
||||||
import { CaretRightOutlined } from '@ant-design/icons' // eslint-disable-line
|
import { CaretRightOutlined } from '@ant-design/icons' // eslint-disable-line
|
||||||
import React, { useState, useContext, useEffect } from 'react'
|
import React, { useState, useContext, useEffect } from 'react'
|
||||||
import { SocketContext } from '../context/SocketContext'
|
import { SocketContext } from '../context/SocketContext'
|
||||||
@ -112,66 +112,83 @@ const SubJobState = ({
|
|||||||
{showControls &&
|
{showControls &&
|
||||||
(currentState.type === 'printing' || currentState.type === 'paused') ? (
|
(currentState.type === 'printing' || currentState.type === 'paused') ? (
|
||||||
<Space.Compact>
|
<Space.Compact>
|
||||||
<Button
|
<Tooltip
|
||||||
onClick={() => {
|
title={currentState.type === 'printing' ? 'Pause' : 'Resume'}
|
||||||
if (currentState.type === 'printing') {
|
arrow={false}
|
||||||
socket.emit('printer.print.pause', {
|
>
|
||||||
printerId: subJob.printer
|
<Button
|
||||||
})
|
onClick={() => {
|
||||||
} else {
|
if (currentState.type === 'printing') {
|
||||||
socket.emit('printer.print.resume', {
|
socket.emit('printer.print.pause', {
|
||||||
printerId: subJob.printer
|
printerId: subJob.printer
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
socket.emit('printer.print.resume', {
|
||||||
|
printerId: subJob.printer
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{ height: '22px' }}
|
||||||
|
type='text'
|
||||||
|
icon={
|
||||||
|
currentState.type === 'printing' ? (
|
||||||
|
<PauseIcon
|
||||||
|
style={{ fontSize: '12px', marginBottom: '3px' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CaretRightOutlined
|
||||||
|
style={{ fontSize: '10px', marginBottom: '3px' }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}}
|
></Button>
|
||||||
style={{ height: '22px' }}
|
</Tooltip>
|
||||||
type='text'
|
<Tooltip title='Cancel' arrow={false}>
|
||||||
icon={
|
<Button
|
||||||
currentState.type === 'printing' ? (
|
onClick={() => {
|
||||||
<PauseIcon style={{ fontSize: '12px', marginBottom: '3px' }} />
|
socket.emit('printer.print.cancel', {
|
||||||
) : (
|
printerId: subJob.printer
|
||||||
<CaretRightOutlined
|
})
|
||||||
style={{ fontSize: '10px', marginBottom: '3px' }}
|
}}
|
||||||
/>
|
type='text'
|
||||||
)
|
style={{ height: '22px' }}
|
||||||
}
|
icon={
|
||||||
></Button>
|
<XMarkIcon style={{ fontSize: '12px', marginBottom: '3px' }} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Space.Compact>
|
||||||
|
) : null}
|
||||||
|
{showControls && currentState.type === 'queued' ? (
|
||||||
|
<Tooltip title='Cancel' arrow={false}>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
socket.emit('printer.print.cancel', {
|
socket.emit('server.job_queue.cancel', {
|
||||||
printerId: subJob.printer
|
subJobId: subJob._id
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
type='text'
|
|
||||||
style={{ height: '22px' }}
|
style={{ height: '22px' }}
|
||||||
|
type='text'
|
||||||
icon={
|
icon={
|
||||||
<XMarkIcon style={{ fontSize: '12px', marginBottom: '3px' }} />
|
<XMarkIcon style={{ fontSize: '12px', marginBottom: '3px' }} />
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Space.Compact>
|
</Tooltip>
|
||||||
) : null}
|
|
||||||
{showControls && currentState.type === 'queued' ? (
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
socket.emit('server.job_queue.cancel', {
|
|
||||||
subJobId: subJob._id
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
style={{ height: '22px' }}
|
|
||||||
type='text'
|
|
||||||
icon={<XMarkIcon style={{ fontSize: '12px', marginBottom: '3px' }} />}
|
|
||||||
/>
|
|
||||||
) : null}
|
) : null}
|
||||||
{showControls && currentState.type === 'draft' ? (
|
{showControls && currentState.type === 'draft' ? (
|
||||||
<Space>
|
<Space>
|
||||||
<Button
|
<Tooltip title='Delete' arrow={false}>
|
||||||
onClick={() => {
|
<Button
|
||||||
console.log('Hello')
|
onClick={() => {
|
||||||
}}
|
console.log('Hello')
|
||||||
type='text'
|
}}
|
||||||
style={{ height: 'unset' }}
|
type='text'
|
||||||
icon={<BinIcon style={{ fontSize: '14px', marginBottom: '2px' }} />}
|
style={{ height: 'unset' }}
|
||||||
/>
|
icon={
|
||||||
|
<BinIcon style={{ fontSize: '14px', marginBottom: '2px' }} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
</Space>
|
</Space>
|
||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
import { Input, Flex, List, Typography, Modal, Spin, message, Form } from 'antd'
|
import { Input, Flex, List, Typography, Modal, Spin, message, Form } from 'antd'
|
||||||
import React, { createContext, useEffect, useState, useRef } from 'react'
|
import React, { createContext, useEffect, useState, useRef } from 'react'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
LoadingOutlined,
|
|
||||||
PrinterOutlined,
|
|
||||||
PlayCircleOutlined
|
|
||||||
} from '@ant-design/icons'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import PrinterState from '../common/PrinterState'
|
import PrinterState from '../common/PrinterState'
|
||||||
import JobState from '../common/JobState'
|
import JobState from '../common/JobState'
|
||||||
import IdText from '../common/IdText'
|
import IdText from '../common/IdText'
|
||||||
|
|
||||||
import config from '../../../config'
|
import config from '../../../config'
|
||||||
|
import JobIcon from '../../Icons/JobIcon'
|
||||||
|
import PrinterIcon from '../../Icons/PrinterIcon'
|
||||||
|
|
||||||
const SpotlightContext = createContext()
|
const SpotlightContext = createContext()
|
||||||
|
|
||||||
@ -316,10 +314,10 @@ const SpotlightProvider = ({ children }) => {
|
|||||||
<Flex gap={'middle'} align='center'>
|
<Flex gap={'middle'} align='center'>
|
||||||
<Text>
|
<Text>
|
||||||
{item.printer ? (
|
{item.printer ? (
|
||||||
<PrinterOutlined style={{ fontSize: '20px' }} />
|
<PrinterIcon style={{ fontSize: '20px' }} />
|
||||||
) : null}
|
) : null}
|
||||||
{item.job ? (
|
{item.job ? (
|
||||||
<PlayCircleOutlined style={{ fontSize: '20px' }} />
|
<JobIcon style={{ fontSize: '20px' }} />
|
||||||
) : null}
|
) : null}
|
||||||
</Text>
|
</Text>
|
||||||
<Flex
|
<Flex
|
||||||
|
|||||||
71
src/components/Dashboard/context/ThemeContext.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import React, { createContext, useContext, useState } from 'react'
|
||||||
|
import { theme } from 'antd'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
const ThemeContext = createContext()
|
||||||
|
|
||||||
|
export const ThemeProvider = ({ children }) => {
|
||||||
|
const [isDarkMode, setIsDarkMode] = useState(true)
|
||||||
|
const [isCompact, setIsCompact] = useState(false)
|
||||||
|
|
||||||
|
const toggleTheme = () => {
|
||||||
|
setIsDarkMode(!isDarkMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleCompact = () => {
|
||||||
|
setIsCompact(!isCompact)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getThemeAlgorithm = () => {
|
||||||
|
var baseAlgorithm
|
||||||
|
if (isDarkMode == true) {
|
||||||
|
baseAlgorithm = theme.darkAlgorithm
|
||||||
|
} else {
|
||||||
|
baseAlgorithm = theme.defaultAlgorithm
|
||||||
|
}
|
||||||
|
return isCompact ? [theme.compactAlgorithm, baseAlgorithm] : [baseAlgorithm]
|
||||||
|
}
|
||||||
|
|
||||||
|
const themeConfig = {
|
||||||
|
algorithm: getThemeAlgorithm(),
|
||||||
|
token: {
|
||||||
|
colorPrimary: '#007AFF',
|
||||||
|
colorSuccess: '#32D74B',
|
||||||
|
colorWarning: '#FF9F0A',
|
||||||
|
colorInfo: '#007AFF',
|
||||||
|
colorLink: '#5AC8F5',
|
||||||
|
borderRadius: '10px'
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Layout: {
|
||||||
|
headerBg: isDarkMode ? '#141414' : '#ffffff'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider
|
||||||
|
value={{
|
||||||
|
isDarkMode,
|
||||||
|
toggleTheme,
|
||||||
|
isCompact,
|
||||||
|
toggleCompact,
|
||||||
|
themeConfig
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ThemeProvider.propTypes = {
|
||||||
|
children: PropTypes.node.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useThemeContext = () => {
|
||||||
|
const context = useContext(ThemeContext)
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useThemeContext must be used within a ThemeProvider')
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
7
src/components/Icons/ArrowDownIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/arrowdownicon.min.svg'
|
||||||
|
|
||||||
|
const ArrowDownIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default ArrowDownIcon
|
||||||
7
src/components/Icons/ArrowLeftIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/arrowlefticon.min.svg'
|
||||||
|
|
||||||
|
const ArrowLeftIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default ArrowLeftIcon
|
||||||
7
src/components/Icons/ArrowRightIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/arrowrighticon.min.svg'
|
||||||
|
|
||||||
|
const ArrowRightIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default ArrowRightIcon
|
||||||
7
src/components/Icons/ArrowUpIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/arrowupicon.min.svg'
|
||||||
|
|
||||||
|
const ArrowUpIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default ArrowUpIcon
|
||||||
7
src/components/Icons/CheckCircleIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/checkcircleicon.min.svg'
|
||||||
|
|
||||||
|
const CheckCircleIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default CheckCircleIcon
|
||||||
9
src/components/Icons/ExclamationOctagonIcon.jsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/exclamationoctagonicon.min.svg'
|
||||||
|
|
||||||
|
const ExclamationOctagonIcon = (props) => (
|
||||||
|
<Icon component={CustomIconSvg} {...props} />
|
||||||
|
)
|
||||||
|
|
||||||
|
export default ExclamationOctagonIcon
|
||||||
7
src/components/Icons/HomeIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/homeicon.min.svg'
|
||||||
|
|
||||||
|
const HomeIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default HomeIcon
|
||||||
7
src/components/Icons/PauseCircleIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/pausecircleicon.min.svg'
|
||||||
|
|
||||||
|
const PauseCircleIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default PauseCircleIcon
|
||||||
7
src/components/Icons/PlayCircleIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/playcircleicon.min.svg'
|
||||||
|
|
||||||
|
const PlayCircleIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default PlayCircleIcon
|
||||||
9
src/components/Icons/QuestionCircleIcon.jsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/questioncircleicon.min.svg'
|
||||||
|
|
||||||
|
const QuestionCircleIcon = (props) => (
|
||||||
|
<Icon component={CustomIconSvg} {...props} />
|
||||||
|
)
|
||||||
|
|
||||||
|
export default QuestionCircleIcon
|
||||||
7
src/components/Icons/StockEventIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/stockeventicon.min.svg'
|
||||||
|
|
||||||
|
const StockEventIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default StockEventIcon
|
||||||
7
src/components/Icons/StopCircleIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/stopcircleicon.min.svg'
|
||||||
|
|
||||||
|
const StopCircleIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default StopCircleIcon
|
||||||
7
src/components/Icons/XMarkCircleIcon.jsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import { ReactComponent as CustomIconSvg } from '../../assets/icons/xmarkcircleicon.min.svg'
|
||||||
|
|
||||||
|
const XMarkCircleIcon = (props) => <Icon component={CustomIconSvg} {...props} />
|
||||||
|
|
||||||
|
export default XMarkCircleIcon
|
||||||
@ -4,8 +4,8 @@ const config = {
|
|||||||
wsUrl: 'ws://localhost:8081'
|
wsUrl: 'ws://localhost:8081'
|
||||||
},
|
},
|
||||||
production: {
|
production: {
|
||||||
backendUrl: 'https://api.farmcontrol.com', // Replace with your production backend URL
|
backendUrl: 'http://localhost:8080', // Replace with your production backend URL
|
||||||
wsUrl: 'wss://api.farmcontrol.com' // Replace with your production WebSocket URL
|
wsUrl: 'ws://localhost:8081' // Replace with your production WebSocket URL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||