fix: resolve all 47 svelte-check errors and 10 a11y warnings
Root cause 1 — OpenAPI types: add @Schema(requiredMode=REQUIRED) to
non-nullable fields on Person, Tag, Document, AppUser, UserGroup;
regenerate api.ts so required fields are no longer optional.
Root cause 2 — Stale types: api.ts regenerated, picking up the Tag
endpoint fix from commit 62189d8 (List<Tag> instead of List<String>).
Root cause 3 — openapi-fetch error pattern: replace `if (apiError)`
(broken when error type is never/undefined) with `if (!result.response.ok)`
across all +page.server.ts files. Cast error via `unknown` to satisfy TS.
Root cause 4 — FormData casts: add `as string` / `as string[]` to
FormData.get() / FormData.getAll() calls in admin/+page.server.ts.
Standalone fixes:
- +page.server.ts: return error field so home page template compiles
- documents/[id]/+page.svelte: type loadFile param, remove invalid iframe `type`
- conversations: type documents as Document[] instead of unknown[]
- persons/[id]: non-null assert person data after ok-check
a11y: aria-label on all icon-only buttons in TagInput and admin page,
replace invalid <label> with <p> for compound controls, remove autofocus.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import org.raddatz.familienarchiv.dto.CreateUserRequest;
|
|||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -24,9 +25,11 @@ public class AppUser {
|
|||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.UUID)
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private UUID id;
|
private UUID id;
|
||||||
|
|
||||||
@Column(unique = true, nullable = false)
|
@Column(unique = true, nullable = false)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@@ -36,15 +39,18 @@ public class AppUser {
|
|||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private boolean enabled = true; // Um User zu sperren ohne sie zu löschen
|
private boolean enabled = true; // Um User zu sperren ohne sie zu löschen
|
||||||
|
|
||||||
// Ein User kann in mehreren Gruppen sein
|
// Ein User kann in mehreren Gruppen sein
|
||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@ManyToMany(fetch = FetchType.EAGER)
|
||||||
@JoinTable(name = "users_groups", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "group_id"))
|
@JoinTable(name = "users_groups", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "group_id"))
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private Set<UserGroup> groups = new HashSet<>();
|
private Set<UserGroup> groups = new HashSet<>();
|
||||||
|
|
||||||
@CreationTimestamp
|
@CreationTimestamp
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
public boolean hasPermission(String permission) {
|
public boolean hasPermission(String permission) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.hibernate.annotations.CreationTimestamp;
|
|||||||
import org.hibernate.annotations.UpdateTimestamp;
|
import org.hibernate.annotations.UpdateTimestamp;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -23,9 +24,11 @@ public class Document {
|
|||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.UUID)
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private UUID id;
|
private UUID id;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
// Der Dateiname im S3 (oder Pfad)
|
// Der Dateiname im S3 (oder Pfad)
|
||||||
@@ -38,11 +41,13 @@ public class Document {
|
|||||||
|
|
||||||
// Originaler Dateiname beim Upload (z.B. "Brief_Oma_1940.pdf")
|
// Originaler Dateiname beim Upload (z.B. "Brief_Oma_1940.pdf")
|
||||||
@Column(name = "original_filename", nullable = false)
|
@Column(name = "original_filename", nullable = false)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String originalFilename;
|
private String originalFilename;
|
||||||
|
|
||||||
// Der Status hilft uns beim Excel-Import Workflow
|
// Der Status hilft uns beim Excel-Import Workflow
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private DocumentStatus status;
|
private DocumentStatus status;
|
||||||
|
|
||||||
// Metadaten aus dem Brief
|
// Metadaten aus dem Brief
|
||||||
@@ -70,9 +75,11 @@ public class Document {
|
|||||||
|
|
||||||
@CreationTimestamp
|
@CreationTimestamp
|
||||||
@Column(updatable = false)
|
@Column(updatable = false)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
@UpdateTimestamp
|
@UpdateTimestamp
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@ManyToMany(fetch = FetchType.EAGER)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.raddatz.familienarchiv.model;
|
package org.raddatz.familienarchiv.model;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
@@ -14,12 +15,15 @@ public class Person {
|
|||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.UUID)
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private UUID id;
|
private UUID id;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String firstName;
|
private String firstName;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String lastName;
|
private String lastName;
|
||||||
|
|
||||||
// Optional: Aliasse für die Suche (z.B. "Opa Hans")
|
// Optional: Aliasse für die Suche (z.B. "Opa Hans")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.raddatz.familienarchiv.model;
|
|||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
@@ -13,8 +14,10 @@ import lombok.*;
|
|||||||
public class Tag {
|
public class Tag {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.UUID)
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private UUID id;
|
private UUID id;
|
||||||
|
|
||||||
@Column(unique = true, nullable = false)
|
@Column(unique = true, nullable = false)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String name;
|
private String name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.raddatz.familienarchiv.model;
|
package org.raddatz.familienarchiv.model;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
@@ -17,9 +18,11 @@ public class UserGroup {
|
|||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.UUID)
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private UUID id;
|
private UUID id;
|
||||||
|
|
||||||
@Column(unique = true, nullable = false)
|
@Column(unique = true, nullable = false)
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String name; // z.B. "Admins", "Familie Müller"
|
private String name; // z.B. "Admins", "Familie Müller"
|
||||||
|
|
||||||
// Wir speichern Rechte als einfache Strings (z.B. "READ_ALL", "WRITE_DOCUMENTS")
|
// Wir speichern Rechte als einfache Strings (z.B. "READ_ALL", "WRITE_DOCUMENTS")
|
||||||
@@ -27,5 +30,6 @@ public class UserGroup {
|
|||||||
@CollectionTable(name = "group_permissions", joinColumns = @JoinColumn(name = "group_id"))
|
@CollectionTable(name = "group_permissions", joinColumns = @JoinColumn(name = "group_id"))
|
||||||
@Column(name = "permission")
|
@Column(name = "permission")
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
|
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private Set<String> permissions = new HashSet<>();
|
private Set<String> permissions = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
on:click={() => removeTag(i)}
|
on:click={() => removeTag(i)}
|
||||||
|
aria-label="Schlagwort entfernen"
|
||||||
class="text-brand-navy/50 hover:text-red-500 focus:outline-none"
|
class="text-brand-navy/50 hover:text-red-500 focus:outline-none"
|
||||||
>
|
>
|
||||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
@@ -117,12 +118,15 @@
|
|||||||
class="absolute left-0 top-full mt-1 w-full bg-white border border-gray-200 rounded shadow-lg z-50 max-h-48 overflow-y-auto"
|
class="absolute left-0 top-full mt-1 w-full bg-white border border-gray-200 rounded shadow-lg z-50 max-h-48 overflow-y-auto"
|
||||||
>
|
>
|
||||||
{#each suggestions as suggestion, i}
|
{#each suggestions as suggestion, i}
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
||||||
<li
|
<li
|
||||||
|
role="option"
|
||||||
|
aria-selected={i === activeIndex}
|
||||||
|
tabindex="0"
|
||||||
class="px-3 py-2 text-sm cursor-pointer hover:bg-brand-sand/20 {i === activeIndex
|
class="px-3 py-2 text-sm cursor-pointer hover:bg-brand-sand/20 {i === activeIndex
|
||||||
? 'bg-brand-sand/20 text-brand-navy font-bold'
|
? 'bg-brand-sand/20 text-brand-navy font-bold'
|
||||||
: 'text-gray-700'}"
|
: 'text-gray-700'}"
|
||||||
on:click={() => addTag(suggestion)}
|
on:click={() => addTag(suggestion)}
|
||||||
|
on:keydown={(e) => e.key === 'Enter' && addTag(suggestion)}
|
||||||
>
|
>
|
||||||
{suggestion}
|
{suggestion}
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -282,14 +282,14 @@ export interface components {
|
|||||||
schemas: {
|
schemas: {
|
||||||
Tag: {
|
Tag: {
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
id?: string;
|
id: string;
|
||||||
name?: string;
|
name: string;
|
||||||
};
|
};
|
||||||
Person: {
|
Person: {
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
id?: string;
|
id: string;
|
||||||
firstName?: string;
|
firstName: string;
|
||||||
lastName?: string;
|
lastName: string;
|
||||||
alias?: string;
|
alias?: string;
|
||||||
};
|
};
|
||||||
DocumentUpdateDTO: {
|
DocumentUpdateDTO: {
|
||||||
@@ -307,13 +307,13 @@ export interface components {
|
|||||||
};
|
};
|
||||||
Document: {
|
Document: {
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
id?: string;
|
id: string;
|
||||||
title?: string;
|
title: string;
|
||||||
filePath?: string;
|
filePath?: string;
|
||||||
contentType?: string;
|
contentType?: string;
|
||||||
originalFilename?: string;
|
originalFilename: string;
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
status?: "PLACEHOLDER" | "UPLOADED" | "TRANSCRIBED" | "REVIEWED" | "ARCHIVED";
|
status: "PLACEHOLDER" | "UPLOADED" | "TRANSCRIBED" | "REVIEWED" | "ARCHIVED";
|
||||||
/** Format: date */
|
/** Format: date */
|
||||||
documentDate?: string;
|
documentDate?: string;
|
||||||
location?: string;
|
location?: string;
|
||||||
@@ -323,9 +323,9 @@ export interface components {
|
|||||||
transcription?: string;
|
transcription?: string;
|
||||||
summary?: string;
|
summary?: string;
|
||||||
/** Format: date-time */
|
/** Format: date-time */
|
||||||
createdAt?: string;
|
createdAt: string;
|
||||||
/** Format: date-time */
|
/** Format: date-time */
|
||||||
updatedAt?: string;
|
updatedAt: string;
|
||||||
receivers?: components["schemas"]["Person"][];
|
receivers?: components["schemas"]["Person"][];
|
||||||
sender?: components["schemas"]["Person"];
|
sender?: components["schemas"]["Person"];
|
||||||
tags?: components["schemas"]["Tag"][];
|
tags?: components["schemas"]["Tag"][];
|
||||||
@@ -338,20 +338,20 @@ export interface components {
|
|||||||
};
|
};
|
||||||
AppUser: {
|
AppUser: {
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
id?: string;
|
id: string;
|
||||||
username?: string;
|
username: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
enabled?: boolean;
|
enabled: boolean;
|
||||||
groups?: components["schemas"]["UserGroup"][];
|
groups: components["schemas"]["UserGroup"][];
|
||||||
/** Format: date-time */
|
/** Format: date-time */
|
||||||
createdAt?: string;
|
createdAt: string;
|
||||||
};
|
};
|
||||||
UserGroup: {
|
UserGroup: {
|
||||||
/** Format: uuid */
|
/** Format: uuid */
|
||||||
id?: string;
|
id: string;
|
||||||
name?: string;
|
name: string;
|
||||||
permissions?: string[];
|
permissions: string[];
|
||||||
};
|
};
|
||||||
GroupDTO: {
|
GroupDTO: {
|
||||||
name?: string;
|
name?: string;
|
||||||
@@ -738,7 +738,7 @@ export interface operations {
|
|||||||
[name: string]: unknown;
|
[name: string]: unknown;
|
||||||
};
|
};
|
||||||
content: {
|
content: {
|
||||||
"*/*": string[];
|
"*/*": components["schemas"]["Tag"][];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ export async function load({ url, fetch }) {
|
|||||||
senderName: senderObj ? `${senderObj.firstName} ${senderObj.lastName}` : '',
|
senderName: senderObj ? `${senderObj.firstName} ${senderObj.lastName}` : '',
|
||||||
receiverName: receiverObj ? `${receiverObj.firstName} ${receiverObj.lastName}` : ''
|
receiverName: receiverObj ? `${receiverObj.firstName} ${receiverObj.lastName}` : ''
|
||||||
},
|
},
|
||||||
filters: { q, from, to, senderId, receiverId, tags }
|
filters: { q, from, to, senderId, receiverId, tags },
|
||||||
|
error: null as string | null
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if ((e as { status?: number }).status) throw e;
|
if ((e as { status?: number }).status) throw e;
|
||||||
@@ -52,7 +53,8 @@ export async function load({ url, fetch }) {
|
|||||||
return {
|
return {
|
||||||
documents: [],
|
documents: [],
|
||||||
initialValues: { senderName: '', receiverName: '' },
|
initialValues: { senderName: '', receiverName: '' },
|
||||||
filters: { q, from, to, senderId, receiverId, tags }
|
filters: { q, from, to, senderId, receiverId, tags },
|
||||||
|
error: 'Daten konnten nicht geladen werden.' as string | null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,9 +143,7 @@
|
|||||||
>
|
>
|
||||||
<!-- Tag Filter -->
|
<!-- Tag Filter -->
|
||||||
<div class="md:col-span-12">
|
<div class="md:col-span-12">
|
||||||
<label class="block text-xs font-bold uppercase tracking-widest text-gray-500 mb-2"
|
<p class="block text-xs font-bold uppercase tracking-widest text-gray-500 mb-2">Schlagworte</p>
|
||||||
>Schlagworte</label
|
|
||||||
>
|
|
||||||
<TagInput bind:tags={tagNames} allowCreation={false} on:change={triggerSearch} />
|
<TagInput bind:tags={tagNames} allowCreation={false} on:change={triggerSearch} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { getErrorMessage } from '$lib/errors';
|
|||||||
|
|
||||||
export async function load({ fetch, locals }) {
|
export async function load({ fetch, locals }) {
|
||||||
const user = locals.user;
|
const user = locals.user;
|
||||||
const hasAdmin = user?.groups.some((g: { permissions: string[] }) => g.permissions.includes('ADMIN'));
|
const hasAdmin = user?.groups?.some((g: { permissions: string[] }) => g.permissions.includes('ADMIN'));
|
||||||
if (!hasAdmin) throw error(403, getErrorMessage('FORBIDDEN'));
|
if (!hasAdmin) throw error(403, getErrorMessage('FORBIDDEN'));
|
||||||
|
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
@@ -27,17 +27,17 @@ export const actions = {
|
|||||||
const data = await request.formData();
|
const data = await request.formData();
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
|
|
||||||
const { error: apiError, response } = await api.POST('/api/users', {
|
const result = await api.POST('/api/users', {
|
||||||
body: {
|
body: {
|
||||||
username: data.get('username'),
|
username: data.get('username') as string,
|
||||||
initialPassword: data.get('password'),
|
initialPassword: data.get('password') as string,
|
||||||
groupIds: data.getAll('groupIds')
|
groupIds: data.getAll('groupIds') as string[]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (apiError) {
|
if (!result.response.ok) {
|
||||||
const code = (apiError as { code?: string })?.code;
|
const code = (result.error as unknown as { code?: string })?.code;
|
||||||
return fail(response.status, { success: false, message: getErrorMessage(code) });
|
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||||
}
|
}
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
},
|
||||||
@@ -47,13 +47,13 @@ export const actions = {
|
|||||||
const id = data.get('id') as string;
|
const id = data.get('id') as string;
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
|
|
||||||
const { error: apiError, response } = await api.DELETE('/api/users/{id}', {
|
const result = await api.DELETE('/api/users/{id}', {
|
||||||
params: { path: { id } }
|
params: { path: { id } }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (apiError) {
|
if (!result.response.ok) {
|
||||||
const code = (apiError as { code?: string })?.code;
|
const code = (result.error as unknown as { code?: string })?.code;
|
||||||
return fail(response.status, { success: false, message: getErrorMessage(code) });
|
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||||
}
|
}
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
},
|
||||||
@@ -63,14 +63,14 @@ export const actions = {
|
|||||||
const id = data.get('id') as string;
|
const id = data.get('id') as string;
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
|
|
||||||
const { error: apiError, response } = await api.PUT('/api/tags/{id}', {
|
const result = await api.PUT('/api/tags/{id}', {
|
||||||
params: { path: { id } },
|
params: { path: { id } },
|
||||||
body: { name: data.get('name') }
|
body: { name: data.get('name') as string }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (apiError) {
|
if (!result.response.ok) {
|
||||||
const code = (apiError as { code?: string })?.code;
|
const code = (result.error as unknown as { code?: string })?.code;
|
||||||
return fail(response.status, { success: false, message: getErrorMessage(code) });
|
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||||
}
|
}
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
},
|
||||||
@@ -80,13 +80,13 @@ export const actions = {
|
|||||||
const id = data.get('id') as string;
|
const id = data.get('id') as string;
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
|
|
||||||
const { error: apiError, response } = await api.DELETE('/api/tags/{id}', {
|
const result = await api.DELETE('/api/tags/{id}', {
|
||||||
params: { path: { id } }
|
params: { path: { id } }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (apiError) {
|
if (!result.response.ok) {
|
||||||
const code = (apiError as { code?: string })?.code;
|
const code = (result.error as unknown as { code?: string })?.code;
|
||||||
return fail(response.status, { success: false, message: getErrorMessage(code) });
|
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||||
}
|
}
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
},
|
||||||
@@ -95,16 +95,16 @@ export const actions = {
|
|||||||
const data = await request.formData();
|
const data = await request.formData();
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
|
|
||||||
const { error: apiError, response } = await api.POST('/api/groups', {
|
const result = await api.POST('/api/groups', {
|
||||||
body: {
|
body: {
|
||||||
name: data.get('name'),
|
name: data.get('name') as string,
|
||||||
permissions: data.getAll('permissions')
|
permissions: data.getAll('permissions') as string[]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (apiError) {
|
if (!result.response.ok) {
|
||||||
const code = (apiError as { code?: string })?.code;
|
const code = (result.error as unknown as { code?: string })?.code;
|
||||||
return fail(response.status, { success: false, message: getErrorMessage(code) });
|
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||||
}
|
}
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
},
|
||||||
@@ -114,17 +114,17 @@ export const actions = {
|
|||||||
const id = data.get('id') as string;
|
const id = data.get('id') as string;
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
|
|
||||||
const { error: apiError, response } = await api.PATCH('/api/groups/{id}', {
|
const result = await api.PATCH('/api/groups/{id}', {
|
||||||
params: { path: { id } },
|
params: { path: { id } },
|
||||||
body: {
|
body: {
|
||||||
name: data.get('name'),
|
name: data.get('name') as string,
|
||||||
permissions: data.getAll('permissions')
|
permissions: data.getAll('permissions') as string[]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (apiError) {
|
if (!result.response.ok) {
|
||||||
const code = (apiError as { code?: string })?.code;
|
const code = (result.error as unknown as { code?: string })?.code;
|
||||||
return fail(response.status, { success: false, message: getErrorMessage(code) });
|
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||||
}
|
}
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
},
|
||||||
@@ -134,13 +134,13 @@ export const actions = {
|
|||||||
const id = data.get('id') as string;
|
const id = data.get('id') as string;
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
|
|
||||||
const { error: apiError, response } = await api.DELETE('/api/groups/{id}', {
|
const result = await api.DELETE('/api/groups/{id}', {
|
||||||
params: { path: { id } }
|
params: { path: { id } }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (apiError) {
|
if (!result.response.ok) {
|
||||||
const code = (apiError as { code?: string })?.code;
|
const code = (result.error as unknown as { code?: string })?.code;
|
||||||
return fail(response.status, { success: false, message: getErrorMessage(code) });
|
return fail(result.response.status, { success: false, message: getErrorMessage(code) });
|
||||||
}
|
}
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -319,9 +319,8 @@
|
|||||||
name="name"
|
name="name"
|
||||||
bind:value={editingTagName}
|
bind:value={editingTagName}
|
||||||
class="flex-1 border-brand-mint ring-1 ring-brand-mint rounded px-2 py-1 text-sm"
|
class="flex-1 border-brand-mint ring-1 ring-brand-mint rounded px-2 py-1 text-sm"
|
||||||
autofocus
|
|
||||||
/>
|
/>
|
||||||
<button class="text-green-600 hover:text-green-800"
|
<button aria-label="Speichern" class="text-green-600 hover:text-green-800"
|
||||||
><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
><path
|
><path
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
@@ -334,7 +333,8 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
on:click={cancelEditTag}
|
on:click={cancelEditTag}
|
||||||
class="text-gray-400 hover:text-gray-600"
|
aria-label="Abbrechen"
|
||||||
|
class="text-gray-400 hover:text-gray-600"
|
||||||
><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
><path
|
><path
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
@@ -354,6 +354,7 @@
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
on:click={() => startEditTag(tag)}
|
on:click={() => startEditTag(tag)}
|
||||||
|
aria-label="Schlagwort bearbeiten"
|
||||||
class="p-1 text-gray-400 hover:text-brand-navy"
|
class="p-1 text-gray-400 hover:text-brand-navy"
|
||||||
>
|
>
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
@@ -386,7 +387,7 @@
|
|||||||
class="inline"
|
class="inline"
|
||||||
>
|
>
|
||||||
<input type="hidden" name="id" value={tag.id} />
|
<input type="hidden" name="id" value={tag.id} />
|
||||||
<button class="p-1 text-gray-400 hover:text-red-600">
|
<button aria-label="Schlagwort löschen" class="p-1 text-gray-400 hover:text-red-600">
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
><path
|
><path
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
@@ -473,7 +474,7 @@
|
|||||||
|
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<div class="flex gap-2 self-start sm:self-center">
|
<div class="flex gap-2 self-start sm:self-center">
|
||||||
<button type="submit" class="text-green-600 hover:text-green-800 p-1">
|
<button type="submit" aria-label="Speichern" class="text-green-600 hover:text-green-800 p-1">
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
><path
|
><path
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
@@ -486,7 +487,8 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
on:click={cancelEditGroup}
|
on:click={cancelEditGroup}
|
||||||
class="text-gray-400 hover:text-red-500 p-1"
|
aria-label="Abbrechen"
|
||||||
|
class="text-gray-400 hover:text-red-500 p-1"
|
||||||
>
|
>
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
><path
|
><path
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { components } from '$lib/generated/api';
|
||||||
import { createApiClient } from '$lib/api.server';
|
import { createApiClient } from '$lib/api.server';
|
||||||
|
|
||||||
export async function load({ url, fetch }) {
|
export async function load({ url, fetch }) {
|
||||||
@@ -9,7 +10,7 @@ export async function load({ url, fetch }) {
|
|||||||
|
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
|
|
||||||
let documents: unknown[] = [];
|
let documents: components['schemas']['Document'][] = [];
|
||||||
let senderName = '';
|
let senderName = '';
|
||||||
let receiverName = '';
|
let receiverName = '';
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
// Data & State
|
// Data & State
|
||||||
let documents = [];
|
let documents: typeof data.documents = [];
|
||||||
let initialValues = { senderName: '', receiverName: '' };
|
let initialValues = { senderName: '', receiverName: '' };
|
||||||
|
|
||||||
// Filter State
|
// Filter State
|
||||||
|
|||||||
@@ -6,16 +6,14 @@ export async function load({ params, fetch }) {
|
|||||||
const { id } = params;
|
const { id } = params;
|
||||||
const api = createApiClient(fetch);
|
const api = createApiClient(fetch);
|
||||||
|
|
||||||
const { data, error: apiError, response } = await api.GET('/api/documents/{id}', {
|
const result = await api.GET('/api/documents/{id}', { params: { path: { id } } });
|
||||||
params: { path: { id } }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status === 401) throw redirect(302, '/login');
|
if (result.response.status === 401) throw redirect(302, '/login');
|
||||||
|
|
||||||
if (apiError) {
|
if (!result.response.ok) {
|
||||||
const code = (apiError as { code?: string })?.code;
|
const code = (result.error as unknown as { code?: string })?.code;
|
||||||
throw error(response.status, getErrorMessage(code));
|
throw error(result.response.status, getErrorMessage(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
return { document: data };
|
return { document: result.data! };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
loadFile(doc.id);
|
loadFile(doc.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadFile(id) {
|
async function loadFile(id: string) {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
error = '';
|
error = '';
|
||||||
fileUrl = ''; // Reset previous URL
|
fileUrl = ''; // Reset previous URL
|
||||||
@@ -430,7 +430,6 @@
|
|||||||
src={fileUrl}
|
src={fileUrl}
|
||||||
title="Document Preview"
|
title="Document Preview"
|
||||||
class="w-full h-full border-none bg-white"
|
class="w-full h-full border-none bg-white"
|
||||||
type="application/pdf"
|
|
||||||
></iframe>
|
></iframe>
|
||||||
{:else if fileUrl}
|
{:else if fileUrl}
|
||||||
<!-- Image with Blob URL -->
|
<!-- Image with Blob URL -->
|
||||||
|
|||||||
@@ -12,16 +12,16 @@ export async function load({ params, fetch }) {
|
|||||||
api.GET('/api/persons')
|
api.GET('/api/persons')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (docResult.error) {
|
if (!docResult.response.ok) {
|
||||||
const code = (docResult.error as { code?: string })?.code;
|
const code = (docResult.error as unknown as { code?: string })?.code;
|
||||||
throw error(docResult.response.status, getErrorMessage(code));
|
throw error(docResult.response.status, getErrorMessage(code));
|
||||||
}
|
}
|
||||||
if (personsResult.error) {
|
if (!personsResult.response.ok) {
|
||||||
throw error(personsResult.response.status, getErrorMessage('INTERNAL_ERROR'));
|
throw error(personsResult.response.status, getErrorMessage('INTERNAL_ERROR'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
document: docResult.data,
|
document: docResult.data!,
|
||||||
persons: personsResult.data
|
persons: personsResult.data
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ export async function load({ params, fetch }) {
|
|||||||
api.GET('/api/persons/{id}/documents', { params: { path: { id } } })
|
api.GET('/api/persons/{id}/documents', { params: { path: { id } } })
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (personResult.error) {
|
if (!personResult.response.ok) {
|
||||||
const code = (personResult.error as { code?: string })?.code;
|
const code = (personResult.error as unknown as { code?: string })?.code;
|
||||||
throw error(personResult.response.status, getErrorMessage(code));
|
throw error(personResult.response.status, getErrorMessage(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
person: personResult.data,
|
person: personResult.data!,
|
||||||
documents: docsResult.data ?? []
|
documents: docsResult.data ?? []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user