Compare commits
4 Commits
59f593280b
...
f0eb3a76be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0eb3a76be | ||
|
|
6d837c518c | ||
|
|
97646a31df | ||
|
|
cfb3260e0e |
@@ -104,7 +104,7 @@ public class PersonController {
|
||||
|
||||
@PostMapping("/{id}/aliases")
|
||||
@RequirePermission(Permission.WRITE_ALL)
|
||||
public PersonNameAlias addAlias(@PathVariable UUID id, @RequestBody PersonNameAliasDTO dto) {
|
||||
public PersonNameAlias addAlias(@PathVariable UUID id, @Valid @RequestBody PersonNameAliasDTO dto) {
|
||||
return personService.addAlias(id, dto);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.raddatz.familienarchiv.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import org.raddatz.familienarchiv.model.PersonNameAliasType;
|
||||
|
||||
public record PersonNameAliasDTO(
|
||||
String lastName,
|
||||
String firstName,
|
||||
PersonNameAliasType type
|
||||
@NotBlank @Size(max = 255) String lastName,
|
||||
@Size(max = 255) String firstName,
|
||||
@NotNull PersonNameAliasType type
|
||||
) {}
|
||||
|
||||
@@ -458,4 +458,22 @@ class PersonControllerTest {
|
||||
mockMvc.perform(delete("/api/persons/{id}/aliases/{aliasId}", UUID.randomUUID(), UUID.randomUUID()))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void addAlias_returns400_whenLastNameIsBlank() throws Exception {
|
||||
mockMvc.perform(post("/api/persons/{id}/aliases", UUID.randomUUID())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"lastName\":\"\",\"type\":\"BIRTH\"}"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(authorities = "WRITE_ALL")
|
||||
void addAlias_returns400_whenTypeIsNull() throws Exception {
|
||||
mockMvc.perform(post("/api/persons/{id}/aliases", UUID.randomUUID())
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"lastName\":\"de Gruyter\"}"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,11 +69,9 @@ const coCorrespondents = $derived.by(() => {
|
||||
<!-- Left column: Person card + name history -->
|
||||
<div>
|
||||
<PersonCard person={person} canWrite={data.canWrite} />
|
||||
{#if data.aliases.length > 0}
|
||||
<div class="mt-6">
|
||||
<NameHistoryCard aliases={data.aliases} personFirstName={person.firstName} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="mt-6">
|
||||
<NameHistoryCard aliases={data.aliases} personFirstName={person.firstName} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right column: correspondents + documents -->
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render } from 'vitest-browser-svelte';
|
||||
import { page } from 'vitest/browser';
|
||||
import NameHistoryCard from './NameHistoryCard.svelte';
|
||||
|
||||
const aliases = [
|
||||
{ id: 'a1', lastName: 'de Gruyter', firstName: null, type: 'BIRTH', sortOrder: 0 },
|
||||
{ id: 'a2', lastName: 'Schmidt', firstName: 'Maria', type: 'WIDOWED', sortOrder: 1 }
|
||||
];
|
||||
|
||||
describe('NameHistoryCard', () => {
|
||||
it('should render one row per alias', async () => {
|
||||
render(NameHistoryCard, { aliases, personFirstName: 'Clara' });
|
||||
|
||||
await expect.element(page.getByText('de Gruyter')).toBeInTheDocument();
|
||||
await expect.element(page.getByText('Schmidt')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show empty state when no aliases', async () => {
|
||||
render(NameHistoryCard, { aliases: [], personFirstName: 'Clara' });
|
||||
|
||||
const emptyText = document.querySelector('.italic');
|
||||
expect(emptyText).not.toBeNull();
|
||||
expect(emptyText!.textContent!.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should use personFirstName when alias firstName is null', async () => {
|
||||
render(NameHistoryCard, {
|
||||
aliases: [{ id: 'a1', lastName: 'de Gruyter', firstName: null, type: 'BIRTH', sortOrder: 0 }],
|
||||
personFirstName: 'Clara'
|
||||
});
|
||||
|
||||
await expect.element(page.getByText('Clara')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should use alias firstName when provided', async () => {
|
||||
render(NameHistoryCard, {
|
||||
aliases: [
|
||||
{ id: 'a1', lastName: 'Schmidt', firstName: 'Maria', type: 'WIDOWED', sortOrder: 0 }
|
||||
],
|
||||
personFirstName: 'Clara'
|
||||
});
|
||||
|
||||
await expect.element(page.getByText('Maria')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show type labels', async () => {
|
||||
render(NameHistoryCard, {
|
||||
aliases: [{ id: 'a1', lastName: 'de Gruyter', firstName: null, type: 'BIRTH', sortOrder: 0 }],
|
||||
personFirstName: 'Clara'
|
||||
});
|
||||
|
||||
await expect.element(page.getByText('geborene/r')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -125,7 +125,7 @@ async function addAlias() {
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => confirmDelete(alias.id)}
|
||||
aria-label={m.person_alias_btn_delete()}
|
||||
aria-label="{m.person_alias_btn_delete()} {alias.lastName}"
|
||||
class="ml-4 inline-flex min-h-[44px] min-w-[44px] items-center justify-center text-red-400 transition-colors hover:text-red-600"
|
||||
>
|
||||
<svg
|
||||
|
||||
Reference in New Issue
Block a user