Compare commits
15 Commits
developmen
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a896cdf223 | |||
| 2df25364a0 | |||
| 75eeed16a0 | |||
| 02b0d245ff | |||
| 86d656720c | |||
| 1474b23b1e | |||
| 657c1dd17e | |||
| 2c3cf9d02b | |||
| 2177870fc9 | |||
| 0e8f4506cc | |||
| 8568a8a8ce | |||
| 991c10d48a | |||
| 8fbcc67230 | |||
| 156d2a8baf | |||
| 8677691da3 |
10
package.json
10
package.json
@ -80,7 +80,7 @@
|
|||||||
"electron": "cross-env ELECTRON_START_URL=http://0.0.0.0:5780 && cross-env NODE_ENV=development && electron .",
|
"electron": "cross-env ELECTRON_START_URL=http://0.0.0.0:5780 && cross-env NODE_ENV=development && electron .",
|
||||||
"start": "serve -s build",
|
"start": "serve -s build",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"dev:electron": "concurrently \"cross-env NODE_ENV=development vite --port 5780 --no-open\" \"cross-env ELECTRON_START_URL=http://localhost:5780 cross-env NODE_ENV=development electron public/electron.js\"",
|
"dev:electron": "concurrently \"cross-env NODE_ENV=development vite --port 5780 --no-open\" \"cross-env ELECTRON_START_URL=http://localhost:5780 NODE_ENV=development electron public/electron.js\"",
|
||||||
"build:electron": "vite build && electron-builder",
|
"build:electron": "vite build && electron-builder",
|
||||||
"build:cloudflare": "cross-env VITE_DEPLOY_TARGET=cloudflare vite build",
|
"build:cloudflare": "cross-env VITE_DEPLOY_TARGET=cloudflare vite build",
|
||||||
"deploy": "npm run build:cloudflare && wrangler pages deploy --branch main"
|
"deploy": "npm run build:cloudflare && wrangler pages deploy --branch main"
|
||||||
@ -213,7 +213,7 @@
|
|||||||
"win": {
|
"win": {
|
||||||
"target": [
|
"target": [
|
||||||
"nsis",
|
"nsis",
|
||||||
"msi"
|
"msiWrapped"
|
||||||
],
|
],
|
||||||
"protocols": [
|
"protocols": [
|
||||||
{
|
{
|
||||||
@ -232,6 +232,12 @@
|
|||||||
"allowToChangeInstallationDirectory": true,
|
"allowToChangeInstallationDirectory": true,
|
||||||
"include": "scripts/installer.nsh",
|
"include": "scripts/installer.nsh",
|
||||||
"perMachine": true
|
"perMachine": true
|
||||||
|
},
|
||||||
|
"msiWrapped": {
|
||||||
|
"upgradeCode": "{735812DB-E33B-57A0-8FBC-5FC3155925AA}",
|
||||||
|
"perMachine": true,
|
||||||
|
"impersonate": false,
|
||||||
|
"wrappedInstallerArgs": "/S"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,82 +1,408 @@
|
|||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
|
import { promises as fs } from 'fs'
|
||||||
|
import os from 'os'
|
||||||
|
import path from 'path'
|
||||||
import process from 'process'
|
import process from 'process'
|
||||||
|
|
||||||
const buildWindowsInstallCommand = (installerPath) => ({
|
const MSI_OLE_HEADER = Buffer.from([0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1])
|
||||||
command: 'cmd.exe',
|
const DEBUG_PREFIX = '[app-update][win-progress]'
|
||||||
args: [
|
|
||||||
'/d',
|
|
||||||
'/s',
|
|
||||||
'/c',
|
|
||||||
`timeout /t 2 /nobreak >NUL && msiexec.exe /i "${installerPath}" /qn /norestart`
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
export const launchWindowsInstaller = (
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
||||||
|
|
||||||
|
const debugLog = (message, details) => {
|
||||||
|
if (details === undefined) {
|
||||||
|
console.log(`${DEBUG_PREFIX} ${message}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`${DEBUG_PREFIX} ${message}`, details)
|
||||||
|
}
|
||||||
|
|
||||||
|
const decodeMsiLogBuffer = (buffer) => {
|
||||||
|
if (!buffer?.length) return ''
|
||||||
|
|
||||||
|
if (buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xfe) {
|
||||||
|
debugLog('decoded MSI log as UTF-16 LE (BOM)')
|
||||||
|
return buffer.subarray(2).toString('utf16le')
|
||||||
|
}
|
||||||
|
|
||||||
|
const sample = buffer.subarray(0, Math.min(buffer.length, 64))
|
||||||
|
const looksUtf16 =
|
||||||
|
sample.length >= 4 &&
|
||||||
|
sample.filter((byte) => byte === 0).length > sample.length / 4
|
||||||
|
|
||||||
|
if (looksUtf16) {
|
||||||
|
debugLog('decoded MSI log as UTF-16 LE (heuristic)')
|
||||||
|
return buffer.toString('utf16le')
|
||||||
|
}
|
||||||
|
|
||||||
|
debugLog('decoded MSI log as UTF-8')
|
||||||
|
return buffer.toString('utf8')
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatMsiActionName = (actionName) => {
|
||||||
|
const humanized = String(actionName)
|
||||||
|
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
||||||
|
.replace(/_/g, ' ')
|
||||||
|
.toLowerCase()
|
||||||
|
.trim()
|
||||||
|
|
||||||
|
if (!humanized) return 'Installing update...'
|
||||||
|
|
||||||
|
return `${humanized.charAt(0).toUpperCase()}${humanized.slice(1)}...`
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseWindowsInstallerProgress = (output) => {
|
||||||
|
const lines = String(output || '').split(/\r?\n/)
|
||||||
|
let percent = null
|
||||||
|
let message = 'Installing update...'
|
||||||
|
let totalTicks = 0
|
||||||
|
let currentTicks = 0
|
||||||
|
let actionStarts = 0
|
||||||
|
let actionEnds = 0
|
||||||
|
const matchedLines = []
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const actionStart = line.match(/^Action start \d{2}:\d{2}:\d{2}: (.+?)\./)
|
||||||
|
if (actionStart) {
|
||||||
|
actionStarts += 1
|
||||||
|
message = formatMsiActionName(actionStart[1])
|
||||||
|
matchedLines.push(`action-start:${actionStart[1]}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const doingAction = line.match(/Doing action:\s*(.+)$/)
|
||||||
|
if (doingAction && !actionStart) {
|
||||||
|
message = formatMsiActionName(doingAction[1])
|
||||||
|
matchedLines.push(`doing-action:${doingAction[1]}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^Action ended \d{2}:\d{2}:\d{2}: .+?\. Return value \d+\./.test(line)) {
|
||||||
|
actionEnds += 1
|
||||||
|
matchedLines.push('action-ended')
|
||||||
|
}
|
||||||
|
|
||||||
|
const progressReset = line.match(/^\s*0\s+(\d+)\s+0(?:\s+\d+)?\s*$/)
|
||||||
|
if (progressReset) {
|
||||||
|
totalTicks = Number.parseInt(progressReset[1], 10) || 0
|
||||||
|
currentTicks = 0
|
||||||
|
matchedLines.push(`progress-reset:${totalTicks}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const progressIncrement = line.match(/^\s*2\s+(\d+)\s*$/)
|
||||||
|
if (progressIncrement) {
|
||||||
|
currentTicks += Number.parseInt(progressIncrement[1], 10) || 0
|
||||||
|
matchedLines.push(`progress-increment:${progressIncrement[1]}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const progressAddition = line.match(/^\s*3\s+(\d+)\s*$/)
|
||||||
|
if (progressAddition) {
|
||||||
|
totalTicks += Number.parseInt(progressAddition[1], 10) || 0
|
||||||
|
matchedLines.push(`progress-addition:${progressAddition[1]}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/Installation success or error status:\s*0\b/.test(line)) {
|
||||||
|
percent = 100
|
||||||
|
message = 'Installation complete. Restarting Farm Control...'
|
||||||
|
matchedLines.push('install-success')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (percent !== 100) {
|
||||||
|
if (totalTicks > 0) {
|
||||||
|
percent = Math.min(99, Math.round((currentTicks / totalTicks) * 100))
|
||||||
|
} else if (actionStarts > 0) {
|
||||||
|
percent = Math.min(
|
||||||
|
95,
|
||||||
|
Math.max(5, Math.round((actionEnds / actionStarts) * 90))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
percent,
|
||||||
|
message,
|
||||||
|
stats: {
|
||||||
|
lineCount: lines.length,
|
||||||
|
actionStarts,
|
||||||
|
actionEnds,
|
||||||
|
totalTicks,
|
||||||
|
currentTicks,
|
||||||
|
matchedLines: matchedLines.slice(-8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isWindowsInstallSuccessful = (output) =>
|
||||||
|
/Installation success or error status:\s*0\b/.test(output) ||
|
||||||
|
/MainEngineThread is returning 0\b/.test(output)
|
||||||
|
|
||||||
|
const isWindowsInstallFailed = (output) =>
|
||||||
|
/Installation success or error status:\s*[1-9]\d*\b/.test(output) ||
|
||||||
|
/MainEngineThread is returning [1-9]\d*\b/.test(output)
|
||||||
|
|
||||||
|
const isValidMsiPackage = async (filePath) => {
|
||||||
|
const handle = await fs.open(filePath, 'r')
|
||||||
|
try {
|
||||||
|
const header = Buffer.alloc(MSI_OLE_HEADER.length)
|
||||||
|
await handle.read(header, 0, header.length, 0)
|
||||||
|
return header.equals(MSI_OLE_HEADER)
|
||||||
|
} finally {
|
||||||
|
await handle.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const prepareInstallerPath = async (installerPath) => {
|
||||||
|
const fileName = path.basename(installerPath)
|
||||||
|
const updateDir = path.join(
|
||||||
|
process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'),
|
||||||
|
'FarmControl',
|
||||||
|
'Updates'
|
||||||
|
)
|
||||||
|
await fs.mkdir(updateDir, { recursive: true })
|
||||||
|
|
||||||
|
const stablePath = path.join(updateDir, fileName)
|
||||||
|
await fs.copyFile(installerPath, stablePath)
|
||||||
|
|
||||||
|
// Resolve to a canonical long path. Short 8.3 paths (e.g. ADMINI~1) break msiexec.
|
||||||
|
const resolvedPath = await fs.realpath(stablePath)
|
||||||
|
const stats = await fs.stat(resolvedPath)
|
||||||
|
|
||||||
|
if (!stats.isFile() || stats.size === 0) {
|
||||||
|
throw new Error('Update installer file is missing or empty.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await isValidMsiPackage(resolvedPath))) {
|
||||||
|
throw new Error(
|
||||||
|
'Downloaded update is not a valid Windows Installer package. The file may be corrupted or incomplete.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedPath
|
||||||
|
}
|
||||||
|
|
||||||
|
const startWindowsInstallerProgressWatch = (
|
||||||
|
logPath,
|
||||||
|
webContents,
|
||||||
|
sendProgress
|
||||||
|
) => {
|
||||||
|
let installerOutput = ''
|
||||||
|
let lastLogSize = 0
|
||||||
|
let lastPercent = null
|
||||||
|
let lastMessage = null
|
||||||
|
let pollCount = 0
|
||||||
|
|
||||||
|
const poll = async () => {
|
||||||
|
pollCount += 1
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stat = await fs.stat(logPath)
|
||||||
|
if (stat.size === 0) {
|
||||||
|
debugLog(`poll #${pollCount}: log exists but is empty`, { logPath })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat.size === lastLogSize) {
|
||||||
|
debugLog(`poll #${pollCount}: no new log data`, {
|
||||||
|
logPath,
|
||||||
|
size: stat.size
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = Buffer.alloc(stat.size)
|
||||||
|
const handle = await fs.open(logPath, 'r')
|
||||||
|
try {
|
||||||
|
await handle.read(buffer, 0, stat.size, 0)
|
||||||
|
} finally {
|
||||||
|
await handle.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
lastLogSize = stat.size
|
||||||
|
installerOutput = decodeMsiLogBuffer(buffer)
|
||||||
|
|
||||||
|
const { percent, message, stats } =
|
||||||
|
parseWindowsInstallerProgress(installerOutput)
|
||||||
|
const resolvedPercent = percent ?? lastPercent ?? 0
|
||||||
|
const resolvedMessage = message || 'Installing update...'
|
||||||
|
|
||||||
|
debugLog(`poll #${pollCount}: parsed installer log`, {
|
||||||
|
logPath,
|
||||||
|
size: stat.size,
|
||||||
|
textLength: installerOutput.length,
|
||||||
|
preview: installerOutput.slice(0, 240).replace(/\s+/g, ' '),
|
||||||
|
parsed: stats,
|
||||||
|
resolvedPercent,
|
||||||
|
resolvedMessage
|
||||||
|
})
|
||||||
|
|
||||||
|
if (
|
||||||
|
resolvedPercent !== lastPercent ||
|
||||||
|
resolvedMessage !== lastMessage
|
||||||
|
) {
|
||||||
|
debugLog(`poll #${pollCount}: sending progress update`, {
|
||||||
|
percent: resolvedPercent,
|
||||||
|
message: resolvedMessage
|
||||||
|
})
|
||||||
|
|
||||||
|
lastPercent = resolvedPercent
|
||||||
|
lastMessage = resolvedMessage
|
||||||
|
sendProgress(webContents, {
|
||||||
|
phase: 'installing',
|
||||||
|
percent: resolvedPercent,
|
||||||
|
message: resolvedMessage
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
debugLog(`poll #${pollCount}: progress unchanged, skipping UI update`, {
|
||||||
|
percent: resolvedPercent,
|
||||||
|
message: resolvedMessage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error?.code === 'ENOENT') {
|
||||||
|
debugLog(`poll #${pollCount}: log file not created yet`, { logPath })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(`${DEBUG_PREFIX} installer log poll error:`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
poll().catch((error) => {
|
||||||
|
console.error(`${DEBUG_PREFIX} installer log poll error:`, error)
|
||||||
|
})
|
||||||
|
}, 300)
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
await poll()
|
||||||
|
debugLog('stopped progress watch', {
|
||||||
|
logPath,
|
||||||
|
finalSize: lastLogSize,
|
||||||
|
textLength: installerOutput.length,
|
||||||
|
pollCount
|
||||||
|
})
|
||||||
|
return installerOutput
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const launchWindowsInstaller = async (
|
||||||
app,
|
app,
|
||||||
installerPath,
|
installerPath,
|
||||||
webContents,
|
webContents,
|
||||||
{ sendProgress, getInstallErrorMessage }
|
{ sendProgress, getInstallErrorMessage }
|
||||||
) => {
|
) => {
|
||||||
const { command, args } = buildWindowsInstallCommand(installerPath)
|
const resolvedPath = await prepareInstallerPath(installerPath)
|
||||||
|
const logPath = path.join(path.dirname(resolvedPath), 'install.log')
|
||||||
|
|
||||||
console.log('[app-update] launching installer:', {
|
debugLog('prepared installer', {
|
||||||
installerPath,
|
installerPath,
|
||||||
command,
|
resolvedPath,
|
||||||
args,
|
logPath
|
||||||
shellCommand: args.join(' '),
|
|
||||||
platform: process.platform
|
|
||||||
})
|
})
|
||||||
|
|
||||||
sendProgress(webContents, {
|
sendProgress(webContents, {
|
||||||
phase: 'installing',
|
phase: 'installing',
|
||||||
percent: 100,
|
percent: 0,
|
||||||
message: 'Installing update. Farm Control will restart automatically.'
|
message: 'Installing update...'
|
||||||
})
|
})
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
await fs.unlink(logPath).catch(() => {})
|
||||||
let installerOutput = ''
|
|
||||||
|
|
||||||
const installerProcess = spawn(command, args, {
|
// Allow file handles from the download/copy to settle before msiexec opens the MSI.
|
||||||
detached: true,
|
await sleep(2000)
|
||||||
|
|
||||||
|
const stopProgressWatch = startWindowsInstallerProgressWatch(
|
||||||
|
logPath,
|
||||||
|
webContents,
|
||||||
|
sendProgress
|
||||||
|
)
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let processOutput = ''
|
||||||
|
const startedAt = Date.now()
|
||||||
|
|
||||||
|
const installerArgs = [
|
||||||
|
'/i',
|
||||||
|
resolvedPath,
|
||||||
|
'/qn',
|
||||||
|
'/norestart',
|
||||||
|
'/L*v!',
|
||||||
|
logPath
|
||||||
|
]
|
||||||
|
|
||||||
|
debugLog('spawning msiexec', {
|
||||||
|
args: installerArgs,
|
||||||
|
elapsedMs: Date.now() - startedAt
|
||||||
|
})
|
||||||
|
|
||||||
|
const installerProcess = spawn('msiexec.exe', installerArgs, {
|
||||||
stdio: ['ignore', 'pipe', 'pipe'],
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
windowsHide: true
|
windowsHide: true
|
||||||
})
|
})
|
||||||
|
|
||||||
installerProcess.stdout?.on('data', (data) => {
|
installerProcess.stdout?.on('data', (data) => {
|
||||||
const text = data.toString()
|
const text = data.toString('utf16le')
|
||||||
installerOutput += text
|
processOutput += text
|
||||||
console.log('[app-update] installer stdout:', text)
|
debugLog('msiexec stdout chunk', {
|
||||||
|
length: text.length,
|
||||||
|
preview: text.slice(0, 200)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
installerProcess.stderr?.on('data', (data) => {
|
installerProcess.stderr?.on('data', (data) => {
|
||||||
const text = data.toString()
|
const text = data.toString('utf16le')
|
||||||
installerOutput += text
|
processOutput += text
|
||||||
console.error('[app-update] installer stderr:', text)
|
debugLog('msiexec stderr chunk', {
|
||||||
|
length: text.length,
|
||||||
|
preview: text.slice(0, 200)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
installerProcess.on('spawn', () => {
|
installerProcess.on('spawn', () => {
|
||||||
console.log('[app-update] installer spawned, pid:', installerProcess.pid)
|
debugLog('msiexec spawned', {
|
||||||
|
pid: installerProcess.pid,
|
||||||
|
elapsedMs: Date.now() - startedAt
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
installerProcess.on('error', (error) => {
|
installerProcess.on('error', async (error) => {
|
||||||
console.error('[app-update] installer spawn error:', error)
|
console.error(`${DEBUG_PREFIX} installer spawn error:`, error)
|
||||||
|
const watchedOutput = await stopProgressWatch()
|
||||||
|
|
||||||
|
debugLog('installer spawn failed', {
|
||||||
|
watchedOutputLength: watchedOutput.length,
|
||||||
|
processOutputLength: processOutput.length
|
||||||
|
})
|
||||||
|
|
||||||
|
const message = error?.message || 'Failed to start update installer.'
|
||||||
sendProgress(webContents, {
|
sendProgress(webContents, {
|
||||||
phase: 'error',
|
phase: 'error',
|
||||||
percent: null,
|
percent: null,
|
||||||
message: error?.message || 'Failed to start update installer.'
|
message
|
||||||
})
|
})
|
||||||
reject(error)
|
reject(error)
|
||||||
})
|
})
|
||||||
|
|
||||||
installerProcess.on('exit', (code, signal) => {
|
installerProcess.on('exit', async (code, signal) => {
|
||||||
console.log('[app-update] installer exited:', {
|
const watchedOutput = await stopProgressWatch()
|
||||||
|
const output = watchedOutput || processOutput
|
||||||
|
const finalParse = parseWindowsInstallerProgress(output)
|
||||||
|
|
||||||
|
debugLog('msiexec exited', {
|
||||||
code,
|
code,
|
||||||
signal,
|
signal,
|
||||||
output: installerOutput
|
elapsedMs: Date.now() - startedAt,
|
||||||
|
watchedOutputLength: watchedOutput.length,
|
||||||
|
processOutputLength: processOutput.length,
|
||||||
|
parsed: finalParse.stats,
|
||||||
|
outputPreview: output.slice(0, 500).replace(/\s+/g, ' ')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
debugLog('keeping install log', { logPath })
|
||||||
|
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
const message = getInstallErrorMessage(null, installerOutput)
|
const message = getInstallErrorMessage(null, output)
|
||||||
sendProgress(webContents, {
|
sendProgress(webContents, {
|
||||||
phase: 'error',
|
phase: 'error',
|
||||||
percent: null,
|
percent: null,
|
||||||
@ -86,14 +412,38 @@ export const launchWindowsInstaller = (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const succeeded =
|
||||||
|
isWindowsInstallSuccessful(output) ||
|
||||||
|
(code === 0 && !isWindowsInstallFailed(output))
|
||||||
|
|
||||||
|
debugLog('install success evaluation', {
|
||||||
|
succeeded,
|
||||||
|
isSuccessful: isWindowsInstallSuccessful(output),
|
||||||
|
isFailed: isWindowsInstallFailed(output),
|
||||||
|
exitCode: code
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!succeeded) {
|
||||||
|
const message = getInstallErrorMessage(null, output)
|
||||||
|
sendProgress(webContents, {
|
||||||
|
phase: 'error',
|
||||||
|
percent: null,
|
||||||
|
message
|
||||||
|
})
|
||||||
|
reject(new Error(message))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { percent, message } = finalParse
|
||||||
|
|
||||||
sendProgress(webContents, {
|
sendProgress(webContents, {
|
||||||
phase: 'installing',
|
phase: 'installing',
|
||||||
percent: 100,
|
percent: percent ?? 100,
|
||||||
message: 'Installation complete. Restarting Farm Control...'
|
message: message || 'Installation complete. Restarting Farm Control...'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
debugLog('installer completed successfully')
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
|
|
||||||
installerProcess.unref()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const Invoices = () => {
|
|||||||
type='invoice'
|
type='invoice'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const Payments = () => {
|
|||||||
type='payment'
|
type='payment'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
@ -105,4 +106,3 @@ const Payments = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default Payments
|
export default Payments
|
||||||
|
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const TaxRecords = () => {
|
|||||||
type='taxRecord'
|
type='taxRecord'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -86,6 +86,7 @@ const FilamentStocks = () => {
|
|||||||
type='filamentStock'
|
type='filamentStock'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const OrderItems = () => {
|
|||||||
type='orderItem'
|
type='orderItem'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -86,6 +86,7 @@ const PartStocks = () => {
|
|||||||
type='partStock'
|
type='partStock'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -87,6 +87,7 @@ const ProductStocks = () => {
|
|||||||
type='productStock'
|
type='productStock'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const PurchaseOrders = () => {
|
|||||||
type='purchaseOrder'
|
type='purchaseOrder'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const Shipments = () => {
|
|||||||
type='shipment'
|
type='shipment'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -86,6 +86,7 @@ const StockAudits = () => {
|
|||||||
type='stockAudit'
|
type='stockAudit'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -69,6 +69,7 @@ const StockEvents = () => {
|
|||||||
type='stockEvent'
|
type='stockEvent'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -84,6 +84,7 @@ const StockLocations = () => {
|
|||||||
type='stockLocation'
|
type='stockLocation'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -84,6 +84,7 @@ const StockTransfers = () => {
|
|||||||
type='stockTransfer'
|
type='stockTransfer'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const AppPasswords = () => {
|
|||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
visibleColumns={columnVisibility}
|
visibleColumns={columnVisibility}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -24,25 +24,12 @@ const NewAppPassword = ({ onOk, reset, defaultValues = {} }) => {
|
|||||||
column={1}
|
column={1}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
|
labelWidth={75}
|
||||||
required={true}
|
required={true}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'Optional',
|
|
||||||
key: 'optional',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='appPassword'
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing={true}
|
|
||||||
required={false}
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'Summary',
|
title: 'Summary',
|
||||||
key: 'summary',
|
key: 'summary',
|
||||||
@ -59,6 +46,7 @@ const NewAppPassword = ({ onOk, reset, defaultValues = {} }) => {
|
|||||||
secret: false
|
secret: false
|
||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
|
labelWidth={70}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -72,11 +72,17 @@ const getDownloadStageStatus = (phase, isError) => {
|
|||||||
return 'pending'
|
return 'pending'
|
||||||
}
|
}
|
||||||
|
|
||||||
const isInstallComplete = (phase, message) =>
|
const isInstallComplete = (phase, message) => {
|
||||||
phase === 'installing' &&
|
if (phase !== 'installing') return false
|
||||||
String(message || '')
|
|
||||||
.toLowerCase()
|
const normalized = String(message || '').toLowerCase()
|
||||||
.includes('complete')
|
|
||||||
|
return (
|
||||||
|
normalized.includes('complete') ||
|
||||||
|
normalized.includes('successful') ||
|
||||||
|
normalized.includes('restarting')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const getInstallStageStatus = (phase, isError, message) => {
|
const getInstallStageStatus = (phase, isError, message) => {
|
||||||
if (isError && ['downloaded', 'installing'].includes(phase)) return 'error'
|
if (isError && ['downloaded', 'installing'].includes(phase)) return 'error'
|
||||||
@ -106,7 +112,7 @@ const UpdateStage = ({ stage, status, percent, detail }) => {
|
|||||||
const resolvedStatus =
|
const resolvedStatus =
|
||||||
status !== 'error' && resolvedPercent === 100 ? 'complete' : status
|
status !== 'error' && resolvedPercent === 100 ? 'complete' : status
|
||||||
const color = getStageColor(resolvedStatus, token)
|
const color = getStageColor(resolvedStatus, token)
|
||||||
const showProgress = resolvedStatus === 'active'
|
const showProgress = resolvedStatus === 'active' && stage !== 'restart'
|
||||||
|
|
||||||
const StatusIcon =
|
const StatusIcon =
|
||||||
resolvedStatus === 'complete'
|
resolvedStatus === 'complete'
|
||||||
@ -173,8 +179,6 @@ const AppUpdateProgress = ({ progress, update, onClose }) => {
|
|||||||
|
|
||||||
const installDetail = installStatus === 'active' ? message : null
|
const installDetail = installStatus === 'active' ? message : null
|
||||||
|
|
||||||
const restartDetail = restartStatus === 'active' ? message : null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex vertical gap='middle'>
|
<Flex vertical gap='middle'>
|
||||||
<Text>
|
<Text>
|
||||||
@ -197,11 +201,7 @@ const AppUpdateProgress = ({ progress, update, onClose }) => {
|
|||||||
percent={installPercent}
|
percent={installPercent}
|
||||||
detail={installDetail}
|
detail={installDetail}
|
||||||
/>
|
/>
|
||||||
<UpdateStage
|
<UpdateStage stage='restart' status={restartStatus} />
|
||||||
stage='restart'
|
|
||||||
status={restartStatus}
|
|
||||||
detail={restartDetail}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -62,6 +62,7 @@ const AuditLogs = () => {
|
|||||||
visibleColumns={columnVisibility}
|
visibleColumns={columnVisibility}
|
||||||
type='auditLog'
|
type='auditLog'
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const CourierServices = () => {
|
|||||||
type='courierService'
|
type='courierService'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -26,6 +26,7 @@ const NewCourierService = ({ onOk, defaultValues }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={80}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -39,6 +40,7 @@ const NewCourierService = ({ onOk, defaultValues }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
|
labelWidth={120}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -59,6 +61,7 @@ const NewCourierService = ({ onOk, defaultValues }) => {
|
|||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={120}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,6 +79,7 @@ const Couriers = () => {
|
|||||||
type='courier'
|
type='courier'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -19,6 +19,7 @@ const NewCourier = ({ onOk, defaultValues }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={75}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -33,6 +34,7 @@ const NewCourier = ({ onOk, defaultValues }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={85}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -52,6 +54,7 @@ const NewCourier = ({ onOk, defaultValues }) => {
|
|||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={85}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const DocumentJobs = () => {
|
|||||||
type='documentJob'
|
type='documentJob'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -35,6 +35,7 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
|
|||||||
column={1}
|
column={1}
|
||||||
visibleProperties={{ name: false }}
|
visibleProperties={{ name: false }}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
|
labelWidth={115}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
|||||||
@ -79,6 +79,7 @@ const DocumentPrinters = () => {
|
|||||||
type='documentPrinter'
|
type='documentPrinter'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -78,6 +78,7 @@ const DocumentSizes = () => {
|
|||||||
type='documentSize'
|
type='documentSize'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -22,6 +22,7 @@ const NewDocumentSize = ({ onOk, defaultValues }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={120}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -39,6 +40,7 @@ const NewDocumentSize = ({ onOk, defaultValues }) => {
|
|||||||
createdAt: false,
|
createdAt: false,
|
||||||
updatedAt: false
|
updatedAt: false
|
||||||
}}
|
}}
|
||||||
|
labelWidth={120}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const DocumentTemplates = () => {
|
|||||||
type='documentTemplate'
|
type='documentTemplate'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -21,6 +21,7 @@ const NewDocumentTemplate = ({ onOk, defaultValues }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
|
labelWidth={130}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -84,6 +84,7 @@ const FilamentSkus = () => {
|
|||||||
type='filamentSku'
|
type='filamentSku'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -8,7 +8,11 @@ const NewFilamentSku = ({ onOk, reset, defaultValues }) => {
|
|||||||
<NewObjectForm
|
<NewObjectForm
|
||||||
type='filamentSku'
|
type='filamentSku'
|
||||||
reset={reset}
|
reset={reset}
|
||||||
defaultValues={defaultValues}
|
defaultValues={{
|
||||||
|
overrideCost: false,
|
||||||
|
color: '#ff0000',
|
||||||
|
...defaultValues
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
const steps = [
|
const steps = [
|
||||||
@ -19,7 +23,7 @@ const NewFilamentSku = ({ onOk, reset, defaultValues }) => {
|
|||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
type='filamentSku'
|
type='filamentSku'
|
||||||
column={1}
|
column={1}
|
||||||
labelWidth={70}
|
labelWidth={80}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
@ -29,27 +33,26 @@ const NewFilamentSku = ({ onOk, reset, defaultValues }) => {
|
|||||||
cost: false,
|
cost: false,
|
||||||
costWithTax: false,
|
costWithTax: false,
|
||||||
costTaxRate: false,
|
costTaxRate: false,
|
||||||
|
overrideCost: false,
|
||||||
vendor: false
|
vendor: false
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Color & Cost',
|
title: 'Cost',
|
||||||
key: 'colorCost',
|
key: 'cost',
|
||||||
content: (
|
content: (
|
||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
type='filamentSku'
|
type='filamentSku'
|
||||||
column={1}
|
column={1}
|
||||||
labelWidth={100}
|
labelWidth={120}
|
||||||
|
required={true}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
_id: false,
|
overrideCost: true,
|
||||||
createdAt: false,
|
cost: true,
|
||||||
updatedAt: false,
|
costTaxRate: true,
|
||||||
barcode: false,
|
costWithTax: true
|
||||||
filament: false,
|
|
||||||
name: false,
|
|
||||||
description: false
|
|
||||||
}}
|
}}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
@ -64,7 +67,7 @@ const NewFilamentSku = ({ onOk, reset, defaultValues }) => {
|
|||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
type='filamentSku'
|
type='filamentSku'
|
||||||
column={1}
|
column={1}
|
||||||
labelWidth={100}
|
labelWidth={110}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
barcode: true,
|
barcode: true,
|
||||||
description: true
|
description: true
|
||||||
@ -88,9 +91,9 @@ const NewFilamentSku = ({ onOk, reset, defaultValues }) => {
|
|||||||
_id: false,
|
_id: false,
|
||||||
_reference: false
|
_reference: false
|
||||||
}}
|
}}
|
||||||
labelWidth={100}
|
|
||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
|
labelWidth={120}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -86,6 +86,7 @@ const Filaments = () => {
|
|||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
visibleColumns={columnVisibility}
|
visibleColumns={columnVisibility}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -238,7 +238,7 @@ const FilamentInfo = () => {
|
|||||||
}}
|
}}
|
||||||
reset={newFilamentSkuOpen}
|
reset={newFilamentSkuOpen}
|
||||||
defaultValues={{
|
defaultValues={{
|
||||||
filament: filamentId ? { _id: filamentId } : undefined
|
filament: objectFormState?.objectData || undefined
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@ -18,7 +18,33 @@ const NewFilament = ({ onOk }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
|
labelWidth={120}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
visibleProperties={{
|
||||||
|
cost: false,
|
||||||
|
costTaxRate: false,
|
||||||
|
costWithTax: false
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Cost',
|
||||||
|
key: 'cost',
|
||||||
|
content: (
|
||||||
|
<ObjectInfo
|
||||||
|
type='filament'
|
||||||
|
column={1}
|
||||||
|
bordered={false}
|
||||||
|
isEditing={true}
|
||||||
|
required={true}
|
||||||
|
labelWidth={120}
|
||||||
|
objectData={objectData}
|
||||||
|
visibleProperties={{
|
||||||
|
cost: true,
|
||||||
|
costTaxRate: true,
|
||||||
|
costWithTax: true
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -32,6 +58,7 @@ const NewFilament = ({ onOk }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
|
labelWidth={90}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -50,6 +77,7 @@ const NewFilament = ({ onOk }) => {
|
|||||||
createdAt: false,
|
createdAt: false,
|
||||||
updatedAt: false
|
updatedAt: false
|
||||||
}}
|
}}
|
||||||
|
labelWidth={120}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -68,6 +68,7 @@ const Files = () => {
|
|||||||
type='file'
|
type='file'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -85,6 +85,7 @@ const Hosts = () => {
|
|||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
visibleColumns={columnVisibility}
|
visibleColumns={columnVisibility}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -83,6 +83,7 @@ const Materials = () => {
|
|||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
visibleColumns={columnVisibility}
|
visibleColumns={columnVisibility}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -18,6 +18,7 @@ const NewMaterial = ({ onOk }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
|
labelWidth={70}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -32,6 +33,7 @@ const NewMaterial = ({ onOk }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
|
labelWidth={62}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -51,6 +53,7 @@ const NewMaterial = ({ onOk }) => {
|
|||||||
updatedAt: false
|
updatedAt: false
|
||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
|
labelWidth={70}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const NoteTypes = () => {
|
|||||||
type='noteType'
|
type='noteType'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -23,6 +23,7 @@ const NewNoteType = ({ onOk }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
|
labelWidth={72}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -38,6 +39,7 @@ const NewNoteType = ({ onOk }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={65}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -57,6 +59,7 @@ const NewNoteType = ({ onOk }) => {
|
|||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={70}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,6 +83,7 @@ const PartSkus = () => {
|
|||||||
type='partSku'
|
type='partSku'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -85,6 +85,7 @@ const Parts = (filter) => {
|
|||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -26,6 +26,7 @@ const NewPart = ({ onOk, defaultValues }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={70}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
file: false,
|
file: false,
|
||||||
priceMode: false,
|
priceMode: false,
|
||||||
@ -51,6 +52,7 @@ const NewPart = ({ onOk, defaultValues }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={120}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
priceMode: true,
|
priceMode: true,
|
||||||
margin: true,
|
margin: true,
|
||||||
@ -74,6 +76,7 @@ const NewPart = ({ onOk, defaultValues }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
|
labelWidth={50}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -83,6 +83,7 @@ const ProductCategories = () => {
|
|||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
visibleColumns={columnVisibility}
|
visibleColumns={columnVisibility}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -19,6 +19,7 @@ const NewProductCategory = ({ onOk }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={70}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -38,6 +39,7 @@ const NewProductCategory = ({ onOk }) => {
|
|||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={70}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,6 +84,7 @@ const ProductSkus = () => {
|
|||||||
type='productSku'
|
type='productSku'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -23,7 +23,7 @@ const NewProductSku = ({ onOk, reset, defaultValues }) => {
|
|||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
type='productSku'
|
type='productSku'
|
||||||
column={1}
|
column={1}
|
||||||
labelWidth={70}
|
labelWidth={80}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
|
|||||||
@ -355,6 +355,7 @@ const Products = () => {
|
|||||||
type={'product'}
|
type={'product'}
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -79,6 +79,7 @@ const TaxRates = () => {
|
|||||||
type='taxRate'
|
type='taxRate'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -5,7 +5,10 @@ import WizardView from '../../common/WizardView'
|
|||||||
|
|
||||||
const NewTaxRate = ({ onOk, defaultValues }) => {
|
const NewTaxRate = ({ onOk, defaultValues }) => {
|
||||||
return (
|
return (
|
||||||
<NewObjectForm type={'taxRate'} defaultValues={{ ...defaultValues }}>
|
<NewObjectForm
|
||||||
|
type={'taxRate'}
|
||||||
|
defaultValues={{ active: true, ...defaultValues }}
|
||||||
|
>
|
||||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
@ -19,6 +22,7 @@ const NewTaxRate = ({ onOk, defaultValues }) => {
|
|||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
labelWidth={100}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -32,6 +36,7 @@ const NewTaxRate = ({ onOk, defaultValues }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
|
labelWidth={130}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -50,6 +55,7 @@ const NewTaxRate = ({ onOk, defaultValues }) => {
|
|||||||
createdAt: false,
|
createdAt: false,
|
||||||
updatedAt: false
|
updatedAt: false
|
||||||
}}
|
}}
|
||||||
|
labelWidth={130}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -79,6 +79,7 @@ const Vendors = () => {
|
|||||||
type='vendor'
|
type='vendor'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -21,6 +21,7 @@ const NewVendor = ({ onOk, defaultValues }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
|
labelWidth={80}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -35,6 +36,7 @@ const NewVendor = ({ onOk, defaultValues }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
|
labelWidth={85}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -54,6 +56,7 @@ const NewVendor = ({ onOk, defaultValues }) => {
|
|||||||
updatedAt: false
|
updatedAt: false
|
||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
|
labelWidth={80}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -82,6 +82,7 @@ const GCodeFiles = () => {
|
|||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
visibleColumns={columnVisibility}
|
visibleColumns={columnVisibility}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -86,6 +86,7 @@ const Jobs = () => {
|
|||||||
visibleColumns={columnVisibility}
|
visibleColumns={columnVisibility}
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -71,6 +71,7 @@ const SubJobs = () => {
|
|||||||
visibleColumns={columnVisibility}
|
visibleColumns={columnVisibility}
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -79,6 +79,7 @@ const Clients = () => {
|
|||||||
type='client'
|
type='client'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
@ -101,4 +102,3 @@ const Clients = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default Clients
|
export default Clients
|
||||||
|
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const Listings = () => {
|
|||||||
type='listing'
|
type='listing'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const Marketplaces = () => {
|
|||||||
type='marketplace'
|
type='marketplace'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -80,6 +80,7 @@ const SalesOrders = () => {
|
|||||||
type='salesOrder'
|
type='salesOrder'
|
||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
showFilterSidebar={showFilterSidebar}
|
showFilterSidebar={showFilterSidebar}
|
||||||
|
expandHeight={true}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
<Modal
|
||||||
@ -105,4 +106,3 @@ const SalesOrders = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default SalesOrders
|
export default SalesOrders
|
||||||
|
|
||||||
|
|||||||
@ -152,8 +152,8 @@ const DashboardNavigation = () => {
|
|||||||
style={{
|
style={{
|
||||||
fontSize: '46px',
|
fontSize: '46px',
|
||||||
height: '16px',
|
height: '16px',
|
||||||
marginLeft: '15px',
|
marginLeft: '13px',
|
||||||
marginRight: '8px'
|
marginRight: '0px'
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -225,7 +225,7 @@ const DashboardNavigation = () => {
|
|||||||
align='center'
|
align='center'
|
||||||
style={{ marginTop: '-2px', marginRight: '6px' }}
|
style={{ marginTop: '-2px', marginRight: '6px' }}
|
||||||
>
|
>
|
||||||
<Space>
|
<Space style={{ paddingTop: '2px', marginRight: '8px' }}>
|
||||||
<WebAppSwitcher />
|
<WebAppSwitcher />
|
||||||
<KeyboardShortcut
|
<KeyboardShortcut
|
||||||
shortcut='alt+q'
|
shortcut='alt+q'
|
||||||
|
|||||||
@ -39,7 +39,7 @@ const DashboardWindowButtons = () => {
|
|||||||
<div style={{ width: '80px' }} />
|
<div style={{ width: '80px' }} />
|
||||||
) : null
|
) : null
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: '95px' }}>
|
<div style={{ width: '95px', marginRight: '2.5px' }}>
|
||||||
<Flex style={{ position: 'relative', zIndex: 9999 }}>
|
<Flex style={{ position: 'relative', zIndex: 9999 }}>
|
||||||
{maximizeButton}
|
{maximizeButton}
|
||||||
{minimizeButton}
|
{minimizeButton}
|
||||||
|
|||||||
@ -101,7 +101,8 @@ const ObjectTable = forwardRef(
|
|||||||
masterFilter = {},
|
masterFilter = {},
|
||||||
size = 'middle',
|
size = 'middle',
|
||||||
onStateChange,
|
onStateChange,
|
||||||
showFilterSidebar = false
|
showFilterSidebar = false,
|
||||||
|
expandHeight = false
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
@ -728,6 +729,30 @@ const ObjectTable = forwardRef(
|
|||||||
setTableData(pages.flatMap((page) => page.items))
|
setTableData(pages.flatMap((page) => page.items))
|
||||||
}, [pages])
|
}, [pages])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!expandHeight || cards) return
|
||||||
|
|
||||||
|
const findAntTableBody = (element) => {
|
||||||
|
if (!element) return null
|
||||||
|
if (element.classList?.contains('ant-table-body')) return element
|
||||||
|
for (const child of element.children) {
|
||||||
|
const found = findAntTableBody(child)
|
||||||
|
if (found) return found
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = tableRef.current?.nativeElement ?? tableRef.current
|
||||||
|
const tableBody = findAntTableBody(root)
|
||||||
|
if (!tableBody) return
|
||||||
|
|
||||||
|
tableBody.style.minHeight = adjustedScrollHeight
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
tableBody.style.minHeight = ''
|
||||||
|
}
|
||||||
|
}, [expandHeight, adjustedScrollHeight, cards, loading, tableData])
|
||||||
|
|
||||||
// Add columns in the order specified by model.columns
|
// Add columns in the order specified by model.columns
|
||||||
model.columns.forEach((colName) => {
|
model.columns.forEach((colName) => {
|
||||||
const prop = modelProperties.find((p) => p.name === colName)
|
const prop = modelProperties.find((p) => p.name === colName)
|
||||||
@ -878,7 +903,10 @@ const ObjectTable = forwardRef(
|
|||||||
return (
|
return (
|
||||||
<Row
|
<Row
|
||||||
gutter={[16, 16]}
|
gutter={[16, 16]}
|
||||||
style={{ overflowY: 'auto', maxHeight: adjustedScrollHeight }}
|
style={{
|
||||||
|
overflowY: 'auto',
|
||||||
|
maxHeight: adjustedScrollHeight
|
||||||
|
}}
|
||||||
ref={cardsContainerRef}
|
ref={cardsContainerRef}
|
||||||
>
|
>
|
||||||
{tableData.map((record) => {
|
{tableData.map((record) => {
|
||||||
@ -1002,7 +1030,8 @@ ObjectTable.propTypes = {
|
|||||||
masterFilter: PropTypes.object,
|
masterFilter: PropTypes.object,
|
||||||
size: PropTypes.string,
|
size: PropTypes.string,
|
||||||
onStateChange: PropTypes.func,
|
onStateChange: PropTypes.func,
|
||||||
showFilterSidebar: PropTypes.bool
|
showFilterSidebar: PropTypes.bool,
|
||||||
|
expandHeight: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectTable
|
export default ObjectTable
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { useContext } from 'react'
|
import { useContext } from 'react'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { Button, Tooltip } from 'antd'
|
import { Button } from 'antd'
|
||||||
import { ElectronContext } from '../context/ElectronContext'
|
import { ElectronContext } from '../context/ElectronContext'
|
||||||
import OpenInAppIcon from '../../Icons/OpenInAppIcon'
|
import OpenInAppIcon from '../../Icons/OpenInAppIcon'
|
||||||
import OpenInBrowserIcon from '../../Icons/OpenInBrowserIcon'
|
import OpenInBrowserIcon from '../../Icons/OpenInBrowserIcon'
|
||||||
import config from '../../../config'
|
import config from '../../../config'
|
||||||
|
import KeyboardShortcut from './KeyboardShortcut'
|
||||||
|
|
||||||
const WebAppSwitcher = () => {
|
const WebAppSwitcher = () => {
|
||||||
const { isElectron, openExternalUrl } = useContext(ElectronContext)
|
const { isElectron, openExternalUrl } = useContext(ElectronContext)
|
||||||
@ -42,17 +43,14 @@ const WebAppSwitcher = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<KeyboardShortcut shortcut='alt+w' hint='ALT W' onTrigger={handleClick}>
|
||||||
title={isElectron ? 'Open in browser' : 'Open in app'}
|
|
||||||
arrow={false}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
icon={isElectron ? <OpenInBrowserIcon /> : <OpenInAppIcon />}
|
icon={isElectron ? <OpenInBrowserIcon /> : <OpenInAppIcon />}
|
||||||
type='text'
|
type='text'
|
||||||
style={{ marginTop: '4px' }}
|
style={{ marginTop: '4px' }}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</KeyboardShortcut>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,9 +30,11 @@ import IdDisplay from '../common/IdDisplay'
|
|||||||
import config from '../../../config'
|
import config from '../../../config'
|
||||||
import {
|
import {
|
||||||
getModelByName,
|
getModelByName,
|
||||||
getModelByPrefix
|
getModelByPrefix,
|
||||||
|
searchModelsByLabel
|
||||||
} from '../../../database/ObjectModels'
|
} from '../../../database/ObjectModels'
|
||||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
||||||
|
import MenuIcon from '../../Icons/MenuIcon'
|
||||||
import { ApiServerContext } from './ApiServerContext'
|
import { ApiServerContext } from './ApiServerContext'
|
||||||
import { AuthContext } from './AuthContext'
|
import { AuthContext } from './AuthContext'
|
||||||
import { ElectronContext } from './ElectronContext'
|
import { ElectronContext } from './ElectronContext'
|
||||||
@ -128,6 +130,22 @@ const SpotlightContent = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
|
const models = searchModelsByLabel(searchQuery.trim()).map((model) => ({
|
||||||
|
type: model.name,
|
||||||
|
name: model.labelPlural,
|
||||||
|
icon: model.icon,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
default: true,
|
||||||
|
row: true,
|
||||||
|
icon: MenuIcon,
|
||||||
|
url: () => {
|
||||||
|
return model.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}))
|
||||||
// If the query contains a prefix mode character, and the response is an object, wrap it in an array
|
// If the query contains a prefix mode character, and the response is an object, wrap it in an array
|
||||||
if (
|
if (
|
||||||
/[:?^]/.test(searchQuery) &&
|
/[:?^]/.test(searchQuery) &&
|
||||||
@ -135,9 +153,9 @@ const SpotlightContent = ({
|
|||||||
!Array.isArray(data) &&
|
!Array.isArray(data) &&
|
||||||
typeof data === 'object'
|
typeof data === 'object'
|
||||||
) {
|
) {
|
||||||
setListData([data])
|
setListData([...models, data])
|
||||||
} else {
|
} else {
|
||||||
setListData(data)
|
setListData([...models, ...data])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there's a pending query after this fetch completes
|
// Check if there's a pending query after this fetch completes
|
||||||
@ -290,7 +308,11 @@ const SpotlightContent = ({
|
|||||||
const item = listData[0]
|
const item = listData[0]
|
||||||
let type = item.type || item.objectType || inputPrefix?.type
|
let type = item.type || item.objectType || inputPrefix?.type
|
||||||
const model = getModelByName(type)
|
const model = getModelByName(type)
|
||||||
const defaultAction = model ? getDefaultRowAction(model) : null
|
const defaultAction = item?.type
|
||||||
|
? getDefaultRowAction(item)
|
||||||
|
: model
|
||||||
|
? getDefaultRowAction(model)
|
||||||
|
: null
|
||||||
if (defaultAction) {
|
if (defaultAction) {
|
||||||
triggerRowAction(defaultAction, item)
|
triggerRowAction(defaultAction, item)
|
||||||
}
|
}
|
||||||
@ -304,7 +326,11 @@ const SpotlightContent = ({
|
|||||||
const item = listData[index]
|
const item = listData[index]
|
||||||
let type = item.type || item.objectType || inputPrefix?.type
|
let type = item.type || item.objectType || inputPrefix?.type
|
||||||
const model = getModelByName(type)
|
const model = getModelByName(type)
|
||||||
const defaultAction = model ? getDefaultRowAction(model) : null
|
const defaultAction = item?.type
|
||||||
|
? getDefaultRowAction(item)
|
||||||
|
: model
|
||||||
|
? getDefaultRowAction(model)
|
||||||
|
: null
|
||||||
if (defaultAction) {
|
if (defaultAction) {
|
||||||
triggerRowAction(defaultAction, item)
|
triggerRowAction(defaultAction, item)
|
||||||
}
|
}
|
||||||
@ -447,10 +473,12 @@ const SpotlightContent = ({
|
|||||||
<List
|
<List
|
||||||
dataSource={listData}
|
dataSource={listData}
|
||||||
renderItem={(item, index) => {
|
renderItem={(item, index) => {
|
||||||
let type = item.objectType || inputPrefix?.type
|
let type = item.objectType || inputPrefix?.type || item?.type
|
||||||
const model = getModelByName(type)
|
const model = getModelByName(type)
|
||||||
const Icon = model?.icon
|
const Icon = model?.icon
|
||||||
const rowActions = getRowActions(model)
|
const rowActions = item?.type
|
||||||
|
? item.actions
|
||||||
|
: getRowActions(model)
|
||||||
let shortcutText = ''
|
let shortcutText = ''
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
shortcutText = 'ENTER'
|
shortcutText = 'ENTER'
|
||||||
@ -492,7 +520,9 @@ const SpotlightContent = ({
|
|||||||
showId={false}
|
showId={false}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<IdDisplay id={item._id} type={type} longId={false} />
|
{item?._id ? (
|
||||||
|
<IdDisplay id={item._id} type={type} longId={false} />
|
||||||
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex gap={'small'}>
|
<Flex gap={'small'}>
|
||||||
{rowActions
|
{rowActions
|
||||||
|
|||||||
@ -213,6 +213,14 @@ export function getModelByPrefix(prefix) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function searchModelsByLabel(label) {
|
||||||
|
return objectModels.filter(
|
||||||
|
(meta) =>
|
||||||
|
meta.label.toLowerCase().includes(label.toLowerCase()) ||
|
||||||
|
meta.labelPlural.toLowerCase().includes(label.toLowerCase())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Utility function to get nested object values
|
// Utility function to get nested object values
|
||||||
export const getPropertyValue = (obj, path) => {
|
export const getPropertyValue = (obj, path) => {
|
||||||
if (!obj || !path) return undefined
|
if (!obj || !path) return undefined
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import LockIcon from '../../components/Icons/LockIcon'
|
|||||||
export const AppPassword = {
|
export const AppPassword = {
|
||||||
name: 'appPassword',
|
name: 'appPassword',
|
||||||
label: 'App Password',
|
label: 'App Password',
|
||||||
|
labelPlural: 'App Passwords',
|
||||||
|
url: '/dashboard/management/apppasswords',
|
||||||
prefix: 'APP',
|
prefix: 'APP',
|
||||||
icon: AppPasswordIcon,
|
icon: AppPasswordIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -3,6 +3,8 @@ import AuditLogIcon from '../../components/Icons/AuditLogIcon'
|
|||||||
export const AuditLog = {
|
export const AuditLog = {
|
||||||
name: 'auditLog',
|
name: 'auditLog',
|
||||||
label: 'Audit Log',
|
label: 'Audit Log',
|
||||||
|
labelPlural: 'Audit Logs',
|
||||||
|
url: '/dashboard/management/auditlogs',
|
||||||
prefix: 'ADL',
|
prefix: 'ADL',
|
||||||
icon: AuditLogIcon,
|
icon: AuditLogIcon,
|
||||||
actions: [],
|
actions: [],
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const Client = {
|
export const Client = {
|
||||||
name: 'client',
|
name: 'client',
|
||||||
label: 'Client',
|
label: 'Client',
|
||||||
|
labelPlural: 'Clients',
|
||||||
|
url: '/dashboard/sales/clients',
|
||||||
prefix: 'CLI',
|
prefix: 'CLI',
|
||||||
icon: ClientIcon,
|
icon: ClientIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const Courier = {
|
export const Courier = {
|
||||||
name: 'courier',
|
name: 'courier',
|
||||||
label: 'Courier',
|
label: 'Courier',
|
||||||
|
labelPlural: 'Couriers',
|
||||||
|
url: '/dashboard/management/couriers',
|
||||||
prefix: 'COR',
|
prefix: 'COR',
|
||||||
icon: CourierIcon,
|
icon: CourierIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const CourierService = {
|
export const CourierService = {
|
||||||
name: 'courierService',
|
name: 'courierService',
|
||||||
label: 'Courier Service',
|
label: 'Courier Service',
|
||||||
|
labelPlural: 'Courier Services',
|
||||||
|
url: '/dashboard/management/courierservices',
|
||||||
prefix: 'COS',
|
prefix: 'COS',
|
||||||
icon: CourierServiceIcon,
|
icon: CourierServiceIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import dayjs from 'dayjs'
|
|||||||
export const DocumentJob = {
|
export const DocumentJob = {
|
||||||
name: 'documentJob',
|
name: 'documentJob',
|
||||||
label: 'Document Job',
|
label: 'Document Job',
|
||||||
|
labelPlural: 'Document Jobs',
|
||||||
|
url: '/dashboard/management/documentjobs',
|
||||||
prefix: 'DJB',
|
prefix: 'DJB',
|
||||||
icon: DocumentJobIcon,
|
icon: DocumentJobIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import XMarkIcon from '../../components/Icons/XMarkIcon'
|
|||||||
export const DocumentPrinter = {
|
export const DocumentPrinter = {
|
||||||
name: 'documentPrinter',
|
name: 'documentPrinter',
|
||||||
label: 'Document Printer',
|
label: 'Document Printer',
|
||||||
|
labelPlural: 'Document Printers',
|
||||||
|
url: '/dashboard/management/documentprinters',
|
||||||
prefix: 'DPR',
|
prefix: 'DPR',
|
||||||
icon: DocumentPrinterIcon,
|
icon: DocumentPrinterIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import DocumentSizeIcon from '../../components/Icons/DocumentSizeIcon'
|
|||||||
export const DocumentSize = {
|
export const DocumentSize = {
|
||||||
name: 'documentSize',
|
name: 'documentSize',
|
||||||
label: 'Document Size',
|
label: 'Document Size',
|
||||||
|
labelPlural: 'Document Sizes',
|
||||||
|
url: '/dashboard/management/documentsizes',
|
||||||
prefix: 'DSZ',
|
prefix: 'DSZ',
|
||||||
icon: DocumentSizeIcon,
|
icon: DocumentSizeIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import DocumentTemplateIcon from '../../components/Icons/DocumentTemplateIcon'
|
|||||||
export const DocumentTemplate = {
|
export const DocumentTemplate = {
|
||||||
name: 'documentTemplate',
|
name: 'documentTemplate',
|
||||||
label: 'Document Template',
|
label: 'Document Template',
|
||||||
|
labelPlural: 'Document Templates',
|
||||||
|
url: '/dashboard/management/documenttemplates',
|
||||||
prefix: 'DTP',
|
prefix: 'DTP',
|
||||||
icon: DocumentTemplateIcon,
|
icon: DocumentTemplateIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const Filament = {
|
export const Filament = {
|
||||||
name: 'filament',
|
name: 'filament',
|
||||||
label: 'Filament',
|
label: 'Filament',
|
||||||
|
labelPlural: 'Filaments',
|
||||||
|
url: '/dashboard/management/filaments',
|
||||||
prefix: 'FIL',
|
prefix: 'FIL',
|
||||||
icon: FilamentIcon,
|
icon: FilamentIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -88,7 +90,14 @@ export const Filament = {
|
|||||||
'updatedAt'
|
'updatedAt'
|
||||||
],
|
],
|
||||||
filters: ['_id', 'name', 'material', 'cost', 'costWithTax'],
|
filters: ['_id', 'name', 'material', 'cost', 'costWithTax'],
|
||||||
sorters: ['name', 'createdAt', 'material', 'cost', 'costWithTax', 'updatedAt'],
|
sorters: [
|
||||||
|
'name',
|
||||||
|
'createdAt',
|
||||||
|
'material',
|
||||||
|
'cost',
|
||||||
|
'costWithTax',
|
||||||
|
'updatedAt'
|
||||||
|
],
|
||||||
group: ['diameter', 'material'],
|
group: ['diameter', 'material'],
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
@ -168,7 +177,7 @@ export const Filament = {
|
|||||||
{
|
{
|
||||||
name: 'cost',
|
name: 'cost',
|
||||||
label: 'Cost',
|
label: 'Cost',
|
||||||
required: false,
|
required: true,
|
||||||
columnWidth: 100,
|
columnWidth: 100,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
prefix: '£',
|
prefix: '£',
|
||||||
@ -178,7 +187,7 @@ export const Filament = {
|
|||||||
{
|
{
|
||||||
name: 'costWithTax',
|
name: 'costWithTax',
|
||||||
label: 'Cost w/ Tax',
|
label: 'Cost w/ Tax',
|
||||||
required: false,
|
required: true,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
prefix: '£',
|
prefix: '£',
|
||||||
@ -201,7 +210,7 @@ export const Filament = {
|
|||||||
{
|
{
|
||||||
name: 'costTaxRate',
|
name: 'costTaxRate',
|
||||||
label: 'Cost Tax Rate',
|
label: 'Cost Tax Rate',
|
||||||
required: false,
|
required: true,
|
||||||
type: 'object',
|
type: 'object',
|
||||||
objectType: 'taxRate',
|
objectType: 'taxRate',
|
||||||
showHyperlink: true,
|
showHyperlink: true,
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const FilamentSku = {
|
export const FilamentSku = {
|
||||||
name: 'filamentSku',
|
name: 'filamentSku',
|
||||||
label: 'Filament SKU',
|
label: 'Filament SKU',
|
||||||
|
labelPlural: 'Filament SKUs',
|
||||||
|
url: '/dashboard/management/filamentskus',
|
||||||
prefix: 'FSU',
|
prefix: 'FSU',
|
||||||
icon: FilamentSkuIcon,
|
icon: FilamentSkuIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -61,7 +63,6 @@ export const FilamentSku = {
|
|||||||
`/dashboard/management/filamentskus/info?filamentSkuId=${_id}&action=delete`
|
`/dashboard/management/filamentskus/info?filamentSkuId=${_id}&action=delete`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
url: (id) => `/dashboard/management/filamentskus/info?filamentSkuId=${id}`,
|
|
||||||
columns: [
|
columns: [
|
||||||
'_reference',
|
'_reference',
|
||||||
'name',
|
'name',
|
||||||
@ -170,36 +171,42 @@ export const FilamentSku = {
|
|||||||
{
|
{
|
||||||
name: 'overrideCost',
|
name: 'overrideCost',
|
||||||
label: 'Override Cost',
|
label: 'Override Cost',
|
||||||
required: false,
|
required: true,
|
||||||
type: 'bool',
|
type: 'bool',
|
||||||
value: (objectData) => objectData?.overrideCost ?? false,
|
|
||||||
columnWidth: 150
|
columnWidth: 150
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cost',
|
name: 'cost',
|
||||||
label: 'Cost',
|
label: 'Cost',
|
||||||
required: false,
|
required: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
prefix: '£',
|
prefix: '£',
|
||||||
min: 0,
|
min: 0,
|
||||||
step: 0.01,
|
step: 0.01,
|
||||||
disabled: (objectData) => !objectData?.overrideCost,
|
visible: (objectData) => {
|
||||||
|
return objectData?.overrideCost
|
||||||
|
},
|
||||||
value: (objectData) =>
|
value: (objectData) =>
|
||||||
objectData?.overrideCost ? objectData?.cost : undefined,
|
objectData?.overrideCost
|
||||||
|
? objectData?.cost
|
||||||
|
: objectData?.filament?.cost,
|
||||||
columnWidth: 100
|
columnWidth: 100
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'costWithTax',
|
name: 'costWithTax',
|
||||||
label: 'Cost w/ Tax',
|
label: 'Cost w/ Tax',
|
||||||
required: false,
|
required: true,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
prefix: '£',
|
prefix: '£',
|
||||||
min: 0,
|
min: 0,
|
||||||
step: 0.01,
|
step: 0.01,
|
||||||
disabled: (objectData) => !objectData?.overrideCost,
|
visible: (objectData) => {
|
||||||
|
return objectData?.overrideCost
|
||||||
|
},
|
||||||
value: (objectData) => {
|
value: (objectData) => {
|
||||||
if (!objectData?.overrideCost) return undefined
|
if (!objectData?.overrideCost)
|
||||||
|
return objectData?.filament?.costWithTax || undefined
|
||||||
const cost = objectData?.cost
|
const cost = objectData?.cost
|
||||||
const taxRate = objectData?.costTaxRate
|
const taxRate = objectData?.costTaxRate
|
||||||
if (!cost) return 0
|
if (!cost) return 0
|
||||||
@ -215,13 +222,17 @@ export const FilamentSku = {
|
|||||||
{
|
{
|
||||||
name: 'costTaxRate',
|
name: 'costTaxRate',
|
||||||
label: 'Cost Tax Rate',
|
label: 'Cost Tax Rate',
|
||||||
required: false,
|
required: true,
|
||||||
type: 'object',
|
type: 'object',
|
||||||
objectType: 'taxRate',
|
objectType: 'taxRate',
|
||||||
showHyperlink: true,
|
showHyperlink: true,
|
||||||
disabled: (objectData) => !objectData?.overrideCost,
|
visible: (objectData) => {
|
||||||
|
return objectData?.overrideCost
|
||||||
|
},
|
||||||
value: (objectData) =>
|
value: (objectData) =>
|
||||||
objectData?.overrideCost ? objectData?.costTaxRate : undefined,
|
objectData?.overrideCost
|
||||||
|
? objectData?.costTaxRate
|
||||||
|
: objectData?.filament?.costTaxRate,
|
||||||
columnWidth: 150
|
columnWidth: 150
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
|||||||
export const FilamentStock = {
|
export const FilamentStock = {
|
||||||
name: 'filamentStock',
|
name: 'filamentStock',
|
||||||
label: 'Filament Stock',
|
label: 'Filament Stock',
|
||||||
|
labelPlural: 'Filament Stocks',
|
||||||
|
url: '/dashboard/inventory/filamentstocks',
|
||||||
prefix: 'FLS',
|
prefix: 'FLS',
|
||||||
icon: FilamentStockIcon,
|
icon: FilamentStockIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const File = {
|
export const File = {
|
||||||
name: 'file',
|
name: 'file',
|
||||||
label: 'File',
|
label: 'File',
|
||||||
|
labelPlural: 'Files',
|
||||||
|
url: '/dashboard/management/files',
|
||||||
prefix: 'FLE',
|
prefix: 'FLE',
|
||||||
icon: FileIcon,
|
icon: FileIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -69,7 +71,6 @@ export const File = {
|
|||||||
`/dashboard/management/files/info?fileId=${_id}&action=delete`
|
`/dashboard/management/files/info?fileId=${_id}&action=delete`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
url: (id) => `/dashboard/management/files/info?fileId=${id}`,
|
|
||||||
columns: [
|
columns: [
|
||||||
'_reference',
|
'_reference',
|
||||||
'name',
|
'name',
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import EyeIcon from '../../components/Icons/EyeIcon'
|
|||||||
export const GCodeFile = {
|
export const GCodeFile = {
|
||||||
name: 'gcodeFile',
|
name: 'gcodeFile',
|
||||||
label: 'GCode File',
|
label: 'GCode File',
|
||||||
|
labelPlural: 'GCode Files',
|
||||||
|
url: '/dashboard/production/gcodefiles',
|
||||||
prefix: 'GCF',
|
prefix: 'GCF',
|
||||||
icon: GCodeFileIcon,
|
icon: GCodeFileIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import OTPIcon from '../../components/Icons/OTPIcon'
|
|||||||
export const Host = {
|
export const Host = {
|
||||||
name: 'host',
|
name: 'host',
|
||||||
label: 'Host',
|
label: 'Host',
|
||||||
|
labelPlural: 'Hosts',
|
||||||
|
url: '/dashboard/management/hosts',
|
||||||
prefix: 'HST',
|
prefix: 'HST',
|
||||||
icon: HostIcon,
|
icon: HostIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
|||||||
export const Initial = {
|
export const Initial = {
|
||||||
name: 'initial',
|
name: 'initial',
|
||||||
label: 'Initial',
|
label: 'Initial',
|
||||||
|
labelPlural: 'Initials',
|
||||||
prefix: 'INT',
|
prefix: 'INT',
|
||||||
icon: QuestionCircleIcon,
|
icon: QuestionCircleIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -16,5 +17,4 @@ export const Initial = {
|
|||||||
url: (_id) => `/dashboard/management/initials/info?initialId=${_id}`
|
url: (_id) => `/dashboard/management/initials/info?initialId=${_id}`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
url: () => `#`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import PlusIcon from '../../components/Icons/PlusIcon'
|
|||||||
export const Invoice = {
|
export const Invoice = {
|
||||||
name: 'invoice',
|
name: 'invoice',
|
||||||
label: 'Invoice',
|
label: 'Invoice',
|
||||||
|
labelPlural: 'Invoices',
|
||||||
|
url: '/dashboard/finance/invoices',
|
||||||
prefix: 'INV',
|
prefix: 'INV',
|
||||||
icon: InvoiceIcon,
|
icon: InvoiceIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import dayjs from 'dayjs'
|
|||||||
export const Job = {
|
export const Job = {
|
||||||
name: 'job',
|
name: 'job',
|
||||||
label: 'Job',
|
label: 'Job',
|
||||||
|
labelPlural: 'Jobs',
|
||||||
|
url: '/dashboard/production/jobs',
|
||||||
prefix: 'JOB',
|
prefix: 'JOB',
|
||||||
icon: JobIcon,
|
icon: JobIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import PlusIcon from '../../components/Icons/PlusIcon'
|
|||||||
export const Listing = {
|
export const Listing = {
|
||||||
name: 'listing',
|
name: 'listing',
|
||||||
label: 'Listing',
|
label: 'Listing',
|
||||||
|
labelPlural: 'Listings',
|
||||||
|
url: '/dashboard/sales/listings',
|
||||||
prefix: 'LST',
|
prefix: 'LST',
|
||||||
icon: ListingIcon,
|
icon: ListingIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const ListingVarient = {
|
export const ListingVarient = {
|
||||||
name: 'listingVarient',
|
name: 'listingVarient',
|
||||||
label: 'Listing Varient',
|
label: 'Listing Varient',
|
||||||
|
labelPlural: 'Listing Varients',
|
||||||
prefix: 'LVR',
|
prefix: 'LVR',
|
||||||
icon: ListingVarientIcon,
|
icon: ListingVarientIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const Marketplace = {
|
export const Marketplace = {
|
||||||
name: 'marketplace',
|
name: 'marketplace',
|
||||||
label: 'Marketplace',
|
label: 'Marketplace',
|
||||||
|
labelPlural: 'Marketplaces',
|
||||||
|
url: '/dashboard/sales/marketplaces',
|
||||||
prefix: 'MKT',
|
prefix: 'MKT',
|
||||||
icon: MarketplaceIcon,
|
icon: MarketplaceIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const Material = {
|
export const Material = {
|
||||||
name: 'material',
|
name: 'material',
|
||||||
label: 'Material',
|
label: 'Material',
|
||||||
|
labelPlural: 'Materials',
|
||||||
|
url: '/dashboard/management/materials',
|
||||||
prefix: 'MAT',
|
prefix: 'MAT',
|
||||||
icon: MaterialIcon,
|
icon: MaterialIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -60,7 +62,6 @@ export const Material = {
|
|||||||
`/dashboard/management/materials/info?materialId=${_id}&action=delete`
|
`/dashboard/management/materials/info?materialId=${_id}&action=delete`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
url: (id) => `/dashboard/management/materials/info?materialId=${id}`,
|
|
||||||
columns: ['_reference', 'name', 'tags', 'createdAt', 'updatedAt'],
|
columns: ['_reference', 'name', 'tags', 'createdAt', 'updatedAt'],
|
||||||
filters: ['_id', 'name', 'tags'],
|
filters: ['_id', 'name', 'tags'],
|
||||||
sorters: ['name', 'createdAt', 'updatedAt', '_id'],
|
sorters: ['name', 'createdAt', 'updatedAt', '_id'],
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
|||||||
export const Note = {
|
export const Note = {
|
||||||
name: 'note',
|
name: 'note',
|
||||||
label: 'Note',
|
label: 'Note',
|
||||||
|
labelPlural: 'Notes',
|
||||||
prefix: 'NTE',
|
prefix: 'NTE',
|
||||||
icon: NoteIcon,
|
icon: NoteIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import XMarkIcon from '../../components/Icons/XMarkIcon'
|
|||||||
export const NoteType = {
|
export const NoteType = {
|
||||||
name: 'noteType',
|
name: 'noteType',
|
||||||
label: 'Note Type',
|
label: 'Note Type',
|
||||||
|
labelPlural: 'Note Types',
|
||||||
|
url: '/dashboard/management/notetypes',
|
||||||
prefix: 'NTY',
|
prefix: 'NTY',
|
||||||
icon: NoteTypeIcon,
|
icon: NoteTypeIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import XMarkIcon from '../../components/Icons/XMarkIcon'
|
|||||||
export const OrderItem = {
|
export const OrderItem = {
|
||||||
name: 'orderItem',
|
name: 'orderItem',
|
||||||
label: 'Order Item',
|
label: 'Order Item',
|
||||||
|
labelPlural: 'Order Items',
|
||||||
|
url: '/dashboard/inventory/orderitems',
|
||||||
prefix: 'ODI',
|
prefix: 'ODI',
|
||||||
icon: OrderItemIcon,
|
icon: OrderItemIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import PlusIcon from '../../components/Icons/PlusIcon'
|
|||||||
export const Part = {
|
export const Part = {
|
||||||
name: 'part',
|
name: 'part',
|
||||||
label: 'Part',
|
label: 'Part',
|
||||||
|
labelPlural: 'Parts',
|
||||||
|
url: '/dashboard/management/parts',
|
||||||
prefix: 'PRT',
|
prefix: 'PRT',
|
||||||
icon: PartIcon,
|
icon: PartIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -74,8 +76,26 @@ export const Part = {
|
|||||||
'createdAt',
|
'createdAt',
|
||||||
'updatedAt'
|
'updatedAt'
|
||||||
],
|
],
|
||||||
filters: ['name', '_id', 'priceMode', 'cost', 'costWithTax', 'price', 'priceWithTax'],
|
filters: [
|
||||||
sorters: ['name', 'priceMode', 'cost', 'costWithTax', 'price', 'priceWithTax', 'createdAt', 'updatedAt', '_id'],
|
'name',
|
||||||
|
'_id',
|
||||||
|
'priceMode',
|
||||||
|
'cost',
|
||||||
|
'costWithTax',
|
||||||
|
'price',
|
||||||
|
'priceWithTax'
|
||||||
|
],
|
||||||
|
sorters: [
|
||||||
|
'name',
|
||||||
|
'priceMode',
|
||||||
|
'cost',
|
||||||
|
'costWithTax',
|
||||||
|
'price',
|
||||||
|
'priceWithTax',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
'_id'
|
||||||
|
],
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
name: '_id',
|
name: '_id',
|
||||||
@ -119,13 +139,6 @@ export const Part = {
|
|||||||
readOnly: true,
|
readOnly: true,
|
||||||
columnWidth: 175
|
columnWidth: 175
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'fileName',
|
|
||||||
label: 'File Name',
|
|
||||||
required: false,
|
|
||||||
type: 'text',
|
|
||||||
columnWidth: 200
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'file',
|
name: 'file',
|
||||||
label: 'File',
|
label: 'File',
|
||||||
@ -137,7 +150,7 @@ export const Part = {
|
|||||||
{
|
{
|
||||||
name: 'cost',
|
name: 'cost',
|
||||||
label: 'Cost',
|
label: 'Cost',
|
||||||
required: false,
|
required: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
prefix: '£',
|
prefix: '£',
|
||||||
min: 0,
|
min: 0,
|
||||||
@ -147,7 +160,7 @@ export const Part = {
|
|||||||
{
|
{
|
||||||
name: 'costWithTax',
|
name: 'costWithTax',
|
||||||
label: 'Cost w/ Tax',
|
label: 'Cost w/ Tax',
|
||||||
required: false,
|
required: true,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
prefix: '£',
|
prefix: '£',
|
||||||
@ -170,7 +183,7 @@ export const Part = {
|
|||||||
{
|
{
|
||||||
name: 'costTaxRate',
|
name: 'costTaxRate',
|
||||||
label: 'Cost Tax Rate',
|
label: 'Cost Tax Rate',
|
||||||
required: false,
|
required: true,
|
||||||
type: 'object',
|
type: 'object',
|
||||||
objectType: 'taxRate',
|
objectType: 'taxRate',
|
||||||
showHyperlink: true,
|
showHyperlink: true,
|
||||||
@ -179,14 +192,14 @@ export const Part = {
|
|||||||
{
|
{
|
||||||
name: 'priceMode',
|
name: 'priceMode',
|
||||||
label: 'Price Mode',
|
label: 'Price Mode',
|
||||||
required: false,
|
required: true,
|
||||||
columnWidth: 150,
|
columnWidth: 150,
|
||||||
type: 'priceMode'
|
type: 'priceMode'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'price',
|
name: 'price',
|
||||||
label: 'Price',
|
label: 'Price',
|
||||||
required: false,
|
required: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
prefix: '£',
|
prefix: '£',
|
||||||
min: 0,
|
min: 0,
|
||||||
@ -211,19 +224,31 @@ export const Part = {
|
|||||||
{
|
{
|
||||||
name: 'margin',
|
name: 'margin',
|
||||||
label: 'Margin',
|
label: 'Margin',
|
||||||
required: false,
|
required: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
disabled: (objectData) => objectData?.priceMode == 'amount',
|
readOnly: (objectData) => objectData?.priceMode == 'amount',
|
||||||
suffix: '%',
|
suffix: '%',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 100,
|
max: 100,
|
||||||
step: 0.01,
|
step: 0.01,
|
||||||
|
value: (objectData) => {
|
||||||
|
const priceMode = objectData?.priceMode
|
||||||
|
const cost = objectData?.cost
|
||||||
|
if (priceMode == 'amount') {
|
||||||
|
const price = objectData?.price
|
||||||
|
if (price != null && cost != null) {
|
||||||
|
return Number(((price / cost - 1) * 100).toFixed(2)) || undefined
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return objectData?.margin
|
||||||
|
},
|
||||||
columnWidth: 85
|
columnWidth: 85
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'priceWithTax',
|
name: 'priceWithTax',
|
||||||
label: 'Price w/ Tax',
|
label: 'Price w/ Tax',
|
||||||
required: false,
|
required: true,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
prefix: '£',
|
prefix: '£',
|
||||||
@ -255,7 +280,7 @@ export const Part = {
|
|||||||
{
|
{
|
||||||
name: 'priceTaxRate',
|
name: 'priceTaxRate',
|
||||||
label: 'Price Tax Rate',
|
label: 'Price Tax Rate',
|
||||||
required: false,
|
required: true,
|
||||||
type: 'object',
|
type: 'object',
|
||||||
objectType: 'taxRate',
|
objectType: 'taxRate',
|
||||||
showHyperlink: true,
|
showHyperlink: true,
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const PartSku = {
|
export const PartSku = {
|
||||||
name: 'partSku',
|
name: 'partSku',
|
||||||
label: 'Part SKU',
|
label: 'Part SKU',
|
||||||
|
labelPlural: 'Part SKUs',
|
||||||
|
url: '/dashboard/management/partskus',
|
||||||
prefix: 'PSU',
|
prefix: 'PSU',
|
||||||
icon: PartSkuIcon,
|
icon: PartSkuIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -60,7 +62,6 @@ export const PartSku = {
|
|||||||
`/dashboard/management/partskus/info?partSkuId=${_id}&action=delete`
|
`/dashboard/management/partskus/info?partSkuId=${_id}&action=delete`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
url: (id) => `/dashboard/management/partskus/info?partSkuId=${id}`,
|
|
||||||
columns: [
|
columns: [
|
||||||
'_reference',
|
'_reference',
|
||||||
'name',
|
'name',
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
|||||||
export const PartStock = {
|
export const PartStock = {
|
||||||
name: 'partStock',
|
name: 'partStock',
|
||||||
label: 'Part Stock',
|
label: 'Part Stock',
|
||||||
|
labelPlural: 'Part Stocks',
|
||||||
|
url: '/dashboard/inventory/partstocks',
|
||||||
prefix: 'PTS',
|
prefix: 'PTS',
|
||||||
icon: PartStockIcon,
|
icon: PartStockIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -16,7 +18,6 @@ export const PartStock = {
|
|||||||
url: (_id) => `/dashboard/inventory/partstocks/info?partStockId=${_id}`
|
url: (_id) => `/dashboard/inventory/partstocks/info?partStockId=${_id}`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
url: (id) => `/dashboard/inventory/partstocks/info?partStockId=${id}`,
|
|
||||||
filters: ['_id', 'partSku', 'startingQuantity', 'currentQuantity'],
|
filters: ['_id', 'partSku', 'startingQuantity', 'currentQuantity'],
|
||||||
sorters: ['partSku', 'startingQuantity', 'currentQuantity'],
|
sorters: ['partSku', 'startingQuantity', 'currentQuantity'],
|
||||||
columns: [
|
columns: [
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const Payment = {
|
export const Payment = {
|
||||||
name: 'payment',
|
name: 'payment',
|
||||||
label: 'Payment',
|
label: 'Payment',
|
||||||
|
labelPlural: 'Payments',
|
||||||
|
url: '/dashboard/finance/payments',
|
||||||
prefix: 'PAY',
|
prefix: 'PAY',
|
||||||
icon: PaymentIcon,
|
icon: PaymentIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -14,6 +14,8 @@ import JobIcon from '../../components/Icons/JobIcon'
|
|||||||
export const Printer = {
|
export const Printer = {
|
||||||
name: 'printer',
|
name: 'printer',
|
||||||
label: 'Printer',
|
label: 'Printer',
|
||||||
|
labelPlural: 'Printers',
|
||||||
|
url: '/dashboard/production/printers',
|
||||||
prefix: 'PRN',
|
prefix: 'PRN',
|
||||||
icon: PrinterIcon,
|
icon: PrinterIcon,
|
||||||
actions: [
|
actions: [
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import PlusIcon from '../../components/Icons/PlusIcon'
|
|||||||
export const Product = {
|
export const Product = {
|
||||||
name: 'product',
|
name: 'product',
|
||||||
label: 'Product',
|
label: 'Product',
|
||||||
|
labelPlural: 'Products',
|
||||||
|
url: '/dashboard/management/products',
|
||||||
prefix: 'PRD',
|
prefix: 'PRD',
|
||||||
icon: ProductIcon,
|
icon: ProductIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -254,11 +256,23 @@ export const Product = {
|
|||||||
label: 'Margin',
|
label: 'Margin',
|
||||||
required: true,
|
required: true,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
disabled: (objectData) => objectData?.priceMode == 'amount',
|
readOnly: (objectData) => objectData?.priceMode == 'amount',
|
||||||
suffix: '%',
|
suffix: '%',
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 100,
|
max: 100,
|
||||||
step: 0.01,
|
step: 0.01,
|
||||||
|
value: (objectData) => {
|
||||||
|
const priceMode = objectData?.priceMode
|
||||||
|
const cost = objectData?.cost
|
||||||
|
if (priceMode == 'amount') {
|
||||||
|
const price = objectData?.price
|
||||||
|
if (price != null && cost != null) {
|
||||||
|
return Number(((price / cost - 1) * 100).toFixed(2)) || undefined
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return objectData?.margin
|
||||||
|
},
|
||||||
columnWidth: 85
|
columnWidth: 85
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const ProductCategory = {
|
export const ProductCategory = {
|
||||||
name: 'productCategory',
|
name: 'productCategory',
|
||||||
label: 'Product Category',
|
label: 'Product Category',
|
||||||
|
labelPlural: 'Product Categories',
|
||||||
|
url: '/dashboard/management/productcategories',
|
||||||
prefix: 'PCG',
|
prefix: 'PCG',
|
||||||
endpoint: 'productcategories',
|
endpoint: 'productcategories',
|
||||||
icon: ProductCategoryIcon,
|
icon: ProductCategoryIcon,
|
||||||
@ -62,8 +64,6 @@ export const ProductCategory = {
|
|||||||
`/dashboard/management/productcategories/info?productCategoryId=${_id}&action=delete`
|
`/dashboard/management/productcategories/info?productCategoryId=${_id}&action=delete`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
url: (id) =>
|
|
||||||
`/dashboard/management/productcategories/info?productCategoryId=${id}`,
|
|
||||||
columns: ['_reference', 'name', 'createdAt', 'updatedAt'],
|
columns: ['_reference', 'name', 'createdAt', 'updatedAt'],
|
||||||
filters: ['_id', 'name'],
|
filters: ['_id', 'name'],
|
||||||
sorters: ['name', 'createdAt', 'updatedAt', '_id'],
|
sorters: ['name', 'createdAt', 'updatedAt', '_id'],
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const ProductSku = {
|
export const ProductSku = {
|
||||||
name: 'productSku',
|
name: 'productSku',
|
||||||
label: 'Product SKU',
|
label: 'Product SKU',
|
||||||
|
labelPlural: 'Product SKUs',
|
||||||
|
url: '/dashboard/management/productskus',
|
||||||
prefix: 'SKU',
|
prefix: 'SKU',
|
||||||
icon: ProductSkuIcon,
|
icon: ProductSkuIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -60,7 +62,6 @@ export const ProductSku = {
|
|||||||
`/dashboard/management/productskus/info?productSkuId=${_id}&action=delete`
|
`/dashboard/management/productskus/info?productSkuId=${_id}&action=delete`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
url: (id) => `/dashboard/management/productskus/info?productSkuId=${id}`,
|
|
||||||
columns: [
|
columns: [
|
||||||
'_reference',
|
'_reference',
|
||||||
'name',
|
'name',
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import BinIcon from '../../components/Icons/BinIcon'
|
|||||||
export const ProductStock = {
|
export const ProductStock = {
|
||||||
name: 'productStock',
|
name: 'productStock',
|
||||||
label: 'Product Stock',
|
label: 'Product Stock',
|
||||||
|
labelPlural: 'Product Stocks',
|
||||||
|
url: '/dashboard/inventory/productstocks',
|
||||||
prefix: 'PDS',
|
prefix: 'PDS',
|
||||||
icon: ProductStockIcon,
|
icon: ProductStockIcon,
|
||||||
actions: [
|
actions: [
|
||||||
@ -84,7 +86,6 @@ export const ProductStock = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
url: (id) => `/dashboard/inventory/productstocks/info?productStockId=${id}`,
|
|
||||||
filters: ['_id', 'productSku', 'currentQuantity'],
|
filters: ['_id', 'productSku', 'currentQuantity'],
|
||||||
sorters: ['productSku', 'currentQuantity'],
|
sorters: ['productSku', 'currentQuantity'],
|
||||||
columns: [
|
columns: [
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user