u
This commit is contained in:
@@ -2,8 +2,8 @@ import { http } from './http.js';
|
|||||||
|
|
||||||
export const posterminal = {
|
export const posterminal = {
|
||||||
/**
|
/**
|
||||||
* GET /api/posterminal → { posterminals: [{ id, name, token, pairingpin }] }
|
* GET /api/posterminal → { posterminals: [{ id, name, token, pairingpin, sort, enabled, clientversion, ... }] }
|
||||||
* List all terminals for the selected company.
|
* List all terminals for the selected company, sorted by sort ASC, id ASC.
|
||||||
*/
|
*/
|
||||||
// #region GET /api/posterminal — list
|
// #region GET /api/posterminal — list
|
||||||
list() {
|
list() {
|
||||||
@@ -13,24 +13,29 @@ export const posterminal = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* PUT /api/posterminal
|
* PUT /api/posterminal
|
||||||
* Body: { name: string }
|
* Body: { name: string, sort?: number, enabled?: boolean, rootCategoryId?: number|null, syncRootArticles?: boolean }
|
||||||
* Creates a new terminal (auto-generates 6-digit pin).
|
* Creates a new terminal (auto-generates 6-digit pin).
|
||||||
* → 200 { posterminal: { id, name, token, pairingpin } }
|
* → 200 { posterminal: { id, name, token, pairingpin, sort, enabled, clientversion, ... } }
|
||||||
*/
|
*/
|
||||||
// #region PUT /api/posterminal — create
|
// #region PUT /api/posterminal — create
|
||||||
create({ name }) {
|
create({ name, sort, enabled, rootCategoryId, syncRootArticles } = {}) {
|
||||||
|
const body = { name };
|
||||||
|
if (sort !== undefined) body.sort = Number(sort);
|
||||||
|
if (enabled !== undefined) body.enabled = Boolean(enabled);
|
||||||
|
if (rootCategoryId !== undefined) body.rootCategoryId = rootCategoryId != null ? Number(rootCategoryId) : null;
|
||||||
|
if (syncRootArticles !== undefined) body.syncRootArticles = Boolean(syncRootArticles);
|
||||||
return http('/api/posterminal', {
|
return http('/api/posterminal', {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: { name },
|
body,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PATCH /api/posterminal/:id
|
* PATCH /api/posterminal/:id
|
||||||
* Body: { name?: string, rootCategoryId?: number|null, syncRootArticles?: boolean, pairingpin?: string }
|
* Body: { name?: string, rootCategoryId?: number|null, syncRootArticles?: boolean, pairingpin?: string, sort?: number, enabled?: boolean }
|
||||||
* Update posterminal configuration.
|
* Update posterminal configuration.
|
||||||
* → 200 { posterminal }
|
* → 200 { posterminal: { id, name, token, pairingpin, sort, enabled, clientversion, ... } }
|
||||||
*/
|
*/
|
||||||
// #region PATCH /api/posterminal/:id — update
|
// #region PATCH /api/posterminal/:id — update
|
||||||
update(terminalId, patch) {
|
update(terminalId, patch) {
|
||||||
|
|||||||
@@ -159,6 +159,15 @@ const sortBrands = (brands) =>
|
|||||||
const sortGroups = (items) =>
|
const sortGroups = (items) =>
|
||||||
[...items].sort((a, b) => (Number(a.sort) || 0) - (Number(b.sort) || 0));
|
[...items].sort((a, b) => (Number(a.sort) || 0) - (Number(b.sort) || 0));
|
||||||
|
|
||||||
|
const sortTerminals = (terminals) => {
|
||||||
|
if (!Array.isArray(terminals)) return [];
|
||||||
|
return [...terminals].sort((a, b) => {
|
||||||
|
const sDiff = (Number(a.sort) || 0) - (Number(b.sort) || 0);
|
||||||
|
if (sDiff !== 0) return sDiff;
|
||||||
|
return (Number(a.id) || 0) - (Number(b.id) || 0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const sortAccounts = (items) =>
|
const sortAccounts = (items) =>
|
||||||
[...items].sort((a, b) => String(a.name ?? '').localeCompare(String(b.name ?? ''), 'de'));
|
[...items].sort((a, b) => String(a.name ?? '').localeCompare(String(b.name ?? ''), 'de'));
|
||||||
|
|
||||||
@@ -578,7 +587,7 @@ const actions = {
|
|||||||
inflightTerminals = (async () => {
|
inflightTerminals = (async () => {
|
||||||
try {
|
try {
|
||||||
const res = await posterminalApi.list();
|
const res = await posterminalApi.list();
|
||||||
const terminals = Array.isArray(res.posterminals) ? res.posterminals : [];
|
const terminals = sortTerminals(Array.isArray(res.posterminals) ? res.posterminals : []);
|
||||||
companyAutomat.setState({
|
companyAutomat.setState({
|
||||||
terminals,
|
terminals,
|
||||||
terminalsCompanyId: companyId,
|
terminalsCompanyId: companyId,
|
||||||
@@ -603,11 +612,15 @@ const actions = {
|
|||||||
return actions.loadTerminals();
|
return actions.loadTerminals();
|
||||||
},
|
},
|
||||||
|
|
||||||
async createTerminal({ name }) {
|
reloadTerminals() {
|
||||||
const res = await posterminalApi.create({ name });
|
return actions.loadTerminals();
|
||||||
|
},
|
||||||
|
|
||||||
|
async createTerminal(payload) {
|
||||||
|
const res = await posterminalApi.create(payload);
|
||||||
const terminal = res.posterminal;
|
const terminal = res.posterminal;
|
||||||
companyAutomat.setState({
|
companyAutomat.setState({
|
||||||
terminals: [...companyAutomat.state.terminals, terminal],
|
terminals: sortTerminals([...companyAutomat.state.terminals, terminal]),
|
||||||
});
|
});
|
||||||
return terminal;
|
return terminal;
|
||||||
},
|
},
|
||||||
@@ -620,8 +633,10 @@ const actions = {
|
|||||||
const res = await posterminalApi.update(terminalId, patch);
|
const res = await posterminalApi.update(terminalId, patch);
|
||||||
const updated = res.posterminal;
|
const updated = res.posterminal;
|
||||||
companyAutomat.setState({
|
companyAutomat.setState({
|
||||||
terminals: companyAutomat.state.terminals.map((t) =>
|
terminals: sortTerminals(
|
||||||
t.id === terminalId ? { ...t, ...updated } : t,
|
companyAutomat.state.terminals.map((t) =>
|
||||||
|
t.id === terminalId ? { ...t, ...updated } : t,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
return updated;
|
return updated;
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import TextField from '@mui/material/TextField';
|
|||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
|
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
||||||
|
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
|
||||||
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
|
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
|
||||||
import FolderIcon from '@mui/icons-material/Folder';
|
import FolderIcon from '@mui/icons-material/Folder';
|
||||||
import PointOfSaleIcon from '@mui/icons-material/PointOfSale';
|
import PointOfSaleIcon from '@mui/icons-material/PointOfSale';
|
||||||
@@ -56,6 +58,7 @@ export default class PosTerminalSettingsPanel extends Component {
|
|||||||
deletingId: null,
|
deletingId: null,
|
||||||
pickerTerminal: null,
|
pickerTerminal: null,
|
||||||
patchingTerminalId: null,
|
patchingTerminalId: null,
|
||||||
|
sortDrafts: {},
|
||||||
};
|
};
|
||||||
this.handleRetry = this.handleRetry.bind(this);
|
this.handleRetry = this.handleRetry.bind(this);
|
||||||
this.loadTerminals = this.loadTerminals.bind(this);
|
this.loadTerminals = this.loadTerminals.bind(this);
|
||||||
@@ -63,6 +66,10 @@ export default class PosTerminalSettingsPanel extends Component {
|
|||||||
this.handleDeleteTerminal = this.handleDeleteTerminal.bind(this);
|
this.handleDeleteTerminal = this.handleDeleteTerminal.bind(this);
|
||||||
this.handleSetRootCategory = this.handleSetRootCategory.bind(this);
|
this.handleSetRootCategory = this.handleSetRootCategory.bind(this);
|
||||||
this.handleToggleSyncRootArticles = this.handleToggleSyncRootArticles.bind(this);
|
this.handleToggleSyncRootArticles = this.handleToggleSyncRootArticles.bind(this);
|
||||||
|
this.handleToggleEnabled = this.handleToggleEnabled.bind(this);
|
||||||
|
this.handleSortDraftChange = this.handleSortDraftChange.bind(this);
|
||||||
|
this.handleSortSubmit = this.handleSortSubmit.bind(this);
|
||||||
|
this.handleMoveTerminal = this.handleMoveTerminal.bind(this);
|
||||||
this.getCategoryLabel = this.getCategoryLabel.bind(this);
|
this.getCategoryLabel = this.getCategoryLabel.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,13 +111,20 @@ export default class PosTerminalSettingsPanel extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleCreateTerminal() {
|
async handleCreateTerminal() {
|
||||||
const { newTerminalName, enableAllForNew } = this.state;
|
const { newTerminalName, enableAllForNew, terminals } = this.state;
|
||||||
const trimmed = newTerminalName.trim();
|
const trimmed = newTerminalName.trim();
|
||||||
if (!trimmed) return;
|
if (!trimmed) return;
|
||||||
|
|
||||||
this.setState({ creatingTerminal: true });
|
this.setState({ creatingTerminal: true });
|
||||||
try {
|
try {
|
||||||
const created = await companyAutomat.actions.createTerminal({ name: trimmed });
|
const nextSort = terminals.length > 0
|
||||||
|
? Math.max(...terminals.map((t) => Number(t.sort) || 0)) + 1
|
||||||
|
: 0;
|
||||||
|
const created = await companyAutomat.actions.createTerminal({
|
||||||
|
name: trimmed,
|
||||||
|
sort: nextSort,
|
||||||
|
enabled: true,
|
||||||
|
});
|
||||||
|
|
||||||
if (enableAllForNew && created?.id != null) {
|
if (enableAllForNew && created?.id != null) {
|
||||||
await companyAutomat.actions.enableAllTerminalCategories({ terminalId: created.id });
|
await companyAutomat.actions.enableAllTerminalCategories({ terminalId: created.id });
|
||||||
@@ -126,6 +140,84 @@ export default class PosTerminalSettingsPanel extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleToggleEnabled(terminal, nextValue) {
|
||||||
|
this.setState({ patchingTerminalId: terminal.id });
|
||||||
|
try {
|
||||||
|
await companyAutomat.actions.updateTerminal(terminal.id, {
|
||||||
|
enabled: Boolean(nextValue),
|
||||||
|
});
|
||||||
|
enqueueSnackbar(nextValue ? 'Terminal aktiviert.' : 'Terminal deaktiviert.', {
|
||||||
|
variant: 'success',
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
notifyError(err);
|
||||||
|
} finally {
|
||||||
|
this.setState({ patchingTerminalId: null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSortDraftChange(terminalId, value) {
|
||||||
|
this.setState((prev) => ({
|
||||||
|
sortDrafts: { ...prev.sortDrafts, [terminalId]: value },
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleSortSubmit(terminal) {
|
||||||
|
const draft = this.state.sortDrafts[terminal.id];
|
||||||
|
if (draft === undefined) return;
|
||||||
|
const nextSort = Number(draft);
|
||||||
|
if (isNaN(nextSort) || nextSort === (terminal.sort ?? 0)) {
|
||||||
|
this.setState((prev) => {
|
||||||
|
const next = { ...prev.sortDrafts };
|
||||||
|
delete next[terminal.id];
|
||||||
|
return { sortDrafts: next };
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({ patchingTerminalId: terminal.id });
|
||||||
|
try {
|
||||||
|
await companyAutomat.actions.updateTerminal(terminal.id, { sort: nextSort });
|
||||||
|
enqueueSnackbar('Reihenfolge aktualisiert.', { variant: 'success' });
|
||||||
|
} catch (err) {
|
||||||
|
notifyError(err);
|
||||||
|
} finally {
|
||||||
|
this.setState((prev) => {
|
||||||
|
const next = { ...prev.sortDrafts };
|
||||||
|
delete next[terminal.id];
|
||||||
|
return { sortDrafts: next, patchingTerminalId: null };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleMoveTerminal(terminal, direction) {
|
||||||
|
const { terminals } = this.state;
|
||||||
|
const currentIndex = terminals.findIndex((t) => t.id === terminal.id);
|
||||||
|
if (currentIndex === -1) return;
|
||||||
|
const targetIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;
|
||||||
|
if (targetIndex < 0 || targetIndex >= terminals.length) return;
|
||||||
|
|
||||||
|
const other = terminals[targetIndex];
|
||||||
|
const currentSort = terminal.sort ?? 0;
|
||||||
|
const otherSort = other.sort ?? 0;
|
||||||
|
|
||||||
|
let newSort;
|
||||||
|
if (direction === 'up') {
|
||||||
|
newSort = otherSort <= currentSort ? otherSort - 1 : otherSort;
|
||||||
|
} else {
|
||||||
|
newSort = otherSort >= currentSort ? otherSort + 1 : otherSort;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ patchingTerminalId: terminal.id });
|
||||||
|
try {
|
||||||
|
await companyAutomat.actions.updateTerminal(terminal.id, { sort: newSort });
|
||||||
|
enqueueSnackbar('Reihenfolge aktualisiert.', { variant: 'success' });
|
||||||
|
} catch (err) {
|
||||||
|
notifyError(err);
|
||||||
|
} finally {
|
||||||
|
this.setState({ patchingTerminalId: null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async handleDeleteTerminal(terminal) {
|
async handleDeleteTerminal(terminal) {
|
||||||
this.setState({ deletingId: terminal.id });
|
this.setState({ deletingId: terminal.id });
|
||||||
try {
|
try {
|
||||||
@@ -261,9 +353,10 @@ export default class PosTerminalSettingsPanel extends Component {
|
|||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||||
{terminals.map((terminal) => {
|
{terminals.map((terminal, index) => {
|
||||||
const isDeleting = deletingId === terminal.id;
|
const isDeleting = deletingId === terminal.id;
|
||||||
const isPatching = patchingTerminalId === terminal.id;
|
const isPatching = patchingTerminalId === terminal.id;
|
||||||
|
const isEnabled = terminal.enabled !== false && terminal.enabled !== 0 && terminal.enabled != null;
|
||||||
const pairedDateText = formatDateTime(terminal.pairedAt);
|
const pairedDateText = formatDateTime(terminal.pairedAt);
|
||||||
const lastseenText = formatDateTime(terminal.lastseenAt) ?? 'Nie';
|
const lastseenText = formatDateTime(terminal.lastseenAt) ?? 'Nie';
|
||||||
const updatedText = formatDateTime(terminal.updatedAt) ?? '—';
|
const updatedText = formatDateTime(terminal.updatedAt) ?? '—';
|
||||||
@@ -275,18 +368,24 @@ export default class PosTerminalSettingsPanel extends Component {
|
|||||||
p: 2,
|
p: 2,
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
border: '1px solid',
|
border: '1px solid',
|
||||||
borderColor: 'divider',
|
borderColor: isEnabled ? 'divider' : 'action.disabledBackground',
|
||||||
bgcolor: 'background.paper',
|
bgcolor: 'background.paper',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: 1.5,
|
gap: 1.5,
|
||||||
|
opacity: isEnabled ? 1 : 0.72,
|
||||||
|
transition: 'opacity 0.2s ease, border-color 0.2s ease',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Header: Icon, Name, PIN, Delete action */}
|
{/* Header: Icon, Name, PIN, Version, Status Switch, Delete action */}
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, flexWrap: 'wrap' }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, flexWrap: 'wrap' }}>
|
||||||
<PointOfSaleIcon sx={{ color: 'primary.main', fontSize: 24 }} />
|
<PointOfSaleIcon sx={{ color: isEnabled ? 'primary.main' : 'text.disabled', fontSize: 24 }} />
|
||||||
<Typography variant="subtitle1" fontWeight={600}>
|
<Typography
|
||||||
|
variant="subtitle1"
|
||||||
|
fontWeight={600}
|
||||||
|
sx={{ color: isEnabled ? 'text.primary' : 'text.secondary' }}
|
||||||
|
>
|
||||||
{terminal.name}
|
{terminal.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
{terminal.pairingpin ? (
|
{terminal.pairingpin ? (
|
||||||
@@ -310,23 +409,68 @@ export default class PosTerminalSettingsPanel extends Component {
|
|||||||
({terminal.clientserial})
|
({terminal.clientserial})
|
||||||
</Typography>
|
</Typography>
|
||||||
) : null}
|
) : null}
|
||||||
|
{terminal.clientversion ? (
|
||||||
|
<Tooltip title="POS Client Software-Version">
|
||||||
|
<Chip
|
||||||
|
label={`v${terminal.clientversion}`}
|
||||||
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
color="info"
|
||||||
|
sx={{ fontFamily: 'monospace', fontWeight: 600, fontSize: '0.75rem' }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
</Box>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
isEnabled
|
||||||
|
? 'Terminal aktiv (kann synchronisieren)'
|
||||||
|
: 'Terminal deaktiviert (POS-Zugriff blockiert)'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
size="small"
|
||||||
|
checked={isEnabled}
|
||||||
|
disabled={isPatching || isDeleting}
|
||||||
|
onChange={(e) => void this.handleToggleEnabled(terminal, e.target.checked)}
|
||||||
|
color="success"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
sx={{
|
||||||
|
fontSize: '0.8125rem',
|
||||||
|
fontWeight: 500,
|
||||||
|
color: isEnabled ? 'text.primary' : 'text.disabled',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isEnabled ? 'Aktiv' : 'Inaktiv'}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
sx={{ m: 0 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
aria-label={`Terminal ${terminal.name} löschen`}
|
||||||
|
onClick={() => void this.handleDeleteTerminal(terminal)}
|
||||||
|
disabled={isDeleting || creatingTerminal || isPatching}
|
||||||
|
color="error"
|
||||||
|
>
|
||||||
|
{isDeleting ? (
|
||||||
|
<CircularProgress size={16} color="inherit" />
|
||||||
|
) : (
|
||||||
|
<DeleteOutlinedIcon fontSize="small" />
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
<IconButton
|
|
||||||
size="small"
|
|
||||||
aria-label={`Terminal ${terminal.name} löschen`}
|
|
||||||
onClick={() => void this.handleDeleteTerminal(terminal)}
|
|
||||||
disabled={isDeleting || creatingTerminal || isPatching}
|
|
||||||
color="error"
|
|
||||||
>
|
|
||||||
{isDeleting ? (
|
|
||||||
<CircularProgress size={16} color="inherit" />
|
|
||||||
) : (
|
|
||||||
<DeleteOutlinedIcon fontSize="small" />
|
|
||||||
)}
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Settings row: Root category picker and syncRootArticles toggle */}
|
{/* Settings row: Root category picker, sort order controls, and syncRootArticles toggle */}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -365,22 +509,75 @@ export default class PosTerminalSettingsPanel extends Component {
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<FormControlLabel
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, flexWrap: 'wrap' }}>
|
||||||
control={
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||||
<Switch
|
<Typography variant="body2" color="text.secondary">
|
||||||
size="small"
|
Reihenfolge:
|
||||||
checked={Boolean(terminal.syncRootArticles)}
|
|
||||||
disabled={isPatching || isDeleting}
|
|
||||||
onChange={(e) => void this.handleToggleSyncRootArticles(terminal, e.target.checked)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<Typography variant="body2">
|
|
||||||
Root-Artikel synchronisieren
|
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
<Tooltip title="Nach oben verschieben">
|
||||||
sx={{ m: 0 }}
|
<span>
|
||||||
/>
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
disabled={index === 0 || isPatching || isDeleting}
|
||||||
|
onClick={() => void this.handleMoveTerminal(terminal, 'up')}
|
||||||
|
sx={{ p: 0.25 }}
|
||||||
|
>
|
||||||
|
<ArrowUpwardIcon sx={{ fontSize: 16 }} />
|
||||||
|
</IconButton>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Nach unten verschieben">
|
||||||
|
<span>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
disabled={index === terminals.length - 1 || isPatching || isDeleting}
|
||||||
|
onClick={() => void this.handleMoveTerminal(terminal, 'down')}
|
||||||
|
sx={{ p: 0.25 }}
|
||||||
|
>
|
||||||
|
<ArrowDownwardIcon sx={{ fontSize: 16 }} />
|
||||||
|
</IconButton>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
<TextField
|
||||||
|
type="number"
|
||||||
|
size="small"
|
||||||
|
disabled={isPatching || isDeleting}
|
||||||
|
value={this.state.sortDrafts[terminal.id] ?? terminal.sort ?? 0}
|
||||||
|
onChange={(e) => this.handleSortDraftChange(terminal.id, e.target.value)}
|
||||||
|
onBlur={() => void this.handleSortSubmit(terminal)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
e.target.blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
slotProps={{
|
||||||
|
htmlInput: {
|
||||||
|
step: 1,
|
||||||
|
style: { width: 44, padding: '3px 6px', textAlign: 'center', fontSize: '0.8125rem' },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
sx={{ width: 56 }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
size="small"
|
||||||
|
checked={Boolean(terminal.syncRootArticles)}
|
||||||
|
disabled={isPatching || isDeleting}
|
||||||
|
onChange={(e) => void this.handleToggleSyncRootArticles(terminal, e.target.checked)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Typography variant="body2">
|
||||||
|
Root-Artikel synchronisieren
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
sx={{ m: 0 }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Dates row */}
|
{/* Dates row */}
|
||||||
@@ -402,6 +599,11 @@ export default class PosTerminalSettingsPanel extends Component {
|
|||||||
<Typography variant="caption" color="text.secondary">
|
<Typography variant="caption" color="text.secondary">
|
||||||
<strong>Zuletzt online:</strong> {lastseenText}
|
<strong>Zuletzt online:</strong> {lastseenText}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
{terminal.clientversion && (
|
||||||
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
<strong>POS-Software:</strong> {terminal.clientversion}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
<Typography variant="caption" color="text.secondary">
|
<Typography variant="caption" color="text.secondary">
|
||||||
<strong>Aktualisiert:</strong> {updatedText}
|
<strong>Aktualisiert:</strong> {updatedText}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
Reference in New Issue
Block a user