Name des Mieters bei Einladung erfassen und anzeigen
This commit is contained in:
parent
29ebb2e6b7
commit
e36a65e63c
@ -20,13 +20,13 @@ function inviteLinkFor(req: Request, token: string): string {
|
|||||||
return `${baseUrl.replace(/\/$/, '')}/?invite=${token}`;
|
return `${baseUrl.replace(/\/$/, '')}/?invite=${token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST /v1/invitations (nur Vermieter/Admin) — { email, roomId?, role? } -> { invitation, inviteLink }
|
// POST /v1/invitations (nur Vermieter/Admin) — { email, fullName?, roomId?, role? } -> { invitation, inviteLink }
|
||||||
invitationsRouter.post(
|
invitationsRouter.post(
|
||||||
'/invitations',
|
'/invitations',
|
||||||
requireAuth,
|
requireAuth,
|
||||||
requireRole('LANDLORD', 'ADMIN'),
|
requireRole('LANDLORD', 'ADMIN'),
|
||||||
async (req: AuthedRequest, res: Response) => {
|
async (req: AuthedRequest, res: Response) => {
|
||||||
const { email, roomId, role } = req.body || {};
|
const { email, fullName, roomId, role } = req.body || {};
|
||||||
if (!email || !String(email).trim()) {
|
if (!email || !String(email).trim()) {
|
||||||
return res.status(400).json({ error: 'E-Mail-Adresse erforderlich' });
|
return res.status(400).json({ error: 'E-Mail-Adresse erforderlich' });
|
||||||
}
|
}
|
||||||
@ -64,6 +64,7 @@ invitationsRouter.post(
|
|||||||
const invitation = await prisma.invitation.create({
|
const invitation = await prisma.invitation.create({
|
||||||
data: {
|
data: {
|
||||||
email: normalizedEmail,
|
email: normalizedEmail,
|
||||||
|
fullName: typeof fullName === 'string' && fullName.trim() ? fullName.trim() : null,
|
||||||
token,
|
token,
|
||||||
role: invitedRole,
|
role: invitedRole,
|
||||||
roomId: roomId || null,
|
roomId: roomId || null,
|
||||||
@ -124,6 +125,7 @@ invitationsRouter.get('/invitations/:token/preview', async (req: Request, res: R
|
|||||||
}
|
}
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
email: invitation.email,
|
email: invitation.email,
|
||||||
|
fullName: invitation.fullName,
|
||||||
role: invitation.role,
|
role: invitation.role,
|
||||||
room: invitation.room,
|
room: invitation.room,
|
||||||
expiresAt: invitation.expiresAt,
|
expiresAt: invitation.expiresAt,
|
||||||
|
|||||||
@ -237,6 +237,7 @@ model Room {
|
|||||||
model Invitation {
|
model Invitation {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
email String
|
email String
|
||||||
|
fullName String? @map("full_name")
|
||||||
token String @unique
|
token String @unique
|
||||||
role UserRole @default(TENANT)
|
role UserRole @default(TENANT)
|
||||||
roomId String? @map("room_id")
|
roomId String? @map("room_id")
|
||||||
|
|||||||
@ -656,6 +656,10 @@
|
|||||||
<div class="invite-card">
|
<div class="invite-card">
|
||||||
<div id="inviteFormError" style="display:none" class="form-error"></div>
|
<div id="inviteFormError" style="display:none" class="form-error"></div>
|
||||||
<form id="inviteForm" class="invite-form-row">
|
<form id="inviteForm" class="invite-form-row">
|
||||||
|
<div class="field">
|
||||||
|
<label for="inviteName">Name des Mieters (optional)</label>
|
||||||
|
<input type="text" id="inviteName" placeholder="Max Mustermann" />
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="inviteEmail">E-Mail-Adresse des Mieters</label>
|
<label for="inviteEmail">E-Mail-Adresse des Mieters</label>
|
||||||
<input type="email" id="inviteEmail" required placeholder="mieter@beispiel.de" />
|
<input type="email" id="inviteEmail" required placeholder="mieter@beispiel.de" />
|
||||||
@ -674,7 +678,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<table class="invite-table" id="inviteTable" style="display:none">
|
<table class="invite-table" id="inviteTable" style="display:none">
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th>E-Mail</th><th>Zimmer</th><th>Status</th><th>Eingeladen am</th><th></th></tr>
|
<tr><th>Name</th><th>E-Mail</th><th>Zimmer</th><th>Status</th><th>Eingeladen am</th><th></th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="inviteTableBody"></tbody>
|
<tbody id="inviteTableBody"></tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -1305,6 +1309,7 @@
|
|||||||
sub.textContent = `Einladung als ${ROLE_LABEL[preview.role] || preview.role}` +
|
sub.textContent = `Einladung als ${ROLE_LABEL[preview.role] || preview.role}` +
|
||||||
(preview.room ? ` für ${preview.room.roomNumber}` : '') + '. Bitte Passwort festlegen.';
|
(preview.room ? ` für ${preview.room.roomNumber}` : '') + '. Bitte Passwort festlegen.';
|
||||||
document.getElementById('acceptEmail').value = preview.email;
|
document.getElementById('acceptEmail').value = preview.email;
|
||||||
|
if (preview.fullName) document.getElementById('acceptFullName').value = preview.fullName;
|
||||||
form.style.display = 'flex';
|
form.style.display = 'flex';
|
||||||
form.style.flexDirection = 'column';
|
form.style.flexDirection = 'column';
|
||||||
|
|
||||||
@ -1511,14 +1516,16 @@
|
|||||||
submitBtn.disabled = true;
|
submitBtn.disabled = true;
|
||||||
submitBtn.textContent = 'Lädt ein…';
|
submitBtn.textContent = 'Lädt ein…';
|
||||||
try {
|
try {
|
||||||
|
const fullName = document.getElementById('inviteName').value.trim() || undefined;
|
||||||
const email = document.getElementById('inviteEmail').value.trim();
|
const email = document.getElementById('inviteEmail').value.trim();
|
||||||
const roomId = document.getElementById('inviteRoom').value || undefined;
|
const roomId = document.getElementById('inviteRoom').value || undefined;
|
||||||
const data = await apiFetch('/invitations', {
|
const data = await apiFetch('/invitations', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ email, roomId }),
|
body: JSON.stringify({ email, fullName, roomId }),
|
||||||
});
|
});
|
||||||
document.getElementById('inviteLinkText').textContent = data.inviteLink;
|
document.getElementById('inviteLinkText').textContent = data.inviteLink;
|
||||||
document.getElementById('inviteLinkResult').style.display = 'flex';
|
document.getElementById('inviteLinkResult').style.display = 'flex';
|
||||||
|
document.getElementById('inviteName').value = '';
|
||||||
document.getElementById('inviteEmail').value = '';
|
document.getElementById('inviteEmail').value = '';
|
||||||
loadInvitations();
|
loadInvitations();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -1551,6 +1558,7 @@
|
|||||||
table.style.display = 'table';
|
table.style.display = 'table';
|
||||||
data.invitations.forEach(inv => {
|
data.invitations.forEach(inv => {
|
||||||
const tr = document.createElement('tr');
|
const tr = document.createElement('tr');
|
||||||
|
tr.appendChild(el('td', '', inv.fullName || '–'));
|
||||||
tr.appendChild(el('td', '', inv.email));
|
tr.appendChild(el('td', '', inv.email));
|
||||||
tr.appendChild(el('td', '', inv.room ? inv.room.roomNumber : '–'));
|
tr.appendChild(el('td', '', inv.room ? inv.room.roomNumber : '–'));
|
||||||
tr.appendChild(el('td', '', `<span class="invite-status-chip ${inv.status}">${INVITE_STATUS_LABEL[inv.status] || inv.status}</span>`));
|
tr.appendChild(el('td', '', `<span class="invite-status-chip ${inv.status}">${INVITE_STATUS_LABEL[inv.status] || inv.status}</span>`));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user