employees

master
alex 2024-01-11 01:14:55 +01:00
parent c3268bd71b
commit 23af00a619
7 changed files with 287 additions and 67 deletions

View File

@ -1,15 +0,0 @@
# Jannex Admin Dashboard
With this system you can easily run your Python scripts and visualize them in the user interface to manage them more easily. You can see who started the task and who worked on it later in the task view. When a group task is started, the backend server starts the Python script and sends the result to the web UI. The web UI can then be used to start the other tasks or to repeat or undo the current task.
## Features
- Dynamic Group Task System
- Equipment Documentation System
- Advanced log system with log viewer and log filter
- Robot control system
- Role System
- Scanner integration to use mobile or pc devices as qrcode scanners
- User login system
- User deactivation system
- User API key system

View File

@ -8,7 +8,12 @@
"confirm": "Bestätigen",
"create": "Erstellen"
},
"contactAdmin": "Bitte kontaktieren Sie einen Administrator"
"action": "Aktion",
"contactAdmin": "Bitte kontaktieren Sie einen Administrator",
"username": "Anzeigename",
"accountName": "Benutzername",
"password": "Passwort",
"noDataFound": "Keine Daten gefunden"
},
"pageNotFound": {
"title": "Seite nicht gefunden",
@ -28,5 +33,12 @@
"calendar": "Kalender",
"support": "Unterstützung",
"feedback": "Feedback"
},
"employees": {
"pageTitle": "Mitarbeiter",
"addEmployee": "Mitarbeiter anlegen",
"modalAddEmployee": {
"checkboxPasswordChange": "Mitarbeiter auffordern, das Passwort zu ändern (empfohlen)"
}
}
}

View File

@ -8,7 +8,12 @@
"confirm": "Confirm",
"create": "Create"
},
"contactAdmin": "Please contact an administrator"
"action": "Action",
"contactAdmin": "Please contact an administrator",
"username": "Username",
"accountName": "Account name",
"password": "Password",
"noDataFound": "No data found"
},
"pageNotFound": {
"title": "Page Not Found",
@ -28,5 +33,12 @@
"calendar": "Calendar",
"support": "Support",
"feedback": "Feedback"
},
"employees": {
"pageTitle": "Employees",
"addEmployee": "Add employee",
"modalAddEmployee": {
"checkboxPasswordChange": "Require employee to change password (recommended)"
}
}
}

View File

@ -0,0 +1,27 @@
import { Table } from "antd";
import { useTranslation } from "react-i18next";
export default function MyTable({ props }) {
const { t } = useTranslation();
return (
<Table
{...props}
scroll={{ x: "max-content" }}
locale={{
emptyText: (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: 110,
}}
>
{t("common.noDataFound")}
</div>
),
}}
/>
);
}

View File

@ -1,67 +1,217 @@
import { PlusOutlined } from "@ant-design/icons";
import { Button, Table } from "antd";
import { LockOutlined, PlusOutlined, UserOutlined } from "@ant-design/icons";
import { Button, Checkbox, Col, Form, Grid, Input, Row, Table } from "antd";
import MyModal, {
MyModalCloseCreateButtonFooter,
} from "../../Components/MyModal";
import { useEffect, useState } from "react";
import { Constants, myFetch, EncodeStringToBase64 } from "../../utils";
import { useTranslation } from "react-i18next";
import MyTable from "../../Components/MyTable";
const { useBreakpoint } = Grid;
export default function Employees() {
const { t } = useTranslation();
const screenBreakpoint = useBreakpoint();
const [employees, setEmployees] = useState([]);
const [isAddEmployeeModalOpen, setIsAddEmployeeModalOpen] = useState(false);
const [username, setUsername] = useState("");
const [accountName, setAccountName] = useState("");
const [password, setPassword] = useState("");
const [isRequesting, setIsRequesting] = useState(false);
const getTableColumns = () => {
return [
{
title: "Name",
dataIndex: "name",
key: "name",
title: t("common.accountName"),
dataIndex: "account_name",
key: "account_name",
},
{
title: "Age",
dataIndex: "age",
key: "age",
title: t("common.username"),
dataIndex: "username",
key: "username",
},
{
title: "Action",
title: t("common.action"),
dataIndex: "action",
key: "actions",
render: () => <a>Delete</a>,
render: () => <a>{t("common.button.delete")}</a>,
},
];
};
const getTableItems = () => {
return [
{
key: "1",
name: "John Brown",
age: 32,
address: "New York No. 1 Lake Park",
},
{
key: "2",
name: "Jim Green",
age: 42,
address: "London No. 1 Lake Park",
},
{
key: "3",
name: "Joe Black",
age: 32,
address: "Sidney No. 1 Lake Park",
},
];
return employees.map((employee) => {
return {
key: employee.account_name,
account_name: employee.account_name,
username: employee.username,
};
});
};
const handleAddEmployeeModalClose = () => {
setIsAddEmployeeModalOpen(false);
setUsername("");
setAccountName("");
setPassword("");
};
const fetchEmployees = () => {
myFetch("/users", "GET")
.then((data) => {
setIsRequesting(false);
setEmployees(data.employees);
})
.catch((errStatus) => {
console.log(errStatus);
});
};
useEffect(() => {
setIsRequesting(true);
fetchEmployees();
}, []);
return (
<>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
flexDirection: screenBreakpoint.xs ? "column" : "row",
}}
>
<h1>Employees</h1>
<Button type="primary" icon={<PlusOutlined />}>
Add employee
<h1>
{t("employees.pageTitle")}
{employees.length > 0 && ` (${employees.length})`}
</h1>
<Button
type="primary"
block={screenBreakpoint.xs}
icon={<PlusOutlined />}
onClick={() => setIsAddEmployeeModalOpen(true)}
>
{t("employees.addEmployee")}
</Button>
</div>
<Table columns={getTableColumns()} dataSource={getTableItems()} />
<MyTable
props={{
loading: isRequesting,
columns: getTableColumns(),
dataSource: getTableItems(),
}}
/>
<MyModal
title={t("employees.addEmployee")}
isOpen={isAddEmployeeModalOpen}
onCancel={handleAddEmployeeModalClose}
footer={
<MyModalCloseCreateButtonFooter
onCancel={handleAddEmployeeModalClose}
isCreateButtonLoading={isRequesting}
onCreate={() => {
setIsRequesting(true);
myFetch("/users", "POST", {
username: username,
accountName: accountName,
password: EncodeStringToBase64(password),
})
.then(() => {
setIsRequesting(false);
handleAddEmployeeModalClose();
fetchEmployees();
})
.catch((errStatus) => {
console.log(errStatus);
setIsRequesting(false);
});
}}
/>
}
>
<Form>
<Form.Item
name="username"
required
rules={[
{ required: true, message: "Please enter your username!" },
{
min: Constants.GLOBALS.MIN_USERNAME_LENGTH,
message: `Please enter a username length of at least ${Constants.GLOBALS.MIN_USERNAME_LENGTH}!`,
},
]}
>
<Input
prefix={<UserOutlined />}
placeholder={t("common.username")}
value={username}
onChange={(e) => setUsername(e.target.value)}
minLength={Constants.GLOBALS.MIN_USERNAME_LENGTH}
maxLength={Constants.GLOBALS.MAX_USERNAME_LENGTH}
/>
</Form.Item>
<Form.Item
name="accountName"
required
rules={[
{ required: true, message: "Please enter an account name!" },
{
min: Constants.GLOBALS.MIN_USERNAME_LENGTH,
message: `Please enter an account name length of at least ${Constants.GLOBALS.MIN_ACCOUNT_NAME_LENGTH}!`,
},
]}
>
<Input
prefix={<UserOutlined />}
placeholder={t("common.accountName")}
value={accountName}
onChange={(e) => setAccountName(e.target.value)}
minLength={Constants.GLOBALS.MIN_USERNAME_LENGTH}
maxLength={Constants.GLOBALS.MAX_USERNAME_LENGTH}
/>
</Form.Item>
<Form.Item
name="password"
required
rules={[
{
required: true,
message: "Please enter your Password!",
},
{
min: Constants.GLOBALS.MIN_PASSWORD_LENGTH,
message: `Please enter a password length of at least ${Constants.GLOBALS.MIN_PASSWORD_LENGTH}!`,
},
]}
>
<Input.Password
prefix={<LockOutlined />}
placeholder={t("common.password")}
value={password}
onChange={(e) => setPassword(e.target.value)}
minLength={Constants.GLOBALS.MIN_PASSWORD_LENGTH}
maxLength={Constants.GLOBALS.MAX_PASSWORD_LENGTH}
/>
</Form.Item>
<Form.Item>
<Checkbox defaultChecked>
{t("employees.modalAddEmployee.checkboxPasswordChange")}
</Checkbox>
</Form.Item>
</Form>
</MyModal>
</>
);
}

View File

@ -11,6 +11,7 @@ import { useState } from "react";
export default function Login() {
const [username, setUsername] = useState("");
const [accountName, setAccountName] = useState("");
const [password, setPassword] = useState("");
const [api, contextHolder] = notification.useNotification();
@ -27,14 +28,14 @@ export default function Login() {
api["error"]({
message: "Login failed",
description: "Please check your username and password!",
description: "Please check your accountName and password!",
});
};
const handleSubmit = () => {
if (
username.length > Constants.GLOBALS.MAX_USERNAME_LENGTH ||
username.length < Constants.GLOBALS.MIN_USERNAME_LENGTH ||
accountName.length > Constants.GLOBALS.MAX_ACCOUNT_NAME_LENGTH ||
accountName.length < Constants.GLOBALS.MIN_ACCOUNT_NAME_LENGTH ||
password.length > Constants.GLOBALS.MAX_PASSWORD_LENGTH ||
password.length < Constants.GLOBALS.MIN_PASSWORD_LENGTH
) {
@ -42,13 +43,19 @@ export default function Login() {
return;
}
let body = {
accountName: accountName.toLocaleLowerCase(),
password: EncodeStringToBase64(password),
};
if (selectedMethod === "2") {
body.username = username;
}
myFetch(
`/user/auth/${selectedMethod === "1" ? "login" : "signup"}`,
"POST",
{
username: username,
password: EncodeStringToBase64(password),
},
body,
{},
myFetchContentType.JSON,
"",
@ -102,25 +109,50 @@ export default function Login() {
/>
<Form>
{selectedMethod === "2" && (
<Form.Item
name="username"
required
rules={[
{ required: true, message: "Please enter your username!" },
{
min: Constants.GLOBALS.MIN_USERNAME_LENGTH,
message: `Please enter a username length of at least ${Constants.GLOBALS.MIN_USERNAME_LENGTH}!`,
},
]}
>
<Input
prefix={<UserOutlined />}
placeholder="Anzeigename"
onChange={(e) => setUsername(e.target.value)}
minLength={Constants.GLOBALS.MIN_USERNAME_LENGTH}
maxLength={Constants.GLOBALS.MAX_USERNAME_LENGTH}
/>
</Form.Item>
)}
<Form.Item
name="username"
hasFeedback
name="accountName"
validateStatus="validating"
required
rules={[
{ required: true, message: "Please enter your username!" },
{ required: true, message: "Please enter your accountName!" },
{
min: Constants.GLOBALS.MIN_USERNAME_LENGTH,
message: `Please enter a username length of at least ${Constants.GLOBALS.MIN_USERNAME_LENGTH}!`,
min: Constants.GLOBALS.MIN_ACCOUNT_NAME_LENGTH,
message: `Please enter a accountName length of at least ${Constants.GLOBALS.MIN_ACCOUNT_NAME_LENGTH}!`,
},
]}
>
<Input
prefix={<UserOutlined />}
placeholder="Benutzername"
onChange={(e) => setUsername(e.target.value)}
minLength={Constants.GLOBALS.MIN_USERNAME_LENGTH}
maxLength={Constants.GLOBALS.MAX_USERNAME_LENGTH}
onChange={(e) => setAccountName(e.target.value)}
minLength={Constants.GLOBALS.MIN_ACCOUNT_NAME_LENGTH}
maxLength={Constants.GLOBALS.MAX_ACCOUNT_NAME_LENGTH}
/>
</Form.Item>
<Form.Item
name="password"
required

View File

@ -55,7 +55,9 @@ export const Constants = {
GLOBALS: {
MIN_USERNAME_LENGTH: 2,
MAX_USERNAME_LENGTH: 20,
MIN_PASSWORD_LENGTH: 6,
MIN_ACCOUNT_NAME_LENGTH: 2,
MAX_ACCOUNT_NAME_LENGTH: 20,
MIN_PASSWORD_LENGTH: 8,
MAX_PASSWORD_LENGTH: 64,
},
MAX_AVATAR_SIZE: 5 * 1024 * 1024,