feat: shop buy, forum replies, tickets, messages, sessions, and UX hardening
Complete remaining product gaps: credit-based shop purchases, guild thread replies, help ticket detail/reply/close, offline message compose, sign-out-everywhere via JWT version, ads delete confirm, soft-fail feedback, rate limits, loading states, and single auth() in site layout. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
1 parent
ed7db6e048
commit
803e8f36c1
47 files changed
+2793
-358
No files matched your search
@@ -16,7 +16,7 @@ import { sanitize } from "@/lib/sanitize";
|
||||
|
||||
export const revalidate = 60;
|
||||
|
||||
type SearchParams = Promise<{ comment?: string; error?: string }>;
|
||||
type SearchParams = Promise<{ comment?: string; reaction?: string; error?: string }>;
|
||||
|
||||
function feedbackStyle(tone: "success" | "error"): CSSProperties {
|
||||
const accent =
|
||||
@@ -87,7 +87,7 @@ export default async function ArticlePage({
|
||||
searchParams: SearchParams;
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
const { comment, error } = await searchParams;
|
||||
const { comment, reaction, error } = await searchParams;
|
||||
const t = await getTranslations("pages.article");
|
||||
|
||||
const article = await cacheQuery(
|
||||
@@ -189,6 +189,11 @@ export default async function ArticlePage({
|
||||
{t("success.posted")}
|
||||
</div>
|
||||
) : null}
|
||||
{reaction === "1" ? (
|
||||
<div role="status" style={feedbackStyle("success")}>
|
||||
{t("success.reaction")}
|
||||
</div>
|
||||
) : null}
|
||||
{errorMessage ? (
|
||||
<div role="alert" style={feedbackStyle("error")}>
|
||||
{errorMessage}
|
||||
@@ -240,6 +245,7 @@ export default async function ArticlePage({
|
||||
name="articleId"
|
||||
value={String(articleId)}
|
||||
/>
|
||||
<input type="hidden" name="slug" value={article.slug} />
|
||||
<input type="hidden" name="reaction" value={r.key} />
|
||||
<button
|
||||
type="submit"
|
||||
|
||||
Reference in new issue
Block a user