mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 20:51:18 +00:00
Merge pull request #83 from Since-3/feature/employee-editing
employee can now be editied
This commit is contained in:
commit
6eac517283
4 changed files with 168 additions and 34 deletions
23
src/app/(main)/employee/edit/[id]/page.tsx
Normal file
23
src/app/(main)/employee/edit/[id]/page.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { requireManagerOrPermission, isEmployee } from "@/src/lib/auth/getUser";
|
||||
import EmployeeEditView from "@/src/modules/employee/ui/views/employee-edit-view";
|
||||
import { getMyEmployeeRoles } from "@/src/modules/employee/actions/employee-actions";
|
||||
|
||||
const EmployeeEdit = async ({ params }: { params: Promise<{ id: string }> }) => {
|
||||
const userData = await requireManagerOrPermission((p) => p.employees.edit);
|
||||
const { id } = await params;
|
||||
|
||||
const rolesResult = await getMyEmployeeRoles();
|
||||
const customRoles = rolesResult.success ? rolesResult.roles : [];
|
||||
|
||||
const callerPermissions = isEmployee(userData) ? userData.permissions : null;
|
||||
|
||||
return (
|
||||
<EmployeeEditView
|
||||
employeeId={id}
|
||||
customRoles={customRoles}
|
||||
callerPermissions={callerPermissions}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmployeeEdit;
|
||||
|
|
@ -469,7 +469,11 @@ export async function updateEmployee(
|
|||
let onboardingTokenExpiry = existingEmployee.onboardingTokenExpiry;
|
||||
let shouldSendEmail = false;
|
||||
|
||||
if (status === "published" && !existingEmployee.onboardingToken) {
|
||||
if (
|
||||
status === "published" &&
|
||||
!existingEmployee.isOnboarded &&
|
||||
!existingEmployee.onboardingToken
|
||||
) {
|
||||
onboardingToken = generateOnboardingToken();
|
||||
onboardingTokenExpiry = new Date();
|
||||
onboardingTokenExpiry.setDate(onboardingTokenExpiry.getDate() + 7);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Checkbox } from "@/src/components/ui/checkbox";
|
|||
import { Label } from "@/src/components/ui/label";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { createEmployee } from "../../actions/employee-actions";
|
||||
import { createEmployee, updateEmployee } from "../../actions/employee-actions";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const LOCATION = [
|
||||
|
|
@ -48,17 +48,55 @@ type CallerPermissions = {
|
|||
rooms: { view: boolean; create: boolean; edit: boolean; delete: boolean };
|
||||
} | null;
|
||||
|
||||
type InitialEmployeeData = {
|
||||
email: string;
|
||||
roles: string[];
|
||||
locations: string[];
|
||||
permissions: unknown;
|
||||
status: string | null;
|
||||
};
|
||||
|
||||
interface EmployeeCreateViewProps {
|
||||
customRoles: Array<{ value: string; label: string }>;
|
||||
callerPermissions: CallerPermissions;
|
||||
mode?: "create" | "edit";
|
||||
employeeId?: string;
|
||||
initialData?: InitialEmployeeData;
|
||||
}
|
||||
|
||||
const EmployeeCreateView = ({ customRoles, callerPermissions }: EmployeeCreateViewProps) => {
|
||||
const EmployeeCreateView = ({
|
||||
customRoles,
|
||||
callerPermissions,
|
||||
mode = "create",
|
||||
employeeId,
|
||||
initialData,
|
||||
}: EmployeeCreateViewProps) => {
|
||||
const router = useRouter();
|
||||
const [employeeMail, setEmplyoeeMail] = useState("");
|
||||
const [isMultipleLocation, setIsMultipleLocation] = useState(false);
|
||||
const [selectedLocations, setSelectedLocations] = useState<string[]>([]);
|
||||
const [selectedRoles, setSelectedRoles] = useState<string[]>([]);
|
||||
|
||||
const initialPermissions = (): PermissionsState => {
|
||||
if (initialData?.permissions && typeof initialData.permissions === "object") {
|
||||
const p = initialData.permissions as Partial<PermissionsState>;
|
||||
return {
|
||||
employees: p.employees ?? { view: false, create: false, edit: false, delete: false },
|
||||
courses: p.courses ?? { view: false, create: false, edit: false, delete: false },
|
||||
rooms: p.rooms ?? { view: false, create: false, edit: false, delete: false },
|
||||
};
|
||||
}
|
||||
return {
|
||||
employees: { view: false, create: false, edit: false, delete: false },
|
||||
courses: { view: false, create: false, edit: false, delete: false },
|
||||
rooms: { view: false, create: false, edit: false, delete: false },
|
||||
};
|
||||
};
|
||||
|
||||
const [employeeMail, setEmplyoeeMail] = useState(initialData?.email ?? "");
|
||||
const [isMultipleLocation, setIsMultipleLocation] = useState(
|
||||
(initialData?.locations?.length ?? 0) > 0
|
||||
);
|
||||
const [selectedLocations, setSelectedLocations] = useState<string[]>(
|
||||
initialData?.locations ?? []
|
||||
);
|
||||
const [selectedRoles, setSelectedRoles] = useState<string[]>(initialData?.roles ?? []);
|
||||
|
||||
// Kombiniere Standard-Rollen mit custom Rollen (ohne Duplikate)
|
||||
const allRoleValues = new Set([
|
||||
|
|
@ -73,11 +111,7 @@ const EmployeeCreateView = ({ customRoles, callerPermissions }: EmployeeCreateVi
|
|||
|
||||
const [roles, setRoles] = useState(combinedRoles);
|
||||
const [newlyCreatedRoles, setNewlyCreatedRoles] = useState<string[]>([]);
|
||||
const [permissionsState, setPermissionsState] = useState<PermissionsState>({
|
||||
employees: { view: false, create: false, edit: false, delete: false },
|
||||
courses: { view: false, create: false, edit: false, delete: false },
|
||||
rooms: { view: false, create: false, edit: false, delete: false },
|
||||
});
|
||||
const [permissionsState, setPermissionsState] = useState<PermissionsState>(initialPermissions());
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [errors, setErrors] = useState<Record<string, string[]>>({});
|
||||
|
||||
|
|
@ -119,17 +153,22 @@ const EmployeeCreateView = ({ customRoles, callerPermissions }: EmployeeCreateVi
|
|||
permissions: permissionsState,
|
||||
});
|
||||
|
||||
const handlePublish = async () => {
|
||||
const handleSubmit = async (status: "draft" | "published") => {
|
||||
setIsSubmitting(true);
|
||||
setErrors({});
|
||||
try {
|
||||
const result = await createEmployee(buildData(), "published");
|
||||
const data = buildData();
|
||||
const result =
|
||||
mode === "edit" && employeeId
|
||||
? await updateEmployee(employeeId, data, status)
|
||||
: await createEmployee(data, status);
|
||||
if (result.success) {
|
||||
toast.success(result.message);
|
||||
router.push("/employee");
|
||||
} else {
|
||||
toast.error(result.error || "Ein Fehler ist aufgetreten");
|
||||
if (result.fieldErrors) setErrors(result.fieldErrors);
|
||||
if ("fieldErrors" in result && result.fieldErrors)
|
||||
setErrors(result.fieldErrors as Record<string, string[]>);
|
||||
}
|
||||
} catch {
|
||||
toast.error("Ein unerwarteter Fehler ist aufgetreten");
|
||||
|
|
@ -138,28 +177,14 @@ const EmployeeCreateView = ({ customRoles, callerPermissions }: EmployeeCreateVi
|
|||
}
|
||||
};
|
||||
|
||||
const handleSaveDraft = async () => {
|
||||
setIsSubmitting(true);
|
||||
setErrors({});
|
||||
try {
|
||||
const result = await createEmployee(buildData(), "draft");
|
||||
if (result.success) {
|
||||
toast.success(result.message);
|
||||
router.push("/employee");
|
||||
} else {
|
||||
toast.error(result.error || "Ein Fehler ist aufgetreten");
|
||||
if (result.fieldErrors) setErrors(result.fieldErrors);
|
||||
}
|
||||
} catch {
|
||||
toast.error("Ein unerwarteter Fehler ist aufgetreten");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
const handlePublish = () => handleSubmit("published");
|
||||
const handleSaveDraft = () => handleSubmit("draft");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold">Mitarbeiter anlegen</h1>
|
||||
<h1 className="text-3xl font-bold">
|
||||
{mode === "edit" ? "Mitarbeiter bearbeiten" : "Mitarbeiter anlegen"}
|
||||
</h1>
|
||||
<p className="text-xl mt-2">
|
||||
Alle Daten können nach dem Speichern geändert oder gelöscht werden.
|
||||
</p>
|
||||
|
|
|
|||
82
src/modules/employee/ui/views/employee-edit-view.tsx
Normal file
82
src/modules/employee/ui/views/employee-edit-view.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { getEmployeeById } from "../../actions/employee-actions";
|
||||
import { toast } from "sonner";
|
||||
import EmployeeCreateView from "./employee-create-view";
|
||||
|
||||
interface EmployeeEditViewProps {
|
||||
employeeId: string;
|
||||
customRoles: Array<{ value: string; label: string }>;
|
||||
callerPermissions: {
|
||||
employees: { view: boolean; create: boolean; edit: boolean; delete: boolean };
|
||||
courses: { view: boolean; create: boolean; edit: boolean; delete: boolean };
|
||||
rooms: { view: boolean; create: boolean; edit: boolean; delete: boolean };
|
||||
} | null;
|
||||
}
|
||||
|
||||
type EmployeeData = {
|
||||
id: string;
|
||||
email: string;
|
||||
roles: string[];
|
||||
locations: string[];
|
||||
permissions: unknown;
|
||||
status: string | null;
|
||||
isOnboarded: boolean;
|
||||
};
|
||||
|
||||
const EmployeeEditView = ({
|
||||
employeeId,
|
||||
customRoles,
|
||||
callerPermissions,
|
||||
}: EmployeeEditViewProps) => {
|
||||
const router = useRouter();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [employeeData, setEmployeeData] = useState<EmployeeData | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
|
||||
const fetchEmployee = async () => {
|
||||
if (alive) setIsLoading(true);
|
||||
const result = await getEmployeeById(employeeId);
|
||||
|
||||
if (result.success && result.employee) {
|
||||
if (alive) setEmployeeData(result.employee as EmployeeData);
|
||||
} else {
|
||||
toast.error(result.error || "Fehler beim Laden des Mitarbeiters");
|
||||
router.push("/employee");
|
||||
}
|
||||
if (alive) setIsLoading(false);
|
||||
};
|
||||
|
||||
fetchEmployee();
|
||||
return () => {
|
||||
alive = false;
|
||||
};
|
||||
}, [employeeId, router]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center min-h-[400px]">
|
||||
<p className="text-xl">Mitarbeiter wird geladen...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!employeeData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EmployeeCreateView
|
||||
mode="edit"
|
||||
employeeId={employeeId}
|
||||
initialData={employeeData}
|
||||
customRoles={customRoles}
|
||||
callerPermissions={callerPermissions}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmployeeEditView;
|
||||
Loading…
Add table
Add a link
Reference in a new issue