feat: add delete functionality for leases, catch books, and permits; implement newsletter update feature
Some checks failed
Workflow / ʦ TypeScript (push) Failing after 4m52s
Workflow / ⚫️ Test (push) Has been skipped

This commit is contained in:
T. Zehetbauer
2026-04-01 17:53:39 +02:00
parent c6b2824da8
commit 080ec1cb47
22 changed files with 798 additions and 210 deletions

View File

@@ -3,7 +3,6 @@
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { useAction } from 'next-safe-action/hooks';
import { useForm } from 'react-hook-form';
import { Button } from '@kit/ui/button';
@@ -17,45 +16,82 @@ import {
FormMessage,
} from '@kit/ui/form';
import { Input } from '@kit/ui/input';
import { toast } from '@kit/ui/sonner';
import { useActionWithToast } from '@kit/ui/use-action-with-toast';
import { CreateNewsletterSchema } from '../schema/newsletter.schema';
import { createNewsletter } from '../server/actions/newsletter-actions';
import {
CreateNewsletterSchema,
UpdateNewsletterSchema,
} from '../schema/newsletter.schema';
import {
createNewsletter,
updateNewsletter,
} from '../server/actions/newsletter-actions';
interface Props {
accountId: string;
account: string;
newsletterId?: string;
initialData?: {
subject: string;
bodyHtml: string;
bodyText: string;
scheduledAt: string;
};
}
export function CreateNewsletterForm({ accountId, account }: Props) {
export function CreateNewsletterForm({
accountId,
account,
newsletterId,
initialData,
}: Props) {
const router = useRouter();
const isEdit = Boolean(newsletterId);
const form = useForm({
resolver: zodResolver(CreateNewsletterSchema),
resolver: zodResolver(
isEdit ? UpdateNewsletterSchema : CreateNewsletterSchema,
),
defaultValues: {
accountId,
subject: '',
bodyHtml: '',
bodyText: '',
scheduledAt: '',
...(isEdit ? { newsletterId } : { accountId }),
subject: initialData?.subject ?? '',
bodyHtml: initialData?.bodyHtml ?? '',
bodyText: initialData?.bodyText ?? '',
scheduledAt: initialData?.scheduledAt ?? '',
},
});
const { execute, isPending } = useAction(createNewsletter, {
onSuccess: ({ data }) => {
if (data?.success) {
toast.success('Newsletter erfolgreich erstellt');
router.push(`/home/${account}/newsletter`);
}
const { execute: execCreate, isPending: isCreating } = useActionWithToast(
createNewsletter,
{
successMessage: 'Newsletter erstellt',
errorMessage: 'Fehler beim Erstellen',
onSuccess: () => router.push(`/home/${account}/newsletter`),
},
onError: ({ error }) => {
toast.error(error.serverError ?? 'Fehler beim Erstellen des Newsletters');
);
const { execute: execUpdate, isPending: isUpdating } = useActionWithToast(
updateNewsletter,
{
successMessage: 'Newsletter aktualisiert',
errorMessage: 'Fehler beim Aktualisieren',
onSuccess: () =>
router.push(`/home/${account}/newsletter/${newsletterId}`),
},
});
);
const isPending = isCreating || isUpdating;
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit((data) => execute(data))}
onSubmit={form.handleSubmit((data) => {
if (isEdit && newsletterId) {
execUpdate({ ...data, newsletterId } as any);
} else {
execCreate({ ...data, accountId } as any);
}
})}
className="space-y-6"
>
<Card>
@@ -146,20 +182,15 @@ export function CreateNewsletterForm({ accountId, account }: Props) {
</Card>
<div className="flex justify-end gap-2">
<Button
type="button"
variant="outline"
onClick={() => router.back()}
data-test="newsletter-cancel-btn"
>
<Button type="button" variant="outline" onClick={() => router.back()}>
Abbrechen
</Button>
<Button
type="submit"
disabled={isPending}
data-test="newsletter-submit-btn"
>
{isPending ? 'Wird erstellt...' : 'Newsletter erstellen'}
<Button type="submit" disabled={isPending}>
{isPending
? 'Wird gespeichert...'
: isEdit
? 'Newsletter aktualisieren'
: 'Newsletter erstellen'}
</Button>
</div>
</form>

View File

@@ -18,6 +18,11 @@ export const CreateNewsletterSchema = z.object({
});
export type CreateNewsletterInput = z.infer<typeof CreateNewsletterSchema>;
export const UpdateNewsletterSchema = CreateNewsletterSchema.partial().extend({
newsletterId: z.string().uuid(),
});
export type UpdateNewsletterInput = z.infer<typeof UpdateNewsletterSchema>;
export const CreateTemplateSchema = z.object({
accountId: z.string().uuid(),
name: z.string().min(1),

View File

@@ -8,6 +8,7 @@ import { getSupabaseServerClient } from '@kit/supabase/server-client';
import {
CreateNewsletterSchema,
UpdateNewsletterSchema,
CreateTemplateSchema,
} from '../../schema/newsletter.schema';
import { createNewsletterApi } from '../api';
@@ -26,6 +27,19 @@ export const createNewsletter = authActionClient
return { success: true, data: result };
});
export const updateNewsletter = authActionClient
.inputSchema(UpdateNewsletterSchema)
.action(async ({ parsedInput: input, ctx }) => {
const client = getSupabaseServerClient();
const logger = await getLogger();
const api = createNewsletterApi(client);
logger.info({ name: 'newsletter.update' }, 'Updating newsletter...');
const result = await api.updateNewsletter(input);
logger.info({ name: 'newsletter.update' }, 'Newsletter updated');
return { success: true, data: result };
});
export const createTemplate = authActionClient
.inputSchema(
z.object({

View File

@@ -2,7 +2,10 @@ import type { SupabaseClient } from '@supabase/supabase-js';
import type { Database } from '@kit/supabase/database';
import type { CreateNewsletterInput } from '../schema/newsletter.schema';
import type {
CreateNewsletterInput,
UpdateNewsletterInput,
} from '../schema/newsletter.schema';
/* eslint-disable @typescript-eslint/no-explicit-any */
@@ -140,6 +143,26 @@ export function createNewsletterApi(client: SupabaseClient<Database>) {
return data;
},
async updateNewsletter(input: UpdateNewsletterInput) {
const update: Record<string, unknown> = {};
if (input.subject !== undefined) update.subject = input.subject;
if (input.bodyHtml !== undefined) update.body_html = input.bodyHtml;
if (input.bodyText !== undefined) update.body_text = input.bodyText;
if (input.templateId !== undefined)
update.template_id = input.templateId || null;
if (input.scheduledAt !== undefined)
update.scheduled_at = input.scheduledAt || null;
const { data, error } = await client
.from('newsletters')
.update(update)
.eq('id', input.newsletterId)
.select()
.single();
if (error) throw error;
return data;
},
async getNewsletter(newsletterId: string) {
const { data, error } = await client
.from('newsletters')