fix(notifications): drop per-category descriptions, capitalise "Mobil Bildirim" #107

Merged
root merged 1 commits from dev into main 2026-06-05 00:11:44 +03:00
3 changed files with 14 additions and 33 deletions

View File

@@ -37,12 +37,11 @@ export class EmailPreferencesController {
@Get()
async list(
@CurrentUser() user: { id: string },
): Promise<Array<{ category: string; label: string; description: string; optedOut: boolean }>> {
): Promise<Array<{ category: string; label: string; optedOut: boolean }>> {
return Promise.all(
NOTIFICATION_CATEGORIES.map(async (cat) => ({
category: cat.key,
label: cat.label,
description: cat.description,
optedOut: await this.preferences.getCategoryState(user.id, cat.key),
})),
);

View File

@@ -35,13 +35,14 @@ export const OPTIONAL_WORKFLOWS = new Set<string>([
*
* Order matters — that's the order the settings UI renders. Keep
* `email_marketing` first since it's the bigger lever today.
*
* No per-category description string — the row title speaks for itself in
* the settings UI, the auth/payment caveat lives in a static footer.
*/
export const NOTIFICATION_CATEGORIES = [
{
key: "email_marketing" as const,
label: "E-posta bildirimleri",
description:
"Hoş geldin, deneme bitişi, davet hatırlatması, ödül ve geri kazanma mailleri. Hesap güvenliği ve ödeme bildirimleri her zaman gelir.",
workflows: [
"welcome",
"trial-ending",
@@ -53,9 +54,7 @@ export const NOTIFICATION_CATEGORIES = [
},
{
key: "mobile_push" as const,
label: "Mobil bildirim",
description:
"Mobil uygulama push bildirimleri. Mobil uygulama yayınlandığında bu tercih kullanılır.",
label: "Mobil Bildirim",
workflows: ["mobile_push"] as const,
},
] as const;

View File

@@ -60,32 +60,20 @@ const TAB_ITEMS = [
* NOTIFICATION_CATEGORIES`. We render two switches, not six, because users
* care about "e-mail vs mobile" not "did the day-3 referral mail fire".
*
* The API also returns these (`label`, `description`) so the server is the
* source of truth for copy; the static fallback here just avoids a flash of
* untitled rows before the GET completes.
* The API also returns each category's label so the server is the source of
* truth for copy; the static fallback below just avoids a flash of untitled
* rows before the GET completes.
*/
type NotificationCategoryKey = "email_marketing" | "mobile_push";
const FALLBACK_CATEGORY_COPY: Record<
NotificationCategoryKey,
{ label: string; description: string }
> = {
email_marketing: {
label: "E-posta bildirimleri",
description:
"Hoş geldin, deneme bitişi, davet hatırlatması, ödül ve geri kazanma mailleri. Hesap güvenliği ve ödeme bildirimleri her zaman gelir.",
},
mobile_push: {
label: "Mobil bildirim",
description:
"Mobil uygulama push bildirimleri. Mobil uygulama yayınlandığında bu tercih kullanılır.",
},
const FALLBACK_CATEGORY_LABEL: Record<NotificationCategoryKey, string> = {
email_marketing: "E-posta bildirimleri",
mobile_push: "Mobil Bildirim",
};
interface NotificationPref {
category: NotificationCategoryKey;
label: string;
description: string;
optedOut: boolean;
}
@@ -745,20 +733,15 @@ function NotificationsCard() {
if (isLoading) {
return <Skeleton key={key} className="h-20 w-full" />;
}
const fallback = FALLBACK_CATEGORY_COPY[key];
const label = row?.label ?? fallback.label;
const description = row?.description ?? fallback.description;
const label = row?.label ?? FALLBACK_CATEGORY_LABEL[key];
const optedOut = row?.optedOut ?? false;
const isPending = pending.has(key);
return (
<div
key={key}
className="flex items-start justify-between gap-4 rounded-lg border p-4"
className="flex items-center justify-between gap-4 rounded-lg border p-4"
>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium">{label}</p>
<p className="mt-1 text-sm text-muted-foreground">{description}</p>
</div>
<p className="min-w-0 flex-1 text-sm font-medium">{label}</p>
<Button
type="button"
variant={optedOut ? "outline" : "default"}