Added NewItemDialog without query connection.

This commit is contained in:
FlorianSpeicher
2025-06-18 23:18:12 +02:00
parent 8fe4f3402c
commit 7da42ccd90
5 changed files with 195 additions and 24 deletions

View File

@@ -149,5 +149,8 @@
"imageUploadedSuccessfully": "Bild hochgeladen",
"uploading": "Lädt hoch...",
"upload": "Hochladen",
"imageUploadNoticeFs": "Die Auflösung der Farming Station beträgt 720 x 960 px"
"imageUploadNoticeFs": "Die Auflösung der Farming Station beträgt 720 x 960 px",
"itemCreatedSuccessfully": "Artikel erfolgreich erstellt",
"createNewItem": "Neuen Artikel erstellen",
"stockExpected": "Erwarteter Bestand"
}

View File

@@ -149,5 +149,8 @@
"imageUploadedSuccessfully": "Uploaded Image Successfully",
"uploading": "Uploading...",
"upload": "Upload",
"imageUploadNoticeFs": "The Resolution of the Farming Station is 720 x 960 px"
"imageUploadNoticeFs": "The Resolution of the Farming Station is 720 x 960 px",
"itemCreatedSuccessfully": "Item created successfully",
"createNewItem": "Create New Item",
"stockExpected": "Expected Stock"
}

View File

@@ -1,25 +1,27 @@
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import {Box, Button, IconButton, Toolbar, useTheme} from "@mui/material";
import { Box, Button, IconButton, Toolbar, useTheme } from "@mui/material";
import { Gauge, gaugeClasses } from "@mui/x-charts";
import { DataGrid, GridColDef, GridRowId, GridRowSelectionModel } from "@mui/x-data-grid";
import { useQuery } from "@tanstack/react-query";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import Item from "../../components/Item";
import {useTranslation} from "react-i18next";
import {DataGrid, GridColDef, GridRowId, GridRowSelectionModel} from "@mui/x-data-grid";
import {useEffect, useState} from "react";
import {Gauge, gaugeClasses} from "@mui/x-charts";
import {useAccount} from "../AccountProvider.tsx";
import {useQuery} from "@tanstack/react-query";
import {fetchItems} from "../query/Queries.tsx";
import { mapValueToColor } from "../../util/ColorUtil.tsx";
import { useAccount } from "../AccountProvider.tsx";
import { fetchItems } from "../query/Queries.tsx";
import ItemImageDialog from "./ItemImageDialog.tsx";
import NewItemDialog from "./NewItemDialog.tsx";
export default function ItemsInfo() {
const theme = useTheme();
const {t} = useTranslation();
const { t } = useTranslation();
const [rows, setRows] = useState<Item[]>([]);
const [selectedRows, setSelectedRows] = useState<Set<GridRowId>>(new Set());
const [editImageDialog, setEditImageDialog] = useState(false);
const [newItemDialog, setNewItemDialog] = useState(false);
const [selectedItem, setSelectedItem] = useState<Item | null>(null);
const [isFarmStationImage, setIsFarmStationImage] = useState(false);
@@ -41,10 +43,10 @@ export default function ItemsInfo() {
function handleAddItem() {
//TODO: flsp
setNewItemDialog(true);
}
const {user: loginData} = useAccount();
const { user: loginData } = useAccount();
const { data } = useQuery({
queryKey: ["fetchItems", loginData],
@@ -73,7 +75,7 @@ export default function ItemsInfo() {
};
const columns: GridColDef<(typeof rows)[number]>[] = [
{field: 'id', headerName: 'ID', width: 60},
{ field: 'id', headerName: 'ID', width: 60 },
{
field: 'uuid',
headerName: t('uuid'),
@@ -123,7 +125,7 @@ export default function ItemsInfo() {
type: 'number',
renderCell: params => <Gauge value={Math.min(params.row.stock, params.row.stockExpected)} valueMin={0} valueMax={params.row.stockExpected} startAngle={-90} endAngle={90} sx={{
[`& .${gaugeClasses.valueArc}`]: {
fill: () => {return mapValueToColor(0, params.row.stockExpected, params.row.stock)},
fill: () => { return mapValueToColor(0, params.row.stockExpected, params.row.stock) },
},
}} text={() => `${params.row.stock}`} />
},
@@ -135,7 +137,7 @@ export default function ItemsInfo() {
type: 'number',
renderCell: params => <Gauge value={Math.min(params.row.rating, 10)} valueMin={0} valueMax={10} startAngle={-90} endAngle={90} sx={{
[`& .${gaugeClasses.valueArc}`]: {
fill: () => {return mapValueToColor(0, 10, params.row.rating)},
fill: () => { return mapValueToColor(0, 10, params.row.rating) },
},
}} text={() => `${params.row.rating.toFixed(2)}`} />
},
@@ -144,21 +146,21 @@ export default function ItemsInfo() {
headerName: t('actualPrice'),
width: 90,
editable: false,
valueGetter: (_, row) => (row.price100 / 100 * ((100-row.discount100)/100)).toFixed(2)
valueGetter: (_, row) => (row.price100 / 100 * ((100 - row.discount100) / 100)).toFixed(2)
},
{
field: 'images',
headerName: t('images'),
width: 90,
editable: false,
renderCell: params => <IconButton onClick={() => handleImageEdit(params.row)}> <EditIcon/> </IconButton>,
renderCell: params => <IconButton onClick={() => handleImageEdit(params.row)}> <EditIcon /> </IconButton>,
},
{
field: 'farmImage',
headerName: t('fsImage'),
width: 90,
editable: false,
renderCell: params => <IconButton onClick={() => handleFarmImageEdit(params.row)}> <EditIcon/> </IconButton>,
renderCell: params => <IconButton onClick={() => handleFarmImageEdit(params.row)}> <EditIcon /> </IconButton>,
}
];
@@ -177,12 +179,13 @@ export default function ItemsInfo() {
checkboxSelection
disableRowSelectionOnClick
onRowSelectionModelChange={handleSelectionChange}
slots={{ toolbar: () => (
slots={{
toolbar: () => (
<Toolbar>
<Button
variant="contained"
color="error"
startIcon={<DeleteIcon/>}
startIcon={<DeleteIcon />}
onClick={handleDeleteSelected}
disabled={selectedRows.size === 0}
sx={{
@@ -194,9 +197,8 @@ export default function ItemsInfo() {
<Button
variant="contained"
color="primary"
startIcon={<DeleteIcon/>}
startIcon={<DeleteIcon />}
onClick={handleAddItem}
disabled={selectedRows.size === 0}
sx={{
marginRight: 1
}}
@@ -204,7 +206,8 @@ export default function ItemsInfo() {
{t('addProduct')}
</Button>
</Toolbar>
)}}
)
}}
showToolbar
processRowUpdate={(updatedRow) => {
setRows(rows.map(row => row.id === updatedRow.id ? updatedRow : row));
@@ -224,6 +227,11 @@ export default function ItemsInfo() {
isFarmStationImage={isFarmStationImage}
/>
)}
<NewItemDialog
open={newItemDialog}
onClose={() => setNewItemDialog(false)}
/>
</Box>
);
}

View File

@@ -0,0 +1,157 @@
import CloseIcon from '@mui/icons-material/Close';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import {
Alert,
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
IconButton,
TextField,
Typography
} from '@mui/material';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Item } from '../../components/Item';
interface NewItemDialogProps {
open: boolean;
onClose: () => void;
}
export default function NewItemDialog({ open, onClose }: NewItemDialogProps) {
const { t } = useTranslation();
const [item, setItem] = useState<Item>({
id: 0,
uuid: "",
name: "",
description: "",
price100: 0,
stock: 0,
stockExpected: 1,
category: "",
rating: -1,
discount100: 0,
});
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false);
const handleClose = () => {
setError(null);
setSuccess(false);
setLoading(false);
onClose();
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setItem({ ...item, [e.target.name]: e.target.value });
};
const handleSave = async () => {
setLoading(true);
setError(null);
setSuccess(false);
};
return (
<Dialog
open={open}
onClose={handleClose}
maxWidth="sm"
fullWidth
>
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
{t('createNewItem')}
<IconButton onClick={handleClose} size="small">
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, py: 1 }}>
{/* Name, Kategorie, Beschreibung, Preis, Rabatt, Bestand, Bestand erwartet */}
<TextField
label={t("name")}
name="name"
value={item.name}
onChange={handleChange}
fullWidth
/>
<TextField
label={t("category")}
name="category"
value={item.category}
onChange={handleChange}
fullWidth
/>
<TextField
label={t("description")}
name="description"
value={item.description}
onChange={handleChange}
fullWidth
/>
<TextField
label={t("price100")}
name="price100"
value={item.price100}
onChange={handleChange}
fullWidth
type='number'
/>
<TextField
label={t("discount100")}
name="discount100"
value={item.discount100}
onChange={handleChange}
fullWidth
type='number'
/>
<TextField
label={t("stockExpected")}
name="stockExpected"
value={item.stockExpected}
onChange={handleChange}
fullWidth
type='number'
/>
<TextField
label={t("stock")}
name="stock"
value={item.stock}
onChange={handleChange}
fullWidth
type='number'
/>
{/* Error Message */}
{error && (
<Alert severity="error" onClose={() => setError(null)}>
{error}
</Alert>
)}
{/* Success Message */}
{success && (
<Alert severity="success">
{t('itemCreatedSuccessfully')}
</Alert>
)}
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleSave} disabled={loading}>
{t('save')}
</Button>
<Button onClick={handleClose} disabled={loading}>
{t('cancel')}
</Button>
</DialogActions>
</Dialog>
);
}