Compare commits
4 Commits
59f593280b
...
f0eb3a76be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0eb3a76be | ||
|
|
6d837c518c | ||
|
|
97646a31df | ||
|
|
cfb3260e0e |
@@ -104,7 +104,7 @@ public class PersonController {
|
|||||||
|
|
||||||
@PostMapping("/{id}/aliases")
|
@PostMapping("/{id}/aliases")
|
||||||
@RequirePermission(Permission.WRITE_ALL)
|
@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);
|
return personService.addAlias(id, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package org.raddatz.familienarchiv.dto;
|
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;
|
import org.raddatz.familienarchiv.model.PersonNameAliasType;
|
||||||
|
|
||||||
public record PersonNameAliasDTO(
|
public record PersonNameAliasDTO(
|
||||||
String lastName,
|
@NotBlank @Size(max = 255) String lastName,
|
||||||
String firstName,
|
@Size(max = 255) String firstName,
|
||||||
PersonNameAliasType type
|
@NotNull PersonNameAliasType type
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@@ -458,4 +458,22 @@ class PersonControllerTest {
|
|||||||
mockMvc.perform(delete("/api/persons/{id}/aliases/{aliasId}", UUID.randomUUID(), UUID.randomUUID()))
|
mockMvc.perform(delete("/api/persons/{id}/aliases/{aliasId}", UUID.randomUUID(), UUID.randomUUID()))
|
||||||
.andExpect(status().isForbidden());
|
.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 -->
|
<!-- Left column: Person card + name history -->
|
||||||
<div>
|
<div>
|
||||||
<PersonCard person={person} canWrite={data.canWrite} />
|
<PersonCard person={person} canWrite={data.canWrite} />
|
||||||
{#if data.aliases.length > 0}
|
<div class="mt-6">
|
||||||
<div class="mt-6">
|
<NameHistoryCard aliases={data.aliases} personFirstName={person.firstName} />
|
||||||
<NameHistoryCard aliases={data.aliases} personFirstName={person.firstName} />
|
</div>
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right column: correspondents + documents -->
|
<!-- 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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => confirmDelete(alias.id)}
|
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"
|
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
|
<svg
|
||||||
|
|||||||
Reference in New Issue
Block a user