Compare commits
4 Commits
4009781064
...
11f6f9e2a2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11f6f9e2a2 | ||
|
|
4771832492 | ||
|
|
c006113db9 | ||
|
|
5160009175 |
@@ -4,6 +4,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.raddatz.familienarchiv.dto.PersonUpdateDTO;
|
||||
import org.raddatz.familienarchiv.model.Document;
|
||||
import org.raddatz.familienarchiv.model.Person;
|
||||
import org.raddatz.familienarchiv.service.DocumentService;
|
||||
@@ -54,17 +55,14 @@ public class PersonController {
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<Person> updatePerson(@PathVariable UUID id, @RequestBody Map<String, String> body) {
|
||||
String firstName = body.get("firstName");
|
||||
String lastName = body.get("lastName");
|
||||
if (firstName == null || firstName.isBlank() || lastName == null || lastName.isBlank()) {
|
||||
public ResponseEntity<Person> updatePerson(@PathVariable UUID id, @RequestBody PersonUpdateDTO dto) {
|
||||
if (dto.getFirstName() == null || dto.getFirstName().isBlank()
|
||||
|| dto.getLastName() == null || dto.getLastName().isBlank()) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Vor- und Nachname sind Pflichtfelder");
|
||||
}
|
||||
Integer birthYear = body.get("birthYear") != null && !body.get("birthYear").isBlank()
|
||||
? Integer.parseInt(body.get("birthYear")) : null;
|
||||
Integer deathYear = body.get("deathYear") != null && !body.get("deathYear").isBlank()
|
||||
? Integer.parseInt(body.get("deathYear")) : null;
|
||||
return ResponseEntity.ok(personService.updatePerson(id, firstName.trim(), lastName.trim(), body.get("alias"), body.get("notes"), birthYear, deathYear));
|
||||
dto.setFirstName(dto.getFirstName().trim());
|
||||
dto.setLastName(dto.getLastName().trim());
|
||||
return ResponseEntity.ok(personService.updatePerson(id, dto));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/merge")
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.raddatz.familienarchiv.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PersonUpdateDTO {
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String alias;
|
||||
private String notes;
|
||||
private Integer birthYear;
|
||||
private Integer deathYear;
|
||||
}
|
||||
@@ -29,8 +29,8 @@ import java.util.UUID;
|
||||
import static org.raddatz.familienarchiv.repository.DocumentSpecifications.*;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor // Lombok: Erzeugt Constructor für 'final' Felder (Dependency Injection)
|
||||
@Slf4j // Lombok: Logging
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DocumentService {
|
||||
|
||||
private final DocumentRepository documentRepository;
|
||||
@@ -102,8 +102,10 @@ public class DocumentService {
|
||||
.filter(s -> !s.isEmpty())
|
||||
.toList();
|
||||
}
|
||||
updateDocumentTags(doc.getId(), tags);
|
||||
doc = documentRepository.findById(doc.getId()).orElseThrow();
|
||||
UUID savedId = doc.getId();
|
||||
updateDocumentTags(savedId, tags);
|
||||
doc = documentRepository.findById(savedId)
|
||||
.orElseThrow(() -> DomainException.notFound(ErrorCode.DOCUMENT_NOT_FOUND, "Document not found after save: " + savedId));
|
||||
|
||||
// Sender
|
||||
if (dto.getSenderId() != null) {
|
||||
@@ -180,7 +182,8 @@ public class DocumentService {
|
||||
}
|
||||
|
||||
public Document updateDocumentTags(UUID docId, List<String> tagNames) {
|
||||
Document doc = documentRepository.findById(docId).orElseThrow();
|
||||
Document doc = documentRepository.findById(docId)
|
||||
.orElseThrow(() -> DomainException.notFound(ErrorCode.DOCUMENT_NOT_FOUND, "Document not found: " + docId));
|
||||
|
||||
Set<Tag> newTags = new HashSet<>();
|
||||
|
||||
@@ -217,12 +220,11 @@ public class DocumentService {
|
||||
}
|
||||
|
||||
// 1. Allgemeine Suche (für das Suchfeld im Frontend)
|
||||
public List<Document> searchDocuments(String text, LocalDate from, LocalDate to, UUID sender, UUID reciever, List<String> tags) {
|
||||
log.info("Tags", tags);
|
||||
public List<Document> searchDocuments(String text, LocalDate from, LocalDate to, UUID sender, UUID receiver, List<String> tags) {
|
||||
Specification<Document> spec = Specification.where(hasText(text))
|
||||
.and(isBetween(from, to))
|
||||
.and(hasSender(sender))
|
||||
.and(hasReceiver(reciever))
|
||||
.and(hasReceiver(receiver))
|
||||
.and(hasTags(tags));
|
||||
|
||||
// Immer sortiert nach Datum
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.raddatz.familienarchiv.service;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.raddatz.familienarchiv.dto.PersonUpdateDTO;
|
||||
import org.raddatz.familienarchiv.model.Person;
|
||||
import org.raddatz.familienarchiv.repository.PersonRepository;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -58,18 +59,18 @@ public class PersonService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Person updatePerson(UUID id, String firstName, String lastName, String alias, String notes, Integer birthYear, Integer deathYear) {
|
||||
if (birthYear != null && deathYear != null && birthYear > deathYear) {
|
||||
public Person updatePerson(UUID id, PersonUpdateDTO dto) {
|
||||
if (dto.getBirthYear() != null && dto.getDeathYear() != null && dto.getBirthYear() > dto.getDeathYear()) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Geburtsjahr darf nicht nach dem Todesjahr liegen");
|
||||
}
|
||||
Person person = personRepository.findById(id)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Person nicht gefunden"));
|
||||
person.setFirstName(firstName);
|
||||
person.setLastName(lastName);
|
||||
person.setAlias(alias == null || alias.isBlank() ? null : alias.trim());
|
||||
person.setNotes(notes == null || notes.isBlank() ? null : notes.trim());
|
||||
person.setBirthYear(birthYear);
|
||||
person.setDeathYear(deathYear);
|
||||
person.setFirstName(dto.getFirstName());
|
||||
person.setLastName(dto.getLastName());
|
||||
person.setAlias(dto.getAlias() == null || dto.getAlias().isBlank() ? null : dto.getAlias().trim());
|
||||
person.setNotes(dto.getNotes() == null || dto.getNotes().isBlank() ? null : dto.getNotes().trim());
|
||||
person.setBirthYear(dto.getBirthYear());
|
||||
person.setDeathYear(dto.getDeathYear());
|
||||
return personRepository.save(person);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,49 +30,45 @@ public class UserService {
|
||||
private final UserGroupRepository groupRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
@Transactional
|
||||
public AppUser createUserOrUpdate(CreateUserRequest request) {
|
||||
log.info("Versuche neuen User anzulegen: {}", request.getUsername());
|
||||
@Transactional
|
||||
public AppUser createUserOrUpdate(CreateUserRequest request) {
|
||||
log.info("Creating or updating user: {}", request.getUsername());
|
||||
|
||||
Set<UserGroup> groups = new HashSet<>();
|
||||
if (request.getGroupIds() != null && !request.getGroupIds().isEmpty()) {
|
||||
List<UserGroup> foundGroups = groupRepository.findAllById(request.getGroupIds());
|
||||
groups.addAll(foundGroups);
|
||||
Set<UserGroup> groups = new HashSet<>();
|
||||
if (request.getGroupIds() != null && !request.getGroupIds().isEmpty()) {
|
||||
groups.addAll(groupRepository.findAllById(request.getGroupIds()));
|
||||
}
|
||||
|
||||
Optional<AppUser> existingUser = userRepository.findByUsername(request.getUsername());
|
||||
AppUser user;
|
||||
|
||||
if (existingUser.isPresent()) {
|
||||
log.info("User exists, updating: {}", request.getUsername());
|
||||
user = existingUser.get().updateFromRequest(request, passwordEncoder, groups);
|
||||
} else {
|
||||
log.info("Creating new user: {}", request.getUsername());
|
||||
user = AppUser.builder()
|
||||
.username(request.getUsername())
|
||||
.email(request.getEmail())
|
||||
.password(passwordEncoder.encode(request.getInitialPassword()))
|
||||
.groups(groups)
|
||||
.enabled(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
return userRepository.save(user);
|
||||
}
|
||||
log.info("GroupsIds {}", groups.toString());
|
||||
log.info("Groupds in DB {}", groupRepository.findAll().toString());
|
||||
|
||||
Optional<AppUser> dbUser = userRepository.findByUsername(request.getUsername());
|
||||
AppUser user;
|
||||
|
||||
if (dbUser.isPresent()) {
|
||||
log.info("Found user in DB. Will update.");
|
||||
user = dbUser.get().updateFromRequest(request, passwordEncoder, groups);
|
||||
} else {
|
||||
log.info("Creating new user.");
|
||||
user = AppUser.builder()
|
||||
.username(request.getUsername())
|
||||
.email(request.getEmail())
|
||||
.password(passwordEncoder.encode(request.getInitialPassword()))
|
||||
.groups(groups)
|
||||
.enabled(true)
|
||||
.build();
|
||||
}
|
||||
log.info("Saving new user {}", user.toString());
|
||||
return userRepository.save(user);
|
||||
}
|
||||
@Transactional
|
||||
public void deleteUser(UUID userId) {
|
||||
log.info("Delete user {}", userId);
|
||||
|
||||
AppUser user = userRepository.findById(userId)
|
||||
.orElseThrow(() -> DomainException.notFound(ErrorCode.USER_NOT_FOUND, String.format("No user found for id %s", userId)));
|
||||
.orElseThrow(() -> DomainException.notFound(ErrorCode.USER_NOT_FOUND, "No user found for id: " + userId));
|
||||
userRepository.delete(user);
|
||||
}
|
||||
|
||||
public AppUser findByUsername(String username) {
|
||||
return userRepository.findByUsername(username).orElseThrow(
|
||||
() -> DomainException.notFound(ErrorCode.USER_NOT_FOUND, String.format("No user found for username %s", username)));
|
||||
return userRepository.findByUsername(username)
|
||||
.orElseThrow(() -> DomainException.notFound(ErrorCode.USER_NOT_FOUND, "No user found for username: " + username));
|
||||
}
|
||||
|
||||
public List<AppUser> getAllUsers() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.raddatz.familienarchiv.dto.PersonUpdateDTO;
|
||||
import org.raddatz.familienarchiv.model.Person;
|
||||
import org.raddatz.familienarchiv.repository.PersonRepository;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
@@ -92,7 +93,9 @@ class PersonServiceTest {
|
||||
when(personRepository.findById(id)).thenReturn(Optional.of(person));
|
||||
when(personRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
Person result = personService.updatePerson(id, "Anna", "Alt", null, "Some notes here.", null, null);
|
||||
PersonUpdateDTO dto = new PersonUpdateDTO();
|
||||
dto.setFirstName("Anna"); dto.setLastName("Alt"); dto.setNotes("Some notes here.");
|
||||
Person result = personService.updatePerson(id, dto);
|
||||
|
||||
assertThat(result.getNotes()).isEqualTo("Some notes here.");
|
||||
}
|
||||
@@ -104,7 +107,9 @@ class PersonServiceTest {
|
||||
when(personRepository.findById(id)).thenReturn(Optional.of(person));
|
||||
when(personRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
Person result = personService.updatePerson(id, "Anna", "Alt", null, " ", null, null);
|
||||
PersonUpdateDTO dto = new PersonUpdateDTO();
|
||||
dto.setFirstName("Anna"); dto.setLastName("Alt"); dto.setNotes(" ");
|
||||
Person result = personService.updatePerson(id, dto);
|
||||
|
||||
assertThat(result.getNotes()).isNull();
|
||||
}
|
||||
@@ -118,7 +123,9 @@ class PersonServiceTest {
|
||||
when(personRepository.findById(id)).thenReturn(Optional.of(person));
|
||||
when(personRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
Person result = personService.updatePerson(id, "Anna", "Alt", null, null, 1890, 1965);
|
||||
PersonUpdateDTO dto = new PersonUpdateDTO();
|
||||
dto.setFirstName("Anna"); dto.setLastName("Alt"); dto.setBirthYear(1890); dto.setDeathYear(1965);
|
||||
Person result = personService.updatePerson(id, dto);
|
||||
|
||||
assertThat(result.getBirthYear()).isEqualTo(1890);
|
||||
assertThat(result.getDeathYear()).isEqualTo(1965);
|
||||
@@ -128,7 +135,9 @@ class PersonServiceTest {
|
||||
void updatePerson_throwsBadRequest_whenBirthYearAfterDeathYear() {
|
||||
UUID id = UUID.randomUUID();
|
||||
|
||||
assertThatThrownBy(() -> personService.updatePerson(id, "Anna", "Alt", null, null, 1970, 1950))
|
||||
PersonUpdateDTO dto = new PersonUpdateDTO();
|
||||
dto.setFirstName("Anna"); dto.setLastName("Alt"); dto.setBirthYear(1970); dto.setDeathYear(1950);
|
||||
assertThatThrownBy(() -> personService.updatePerson(id, dto))
|
||||
.isInstanceOf(ResponseStatusException.class)
|
||||
.extracting(e -> ((ResponseStatusException) e).getStatusCode().value())
|
||||
.isEqualTo(400);
|
||||
@@ -141,7 +150,9 @@ class PersonServiceTest {
|
||||
when(personRepository.findById(id)).thenReturn(Optional.of(person));
|
||||
when(personRepository.save(any())).thenAnswer(inv -> inv.getArgument(0));
|
||||
|
||||
Person result = personService.updatePerson(id, "Anna", "Alt", null, null, 1900, 1900);
|
||||
PersonUpdateDTO dto = new PersonUpdateDTO();
|
||||
dto.setFirstName("Anna"); dto.setLastName("Alt"); dto.setBirthYear(1900); dto.setDeathYear(1900);
|
||||
Person result = personService.updatePerson(id, dto);
|
||||
|
||||
assertThat(result.getBirthYear()).isEqualTo(1900);
|
||||
assertThat(result.getDeathYear()).isEqualTo(1900);
|
||||
|
||||
11
frontend/src/lib/utils/date.ts
Normal file
11
frontend/src/lib/utils/date.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Format an ISO date string (YYYY-MM-DD) for display.
|
||||
* Uses T12:00:00 to avoid UTC timezone off-by-one when converting to local time.
|
||||
*/
|
||||
export function formatDate(isoDate: string): string {
|
||||
return new Intl.DateTimeFormat('de-DE', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric'
|
||||
}).format(new Date(isoDate + 'T12:00:00'));
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import TagInput from '$lib/components/TagInput.svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { untrack } from 'svelte';
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import { formatDate } from '$lib/utils/date';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
@@ -240,7 +241,7 @@ $effect(() => {
|
||||
<div class="mb-4 flex flex-wrap gap-6 font-sans text-sm text-gray-500">
|
||||
<div class="flex items-center">
|
||||
<img src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Calendar/Calendar-Add-MD.svg" alt="" aria-hidden="true" class="mr-1.5 h-4 w-4" />
|
||||
{doc.documentDate ? new Intl.DateTimeFormat('de-DE', { day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(doc.documentDate + 'T12:00:00')) : '—'}
|
||||
{doc.documentDate ? formatDate(doc.documentDate) : '—'}
|
||||
</div>
|
||||
{#if doc.location}
|
||||
<div class="flex items-center">
|
||||
|
||||
@@ -2,6 +2,16 @@ import { error, fail } from '@sveltejs/kit';
|
||||
import { createApiClient } from '$lib/api.server';
|
||||
import { getErrorMessage } from '$lib/errors';
|
||||
|
||||
type ApiResult = { response: Response; error?: unknown };
|
||||
|
||||
function toActionResult(result: ApiResult) {
|
||||
if (!result.response.ok) {
|
||||
const code = (result.error as { code?: string } | undefined)?.code;
|
||||
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
export async function load({ fetch, locals }) {
|
||||
const user = locals.user;
|
||||
const hasAdmin = user?.groups?.some((g: { permissions: string[] }) => g.permissions.includes('ADMIN'));
|
||||
@@ -35,11 +45,7 @@ export const actions = {
|
||||
}
|
||||
});
|
||||
|
||||
if (!result.response.ok) {
|
||||
const code = (result.error as unknown as { code?: string })?.code;
|
||||
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||
}
|
||||
return { success: true };
|
||||
return toActionResult(result);
|
||||
},
|
||||
|
||||
deleteUser: async ({ request, fetch }) => {
|
||||
@@ -51,11 +57,7 @@ export const actions = {
|
||||
params: { path: { id } }
|
||||
});
|
||||
|
||||
if (!result.response.ok) {
|
||||
const code = (result.error as unknown as { code?: string })?.code;
|
||||
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||
}
|
||||
return { success: true };
|
||||
return toActionResult(result);
|
||||
},
|
||||
|
||||
updateTag: async ({ request, fetch }) => {
|
||||
@@ -68,11 +70,7 @@ export const actions = {
|
||||
body: { name: data.get('name') as string }
|
||||
});
|
||||
|
||||
if (!result.response.ok) {
|
||||
const code = (result.error as unknown as { code?: string })?.code;
|
||||
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||
}
|
||||
return { success: true };
|
||||
return toActionResult(result);
|
||||
},
|
||||
|
||||
deleteTag: async ({ request, fetch }) => {
|
||||
@@ -84,11 +82,7 @@ export const actions = {
|
||||
params: { path: { id } }
|
||||
});
|
||||
|
||||
if (!result.response.ok) {
|
||||
const code = (result.error as unknown as { code?: string })?.code;
|
||||
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||
}
|
||||
return { success: true };
|
||||
return toActionResult(result);
|
||||
},
|
||||
|
||||
createGroup: async ({ request, fetch }) => {
|
||||
@@ -102,11 +96,7 @@ export const actions = {
|
||||
}
|
||||
});
|
||||
|
||||
if (!result.response.ok) {
|
||||
const code = (result.error as unknown as { code?: string })?.code;
|
||||
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||
}
|
||||
return { success: true };
|
||||
return toActionResult(result);
|
||||
},
|
||||
|
||||
updateGroup: async ({ request, fetch }) => {
|
||||
@@ -122,11 +112,7 @@ export const actions = {
|
||||
}
|
||||
});
|
||||
|
||||
if (!result.response.ok) {
|
||||
const code = (result.error as unknown as { code?: string })?.code;
|
||||
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||
}
|
||||
return { success: true };
|
||||
return toActionResult(result);
|
||||
},
|
||||
|
||||
deleteGroup: async ({ request, fetch }) => {
|
||||
@@ -138,10 +124,6 @@ export const actions = {
|
||||
params: { path: { id } }
|
||||
});
|
||||
|
||||
if (!result.response.ok) {
|
||||
const code = (result.error as unknown as { code?: string })?.code;
|
||||
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||
}
|
||||
return { success: true };
|
||||
return toActionResult(result);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import PersonTypeahead from '$lib/components/PersonTypeahead.svelte';
|
||||
import { untrack } from 'svelte';
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import { formatDate } from '$lib/utils/date';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
@@ -231,7 +232,7 @@
|
||||
: 'text-gray-500'}"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
{doc.documentDate ? new Intl.DateTimeFormat('de-DE', { day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(doc.documentDate + 'T12:00:00')) : '—'}
|
||||
{doc.documentDate ? formatDate(doc.documentDate) : '—'}
|
||||
</span>
|
||||
{#if doc.location}
|
||||
<span class="flex items-center">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import { formatDate } from '$lib/utils/date';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
@@ -119,7 +120,7 @@
|
||||
</span>
|
||||
<div>
|
||||
<span class="block font-serif text-lg text-brand-navy">
|
||||
{doc.documentDate ? new Intl.DateTimeFormat('de-DE', { day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(doc.documentDate + 'T12:00:00')) : '—'}
|
||||
{doc.documentDate ? formatDate(doc.documentDate) : '—'}
|
||||
</span>
|
||||
<span class="text-xs font-sans text-gray-500">{m.doc_label_document_date()}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { createApiClient } from '$lib/api.server';
|
||||
import { getErrorMessage } from '$lib/errors';
|
||||
|
||||
export async function load({ url, fetch }) {
|
||||
const q = url.searchParams.get('q') || '';
|
||||
const api = createApiClient(fetch);
|
||||
|
||||
const { data } = await api.GET('/api/persons', {
|
||||
const result = await api.GET('/api/persons', {
|
||||
params: { query: { q: q || undefined } }
|
||||
});
|
||||
|
||||
return { persons: data ?? [], q };
|
||||
if (!result.response.ok) {
|
||||
throw error(result.response.status, getErrorMessage(undefined));
|
||||
}
|
||||
|
||||
return { persons: result.data!, q };
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import PersonTypeahead from '$lib/components/PersonTypeahead.svelte';
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
import { sortDocumentsByDate, type SortDir } from '$lib/utils/sort';
|
||||
import { formatDate } from '$lib/utils/date';
|
||||
|
||||
let { data, form } = $props();
|
||||
|
||||
@@ -38,7 +39,7 @@
|
||||
const sentYearRange = $derived(yearRange(sentDocuments));
|
||||
const receivedYearRange = $derived(yearRange(receivedDocuments));
|
||||
|
||||
const coCorrespondents = $derived(() => {
|
||||
const coCorrespondents = $derived.by(() => {
|
||||
const freq = new Map<string, { id: string; name: string; count: number }>();
|
||||
|
||||
for (const doc of sentDocuments) {
|
||||
@@ -312,11 +313,11 @@
|
||||
{/if}
|
||||
|
||||
<!-- Co-Correspondents Section -->
|
||||
{#if coCorrespondents().length > 0}
|
||||
{#if coCorrespondents.length > 0}
|
||||
<div class="mb-6">
|
||||
<h3 class="text-xs font-bold uppercase tracking-widest text-gray-400 mb-3">{m.person_co_correspondents_heading()}</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each coCorrespondents() as c}
|
||||
{#each coCorrespondents as c}
|
||||
<a href="/conversations?senderId={person.id}&receiverId={c.id}"
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full border border-brand-sand text-sm font-serif text-brand-navy hover:border-brand-navy transition-colors">
|
||||
{c.name}
|
||||
@@ -366,7 +367,7 @@
|
||||
{doc.title || doc.originalFilename}
|
||||
</div>
|
||||
<div class="flex items-center text-xs font-sans text-gray-500 mt-0.5 space-x-2">
|
||||
<span>{doc.documentDate ? new Intl.DateTimeFormat('de-DE', { day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(doc.documentDate + 'T12:00:00')) : m.doc_no_date()}</span>
|
||||
<span>{doc.documentDate ? formatDate(doc.documentDate) : m.doc_no_date()}</span>
|
||||
{#if doc.location}
|
||||
<span class="text-brand-mint">•</span>
|
||||
<span>{doc.location}</span>
|
||||
@@ -438,7 +439,7 @@
|
||||
{doc.title || doc.originalFilename}
|
||||
</div>
|
||||
<div class="flex items-center text-xs font-sans text-gray-500 mt-0.5 space-x-2">
|
||||
<span>{doc.documentDate ? new Intl.DateTimeFormat('de-DE', { day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(doc.documentDate + 'T12:00:00')) : m.doc_no_date()}</span>
|
||||
<span>{doc.documentDate ? formatDate(doc.documentDate) : m.doc_no_date()}</span>
|
||||
{#if doc.location}
|
||||
<span class="text-brand-mint">•</span>
|
||||
<span>{doc.location}</span>
|
||||
|
||||
Reference in New Issue
Block a user