| @@ -12,6 +12,7 @@ export interface PrinterInputs { | |||||
| name?: string; | name?: string; | ||||
| code?: string; | code?: string; | ||||
| type?: string; | type?: string; | ||||
| brand?: string; | |||||
| description?: string; | description?: string; | ||||
| ip?: string; | ip?: string; | ||||
| port?: number; | port?: number; | ||||
| @@ -10,6 +10,7 @@ export interface PrinterCombo { | |||||
| code?: string; | code?: string; | ||||
| name?: string; | name?: string; | ||||
| type?: string; | type?: string; | ||||
| brand?: string; | |||||
| description?: string; | description?: string; | ||||
| ip?: string; | ip?: string; | ||||
| port?: number; | port?: number; | ||||
| @@ -21,6 +22,7 @@ export interface PrinterResult { | |||||
| name?: string; | name?: string; | ||||
| code?: string; | code?: string; | ||||
| type?: string; | type?: string; | ||||
| brand?: string; | |||||
| description?: string; | description?: string; | ||||
| ip?: string; | ip?: string; | ||||
| port?: number; | port?: number; | ||||
| @@ -30,6 +30,7 @@ const CreatePrinter: React.FC = () => { | |||||
| ip: "", | ip: "", | ||||
| port: undefined, | port: undefined, | ||||
| type: "A4", | type: "A4", | ||||
| brand: "Canon", | |||||
| dpi: undefined, | dpi: undefined, | ||||
| description: "", | description: "", | ||||
| }); | }); | ||||
| @@ -50,6 +51,18 @@ const CreatePrinter: React.FC = () => { | |||||
| if (formData.type !== "Label") { | if (formData.type !== "Label") { | ||||
| setFormData((prev) => ({ ...prev, dpi: undefined })); | setFormData((prev) => ({ ...prev, dpi: undefined })); | ||||
| } | } | ||||
| if (formData.type === "A4") { | |||||
| // A4: allow Canon/Brother (keep whatever is selected, default Canon from initial state) | |||||
| if (!formData.brand) { | |||||
| setFormData((prev) => ({ ...prev, brand: "Canon" })); | |||||
| } | |||||
| } else if (formData.type === "Label") { | |||||
| // Label: preview Zebra | |||||
| setFormData((prev) => ({ ...prev, brand: "Zebra" })); | |||||
| } else { | |||||
| // Other types (if any): no brand | |||||
| setFormData((prev) => ({ ...prev, brand: undefined })); | |||||
| } | |||||
| }, [formData.type]); | }, [formData.type]); | ||||
| const handleChange = useCallback((field: keyof PrinterInputs) => { | const handleChange = useCallback((field: keyof PrinterInputs) => { | ||||
| @@ -74,6 +87,13 @@ const CreatePrinter: React.FC = () => { | |||||
| })); | })); | ||||
| }, []); | }, []); | ||||
| const handleBrandChange = useCallback((e: SelectChangeEvent) => { | |||||
| setFormData((prev) => ({ | |||||
| ...prev, | |||||
| brand: e.target.value, | |||||
| })); | |||||
| }, []); | |||||
| const handleDescriptionChange = useCallback((_e: any, newValue: string | null) => { | const handleDescriptionChange = useCallback((_e: any, newValue: string | null) => { | ||||
| setFormData((prev) => ({ | setFormData((prev) => ({ | ||||
| ...prev, | ...prev, | ||||
| @@ -155,6 +175,32 @@ const CreatePrinter: React.FC = () => { | |||||
| </Select> | </Select> | ||||
| </FormControl> | </FormControl> | ||||
| </Grid> | </Grid> | ||||
| {formData.type === "A4" && ( | |||||
| <Grid item xs={12} md={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel>{t("Brand")}</InputLabel> | |||||
| <Select | |||||
| label={t("Brand")} | |||||
| value={formData.brand ?? "Canon"} | |||||
| onChange={handleBrandChange} | |||||
| > | |||||
| <MenuItem value="Canon">Canon</MenuItem> | |||||
| <MenuItem value="Brother">Brother</MenuItem> | |||||
| </Select> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| )} | |||||
| {formData.type === "Label" && ( | |||||
| <Grid item xs={12} md={6}> | |||||
| <TextField | |||||
| fullWidth | |||||
| label={t("Brand")} | |||||
| value={formData.brand ?? "Zebra"} | |||||
| disabled | |||||
| /> | |||||
| </Grid> | |||||
| )} | |||||
| <Grid item xs={12} md={6}> | <Grid item xs={12} md={6}> | ||||
| <TextField | <TextField | ||||
| fullWidth | fullWidth | ||||
| @@ -16,7 +16,6 @@ import { | |||||
| SelectChangeEvent, | SelectChangeEvent, | ||||
| Stack, | Stack, | ||||
| TextField, | TextField, | ||||
| Typography, | |||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import { Check, ArrowBack } from "@mui/icons-material"; | import { Check, ArrowBack } from "@mui/icons-material"; | ||||
| import { successDialog } from "../Swal/CustomAlerts"; | import { successDialog } from "../Swal/CustomAlerts"; | ||||
| @@ -34,13 +33,24 @@ const EditPrinter: React.FC<Props> = ({ printer }) => { | |||||
| ip: printer.ip || "", | ip: printer.ip || "", | ||||
| port: printer.port || undefined, | port: printer.port || undefined, | ||||
| type: printer.type || "", | type: printer.type || "", | ||||
| brand: printer.brand ?? "Canon", | |||||
| dpi: printer.dpi || undefined, | dpi: printer.dpi || undefined, | ||||
| description: printer.description || "", | |||||
| }); | }); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| if (formData.type !== "Label") { | if (formData.type !== "Label") { | ||||
| setFormData((prev) => ({ ...prev, dpi: undefined })); | setFormData((prev) => ({ ...prev, dpi: undefined })); | ||||
| } | } | ||||
| if (formData.type === "A4") { | |||||
| if (!formData.brand) { | |||||
| setFormData((prev) => ({ ...prev, brand: "Canon" })); | |||||
| } | |||||
| } else if (formData.type === "Label") { | |||||
| setFormData((prev) => ({ ...prev, brand: "Zebra" })); | |||||
| } else { | |||||
| setFormData((prev) => ({ ...prev, brand: undefined })); | |||||
| } | |||||
| }, [formData.type]); | }, [formData.type]); | ||||
| const handleChange = useCallback((field: keyof PrinterInputs) => { | const handleChange = useCallback((field: keyof PrinterInputs) => { | ||||
| @@ -63,6 +73,14 @@ const EditPrinter: React.FC<Props> = ({ printer }) => { | |||||
| })); | })); | ||||
| }, []); | }, []); | ||||
| const handleBrandChange = useCallback((e: SelectChangeEvent) => { | |||||
| const value = e.target.value; | |||||
| setFormData((prev) => ({ | |||||
| ...prev, | |||||
| brand: value, | |||||
| })); | |||||
| }, []); | |||||
| const handleSubmit = useCallback(async () => { | const handleSubmit = useCallback(async () => { | ||||
| setIsSubmitting(true); | setIsSubmitting(true); | ||||
| try { | try { | ||||
| @@ -123,6 +141,31 @@ const EditPrinter: React.FC<Props> = ({ printer }) => { | |||||
| </Select> | </Select> | ||||
| </FormControl> | </FormControl> | ||||
| </Grid> | </Grid> | ||||
| {formData.type === "A4" && ( | |||||
| <Grid item xs={12} md={6}> | |||||
| <FormControl fullWidth> | |||||
| <InputLabel>{t("Brand")}</InputLabel> | |||||
| <Select | |||||
| label={t("Brand")} | |||||
| value={formData.brand ?? "Canon"} | |||||
| onChange={handleBrandChange} | |||||
| > | |||||
| <MenuItem value="Canon">Canon</MenuItem> | |||||
| <MenuItem value="Brother">Brother</MenuItem> | |||||
| </Select> | |||||
| </FormControl> | |||||
| </Grid> | |||||
| )} | |||||
| {formData.type === "Label" && ( | |||||
| <Grid item xs={12} md={6}> | |||||
| <TextField | |||||
| fullWidth | |||||
| label={t("Brand")} | |||||
| value={formData.brand ?? "Zebra"} | |||||
| disabled | |||||
| /> | |||||
| </Grid> | |||||
| )} | |||||
| <Grid item xs={12} md={6}> | <Grid item xs={12} md={6}> | ||||
| <TextField | <TextField | ||||
| fullWidth | fullWidth | ||||
| @@ -134,6 +177,15 @@ const EditPrinter: React.FC<Props> = ({ printer }) => { | |||||
| disabled={formData.type !== "Label"} | disabled={formData.type !== "Label"} | ||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} md={6}> | |||||
| <TextField | |||||
| fullWidth | |||||
| label={t("Description")} | |||||
| value={formData.description || ""} | |||||
| onChange={handleChange("description")} | |||||
| variant="outlined" | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <Stack direction="row" spacing={2}> | <Stack direction="row" spacing={2}> | ||||
| <Button | <Button | ||||
| @@ -51,6 +51,10 @@ const PrinterSearch: React.FC<Props> = ({ printers }) => { | |||||
| paramName: "type", | paramName: "type", | ||||
| type: "text", | type: "text", | ||||
| }, | }, | ||||
| { | |||||
| label: t("Brand"), | |||||
| paramName: "brand", | |||||
| type: "text" }, | |||||
| ], | ], | ||||
| [t], | [t], | ||||
| ); | ); | ||||
| @@ -129,6 +133,13 @@ const PrinterSearch: React.FC<Props> = ({ printers }) => { | |||||
| align: "left", | align: "left", | ||||
| headerAlign: "left", | headerAlign: "left", | ||||
| sx: { width: "15%", minWidth: "100px" }, | sx: { width: "15%", minWidth: "100px" }, | ||||
| }, | |||||
| { | |||||
| name: "brand", | |||||
| label: t("Brand"), | |||||
| align: "left", | |||||
| headerAlign: "left", | |||||
| sx: { width: "12%", minWidth: "90px" }, | |||||
| }, | }, | ||||
| { | { | ||||
| name: "dpi", | name: "dpi", | ||||
| @@ -182,6 +193,11 @@ const PrinterSearch: React.FC<Props> = ({ printers }) => { | |||||
| printer.type?.toLowerCase().includes(query.type?.toLowerCase() || "") | printer.type?.toLowerCase().includes(query.type?.toLowerCase() || "") | ||||
| ); | ); | ||||
| } | } | ||||
| if (query.brand && query.brand.trim()) { | |||||
| results = results.filter((printer) => | |||||
| printer.brand?.toLowerCase().includes(query.brand?.toLowerCase() || "") | |||||
| ); | |||||
| } | |||||
| setFilteredPrinters(results); | setFilteredPrinters(results); | ||||
| setPagingController({ pageNum: 1, pageSize: pagingController.pageSize }); | setPagingController({ pageNum: 1, pageSize: pagingController.pageSize }); | ||||
| @@ -526,5 +526,6 @@ | |||||
| "Auto-refresh every 5 minutes": "每5分鐘自動刷新", | "Auto-refresh every 5 minutes": "每5分鐘自動刷新", | ||||
| "Auto-refresh every 10 minutes": "每10分鐘自動刷新", | "Auto-refresh every 10 minutes": "每10分鐘自動刷新", | ||||
| "Auto-refresh every 15 minutes": "每15分鐘自動刷新", | "Auto-refresh every 15 minutes": "每15分鐘自動刷新", | ||||
| "Auto-refresh every 1 minute": "每1分鐘自動刷新" | |||||
| "Auto-refresh every 1 minute": "每1分鐘自動刷新", | |||||
| "Brand": "品牌" | |||||
| } | } | ||||