This commit is contained in:
seb
2026-08-03 07:13:42 +02:00
parent 4db4948090
commit f302eb9e67
9 changed files with 985 additions and 51 deletions

View File

@@ -2,18 +2,22 @@ import { Component } from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import IconButton from '@mui/material/IconButton';
import Switch from '@mui/material/Switch';
import DragHandleIcon from '@mui/icons-material/DragHandle';
import AccountTreeIcon from '@mui/icons-material/AccountTree';
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
import { Tile, tileBaseSx } from './Tile';
import SettingsIcon from '@mui/icons-material/Settings';
import PointOfSaleIcon from '@mui/icons-material/PointOfSale';
import { tileBaseSx } from './Tile';
/**
* Top edit band: left = reorder handle (SortableJS), right = drop-into-subcategory
* handle (Shopify Draggable Droppable).
* Top row (1/3): sort handle | settings toggle | move handle.
*/
function TopBand() {
function TopBand({ onSettingsClick, settingsTab, terminalCount }) {
// settingsTab >= 0 means we're on a terminal tab
const isTerminalMode = settingsTab >= 0 && settingsTab < terminalCount;
return (
<Box
sx={{
@@ -36,6 +40,20 @@ function TopBand() {
>
<DragHandleIcon sx={{ fontSize: 36 }} />
</IconButton>
<IconButton
aria-label="Einstellungen"
disableRipple
onClick={onSettingsClick}
sx={{
color: isTerminalMode ? 'primary.main' : 'action.active',
}}
>
{isTerminalMode ? (
<PointOfSaleIcon sx={{ fontSize: 28 }} />
) : (
<SettingsIcon sx={{ fontSize: 28 }} />
)}
</IconButton>
<IconButton
className="droppable-handle"
aria-label="In Unterkategorie ablegen"
@@ -49,10 +67,48 @@ function TopBand() {
}
/**
* Shared grey name band spanning the middle third.
* Middle row (1/3): gray band — TextField or terminal toggle.
*/
function NameBand({ label, nameInput }) {
if (label == null) return null;
function MiddleBand({ settingsTab, nameInput, terminals, categoryPosterminalIds, onTerminalToggle }) {
console.log('[MiddleBand] settingsTab:', settingsTab, 'terminals:', terminals?.length, 'categoryPosterminalIds:', categoryPosterminalIds);
// Show terminal-specific toggle when on a terminal tab
if (settingsTab >= 0 && settingsTab < terminals.length) {
const terminal = terminals[settingsTab];
const isEnabled = categoryPosterminalIds.includes(terminal.id);
return (
<Box
sx={{
position: 'absolute',
top: '33.33%',
bottom: '33.33%',
left: 0,
right: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'grey.300',
px: 1,
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<PointOfSaleIcon sx={{ fontSize: 20, color: 'text.secondary' }} />
<Typography
variant="caption"
noWrap
sx={{ maxWidth: 80, color: 'text.secondary' }}
>
{terminal.name}
</Typography>
<Switch
checked={isEnabled}
onChange={(e) => onTerminalToggle?.(terminal.id, e.target.checked)}
size="small"
/>
</Box>
</Box>
);
}
return (
<Box
sx={{
@@ -68,24 +124,17 @@ function NameBand({ label, nameInput }) {
px: 1,
}}
>
{nameInput ?? (
<Typography
variant="body2"
fontWeight={600}
noWrap
sx={{ lineHeight: 1.2, color: 'text.primary' }}
>
{label}
</Typography>
)}
{nameInput}
</Box>
);
}
/**
* Bottom edit band with left/right chevron reorder buttons and a center delete.
* Bottom row (1/3): split into two sub-rows (each 1/6 of tile).
* Upper: click zone → navigate into category.
* Lower: gray band — left chevron | delete | right chevron.
*/
function EditBand({ onMoveLeft, onMoveRight, onDelete, canLeft, canRight }) {
function BottomRow({ onOpen, editBand }) {
return (
<Box
sx={{
@@ -93,23 +142,67 @@ function EditBand({ onMoveLeft, onMoveRight, onDelete, canLeft, canRight }) {
bottom: 0,
left: 0,
right: 0,
height: '16.66%',
height: '33.33%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
px: 0.5,
bgcolor: 'grey.400',
flexDirection: 'column',
}}
>
<IconButton className="sortable-handle-btn" size="small" aria-label="Nach links verschieben" disabled={!canLeft} onClick={onMoveLeft}>
<ChevronLeftIcon sx={{ fontSize: 18 }} />
</IconButton>
<IconButton className="sortable-handle-btn" size="small" aria-label="Löschen" onClick={onDelete}>
<DeleteOutlinedIcon sx={{ fontSize: 18 }} />
</IconButton>
<IconButton className="sortable-handle-btn" size="small" aria-label="Nach rechts verschieben" disabled={!canRight} onClick={onMoveRight}>
<ChevronRightIcon sx={{ fontSize: 18 }} />
</IconButton>
{/* Upper half: click-through to open category */}
<Box
role="button"
tabIndex={0}
onClick={onOpen}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onOpen?.();
}
}}
sx={{
flex: 1,
cursor: 'pointer',
'&:hover': { bgcolor: 'action.hover' },
}}
/>
{/* Lower half: reorder & delete */}
<Box
sx={{
height: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
px: 0.5,
bgcolor: 'grey.400',
}}
>
<IconButton
className="sortable-handle-btn"
size="small"
aria-label="Nach links verschieben"
disabled={!editBand.canLeft}
onClick={editBand.onMoveLeft}
>
<ChevronLeftIcon sx={{ fontSize: 18 }} />
</IconButton>
<IconButton
className="sortable-handle-btn"
size="small"
aria-label="Löschen"
onClick={editBand.onDelete}
>
<DeleteOutlinedIcon sx={{ fontSize: 18 }} />
</IconButton>
<IconButton
className="sortable-handle-btn"
size="small"
aria-label="Nach rechts verschieben"
disabled={!editBand.canRight}
onClick={editBand.onMoveRight}
>
<ChevronRightIcon sx={{ fontSize: 18 }} />
</IconButton>
</Box>
</Box>
);
}
@@ -117,24 +210,105 @@ function EditBand({ onMoveLeft, onMoveRight, onDelete, canLeft, canRight }) {
/**
* Category tile (normal mode): no icon, click navigates to the category.
*/
export class CategoryTile extends Tile {
renderIcon() {
return null;
}
export class CategoryTile extends Component {
handleClick = () => {
this.props.onClick?.();
};
renderLabel() {
const { label, nameInput } = this.props;
return <NameBand label={label} nameInput={nameInput} />;
if (label == null) return null;
return (
<Box
sx={{
position: 'absolute',
top: '33.33%',
bottom: '33.33%',
left: 0,
right: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
bgcolor: 'grey.300',
px: 1,
}}
>
{nameInput ?? (
<Typography
variant="body2"
fontWeight={600}
noWrap
sx={{ lineHeight: 1.2, color: 'text.primary' }}
>
{label}
</Typography>
)}
</Box>
);
}
render() {
const { sx, onClick: _onClick, label, ...rest } = this.props;
return (
<Box
component="button"
type="button"
onClick={this.handleClick}
{...rest}
sx={{
...tileBaseSx,
position: 'relative',
...sx,
}}
>
{this.renderLabel()}
</Box>
);
}
}
/**
* Category tile for edit mode: no ButtonBase (no ripple, no click navigation),
* always shows the bottom edit band with chevron reorder/delete actions.
* Category tile for edit mode: three rows of 1/3 each.
*
* Top row: sort-handle | settings toggle | drop-handle
* Middle row: gray band — TextField or POS toggle
* Bottom row: upper half = open-category click zone
* lower half = gray band with chevrons + delete
*/
export class CategoryEditTile extends Component {
state = { settingsTab: -1 }; // -1 = rename, 0..n-1 = terminal index
handleSettingsToggle = () => {
const { terminals } = this.props;
const terminalCount = terminals?.length || 0;
this.setState((prev) => {
const next = prev.settingsTab + 1;
const nextTab = next > terminalCount ? -1 : next;
console.log('[CategoryEditTile] settings toggle', {
terminals: terminals?.length,
terminalCount,
from: prev.settingsTab,
to: nextTab,
});
return { settingsTab: nextTab };
});
};
render() {
const { label, nameInput, editBand, sx, ...rest } = this.props;
const {
label,
nameInput,
editBand,
onOpen,
terminals,
categoryPosterminalIds,
onTerminalToggle,
hasTerminals: _hasTerminals,
sx,
...rest
} = this.props;
const { settingsTab } = this.state;
return (
<Box
{...rest}
@@ -146,15 +320,19 @@ export class CategoryEditTile extends Component {
...sx,
}}
>
<TopBand />
<NameBand label={label} nameInput={nameInput} />
<EditBand
onMoveLeft={editBand.onMoveLeft}
onMoveRight={editBand.onMoveRight}
onDelete={editBand.onDelete}
canLeft={editBand.canLeft}
canRight={editBand.canRight}
<TopBand
onSettingsClick={this.handleSettingsToggle}
settingsTab={settingsTab}
terminalCount={terminals?.length || 0}
/>
<MiddleBand
settingsTab={settingsTab}
nameInput={nameInput}
terminals={terminals}
categoryPosterminalIds={categoryPosterminalIds}
onTerminalToggle={onTerminalToggle}
/>
<BottomRow onOpen={onOpen} editBand={editBand} />
</Box>
);
}