Files
Epicnabbo-Catalogus-Updated…/src/app/(site)/me/DashboardTasks.tsx
T
openhands c3ff497050 feat(referrals): add referral attribution and daily login rewards
- Track referral attribution at registration via ?ref code with
  same-IP and duplicate-pair guards
- Add daily login rewards with streak tracking, claim flow and
  sendCurrency payout backed by RCON with DB fallback
- Add admin pages for referral settings and the daily reward schedule
- Add migration 0033 with tables, seed schedule, settings and ACL grants
- Add admin.referrals.* and admin.dailyrewards.* permission slugs
- Localize new copy in en, nl and it
2026-09-20 12:29:01 +02:00

78 lines
2.2 KiB
TypeScript

import { getFormatter, getTranslations } from "next-intl/server";
import Link from "@/components/link";
import { SurfaceCard } from "@/components/surface-card";
import { slugify } from "@/lib/format";
import { loadDashboardTasks } from "@/lib/services/dashboard-tasks";
import styles from "./dashboard.module.css";
export default async function DashboardTasks({
userId,
rewardAvailable,
dailyRewardAvailable,
}: {
userId: number;
rewardAvailable: boolean | null;
/** Claimable daily login reward (false when claimed/disabled or loader down). */
dailyRewardAvailable?: boolean;
}) {
const [data, t, format] = await Promise.all([
loadDashboardTasks(userId),
getTranslations("pages.myDashboard.tasks"),
getFormatter(),
]);
const items: { href: string; label: string }[] = [];
if (data.support)
items.push({
href: "/help/tickets",
label: t("support", { count: data.support }),
});
if (data.friendRequests)
items.push({
href: "/friends",
label: t("friends", { count: data.friendRequests }),
});
if (data.registeredEvent)
items.push({
href: `/events/${slugify(data.registeredEvent.title)}-${data.registeredEvent.id}`,
label: t("event", {
title: data.registeredEvent.title,
date: format.dateTime(data.registeredEvent.startsAt, {
dateStyle: "medium",
timeStyle: "short",
}),
}),
});
if (rewardAvailable)
items.push({ href: "#referral-reward", label: t("reward") });
if (dailyRewardAvailable)
items.push({ href: "#daily-reward", label: t("dailyReward") });
const unavailable =
data.support === null ||
data.friendRequests === null ||
data.registeredEvent === null ||
rewardAvailable === null;
return (
<SurfaceCard title={t("title")} bodyClassName="p-5">
{items.length > 0 ? (
<ul className={styles.tasks}>
{items.map((item) => (
<li key={item.href}>
<Link href={item.href}>
{item.label}
<span aria-hidden="true"> →</span>
</Link>
</li>
))}
</ul>
) : !unavailable ? (
<p className="muted">{t("empty")}</p>
) : null}
{unavailable && (
<p role="status" className="muted">
{t("unavailable")} <Link href="/me">{t("retry")}</Link>
</p>
)}
</SurfaceCard>
);
}