Add balance transactions to user admin panel
Introduces BalanceTransaction model to track per-user balance history (prepayments, refunds, partner credits). Admin can add/delete transactions; current balance is computed as the running sum. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,25 @@ export async function updateUserContact(
|
||||
revalidatePath(`/admin/users/${userId}`);
|
||||
}
|
||||
|
||||
export async function addBalanceTransaction(
|
||||
userId: string,
|
||||
data: { amount: string; description: string }
|
||||
) {
|
||||
await requireAdmin();
|
||||
const amount = parseFloat(data.amount.replace(",", "."));
|
||||
if (isNaN(amount) || amount === 0) throw new Error("Некорректная сумма");
|
||||
await prisma.balanceTransaction.create({
|
||||
data: { userId, amount, description: data.description.trim() },
|
||||
});
|
||||
revalidatePath(`/admin/users/${userId}`);
|
||||
}
|
||||
|
||||
export async function deleteBalanceTransaction(userId: string, txId: string) {
|
||||
await requireAdmin();
|
||||
await prisma.balanceTransaction.delete({ where: { id: txId } });
|
||||
revalidatePath(`/admin/users/${userId}`);
|
||||
}
|
||||
|
||||
export async function revokeUserAccess(userId: string, courseId: string) {
|
||||
const session = await requireAdmin();
|
||||
await prisma.courseEnrollment.delete({
|
||||
|
||||
@@ -3,6 +3,7 @@ import { notFound } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { UserEnrollmentManager } from "@/components/admin/user-enrollment-manager";
|
||||
import { UserContactEditor } from "@/components/admin/user-contact-editor";
|
||||
import { UserBalanceBlock } from "@/components/admin/user-balance-block";
|
||||
|
||||
interface Props {
|
||||
params: Promise<{ userId: string }>;
|
||||
@@ -27,6 +28,9 @@ export default async function UserPage({ params }: Props) {
|
||||
grantedBy: { select: { name: true } },
|
||||
},
|
||||
},
|
||||
balanceTransactions: {
|
||||
orderBy: { createdAt: "desc" },
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.course.findMany({
|
||||
@@ -73,6 +77,22 @@ export default async function UserPage({ params }: Props) {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Balance */}
|
||||
<section className="card-aubade p-6 mb-6">
|
||||
<p className="text-xs font-bold uppercase tracking-widest mb-5" style={{ color: "var(--muted-foreground)" }}>
|
||||
Баланс
|
||||
</p>
|
||||
<UserBalanceBlock
|
||||
userId={userId}
|
||||
transactions={user.balanceTransactions.map((tx) => ({
|
||||
id: tx.id,
|
||||
amount: Number(tx.amount),
|
||||
description: tx.description,
|
||||
createdAt: tx.createdAt,
|
||||
}))}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{/* Enrollments + bulk grant */}
|
||||
<section className="card-aubade p-6 mb-6">
|
||||
<p className="text-xs font-bold uppercase tracking-widest mb-5" style={{ color: "var(--muted-foreground)" }}>
|
||||
|
||||
Reference in New Issue
Block a user