diff --git a/src/components/StockIn/CalculateExpiryDateModal.tsx b/src/components/StockIn/CalculateExpiryDateModal.tsx
index 8d3ab5f..bcda7c2 100644
--- a/src/components/StockIn/CalculateExpiryDateModal.tsx
+++ b/src/components/StockIn/CalculateExpiryDateModal.tsx
@@ -1,7 +1,7 @@
import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import { Check, SwapHoriz, Add } from "@mui/icons-material";
-import { Box, Button, Card, Grid, Modal, Stack, TextField, Typography } from "@mui/material";
+import { Box, Button, Card, FormHelperText, Modal, Stack, Typography } from "@mui/material";
import { DatePicker, DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs, { Dayjs } from "dayjs";
@@ -24,6 +24,78 @@ type EntryError =
shelfLife: string,
}
+const INPUT_HEIGHT = "5rem";
+const ICON_COL_WIDTH = 48;
+
+const iconColumnSx = {
+ width: ICON_COL_WIDTH,
+ minWidth: ICON_COL_WIDTH,
+ height: INPUT_HEIGHT,
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ flexShrink: 0,
+};
+
+const columnSx = {
+ flex: { sm: "1 1 22%" },
+ minWidth: { sm: 180 },
+ width: { xs: "100%", sm: "auto" },
+};
+
+const shelfColumnSx = {
+ flex: { sm: "1 1 28%" },
+ minWidth: { sm: 200 },
+ width: { xs: "100%", sm: "auto" },
+};
+
+const rowSx = {
+ display: "flex",
+ flexDirection: { xs: "column", sm: "row" },
+ gap: { xs: 2, sm: 2 },
+ overflowX: { sm: "auto" },
+};
+
+const defaultModalFieldSx = {
+ width: "100%",
+ "& .MuiInputBase-root": {
+ height: INPUT_HEIGHT,
+ },
+ "& .MuiInputBase-input": {
+ height: "100%",
+ boxSizing: "border-box",
+ padding: "0.75rem 2.75rem 0.75rem 0.75rem",
+ fontSize: { xs: 22, sm: 26 },
+ "&::placeholder": {
+ fontSize: { xs: 20, sm: 24 },
+ opacity: 0.55,
+ },
+ },
+ "& .MuiInputLabel-root": {
+ display: "none",
+ },
+};
+
+const hiddenPickerLabelProps = {
+ shrink: true,
+ sx: { display: "none" },
+};
+
+const FieldLabel: React.FC<{ children: React.ReactNode; sx?: object }> = ({ children, sx }) => (
+
+ {children}
+
+);
+
const CalculateExpiryDateModal: React.FC = ({
open,
onClose,
@@ -31,6 +103,14 @@ const CalculateExpiryDateModal: React.FC = ({
textfieldSx,
}) => {
const { t, i18n: { language }, } = useTranslation("purchaseOrder");
+ const fieldSx = {
+ ...(textfieldSx ?? defaultModalFieldSx),
+ "& .MuiInputLabel-root": { display: "none" },
+ "& .MuiInputBase-input::placeholder": {
+ fontSize: { xs: 20, sm: 24 },
+ opacity: 0.55,
+ },
+ };
const [productionDate, setProductionDate] = useState();
const [shelfLife, setShelfLife] = useState();
@@ -158,24 +238,27 @@ const CalculateExpiryDateModal: React.FC = ({
onClose={onModalClose}
>
= ({
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
-
-
+
+
{t("Fill in Expiry Date")}
-
-
-
+
+ {/* Label row */}
+
+
+ {t("productionDate")}
+
+
+
+
+ 年
+ 月
+ 日
+
+
+
+
+ {t("expiryDate")}
+
+
+
+ {/* Input row — icons align with fields only */}
+
+
{
@@ -218,51 +320,38 @@ const CalculateExpiryDateModal: React.FC = ({
}}
slotProps={{
textField: {
- error: errors.productionDate.length > 0,
- helperText: errors.productionDate,
+ fullWidth: true,
+ InputLabelProps: hiddenPickerLabelProps,
+ error: errors.productionDate.length > 0,
},
}}
/>
-
-
-
-
-
+
+
+
+
+
{
const val = value == 0 ? undefined : value;
setShelfLife(val);
calculateDates(val, "shelfLife");
}}
/>
-
-
-
-
-
-
+
+
+
+
+
{
@@ -275,15 +364,48 @@ const CalculateExpiryDateModal: React.FC = ({
}}
slotProps={{
textField: {
- error: errors.expiryDate.length > 0,
- helperText: errors.expiryDate,
+ fullWidth: true,
+ InputLabelProps: hiddenPickerLabelProps,
+ error: errors.expiryDate.length > 0,
},
}}
- disabled={true}
+ disabled
/>
-
-
-
+
+
+
+ {/* Helper / error row */}
+
+
+ {errors.productionDate && (
+ {errors.productionDate}
+ )}
+
+
+
+
+ {t("shelfLife")}:{" "}
+
+ {shelfLife ?? 0} 日
+
+
+
+
+
+ {errors.expiryDate && (
+ {errors.expiryDate}
+ )}
+
+
+
= ({
}
+ startIcon={}
disabled={expiryDate === undefined || hasError}
onClick={handleSubmit}
+ sx={{
+ minHeight: "3.5rem",
+ px: 3,
+ fontSize: { xs: "1rem", sm: "1.25rem" },
+ }}
>
{t("confirm expiry date")}
diff --git a/src/components/StockIn/ShelfLifeInput.tsx b/src/components/StockIn/ShelfLifeInput.tsx
index 3cef1d8..978ba99 100644
--- a/src/components/StockIn/ShelfLifeInput.tsx
+++ b/src/components/StockIn/ShelfLifeInput.tsx
@@ -1,7 +1,7 @@
'use client';
import { useState, useEffect, useMemo } from 'react';
-import { Box, TextField, FormHelperText, styled } from '@mui/material';
+import { Box, TextField, FormHelperText, Typography, styled } from '@mui/material';
import { useTranslation } from "react-i18next";
interface ShelfLifeInputProps {
@@ -9,16 +9,39 @@ interface ShelfLifeInputProps {
onChange?: (value: number) => void;
label?: string;
sx?: any;
- showHelperText?: boolean; // Option to show/hide the helper text
+ showHelperText?: boolean;
+ /** When true, shows "保質期: X 日" under the 年 field only */
+ helperUnderYear?: boolean;
+ /** Labels above fields instead of on the outline (avoids clipping) */
+ externalLabels?: boolean;
+ /** Only render Y/M/D inputs (labels & helper rendered by parent) */
+ inputsOnly?: boolean;
}
+const UnitLabel: React.FC<{ children: React.ReactNode }> = ({ children }) => (
+
+ {children}
+
+);
+
const ShelfLifeContainer = styled(Box)(({ theme }) => ({
display: 'flex',
- gap: theme.spacing(1),
+ gap: theme.spacing(2),
alignItems: 'flex-start',
width: '100%',
'& .MuiTextField-root': {
flex: 1,
+ minWidth: 88,
'& input': {
textAlign: 'center',
},
@@ -62,7 +85,16 @@ const formatDuration = (years: number, months: number, days: number) => {
return parts.length > 0 ? parts.join(' ') : '0 日';
};
-const ShelfLifeInput: React.FC = ({ value = 0, onChange = () => {}, label = 'Shelf Life', sx, showHelperText = true }) => {
+const ShelfLifeInput: React.FC = ({
+ value = 0,
+ onChange = () => {},
+ label = 'Shelf Life',
+ sx,
+ showHelperText = true,
+ helperUnderYear = false,
+ externalLabels = false,
+ inputsOnly = false,
+}) => {
const { t } = useTranslation("purchaseOrder");
const { years, months, days } = daysToDuration(value);
@@ -101,54 +133,103 @@ const ShelfLifeInput: React.FC = ({ value = 0, onChange = (
}
};
+ const shelfLifeSummary = showHelperText && (
+
+ {label}:{' '}
+
+ {totalDays} 日
+
+
+ );
+
+ const hiddenLabelProps = { shrink: true, sx: { display: 'none' } };
+
+ const yearField = (
+
+ );
+
+ const monthField = (
+
+ );
+
+ const dayField = (
+
+ );
+
+ const inputsRow = (
+
+
+ {!inputsOnly && externalLabels && 年}
+ {yearField}
+ {!inputsOnly && helperUnderYear && shelfLifeSummary}
+
+
+ {!inputsOnly && externalLabels && 月}
+ {monthField}
+
+
+ {!inputsOnly && externalLabels && 日}
+ {dayField}
+
+
+ );
+
+ if (inputsOnly) {
+ return {inputsRow};
+ }
+
return (
-
-
-
-
-
-
- {showHelperText && (
-
- {label}:
- {/* {formatDuration(duration.years, duration.months, duration.days)} */}
- {totalDays} 日
-
-
- )}
+
+ {inputsRow}
+ {showHelperText && !helperUnderYear && shelfLifeSummary}
);
};