Merge pull request #83 from Since-3/feature/employee-editing

employee can now be editied
This commit is contained in:
Rajpreet0 2026-04-17 20:03:54 +02:00 • committed by GitHub
commit 6eac517283
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 168 additions and 34 deletions

View 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;

View file

@ -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);

View file

@ -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>

View 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;