feat(web): split contact name into first and last name

This commit is contained in:
2026-08-31 22:08:53 +02:00
parent ea917b4148
commit c945e785c8
3 changed files with 354 additions and 237 deletions
+48 -29
View File
@@ -42,7 +42,8 @@ type AddressValue struct {
type ContactFormData struct {
Book string
ID string // empty when creating a new contact
FullName string
Forename string
Surname string
Organization string
Birthday string // "YYYY-MM-DD", empty if not set
Note string
@@ -158,30 +159,41 @@ templ ContactsList(username, book string, contacts []ContactSummary) {
}
}
// typeSelect renders the TYPE dropdown shared by phone/email/address rows.
// typeSelect renders the TYPE dropdown shared by email/address rows.
templ typeSelect(name, selected string) {
<select name={ name } class="rounded-md border-gray-300 border px-2 py-1.5 text-sm">
<option value="" selected?={ selected == "" }>Sonstige</option>
<option value="home" selected?={ selected == "home" }>Privat</option>
<option value="work" selected?={ selected == "work" }>Geschäftlich</option>
<option value="" selected?={ selected == "" }>Other</option>
<option value="home" selected?={ selected == "home" }>Home</option>
<option value="work" selected?={ selected == "work" }>Work</option>
</select>
}
// phoneTypeSelect renders the phone TYPE dropdown. An unspecified number is
// shown as "Mobile" (the vCard "cell" type), matching how mobile clients
// label a contact's main number.
templ phoneTypeSelect(name, selected string) {
<select name={ name } class="rounded-md border-gray-300 border px-2 py-1.5 text-sm">
<option value="cell" selected?={ selected == "cell" || selected == "" }>Mobile</option>
<option value="home" selected?={ selected == "home" }>Home</option>
<option value="work" selected?={ selected == "work" }>Work</option>
</select>
}
templ phoneRow(p LabeledValue) {
<div class="form-row flex gap-2 items-center">
@typeSelect("phone_type", p.Type)
<input name="phone_value" type="tel" value={ p.Value } placeholder="Telefonnummer"
@phoneTypeSelect("phone_type", p.Type)
<input name="phone_value" type="tel" value={ p.Value } placeholder="Phone number"
class="flex-1 rounded-md border-gray-300 border px-2 py-1.5 text-sm"/>
<button type="button" class="remove-row-btn text-red-600 hover:underline text-xs shrink-0">Entfernen</button>
<button type="button" class="remove-row-btn text-red-600 hover:underline text-xs shrink-0">Remove</button>
</div>
}
templ emailRow(p LabeledValue) {
<div class="form-row flex gap-2 items-center">
@typeSelect("email_type", p.Type)
<input name="email_value" type="email" value={ p.Value } placeholder="E-Mail-Adresse"
<input name="email_value" type="email" value={ p.Value } placeholder="Email address"
class="flex-1 rounded-md border-gray-300 border px-2 py-1.5 text-sm"/>
<button type="button" class="remove-row-btn text-red-600 hover:underline text-xs shrink-0">Entfernen</button>
<button type="button" class="remove-row-btn text-red-600 hover:underline text-xs shrink-0">Remove</button>
</div>
}
@@ -190,17 +202,17 @@ templ addressRow(a AddressValue) {
<div class="col-span-2">
@typeSelect("address_type", a.Type)
</div>
<input name="address_street" type="text" value={ a.Street } placeholder="Straße und Hausnummer"
<input name="address_street" type="text" value={ a.Street } placeholder="Street and number"
class="col-span-2 rounded-md border-gray-300 border px-2 py-1.5 text-sm"/>
<input name="address_city" type="text" value={ a.City } placeholder="Stadt"
<input name="address_city" type="text" value={ a.City } placeholder="City"
class="rounded-md border-gray-300 border px-2 py-1.5 text-sm"/>
<input name="address_postal_code" type="text" value={ a.PostalCode } placeholder="PLZ"
<input name="address_postal_code" type="text" value={ a.PostalCode } placeholder="Postal code"
class="rounded-md border-gray-300 border px-2 py-1.5 text-sm"/>
<input name="address_region" type="text" value={ a.Region } placeholder="Bundesland/Region"
<input name="address_region" type="text" value={ a.Region } placeholder="State / Region"
class="rounded-md border-gray-300 border px-2 py-1.5 text-sm"/>
<input name="address_country" type="text" value={ a.Country } placeholder="Land"
<input name="address_country" type="text" value={ a.Country } placeholder="Country"
class="rounded-md border-gray-300 border px-2 py-1.5 text-sm"/>
<button type="button" class="remove-row-btn text-red-600 hover:underline text-xs col-span-2 text-left">Entfernen</button>
<button type="button" class="remove-row-btn text-red-600 hover:underline text-xs col-span-2 text-left">Remove</button>
</div>
}
@@ -230,22 +242,29 @@ templ ContactForm(username string, data ContactFormData, errMsg string) {
<img id="photo-preview" src="" alt="" class="w-20 h-20 rounded-full object-cover hidden"/>
<div class="space-y-1">
<label class="cursor-pointer inline-block bg-white border border-gray-300 rounded-md px-3 py-1.5 text-sm font-medium text-gray-700 hover:bg-gray-50">
Foto wählen
Choose photo
<input id="photo-input" name="photo" type="file" accept="image/*" class="hidden"/>
</label>
if data.PhotoDataURL != "" {
<label class="flex items-center gap-1 text-xs text-gray-500">
<input id="remove-photo" name="remove_photo" type="checkbox" value="1"/>
Foto entfernen
Remove photo
</label>
}
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Full name</label>
<input name="full_name" type="text" required value={ data.FullName }
class="mt-1 block w-full rounded-md border-gray-300 border px-3 py-2 shadow-sm"/>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label class="block text-sm font-medium text-gray-700">First name</label>
<input name="forename" type="text" value={ data.Forename }
class="mt-1 block w-full rounded-md border-gray-300 border px-3 py-2 shadow-sm"/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Last name</label>
<input name="surname" type="text" value={ data.Surname }
class="mt-1 block w-full rounded-md border-gray-300 border px-3 py-2 shadow-sm"/>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Organization</label>
@@ -253,44 +272,44 @@ templ ContactForm(username string, data ContactFormData, errMsg string) {
class="mt-1 block w-full rounded-md border-gray-300 border px-3 py-2 shadow-sm"/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Geburtstag</label>
<label class="block text-sm font-medium text-gray-700">Birthday</label>
<input name="birthday" type="date" value={ data.Birthday }
class="mt-1 block w-full rounded-md border-gray-300 border px-3 py-2 shadow-sm"/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Telefonnummern</label>
<label class="block text-sm font-medium text-gray-700 mb-2">Phones</label>
<div id="phones-container" class="space-y-2">
for _, p := range data.Phones {
@phoneRow(p)
}
</div>
<button type="button" data-add-target="phones-container" class="add-row-btn mt-2 text-sm text-indigo-600 hover:underline">
+ Telefonnummer hinzufügen
+ Add phone number
</button>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">E-Mail-Adressen</label>
<label class="block text-sm font-medium text-gray-700 mb-2">Emails</label>
<div id="emails-container" class="space-y-2">
for _, e := range data.Emails {
@emailRow(e)
}
</div>
<button type="button" data-add-target="emails-container" class="add-row-btn mt-2 text-sm text-indigo-600 hover:underline">
+ E-Mail-Adresse hinzufügen
+ Add email address
</button>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Adressen</label>
<label class="block text-sm font-medium text-gray-700 mb-2">Addresses</label>
<div id="addresses-container" class="space-y-2">
for _, a := range data.Addresses {
@addressRow(a)
}
</div>
<button type="button" data-add-target="addresses-container" class="add-row-btn mt-2 text-sm text-indigo-600 hover:underline">
+ Adresse hinzufügen
+ Add address
</button>
</div>