diff --git a/src/app/api/qc/index.ts b/src/app/api/qc/index.ts index 7fb9691..95ffefd 100644 --- a/src/app/api/qc/index.ts +++ b/src/app/api/qc/index.ts @@ -20,7 +20,7 @@ export interface QcData { qcItemId: number, code: string, name: string, - qcDescription: string, + description: string, qcPassed: boolean | undefined failQty: number | undefined remarks: string | undefined diff --git a/src/app/api/qrcode/index.ts b/src/app/api/qrcode/index.ts index 2a09982..9e5dfcf 100644 --- a/src/app/api/qrcode/index.ts +++ b/src/app/api/qrcode/index.ts @@ -6,10 +6,11 @@ import { serverFetchJson } from "../../utils/fetchUtil"; import { BASE_API_URL } from "../../../config/api"; export interface QrCodeInfo { + value: string; // warehouse qrcode warehouseId?: number; // item qrcode stockInLineId?: number; - itemId: number; + itemId?: number; lotNo?: string; } diff --git a/src/app/global.css b/src/app/global.css index 8410aea..7d2ff9a 100644 --- a/src/app/global.css +++ b/src/app/global.css @@ -4,8 +4,4 @@ html, body { overscroll-behavior: none; -} - -.swal2-custom-zindex { - z-index: 10000 !important; } \ No newline at end of file diff --git a/src/components/PoDetail/PutAwayForm.tsx b/src/components/PoDetail/PutAwayForm.tsx index e02e064..9706376 100644 --- a/src/components/PoDetail/PutAwayForm.tsx +++ b/src/components/PoDetail/PutAwayForm.tsx @@ -229,7 +229,9 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM flex: 0.2, editable: false, renderCell(params) { - return `${params.api.getRowIndexRelativeToVisibleRows(params.id) + 1}.` + return + {`${params.api.getRowIndexRelativeToVisibleRows(params.id) + 1}.`} + }, }, { @@ -238,7 +240,9 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM flex: 1, editable: false, renderCell(params) { - return `${(arrayToDateTimeString(params.value))}`; + return + {`${(arrayToDateTimeString(params.value))}`} + }, }, { @@ -246,6 +250,11 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM headerName: t("putawayUser"), flex: 1, editable: false, + renderCell(params) { + return + {params.value} + + } }, { field: "qty", @@ -254,38 +263,55 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM editable: false, headerAlign: "right", align: "right", + renderCell(params) { + return + {params.value} + + } }, { field: "warehouse", headerName: t("warehouse"), flex: 2, editable: false, - renderEditCell: (params) => { - const index = params.api.getRowIndexRelativeToVisibleRows(params.row.id) - // console.log(index) - // console.log(watch(`putAwayLines.${index}.warehouse`)) - return o.value === itemDetail.defaultWarehouseId)} - // value={options.find((o) => o.value === watch(`putAwayLines.${index}.warehouseId`))} - defaultValue={options.find((o) => o.value === watch(`putAwayLines.${index}.warehouseId`))} - onChange={(event, value) => { - params.api.setEditCellValue({ id: params.id, field: params.field, value: options.find((o) => o.value === value.value)?.label ?? ""}) - params.api.setEditCellValue({ id: params.id, field: "warehouseId", value: value.value}) - // setValue(`putAwayLines.${index}.warehouseId`, value.value) - // setValue(`putAwayLines.${index}.warehouse`, options.find((o) => o.value === value.value)?.label ?? "") - }} - renderInput={(params) => ( - - )} - /> + renderCell(params) { + return + {params.value} + } + // renderEditCell: (params) => { + // const index = params.api.getRowIndexRelativeToVisibleRows(params.row.id) + // // console.log(index) + // // console.log(watch(`putAwayLines.${index}.warehouse`)) + // return o.value === itemDetail.defaultWarehouseId)} + // // value={options.find((o) => o.value === watch(`putAwayLines.${index}.warehouseId`))} + // defaultValue={options.find((o) => o.value === watch(`putAwayLines.${index}.warehouseId`))} + // onChange={(event, value) => { + // params.api.setEditCellValue({ id: params.id, field: params.field, value: options.find((o) => o.value === value.value)?.label ?? ""}) + // params.api.setEditCellValue({ id: params.id, field: "warehouseId", value: value.value}) + // // setValue(`putAwayLines.${index}.warehouseId`, value.value) + // // setValue(`putAwayLines.${index}.warehouse`, options.find((o) => o.value === value.value)?.label ?? "") + // }} + // sx={{ + // // Style the dropdown options + // "& .MuiAutocomplete-option": { + // fontSize: 24, + // }, + // }} + // renderInput={(params) => ( + // + // )} + // /> + // } // renderCell(params) { // return <>{filteredWarehouse[0].name} // }, diff --git a/src/components/PoDetail/QcComponent.tsx b/src/components/PoDetail/QcComponent.tsx index c9e770e..83cf68a 100644 --- a/src/components/PoDetail/QcComponent.tsx +++ b/src/components/PoDetail/QcComponent.tsx @@ -237,10 +237,10 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { const qcColumns: GridColDef[] = useMemo(() => [ { - field: "code", + field: "name", headerName: t("qcItem"), wrapText: true, - flex: 2, + flex: 2.5, renderCell: (params) => { const index = params.api.getRowIndexRelativeToVisibleRows(params.id) + 1; return ( @@ -248,17 +248,18 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { sx={{ lineHeight: 1.5, padding: "4px", + fontSize: 18, }} > {`${index}. ${params.value}`}
- {params.row.name} + {params.row.description} )}, }, { field: 'qcResult', headerName: t("qcResult"), - flex: 1.5, + flex: 1, renderCell: (params) => { const rowValue = params.row; const index = Number(params.id);//params.api.getRowIndexRelativeToVisibleRows(params.id); @@ -307,14 +308,13 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { { field: "failQty", headerName: t("failedQty"), - flex: 1, + flex: 0.5, // editable: true, renderCell: (params) => { const index = Number(params.id);//params.api.getRowIndexRelativeToVisibleRows(params.id); return ( = ({ itemDetail, disabled = false }) => { onMouseDown={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()} inputProps={{ min: 0 }} - sx={{ width: '100%' }} + sx={{ width: '100%', + "& .MuiInputBase-input": { + padding: "0.75rem", + fontSize: 24, + }, + }} /> ); }, @@ -369,7 +374,12 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { onClick={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()} - sx={{ width: '100%' }} + sx={{ width: '100%', + "& .MuiInputBase-input": { + padding: "0.75rem", + fontSize: 24, + }, + }} /> ); }, diff --git a/src/components/PoDetail/StockInForm.tsx b/src/components/PoDetail/StockInForm.tsx index 1a770f5..9373d74 100644 --- a/src/components/PoDetail/StockInForm.tsx +++ b/src/components/PoDetail/StockInForm.tsx @@ -54,6 +54,35 @@ type EntryError = // type PoQcRow = TableRow, EntryError>; +const textfieldSx = { + width: "100%", + "& .MuiInputBase-root": { + // height: "120", // Scales with root font size + height: "5rem", // Scales with root font size + }, + "& .MuiInputBase-input": { + height: "100%", + boxSizing: "border-box", + padding: "0.75rem", + fontSize: 24, + }, + "& .MuiInputLabel-root": { + fontSize: 24, + transform: "translate(14px, 1.5rem) scale(1)", + "&.MuiInputLabel-shrink": { + fontSize: 18, + transform: "translate(14px, -9px) scale(1)", + }, + // [theme.breakpoints.down("sm")]: { + // fontSize: "1rem", + // transform: "translate(14px, 1.5rem) scale(1)", + // "&.MuiInputLabel-shrink": { + // fontSize: "0.875rem", + // }, + // }, + }, +}; + const StockInForm: React.FC = ({ // qc, itemDetail, @@ -136,6 +165,7 @@ const StockInForm: React.FC = ({ {...register("dnNo", { // required: "productLotNo required!", })} + sx={textfieldSx} disabled={disabled} // error={Boolean(errors.productLotNo)} // helperText={errors.productLotNo?.message} @@ -148,6 +178,7 @@ const StockInForm: React.FC = ({ {...register("itemName", { // required: "productLotNo required!", })} + sx={textfieldSx} disabled={true} // error={Boolean(errors.productLotNo)} // helperText={errors.productLotNo?.message} @@ -160,6 +191,7 @@ const StockInForm: React.FC = ({ {...register("poCode", { // required: "productLotNo required!", })} + sx={textfieldSx} disabled={true} // error={Boolean(errors.productLotNo)} // helperText={errors.productLotNo?.message} @@ -180,7 +212,7 @@ const StockInForm: React.FC = ({ > = ({ {...register("supplier", { // required: "productLotNo required!", })} + sx={textfieldSx} disabled={true} /> @@ -222,51 +255,52 @@ const StockInForm: React.FC = ({ {...register("productLotNo", { // required: "productLotNo required!", })} + sx={textfieldSx} disabled={disabled} error={Boolean(errors.productLotNo)} helperText={errors.productLotNo?.message} /> - - { - return ( - - { - if (!date) return; - setValue( - "productionDate", - date.format(INPUT_DATE_FORMAT), - ); - // field.onChange(date); - }} - inputRef={field.ref} - slotProps={{ - textField: { - // required: true, - error: Boolean(errors.productionDate?.message), - helperText: errors.productionDate?.message, - }, - }} - /> - - ); - }} - /> - - {putawayMode || ( + {putawayMode || (<> + + { + return ( + + { + if (!date) return; + setValue( + "productionDate", + date.format(INPUT_DATE_FORMAT), + ); + // field.onChange(date); + }} + inputRef={field.ref} + slotProps={{ + textField: { + // required: true, + error: Boolean(errors.productionDate?.message), + helperText: errors.productionDate?.message, + }, + }} + /> + + ); + }} + /> + = ({ {...register("qty", { required: "qty required!", })} + sx={textfieldSx} disabled={true} /> - + )} = ({ > = ({ {...register("receivedQty", { required: "receivedQty required!", })} + sx={textfieldSx} disabled={true} /> @@ -331,33 +367,47 @@ const StockInForm: React.FC = ({ - {putawayMode ? ( + {putawayMode ? (<> sum + p.qty, 0) ?? 0} / ${itemDetail.acceptedQty}` - } + value={itemDetail.acceptedQty} // disabled={true} // disabled={disabled} // error={Boolean(errors.acceptedQty)} // helperText={errors.acceptedQty?.message} /> + + sum + p.qty, 0) ?? 0} + // disabled={true} + // disabled={disabled} + // error={Boolean(errors.acceptedQty)} + // helperText={errors.acceptedQty?.message} + /> + ) : ( = ({ warehouse }) => { useEffect(() => { if (!scanner.isScanning) { scanner.startScan(); - console.log("%c Scanning started ", "color:cyan"); } else if (scanner.isScanning) { scanner.stopScan(); - console.log("%c Scanning stopped ", "color:cyan"); } }, []); @@ -87,37 +85,6 @@ const PutAwayScan: React.FC = ({ warehouse }) => { setOpenPutAwayModal(false); } - - const findIdByRoughMatch = (inputString : string, keyword : string) => { - const keywordIndex = inputString.indexOf(keyword); - - if (keywordIndex === -1) { - return { - keywordFound: false, - number: null, - message: `${keyword} not found in the input`, - }; - } - - const substringAfterKeyword = inputString.slice(keywordIndex + keyword.length); - - const numberMatch = substringAfterKeyword.match(/\d+/); - - if (!numberMatch) { - return { - keywordFound: true, - number: null, - message: `No valid number found after ${keyword}`, - }; - } - - return { - keywordFound: true, - number: parseInt(numberMatch[0], 10), - message: `Found ${keyword} at index ${keywordIndex}, first number found after is: ${numberMatch[0]}`, - }; - } - const addPutAwayHistory = (putAwayData: PutAwayRecord) => { console.log("%c Added new data to Putaway history: ", "color:orange", putAwayData); const newPutaway = { ...putAwayData, id: putAwayHistory.length + 1 }; @@ -133,51 +100,22 @@ const PutAwayScan: React.FC = ({ warehouse }) => { // Get Scanned Values useEffect(() => { - if (scanner.values.length > 0) {//} && !Boolean(itemDetail)) { - const scannedValues = scanner.values[0]; - console.log("%c Scanned: ", "color:cyan", scannedValues); - - if (scannedValues.substring(0, 8) == "{2fitest") { // DEBUGGING - const number = scannedValues.substring(8, scannedValues.length - 1); - if (/^\d+$/.test(number)) { // Check if number contains only digits - console.log("%c DEBUG: Testing SIL ID: ", "color:cyan", number); - if (scannedSilId === 0) { - setScannedSilId(Number(number)); - } else setScannedWareHouseId(Number(number)); - } else { - console.error("%c DEBUG: Invalid number format: ", "color:red", number); - resetScan(); - } - return; - } - try { - const data: QrCodeInfo = JSON.parse(scannedValues); - console.log("%c Scanned with data", "color:green", data); - - if (scannedSilId == 0) { // Initial State - if (data.stockInLineId !== undefined) { - setScannedSilId(Number(data.stockInLineId)); - } else resetScan("Cannot read Stock In Line Id"); - } else { // Processing - if (data.warehouseId !== undefined) { - setScannedWareHouseId(Number(data.warehouseId)); - } else resetScan("Cannot read Warehouse Id"); - } - } catch (error) { // Rought match for other scanner -- Pending Review - if (scannedSilId == 0) { - const silId = findIdByRoughMatch(scannedValues, "StockInLine").number ?? 0; - setScannedSilId(silId); - - } else { - const whId = findIdByRoughMatch(scannedValues, "warehouseId").number ?? 0; - setScannedWareHouseId(whId); - } - - resetScan(String(error)); + if (scanner.result) { + const data = scanner.result; + if (scannedSilId == 0) { // Initial State + if (!isNaN(Number(data.value))) { setScannedSilId(Number(data.value)); } + else if (data.stockInLineId) { + setScannedSilId(Number(data.stockInLineId)); + } else resetScan("Cannot read Stock In Line Id"); + } else { // Processing + if (!isNaN(Number(data.value))) { setScannedWareHouseId(Number(data.value)); } + else if (data.warehouseId) { + setScannedWareHouseId(Number(data.warehouseId)); + } else resetScan("Cannot read Warehouse Id"); } scanner.resetScan(); } - }, [scanner.values]); + }, [scanner.result]); return (<> void; stopScan: () => void; resetScan: () => void; + result: QrCodeInfo | undefined; } interface QrCodeScannerProviderProps { @@ -32,19 +34,73 @@ const QrCodeScannerProvider: React.FC = ({ const [keys, setKeys] = useState([]); const [leftCurlyBraceCount, setLeftCurlyBraceCount] = useState(0); const [rightCurlyBraceCount, setRightCurlyBraceCount] = useState(0); - const resetQrCodeScanner = useCallback(() => { + const [scanResult, setScanResult] = useState() + + const resetScannerInput = useCallback(() => { + setKeys(() => []); + setLeftCurlyBraceCount(() => 0); + setRightCurlyBraceCount(() => 0); + }, []); + + const resetQrCodeScanner = useCallback((error : string = "") => { setQrCodeScannerValues(() => []); + setScanResult(undefined); + resetScannerInput(); + + console.log("%c Scanner Reset", "color:cyan"); + + if (error.length > 0) { + console.log("%c Error:", "color:red", error); + } }, []); const startQrCodeScanner = useCallback(() => { resetQrCodeScanner(); setIsScanning(() => true); + console.log("%c Scanning started ", "color:cyan"); }, []); const endQrCodeScanner = useCallback(() => { setIsScanning(() => false); + console.log("%c Scanning stopped ", "color:cyan"); }, []); + // Find by rough match, return 0 if not found + const findIdByRoughMatch = (inputString : string, keyword : string) => { + console.log(`%c Performed rough match for ${keyword} within ${inputString}`, "color:brown"); + + const keywordIndex = inputString.indexOf(keyword); + + let result : {keywordFound: boolean; number: number | null; message: string} = { + keywordFound: false, + number: null, + message: `${keyword} not found in the input`, + }; + + if (keywordIndex !== -1) { + const substringAfterKeyword = inputString.slice(keywordIndex + keyword.length); + + const numberMatch = substringAfterKeyword.match(/\d+/); + + if (!numberMatch) { + result = { + keywordFound: true, + number: null, + message: `No valid number found after ${keyword}`, + }; + } else { + result = { + keywordFound: true, + number: parseInt(numberMatch[0], 10), + message: `Found ${keyword} at index ${keywordIndex}, first number found after is: ${numberMatch[0]}`, + }; + } + } + + console.log(`%c ${result.message}`, "color:brown"); + return result; + }; + // Check the KeyDown useEffect(() => { if (isScanning) { @@ -70,27 +126,72 @@ const QrCodeScannerProvider: React.FC = ({ // Update Qr Code Scanner Values useEffect(() => { - if ( - leftCurlyBraceCount !== 0 && - rightCurlyBraceCount !== 0 && - leftCurlyBraceCount === rightCurlyBraceCount - ) { - const startBrace = keys.indexOf("{"); - const endBrace = keys.lastIndexOf("}"); - setQrCodeScannerValues((value) => [ - ...value, - keys.join("").substring(startBrace, endBrace + 1), - ]); - console.log(keys); - console.log("%c QR Scanner Values:", "color:cyan", qrCodeScannerValues); - - // reset - setKeys(() => []); - setLeftCurlyBraceCount(() => 0); - setRightCurlyBraceCount(() => 0); + if (rightCurlyBraceCount > leftCurlyBraceCount || leftCurlyBraceCount > 1) { // Prevent multiple scan + resetQrCodeScanner("Too many scans at once"); + } else { + if (leftCurlyBraceCount == 1 && keys.length == 1) + { console.log("%c Scan detected, waiting for inputs...", "color:cyan"); } + if ( + leftCurlyBraceCount !== 0 && + rightCurlyBraceCount !== 0 && + leftCurlyBraceCount === rightCurlyBraceCount + ) { + const startBrace = keys.indexOf("{"); + const endBrace = keys.lastIndexOf("}"); + setQrCodeScannerValues((value) => [ + ...value, + keys.join("").substring(startBrace, endBrace + 1), + ]); + // console.log(keys); + // console.log("%c QR Scanner Values:", "color:cyan", qrCodeScannerValues); + + resetScannerInput(); + } } }, [keys, leftCurlyBraceCount, rightCurlyBraceCount]); + useEffect(() => { + if (qrCodeScannerValues.length > 0) { + const scannedValues = qrCodeScannerValues[0]; + console.log("%c Scanned Result: ", "color:cyan", scannedValues); + + if (scannedValues.substring(0, 8) == "{2fitest") { // DEBUGGING + const number = scannedValues.substring(8, scannedValues.length - 1); + if (/^\d+$/.test(number)) { // Check if number contains only digits + console.log("%c DEBUG: detected ID: ", "color:pink", number); + const debugValue = { + value: number + } + setScanResult(debugValue); + } else { + resetQrCodeScanner("DEBUG -- Invalid number format: " + number); + } + return; + } + + try { + const data: QrCodeInfo = JSON.parse(scannedValues); + console.log("%c Parsed scan data", "color:green", data); + + const content = scannedValues.substring(1, scannedValues.length - 1); + data.value = content; + setScanResult(data); + + } catch (error) { // Rought match for other scanner input -- Pending Review + const silId = findIdByRoughMatch(scannedValues, "StockInLine").number ?? 0; + + if (silId == 0) { + const whId = findIdByRoughMatch(scannedValues, "warehouseId").number ?? 0; + setScanResult({...scanResult, stockInLineId: whId, value: whId.toString()}); + } else { setScanResult({...scanResult, stockInLineId: silId, value: silId.toString()}); } + + resetQrCodeScanner(String(error)); + } + + // resetQrCodeScanner(); + } + }, [qrCodeScannerValues]); + return ( = ({ startScan: startQrCodeScanner, stopScan: endQrCodeScanner, resetScan: resetQrCodeScanner, + result: scanResult, }} > {children} diff --git a/src/theme/devias-material-kit/components.ts b/src/theme/devias-material-kit/components.ts index 50595ad..ab1c66e 100644 --- a/src/theme/devias-material-kit/components.ts +++ b/src/theme/devias-material-kit/components.ts @@ -150,10 +150,37 @@ const components: ThemeOptions["components"] = { width: "100%", zIndex: 2000, }, + ".swal2-custom-zindex": { + zIndex: 10000, // Set z-index for SweetAlert2 modals + }, }, }, MuiInputBase: { styleOverrides: { + root: { + "&:not(.Mui-disabled)": { + backgroundColor: "rgba(200, 230, 255, 0.2)", // Slightly cyan-ish background + "&:hover": { + backgroundColor: "rgba(200, 230, 255, 0.25)", // Slightly darker on hover + }, + "&.Mui-focused": { + backgroundColor: "rgba(200, 230, 255, 0.3)", // More pronounced on focus + }, + }, + "&.Mui-disabled": { + backgroundColor: "#ffffff", // White background + opacity: 1, // Remove MUI's default opacity reduction + "& .MuiInputBase-input": { + color: "#000", // Black text + cursor: "default", // Default cursor + WebkitTextFillColor: "#000", // Ensure text color isn't grayed out in WebKit browsers + }, + "& fieldset": { + backgroundColor: "transparent", // Ensure no extra background effects + boxShadow: "none", // Remove any box-shadow + }, + }, + }, input: { "&::placeholder": { opacity: 1, @@ -244,6 +271,17 @@ const components: ThemeOptions["components"] = { boxShadow: `${palette.error.main} 0 0 0 2px`, }, }, + "&:not(.Mui-disabled)": { + "&:hover .MuiOutlinedInput-notchedOutline": { + borderColor: "rgba(0, 0, 0, 0.6)", // Darker border on hover + }, + "&.Mui-focused .MuiOutlinedInput-notchedOutline": { + borderColor: "rgba(0, 0, 0, 0.7)", // Darkest border when focused + }, + }, + "&.Mui-disabled .MuiOutlinedInput-notchedOutline": { + border: "1px solid #ccc", // Simple gray border for disabled + }, }, input: { fontSize: defaultFontSize, @@ -278,6 +316,13 @@ const components: ThemeOptions["components"] = { background: palette.primary.contrastText, }, }, + "&.Mui-disabled": { + color: "rgba(0, 0, 0, 0.6)", // Black label + transform: "translate(14px, 16px) scale(1)", // Position like placeholder + "&.Mui-focused": { + transform: "translate(14px, 16px) scale(1)", // Keep label in place when "focused" + }, + }, }, }, },