All files / src/pages/inventory/dialogs/PriceChangeDialog PriceChangeDialog.tsx

97.67% Statements 126/129
100% Branches 7/7
60% Functions 3/5
97.67% Lines 126/129

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 1301x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 1x 1x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x       10x 10x 10x 10x 1x 1x 1x 1x 9x 9x 10x 10x 10x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 10x 10x 10x 10x 10x 10x  
/**
 * PriceChangeDialog - Main dialog container for price adjustment
 * 
 * @module dialogs/PriceChangeDialog/PriceChangeDialog
 * @description
 * Manages dialog lifecycle (title, open/close, actions).
 * Delegates all form logic to usePriceChangeForm hook.
 * 
 * @enterprise
 * - Thin container following separation of concerns
 * - Help button links to price change documentation
 * - Cancel button dismisses without changes
 * - Apply button triggers submission with loading state
 */
 
import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  Button,
  Box,
  CircularProgress,
  Stack,
  Tooltip,
  IconButton,
} from '@mui/material';
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
import { useTranslation } from 'react-i18next';
import { useRef } from 'react';
import { PriceChangeForm } from './PriceChangeForm';
import { usePriceChangeForm } from './usePriceChangeForm';
import type { PriceChangeDialogProps } from './PriceChangeDialog.types';
 
/**
 * PriceChangeDialog - Main dialog component
 * 
 * Opens when open is true. Renders PriceChangeForm and manages dialog actions.
 * All form state/queries/validation delegated to usePriceChangeForm hook.
 * 
 * @param open - Whether dialog is visible
 * @param onClose - Called on cancel or successful save
 * @param onPriceChanged - Called after successful price change
 * @param readOnly - Demo mode flag (disables submission)
 */
export function PriceChangeDialog({
  open,
  onClose,
  onPriceChanged,
  readOnly = false,
}: PriceChangeDialogProps) {
  const { t } = useTranslation(['common', 'inventory']);
  const dialogRef = useRef<HTMLDivElement>(null);
 
  // All form state and handlers delegated to hook
  const state = usePriceChangeForm({ isOpen: open, onClose, onPriceChanged, readOnly });
 
  return (
    <Dialog
      ref={dialogRef}
      open={open}
      onClose={() => state.handleClose()}
      maxWidth="sm"
      fullWidth
    >
      {/* Title with help icon */}
      <DialogTitle>
        <Stack direction="row" alignItems="center" justifyContent="space-between">
          <Box>{t('inventory:toolbar.changePrice', 'Change Price')}</Box>
          <Tooltip title={t('common:help', 'Help')}>
            <IconButton
              size="small"
              onClick={() => {
                window.open('#/help?section=inventory.changePrice', '_blank');
              }}
              aria-label="help"
            >
              <HelpOutlineIcon fontSize="small" />
            </IconButton>
          </Tooltip>
        </Stack>
      </DialogTitle>
 
      {/* Form content */}
      <DialogContent dividers>
        <PriceChangeForm state={state} />
      </DialogContent>
 
      {/* Dialog actions */}
      <DialogActions>
        <Button onClick={() => state.handleClose()} disabled={state.formState.isSubmitting}>
          {t('common:actions.cancel', 'Cancel')}
        </Button>
        <Box sx={{ position: 'relative', display: 'inline-block' }}>
          <Button
            variant="contained"
            onClick={(e) => {
              e.preventDefault();
              state.onSubmit();
            }}
            disabled={state.formState.isSubmitting || !state.selectedItem}
            data-testid="apply-price-change-button"
          >
            {state.formState.isSubmitting ? (
              <>
                <CircularProgress size={16} sx={{ mr: 1 }} />
                {t('common:saving', 'Saving...')}
              </>
            ) : (
              t('inventory:buttons.applyPriceChange', 'Apply Price Change')
            )}
          </Button>
          {state.formState.isSubmitting && (
            <CircularProgress
              size={24}
              sx={{
                position: 'absolute',
                top: '50%',
                left: '50%',
                marginTop: '-12px',
                marginLeft: '-12px',
              }}
            />
          )}
        </Box>
      </DialogActions>
    </Dialog>
  );
}