Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/src/app/app/changelog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@

return (
<div className="flex flex-col gap-4">
<Day heading="18. Januar 2026">
<DayItem
heading="Transaktion erstellen über TopBar"
badges={["Neu", "SILC"]}
>
<p>
Du kannst jetzt SILC-Transaktionen direkt über den &ldquo;Neu&rdquo;-Button in der
TopBar erstellen.
</p>
</DayItem>
</Day>

<Day heading="2. Januar 2026">
<DayItem
heading="Neuer Avatar-Rahmen"
Expand Down Expand Up @@ -308,10 +320,10 @@
Verlauf aller Änderungen an den Rollen dieses Citizen.
</p>

<Link href={image20251013rolesHistory.src}>

Check failure on line 323 in app/src/app/app/changelog/page.tsx

View workflow job for this annotation

GitHub Actions / lint

Unsafe member access .src on an `error` typed value

Check failure on line 323 in app/src/app/app/changelog/page.tsx

View workflow job for this annotation

GitHub Actions / lint

Unsafe assignment of an error typed value
<Image
quality={100}
src={image20251013rolesHistory}

Check failure on line 326 in app/src/app/app/changelog/page.tsx

View workflow job for this annotation

GitHub Actions / lint

Unsafe assignment of an error typed value
alt=""
loading="eager"
/>
Expand Down Expand Up @@ -361,10 +373,10 @@
</Link>
</p>

<Link href={image20251007sincome.src}>

Check failure on line 376 in app/src/app/app/changelog/page.tsx

View workflow job for this annotation

GitHub Actions / lint

Unsafe member access .src on an `error` typed value

Check failure on line 376 in app/src/app/app/changelog/page.tsx

View workflow job for this annotation

GitHub Actions / lint

Unsafe assignment of an error typed value
<Image
quality={100}
src={image20251007sincome}

Check failure on line 379 in app/src/app/app/changelog/page.tsx

View workflow job for this annotation

GitHub Actions / lint

Unsafe assignment of an error typed value
alt=""
loading="lazy"
/>
Expand Down
11 changes: 11 additions & 0 deletions app/src/modules/common/components/CreateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ const CreateProfitDistributionCycleForm = dynamic(() =>
).then((mod) => mod.CreateProfitDistributionCycleForm),
);

const CreateSilcTransactionForm = dynamic(() =>
import(
"@/modules/silc/components/CreateSilcTransactionForm"
).then((mod) => mod.CreateSilcTransactionForm),
);

export const createForms = {
citizen: {
formComponent: CreateCitizenForm,
Expand Down Expand Up @@ -80,6 +86,11 @@ export const createForms = {
modalHeading: "Neuer Task",
modalWidth: "w-[768px]",
},
silcTransaction: {
formComponent: CreateSilcTransactionForm,
modalHeading: "Neue SILC-Transaktion",
modalWidth: "w-[480px]",
},
};

interface CreateContext {
Expand Down
16 changes: 15 additions & 1 deletion app/src/modules/shell/components/TopBar/Create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,19 @@ export const Create = ({ className }: Props) => {
const showCreateTask = Boolean(
authentication && authentication.authorize("task", "create"),
);
const showCreateSilcTransaction = Boolean(
authentication &&
authentication.authorize("silcTransactionOfOtherCitizen", "create"),
);

if (
!showCreateCitizen &&
!showCreateDistributionCycle &&
!showCreateOrganization &&
!showCreateRole &&
!showCreatePenaltyEntry &&
!showCreateTask
!showCreateTask &&
!showCreateSilcTransaction
)
return null;

Expand All @@ -73,6 +78,7 @@ export const Create = ({ className }: Props) => {
showCreateRole={showCreateRole}
showCreatePenaltyEntry={showCreatePenaltyEntry}
showCreateTask={showCreateTask}
showCreateSilcTransaction={showCreateSilcTransaction}
/>
</Popover>
</div>
Expand All @@ -86,6 +92,7 @@ interface PopoverChildrenProps {
readonly showCreateRole: boolean;
readonly showCreatePenaltyEntry: boolean;
readonly showCreateTask: boolean;
readonly showCreateSilcTransaction: boolean;
}

const PopoverChildren = ({
Expand All @@ -95,6 +102,7 @@ const PopoverChildren = ({
showCreateRole,
showCreatePenaltyEntry,
showCreateTask,
showCreateSilcTransaction,
}: PopoverChildrenProps) => {
const { closePopover } = usePopover();
const { openCreateModal } = useCreateContext();
Expand Down Expand Up @@ -157,6 +165,12 @@ const PopoverChildren = ({
});
if (showCreateTask)
items.push({ label: "Task", type: "button", modalId: "task" });
if (showCreateSilcTransaction)
items.push({
label: "SILC-Transaktion",
type: "button",
modalId: "silcTransaction",
});

items = items.toSorted((a, b) => a.label.localeCompare(b.label));

Expand Down
106 changes: 106 additions & 0 deletions app/src/modules/silc/components/CreateSilcTransactionForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"use client";

import { CitizenInput } from "@/modules/citizen/components/CitizenInput";
import Button from "@/modules/common/components/Button";
import { Button2 } from "@/modules/common/components/Button2";
import { NumberInput } from "@/modules/common/components/form/NumberInput";
import { Textarea } from "@/modules/common/components/form/Textarea";
import Note from "@/modules/common/components/Note";
import { unstable_rethrow } from "next/navigation";
import { useActionState } from "react";
import toast from "react-hot-toast";
import { FaSave, FaSpinner } from "react-icons/fa";
import { createSilcTransaction } from "../actions/createSilcTransaction";

interface Props {
readonly onSuccess?: () => void;
}

export const CreateSilcTransactionForm = ({ onSuccess }: Props) => {
const [state, formAction, isPending] = useActionState(
async (previousState: unknown, formData: FormData) => {
try {
const response = await createSilcTransaction(formData);

if (response.error) {
toast.error(response.error);
console.error(response);
return response;
}

toast.success(response.success!);
if (formData.has("createAnother")) {
return response;
}

onSuccess?.();
return response;
} catch (error) {
unstable_rethrow(error);
toast.error(
"Ein unbekannter Fehler ist aufgetreten. Bitte versuche es später erneut.",
);
console.error(error);
return {
error:
"Ein unbekannter Fehler ist aufgetreten. Bitte versuche es später erneut.",
requestPayload: formData,
};
}
},
null,
);

return (
<form action={formAction}>
<CitizenInput name="receiverId" multiple autoFocus />

<NumberInput
name="value"
label="Wert"
hint="Kann negativ sein, um Guthaben zu entziehen."
required
defaultValue={
state?.requestPayload?.has("value")
? (state.requestPayload.get("value") as string)
: 1
}
labelClassName="mt-4"
/>

<Textarea
name="description"
label="Beschreibung"
hint="optional"
maxLength={512}
defaultValue={
state?.requestPayload?.has("description")
? (state.requestPayload.get("description") as string)
: ""
}
className="mt-4"
/>

<div className="flex flex-col gap-2 mt-4">
<Button2 type="submit" disabled={isPending}>
{isPending ? <FaSpinner className="animate-spin" /> : <FaSave />}
Speichern
</Button2>

<Button
type="submit"
disabled={isPending}
variant="tertiary"
name="createAnother"
>
{isPending ? <FaSpinner className="animate-spin" /> : <FaSave />}
Speichern und weitere Transaktion erstellen
</Button>
</div>

{state?.error && (
<Note type="error" message={state.error} className="mt-4" />
)}
</form>
);
};
Loading