diff --git a/.github/workflows/deploy_prod.yaml b/.github/workflows/deploy_prod.yaml index ae47ca706..7799a4906 100644 --- a/.github/workflows/deploy_prod.yaml +++ b/.github/workflows/deploy_prod.yaml @@ -19,3 +19,19 @@ jobs: secrets: inherit with: environment: production + + # Kubernetes deploy. Activated when the repo variable DEPLOY_PROVIDER is + # 'k8s' — mutually exclusive with the Vercel job above. Reuses the + # platform-grade deploy_k8s.yaml: build → push GHCR → envsubst manifests + # under .deploy/k8s-platform/ → kubectl apply → wait for rollout. Reads + # K8S_TOKEN / K8S_INGRESS_HOST / K8S_INGRESS_CLASS / K8S_NAMESPACE / + # (optional) K8S_TLS_ISSUER from repo secrets. For Ever Works' own + # k8s-works cluster, leave K8S_TLS_ISSUER unset — Cloudflare terminates + # TLS upstream and the workflow emits the matching nginx annotations + # (ssl-redirect: "false"), matching every other *.ever.works ingress. + K8s: + if: ${{ vars.DEPLOY_PROVIDER == 'k8s' }} + uses: ./.github/workflows/deploy_k8s.yaml + secrets: inherit + with: + environment: production diff --git a/apps/web-e2e/verify-deactivation.mjs b/apps/web-e2e/verify-deactivation.mjs new file mode 100644 index 000000000..3b3879c95 --- /dev/null +++ b/apps/web-e2e/verify-deactivation.mjs @@ -0,0 +1,200 @@ +/** + * End-to-end verification of account deactivation / reactivation. + * Uses selectors and timing from global-setup.ts. + */ + +import { chromium } from '@playwright/test'; + +const BASE = 'http://localhost:3000'; +const EMAIL = `e2e-deactivate-${Date.now()}@test.local`; +const PASSWORD = 'TestClient123!'; +const NAME = 'Deactivation Tester'; + +let passed = 0; +let failed = 0; +const findings = []; + +const ok = (label, detail = '') => { passed++; console.log(` ✅ ${label}${detail ? ' — ' + detail : ''}`); }; +const fail = (label, detail = '') => { failed++; console.log(` ❌ ${label}${detail ? ' — ' + detail : ''}`); }; +const probe = (label, detail = '') => console.log(` 🔍 ${label}${detail ? ' — ' + detail : ''}`); +const warn = (label) => { findings.push(`⚠️ ${label}`); console.log(` ⚠️ ${label}`); }; +const note = (label) => { findings.push(` ${label}`); console.log(` ${label}`); }; + +async function bodyText(page) { + return page.evaluate(() => document.body.innerText).catch(() => ''); +} + +async function toastText(page) { + return page.evaluate(() => + [...document.querySelectorAll('[data-sonner-toast], [role="status"], [role="alert"]')] + .map(t => t.textContent).join(' ') + ).catch(() => ''); +} + +async function run() { + const browser = await chromium.launch({ headless: true }); + const ctx = await browser.newContext({ baseURL: BASE }); + const page = await ctx.newPage(); + + const consoleErrors = []; + page.on('console', msg => { if (msg.type() === 'error') consoleErrors.push(msg.text()); }); + + try { + // ── Step 1: Register a fresh throwaway account ────────────────────── + console.log('\n[Step 1] Register fresh test account'); + await page.goto('/auth/register', { waitUntil: 'domcontentloaded' }); + await page.locator('#name').waitFor({ state: 'visible', timeout: 15000 }); + await page.locator('#name').fill(NAME); + await page.locator('#email').fill(EMAIL); + await page.locator('#password').fill(PASSWORD); + await page.locator('#password').press('Enter'); + + try { + await page.waitForURL(/\/client\/dashboard/, { timeout: 60000, waitUntil: 'domcontentloaded' }); + ok('Registered and auto-logged in', page.url()); + } catch { + fail('Did not reach /client/dashboard after sign-up', page.url()); + console.log(' Body:', (await bodyText(page)).slice(0, 300)); + await browser.close(); + process.exit(1); + } + + // ── Step 2: Go to danger zone ─────────────────────────────────────── + console.log('\n[Step 2] Navigate to danger-zone settings'); + await page.goto('/client/settings/danger-zone', { waitUntil: 'domcontentloaded' }); + await page.waitForTimeout(1500); + + if (page.url().includes('danger-zone')) { + ok('Danger zone page loaded', page.url()); + } else { + fail('Could not reach danger-zone', page.url()); + } + + // ── Step 3: Open deactivate modal ─────────────────────────────────── + console.log('\n[Step 3] Open Deactivate Account modal'); + const deactivateBtn = page.getByRole('button', { name: /deactivate\s*account/i }).first(); + await deactivateBtn.waitFor({ state: 'visible', timeout: 10000 }); + ok('Deactivate button visible'); + await deactivateBtn.click(); + await page.waitForTimeout(700); + + const pwInModal = page.locator('[role="dialog"] input[type="password"], [aria-modal="true"] input[type="password"]').first(); + if (await pwInModal.isVisible().catch(() => false)) { + ok('Confirmation modal opened with password field'); + } else { + warn('Modal role="dialog" not found — checking for password field anyway'); + } + + // ── Step 4: Wrong password ─────────────────────────────────────────── + console.log('\n[Step 4] 🔍 Wrong password → must show "Incorrect password", not "User not found"'); + const pwField = page.locator('input[autocomplete="current-password"]').last(); + await pwField.fill('WrongPassword99!'); + await page.locator('button[type="submit"]').last().click(); + await page.waitForTimeout(3000); + + const bodyW = await bodyText(page); + const toastW = await toastText(page); + const combined = bodyW + ' ' + toastW; + + if (/user not found/i.test(combined)) { + fail('"User not found" appeared with wrong password — migration did not take effect'); + } else if (/incorrect password/i.test(combined)) { + ok('Wrong password → "Incorrect password" error ✓ (no "User not found")'); + } else { + probe('Error text unclear — capturing snippet', combined.slice(0, 200)); + } + + // ── Step 5: Correct password → deactivation ────────────────────────── + console.log('\n[Step 5] Correct password → deactivation → redirect'); + const pwField2 = page.locator('input[autocomplete="current-password"]').last(); + await pwField2.fill(PASSWORD); + await page.locator('button[type="submit"]').last().click(); + + try { await page.waitForURL(/auth\/signin/, { timeout: 10000 }); } catch { /* may already be there */ } + await page.waitForTimeout(1000); + + const afterDeactivate = page.url(); + const bodyD = await bodyText(page); + const toastD = await toastText(page); + + if (/user not found/i.test(bodyD + toastD)) { + fail('"User not found" with correct password — deactivation broken'); + note(`URL: ${afterDeactivate} | body: ${bodyD.slice(0, 300)}`); + } else if (afterDeactivate.includes('deactivated=true')) { + ok('Deactivated → /auth/signin?deactivated=true ✓', afterDeactivate); + } else if (afterDeactivate.includes('/auth/signin')) { + ok('Deactivated → redirected to sign-in page', afterDeactivate); + probe('?deactivated=true param absent from URL — minor UX gap'); + } else { + fail('Unexpected location after deactivation', afterDeactivate); + note(`Body: ${bodyD.slice(0, 300)}`); + } + + // ── Step 6: Sign-in with deactivated account ───────────────────────── + console.log('\n[Step 6] 🔍 Sign-in with deactivated account — must not reach dashboard'); + await page.goto('/auth/signin', { waitUntil: 'domcontentloaded' }); + await page.locator('#email').waitFor({ state: 'visible', timeout: 10000 }); + await page.locator('#email').fill(EMAIL); + await page.locator('#password').fill(PASSWORD); + await page.getByRole('button', { name: /sign in/i }).click(); + await page.waitForTimeout(4000); + + const afterLogin = page.url(); + const bodyL = await bodyText(page); + const toastL = await toastText(page); + + if (afterLogin.includes('/dashboard')) { + fail('Deactivated account reached dashboard — no guard in place'); + } else if (/deactivat/i.test(bodyL + toastL)) { + ok('Sign-in blocked with deactivation message ✓', `url: ${afterLogin}`); + } else { + probe('Deactivated sign-in — no explicit "deactivated" message found'); + note(`URL: ${afterLogin} | body: ${bodyL.slice(0, 200)}`); + } + + // ── Step 7: Reactivation UI present ───────────────────────────────── + console.log('\n[Step 7] Reactivation UI check'); + const pageBodyFull = await bodyText(page); + const toastFull = await toastText(page); + if (/reactivat/i.test(pageBodyFull + toastFull)) { + ok('Reactivation option visible to user after deactivated login attempt'); + } else { + probe('No reactivation prompt on sign-in page'); + note('Reactivation may require a separate flow — not blocking'); + } + + // ── Step 8: Session cleared ─────────────────────────────────────────── + console.log('\n[Step 8] Session must be cleared after deactivation'); + const session = await page.evaluate(async () => { + const r = await fetch('/api/auth/session'); + return r.json(); + }).catch(() => null); + + if (!session || !session.user) { + ok('Session cleared — no active JWT after deactivation ✓'); + } else { + warn('Session still active after deactivation'); + note(`Session user: ${JSON.stringify(session.user).slice(0, 150)}`); + } + + } catch (err) { + fail('Unexpected script error', err.message); + console.error(err); + } finally { + await browser.close(); + } + + // ── Final report ───────────────────────────────────────────────────────── + console.log('\n' + '─'.repeat(60)); + console.log(`Passed: ${passed} Failed: ${failed}`); + if (findings.length) { console.log('\nFindings:'); findings.forEach(f => console.log(f)); } + if (consoleErrors.length) { + console.log('\nBrowser console errors (first 5):'); + consoleErrors.slice(0, 5).forEach(e => console.log(' ', e)); + } + console.log('─'.repeat(60)); + console.log(`Verdict: ${failed === 0 ? 'PASS ✓' : 'FAIL ✗'}`); + process.exit(failed > 0 ? 1 : 0); +} + +run(); diff --git a/apps/web/app/[locale]/auth/actions.ts b/apps/web/app/[locale]/auth/actions.ts index fc4321b80..32d59bb69 100644 --- a/apps/web/app/[locale]/auth/actions.ts +++ b/apps/web/app/[locale]/auth/actions.ts @@ -7,7 +7,7 @@ import type { AdapterAccountType } from 'next-auth/adapters'; import { db } from '@/lib/db/drizzle'; import { getTenantId } from '@/lib/auth/tenant'; -import { signOut } from '@/lib/auth'; +import { signOut, unstable_update } from '@/lib/auth'; import { validatedAction, validatedActionWithUser } from '@/lib/auth/middleware'; import { comparePasswords, hashPassword, AuthProviders, AuthErrorCode } from '@/lib/auth/credentials'; import { @@ -18,6 +18,8 @@ import { getVerificationTokenByToken, getVerificationTokenByEmail, hardDeleteUser, + deactivateUser, + reactivateUser, logActivity, updateUser, updateUserPassword, @@ -537,3 +539,72 @@ export const newPasswordAction = validatedAction(newPasswordSchema, async (data) return { success: true }; }); + +// ─── Account Deactivation ───────────────────────────────────────────────────── + +const deactivateAccountSchema = z.object({ + password: z.string().min(PASSWORD_MIN_LENGTH).max(100), + provider: z.enum(authProviderTypes).default('next-auth') +}); + +/** + * Temporarily deactivate the signed-in user's account. + * Sets deactivatedAt on the users row and signs the user out. + * The account (credentials, profile, listings) is fully preserved and can be + * restored at any time via reactivateAccount. + */ +export const deactivateAccount = validatedActionWithUser(deactivateAccountSchema, async (data, _, user) => { + const { password } = data; + const dbUser = await getUserByEmail(user.email!).catch(() => null); + if (!dbUser) { + return { error: 'User not found' }; + } + + if (dbUser.deactivatedAt) { + return { error: 'Account is already deactivated.' }; + } + + // Mirror the same dual-path password check used by deleteAccount. + const isClientPasswordValid = await verifyClientPassword(user.email!, password); + const isPasswordValid = + isClientPasswordValid || (await comparePasswords(password, dbUser.passwordHash)); + if (!isPasswordValid) { + return { error: 'Incorrect password. Account deactivation failed.' }; + } + + await deactivateUser(dbUser.id); + await logActivity(ActivityType.UPDATE_ACCOUNT, dbUser.id, 'user'); + + // Sign the user out so the stale (active) session is cleared. The client + // then navigates to /auth/signin where they can log back in and reactivate. + await signOut({ redirect: false }); + return { success: true, redirect: '/auth/signin?deactivated=true' }; +}); + +const reactivateAccountSchema = z.object({ + provider: z.enum(authProviderTypes).default('next-auth').optional() +}); + +/** + * Reactivate the signed-in user's previously deactivated account. + * Clears deactivatedAt, restoring full account visibility. Updates the + * JWT in-place so the user stays on the current page with a clean session. + */ +export const reactivateAccount = validatedActionWithUser(reactivateAccountSchema, async (_data, __, user) => { + const dbUser = await getUserByEmail(user.email!).catch(() => null); + if (!dbUser) { + return { error: 'User not found' }; + } + + if (!dbUser.deactivatedAt) { + return { error: 'Account is not deactivated.' }; + } + + await reactivateUser(dbUser.id); + await logActivity(ActivityType.UPDATE_ACCOUNT, dbUser.id, 'user'); + + // Update the JWT in-place so isDeactivated is cleared without signing out. + // The user stays on the danger-zone page and the session is immediately fresh. + await unstable_update({ user: { isDeactivated: false } }); + return { success: true }; +}); diff --git a/apps/web/app/[locale]/client/settings/danger-zone/page.tsx b/apps/web/app/[locale]/client/settings/danger-zone/page.tsx index 9afc20d46..6caccbdc0 100644 --- a/apps/web/app/[locale]/client/settings/danger-zone/page.tsx +++ b/apps/web/app/[locale]/client/settings/danger-zone/page.tsx @@ -3,11 +3,31 @@ import { FiArrowLeft } from 'react-icons/fi'; import { ShieldAlert, Info } from 'lucide-react'; import { Link } from '@/i18n/navigation'; import { getTranslations } from 'next-intl/server'; +import { auth } from '@/lib/auth'; +import { getUserById } from '@/lib/db/queries'; import { DeleteAccountCard } from '@/components/settings/danger-zone/delete-account-card'; +import { DeactivateAccountCard } from '@/components/settings/danger-zone/deactivate-account-card'; +import { ReactivateAccountCard } from '@/components/settings/danger-zone/reactivate-account-card'; export default async function DangerZoneSettingsPage() { const t = await getTranslations('settings.DANGER_ZONE_PAGE'); + // Read current deactivation state from DB so the page always reflects the + // live value — the JWT may lag one refresh cycle after reactivation. + const session = await auth(); + const userId = session?.user?.id; + let isDeactivated = false; + + if (userId) { + try { + const dbUser = await getUserById(userId); + isDeactivated = dbUser?.deactivatedAt != null; + } catch { + // Non-critical — fall back to session value + isDeactivated = session?.user?.isDeactivated ?? false; + } + } + return (
@@ -39,7 +59,7 @@ export default async function DangerZoneSettingsPage() {

- {/* Calm intro banner — neutral surface, just sets expectations */} + {/* Calm intro banner */} - {/* Destructive actions */} + {/* Deactivation section — shown conditionally */} + {isDeactivated ? ( + + ) : ( + + )} + + {/* Permanent delete — always shown */}
diff --git a/apps/web/components/settings/danger-zone/deactivate-account-card.tsx b/apps/web/components/settings/danger-zone/deactivate-account-card.tsx new file mode 100644 index 000000000..4ba4514e2 --- /dev/null +++ b/apps/web/components/settings/danger-zone/deactivate-account-card.tsx @@ -0,0 +1,298 @@ +'use client'; + +import { useActionState, useEffect, useId, useState } from 'react'; +import { useTranslations, useLocale } from 'next-intl'; +import { PauseCircle, Eye, EyeOff, Loader2 } from 'lucide-react'; +import { toast } from 'sonner'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { z } from 'zod'; +import { deactivateAccount } from '@/app/[locale]/auth/actions'; +import type { ActionState } from '@/lib/auth/middleware'; +import { Modal } from '@/components/ui/modal'; +import { Button } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; +import { DEFAULT_LOCALE } from '@/lib/constants'; + +const CLIENT_PASSWORD_MIN_LENGTH = 8; + +type FormValues = { password: string }; + +export function DeactivateAccountCard() { + const t = useTranslations('settings.DANGER_ZONE_PAGE.DEACTIVATE_ACCOUNT'); + const [isModalOpen, setIsModalOpen] = useState(false); + + return ( + <> +
+ {/* Header */} +
+
+
+
+
+
+

+ {t('TITLE')} +

+ + {t('REVERSIBLE_BADGE')} + +
+

+ {t('DESCRIPTION')} +

+
+
+ + {/* What happens */} +
+

+ {t('EFFECTS_TITLE')} +

+
    + {(['EFFECT_1', 'EFFECT_2', 'EFFECT_3', 'EFFECT_4'] as const).map((key) => ( +
  • + + {t(key)} +
  • + ))} +
+
+
+ + {/* Footer */} +
+

{t('CTA_HELPER')}

+ +
+
+ + setIsModalOpen(false)} /> + + ); +} + +interface DeactivateAccountModalProps { + isOpen: boolean; + onClose: () => void; +} + +function DeactivateAccountModal({ isOpen, onClose }: DeactivateAccountModalProps) { + const tModal = useTranslations('settings.DANGER_ZONE_PAGE.DEACTIVATE_ACCOUNT.CONFIRM_MODAL'); + const tErrors = useTranslations('settings.DANGER_ZONE_PAGE.DEACTIVATE_ACCOUNT.ERRORS'); + const locale = useLocale(); + + const passwordRequired = tErrors('PASSWORD_REQUIRED'); + const passwordTooShort = tErrors('PASSWORD_TOO_SHORT'); + + const schema = z.object({ + password: z.string().min(1, passwordRequired).min(CLIENT_PASSWORD_MIN_LENGTH, passwordTooShort) + }); + + const { + register, + handleSubmit, + reset, + formState: { errors } + } = useForm({ + resolver: zodResolver(schema), + mode: 'onChange', + defaultValues: { password: '' } + }); + + const [state, formAction, isPending] = useActionState(deactivateAccount, {}); + const [showPassword, setShowPassword] = useState(false); + const titleId = useId(); + const passwordInputId = useId(); + const passwordErrorId = useId(); + + useEffect(() => { + if (isOpen) { + reset(); + setShowPassword(false); + } + }, [isOpen, reset]); + + useEffect(() => { + if (state?.error) { + toast.error(state.error); + } + }, [state]); + + useEffect(() => { + if (!state?.success || !state.redirect) return; + const redirectPath = state.redirect as string; + const shouldPrefixLocale = locale !== DEFAULT_LOCALE && !redirectPath.startsWith(`/${locale}`); + window.location.href = shouldPrefixLocale ? `/${locale}${redirectPath}` : redirectPath; + }, [state, locale]); + + const onValid = (data: FormValues) => { + const fd = new FormData(); + fd.set('password', data.password); + formAction(fd); + }; + + return ( + {} : onClose} + isDismissable={!isPending} + size="lg" + backdrop="blur" + showHeaderBorder={false} + customHeader={ +
+ +
+

+ {tModal('TITLE')} +

+

+ {tModal('SUBTITLE')} +

+
+
+ } + > +
+ {/* What deactivation means */} +
+

+ {tModal('EFFECTS_HEADING')} +

+
    + {(['EFFECT_1', 'EFFECT_2', 'EFFECT_3', 'EFFECT_4'] as const).map((key) => ( +
  • + + {tModal(key)} +
  • + ))} +
+
+

+ {tModal('REACTIVATION_NOTE')} +

+
+
+ + {/* Confirmation form */} +
+
+

+ {tModal('CONFIRMATION_HEADING')} +

+ +
+ +
+ + +
+ {errors.password ? ( + + ) : null} +
+
+ + {/* Sticky footer */} +
+ + +
+
+
+
+ ); +} diff --git a/apps/web/components/settings/danger-zone/reactivate-account-card.tsx b/apps/web/components/settings/danger-zone/reactivate-account-card.tsx new file mode 100644 index 000000000..17386a770 --- /dev/null +++ b/apps/web/components/settings/danger-zone/reactivate-account-card.tsx @@ -0,0 +1,108 @@ +'use client'; + +import { useActionState, useEffect } from 'react'; +import { useTranslations } from 'next-intl'; +import { PlayCircle, Loader2 } from 'lucide-react'; +import { toast } from 'sonner'; +import { useRouter } from 'next/navigation'; +import { reactivateAccount } from '@/app/[locale]/auth/actions'; +import type { ActionState } from '@/lib/auth/middleware'; +import { Button } from '@/components/ui/button'; + +export function ReactivateAccountCard() { + const t = useTranslations('settings.DANGER_ZONE_PAGE.REACTIVATE_ACCOUNT'); + const router = useRouter(); + + const [state, formAction, isPending] = useActionState(reactivateAccount, {}); + + useEffect(() => { + if (state?.error) { + toast.error(state.error); + } + }, [state]); + + useEffect(() => { + if (!state?.success) return; + toast.success(t('SUCCESS_TOAST')); + router.refresh(); + }, [state, router, t]); + + const handleReactivate = () => { + const fd = new FormData(); + formAction(fd); + }; + + return ( +
+ {/* Status banner */} +
+

+ {t('STATUS_BADGE')} +

+
+ + {/* Header */} +
+
+
+
+
+

+ {t('TITLE')} +

+

+ {t('DESCRIPTION')} +

+
+
+ + {/* What reactivation restores */} +
+

+ {t('RESTORE_TITLE')} +

+
    + {(['RESTORE_1', 'RESTORE_2', 'RESTORE_3'] as const).map((key) => ( +
  • + + {t(key)} +
  • + ))} +
+
+
+ + {/* Footer */} +
+

{t('CTA_HELPER')}

+ +
+
+ ); +} diff --git a/apps/web/lib/auth/index.ts b/apps/web/lib/auth/index.ts index 63a5b5c9f..0bd497182 100644 --- a/apps/web/lib/auth/index.ts +++ b/apps/web/lib/auth/index.ts @@ -231,18 +231,35 @@ export const { handlers, auth, signIn, signOut, unstable_update } = NextAuth({ token.tenantIdChecked = true; const [dbUser] = await getDrizzleInstance() - .select({ tenantId: users.tenantId }) + .select({ tenantId: users.tenantId, deactivatedAt: users.deactivatedAt }) .from(users) .where(eq(users.id, token.userId)) .limit(1); if (dbUser?.tenantId) { token.tenantId = dbUser.tenantId; } + // Propagate deactivation state into the JWT on sign-in. + token.isDeactivated = dbUser?.deactivatedAt != null; } catch (error) { console.error('[auth][jwt] Error resolving tenantId:', error); } } + // On subsequent JWT refreshes (no user object), sync deactivation state + // from the DB so a reactivation takes effect without requiring a new login. + if (!user && token.userId && isDatabaseAvailable) { + try { + const [dbUser] = await getDrizzleInstance() + .select({ deactivatedAt: users.deactivatedAt }) + .from(users) + .where(eq(users.id, token.userId)) + .limit(1); + token.isDeactivated = dbUser?.deactivatedAt != null; + } catch { + // Non-critical — keep existing token value on DB error + } + } + // Detect and update currency/country for client profiles on login // Note: Full detection with headers happens on first API call with request context // This is just a placeholder - actual detection happens in getUserCurrency with request headers @@ -295,6 +312,7 @@ export const { handlers, auth, signIn, signOut, unstable_update } = NextAuth({ if (typeof token.tenantId === 'string') { session.user.tenantId = token.tenantId; } + session.user.isDeactivated = token.isDeactivated === true; } // Debug (dev only): trace session payload without PII diff --git a/apps/web/lib/db/migrations/0039_add_account_deactivation.sql b/apps/web/lib/db/migrations/0039_add_account_deactivation.sql new file mode 100644 index 000000000..d64f4cbf7 --- /dev/null +++ b/apps/web/lib/db/migrations/0039_add_account_deactivation.sql @@ -0,0 +1,13 @@ +-- Account Deactivation (reversible soft-pause) +-- Adds a deactivated_at timestamp to the users table. NULL means the account +-- is active; a non-NULL value means the account is temporarily deactivated. +-- This is intentionally separate from deleted_at (which is used for soft-delete / +-- hard-delete flows). Existing rows default to NULL (active). +-- +-- Additive, idempotent — safe to re-run. + +ALTER TABLE "users" + ADD COLUMN IF NOT EXISTS "deactivated_at" timestamp; + +-- Index for efficient admin queries that filter by deactivation status +CREATE INDEX IF NOT EXISTS "users_deactivated_at_idx" ON "users" ("deactivated_at"); diff --git a/apps/web/lib/db/queries/client.queries.ts b/apps/web/lib/db/queries/client.queries.ts index f1322e49b..14a64d0e6 100644 --- a/apps/web/lib/db/queries/client.queries.ts +++ b/apps/web/lib/db/queries/client.queries.ts @@ -1,6 +1,6 @@ import { and, eq, desc, asc, lte, sql, isNull, or, gte, countDistinct, type SQL } from 'drizzle-orm'; import { db } from '../drizzle'; -import { clientProfiles, accounts, userRoles, roles, type ClientProfile, type NewClientProfile } from '../schema'; +import { clientProfiles, accounts, userRoles, roles, users, type ClientProfile, type NewClientProfile } from '../schema'; import type { AdapterAccountType } from 'next-auth/adapters'; import type { ClientStatus, ClientPlan, ClientAccountType, ClientProfileWithAuth, ClientAccount } from './types'; import { extractUsernameFromEmail, ensureUniqueUsername } from './utils'; @@ -119,8 +119,9 @@ export async function searchPublicProfiles(params: { const whereConditions: SQL[] = [ eq(clientProfiles.tenantId, tenantId), - // Only show active accounts - eq(clientProfiles.status, 'active') + // Only show active, non-deactivated accounts + eq(clientProfiles.status, 'active'), + isNull(users.deactivatedAt) ]; if (params.query && params.query.trim()) { @@ -142,6 +143,7 @@ export async function searchPublicProfiles(params: { const [countResult] = await db .select({ count: sql`count(distinct ${clientProfiles.id})::int` }) .from(clientProfiles) + .innerJoin(users, eq(users.id, clientProfiles.userId)) .leftJoin(userRoles, eq(userRoles.userId, clientProfiles.userId)) .leftJoin(roles, and(eq(userRoles.roleId, roles.id), eq(roles.isAdmin, true))) .where(and(whereClause, excludeAdmins)); @@ -160,6 +162,7 @@ export async function searchPublicProfiles(params: { location: clientProfiles.location }) .from(clientProfiles) + .innerJoin(users, eq(users.id, clientProfiles.userId)) .leftJoin(userRoles, eq(userRoles.userId, clientProfiles.userId)) .leftJoin(roles, and(eq(userRoles.roleId, roles.id), eq(roles.isAdmin, true))) .where(and(whereClause, excludeAdmins)) @@ -189,7 +192,8 @@ export async function getClientProfileByUsername(username: string): Promise ({ createdAtIndex: index('users_created_at_idx').on(table.createdAt), diff --git a/apps/web/messages/ar.json b/apps/web/messages/ar.json index fa38c9bdb..afd600d68 100644 --- a/apps/web/messages/ar.json +++ b/apps/web/messages/ar.json @@ -670,6 +670,51 @@ "INTRO_LABEL": "تنبيه", "INTRO_BODY": "كل إجراء في هذا القسم نهائي. سنطلب منك دائمًا التأكيد قبل إزالة أي شيء.", "IRREVERSIBLE_BADGE": "إجراء مدمر", + "DEACTIVATE_ACCOUNT": { + "TITLE": "تعطيل الحساب", + "REVERSIBLE_BADGE": "قابل للعكس", + "DESCRIPTION": "قم بإيقاف حسابك مؤقتاً. يمكنك إعادة تفعيله في أي وقت عن طريق تسجيل الدخول مجدداً.", + "EFFECTS_TITLE": "ما يحدث عند التعطيل", + "EFFECT_1": "سيتم إخفاء ملفك الشخصي وقوائمك من الدليل.", + "EFFECT_2": "ستُسجَّل خروجك من جميع الجلسات النشطة.", + "EFFECT_3": "سيتم إيقاف رسائل البريد الإلكتروني والإشعارات المجدولة مؤقتاً.", + "EFFECT_4": "بياناتك محفوظة بأمان ولن يتم حذف أي شيء.", + "CTA_HELPER": "يمكنك إعادة التفعيل في أي وقت.", + "CTA": "تعطيل الحساب…", + "CONFIRM_MODAL": { + "TITLE": "تعطيل حسابك", + "SUBTITLE": "سيتم الاحتفاظ ببياناتك — يمكنك إعادة التفعيل في أي وقت.", + "EFFECTS_HEADING": "ما يعنيه التعطيل", + "EFFECT_1": "سيتم إخفاء ملفك الشخصي وقوائمك من الدليل.", + "EFFECT_2": "ستُسجَّل خروجك من جميع الجلسات النشطة.", + "EFFECT_3": "سيتم إيقاف رسائل البريد الإلكتروني والإشعارات المجدولة مؤقتاً.", + "EFFECT_4": "بياناتك محفوظة بأمان ولن يتم حذف أي شيء.", + "REACTIVATION_NOTE": "لإعادة التفعيل، ما عليك سوى تسجيل الدخول مرة أخرى باستخدام بياناتك.", + "CONFIRMATION_HEADING": "تأكيد هويتك", + "PASSWORD_LABEL": "كلمة المرور الحالية", + "PASSWORD_PLACEHOLDER": "أدخل كلمة المرور", + "CANCEL_CTA": "إلغاء", + "CONFIRM_CTA": "تعطيل الحساب", + "DEACTIVATING": "جارٍ التعطيل…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "كلمة المرور مطلوبة.", + "PASSWORD_TOO_SHORT": "يجب أن تتكون كلمة المرور من 8 أحرف على الأقل." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "الحساب معطل", + "TITLE": "إعادة تفعيل حسابك", + "DESCRIPTION": "استعد الوصول الكامل إلى حسابك واجعل ملفك الشخصي مرئياً مرة أخرى.", + "RESTORE_TITLE": "ما تستعيده إعادة التفعيل", + "RESTORE_1": "سيكون ملفك الشخصي وقوائمك مرئية مرة أخرى في الدليل.", + "RESTORE_2": "ستُستأنف جميع الإشعارات ورسائل البريد الإلكتروني المجدولة.", + "RESTORE_3": "جميع بياناتك وإعداداتك وسجلك سليمة تماماً.", + "CTA_HELPER": "إعادة التفعيل فورية — لا يلزم موافقة.", + "CTA": "إعادة تفعيل الحساب", + "REACTIVATING": "جارٍ إعادة التفعيل…", + "SUCCESS_TOAST": "تم إعادة تفعيل حسابك بنجاح." + }, "DELETE_ACCOUNT": { "TITLE": "حذف هذا الحساب", "DESCRIPTION": "إزالة هذا الحساب وكل ما يرتبط به بشكل نهائي. لا يمكن التراجع عن هذا الإجراء.", diff --git a/apps/web/messages/bg.json b/apps/web/messages/bg.json index 0576288e3..871b3cfac 100644 --- a/apps/web/messages/bg.json +++ b/apps/web/messages/bg.json @@ -749,6 +749,51 @@ "INTRO_LABEL": "Внимание", "INTRO_BODY": "Всяко действие в този раздел е окончателно. Винаги ще поискаме потвърждение от вас, преди да премахнем нещо.", "IRREVERSIBLE_BADGE": "Деструктивно", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Деактивиране на акаунт", + "REVERSIBLE_BADGE": "Обратимо", + "DESCRIPTION": "Временно спрете акаунта си. Можете да го реактивирате по всяко време, като влезете отново.", + "EFFECTS_TITLE": "Какво се случва при деактивиране", + "EFFECT_1": "Вашият профил и обяви ще бъдат скрити от директорията.", + "EFFECT_2": "Ще бъдете излезли от всички активни сесии.", + "EFFECT_3": "Планираните имейли и известия ще бъдат спрени.", + "EFFECT_4": "Вашите данни се съхраняват сигурно и нищо не се изтрива.", + "CTA_HELPER": "Можете да реактивирате по всяко време.", + "CTA": "Деактивиране на акаунт…", + "CONFIRM_MODAL": { + "TITLE": "Деактивиране на вашия акаунт", + "SUBTITLE": "Вашите данни ще бъдат запазени — можете да реактивирате по всяко време.", + "EFFECTS_HEADING": "Какво означава деактивирането", + "EFFECT_1": "Вашият профил и обяви ще бъдат скрити от директорията.", + "EFFECT_2": "Ще бъдете излезли от всички активни сесии.", + "EFFECT_3": "Планираните имейли и известия ще бъдат спрени.", + "EFFECT_4": "Вашите данни се съхраняват сигурно и нищо не се изтрива.", + "REACTIVATION_NOTE": "За да реактивирате, просто влезте отново с вашите данни за достъп.", + "CONFIRMATION_HEADING": "Потвърдете, че наистина сте вие", + "PASSWORD_LABEL": "Вашата текуща парола", + "PASSWORD_PLACEHOLDER": "Въведете паролата си", + "CANCEL_CTA": "Отказ", + "CONFIRM_CTA": "Деактивиране на акаунт", + "DEACTIVATING": "Деактивиране…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Паролата е задължителна.", + "PASSWORD_TOO_SHORT": "Паролата трябва да съдържа поне 8 символа." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Акаунтът е деактивиран", + "TITLE": "Реактивиране на вашия акаунт", + "DESCRIPTION": "Възстановете пълния достъп до акаунта си и направете профила си отново видим.", + "RESTORE_TITLE": "Какво възстановява реактивирането", + "RESTORE_1": "Вашият профил и обяви ще бъдат отново видими в директорията.", + "RESTORE_2": "Всички известия и планирани имейли ще се възобновят.", + "RESTORE_3": "Всички ваши данни, настройки и история са напълно непокътнати.", + "CTA_HELPER": "Реактивирането е незабавно — не е необходимо одобрение.", + "CTA": "Реактивиране на акаунт", + "REACTIVATING": "Реактивиране…", + "SUCCESS_TOAST": "Вашият акаунт беше успешно реактивиран." + }, "DELETE_ACCOUNT": { "TITLE": "Изтриване на този акаунт", "DESCRIPTION": "Премахнете завинаги този акаунт и всичко свързано с него. Това действие не може да бъде отменено.", diff --git a/apps/web/messages/de.json b/apps/web/messages/de.json index 2924f39d4..4c592d7b7 100644 --- a/apps/web/messages/de.json +++ b/apps/web/messages/de.json @@ -683,6 +683,51 @@ "INTRO_LABEL": "Achtung", "INTRO_BODY": "Jede Aktion in diesem Bereich ist endgültig. Wir bitten Sie immer um eine Bestätigung, bevor etwas entfernt wird.", "IRREVERSIBLE_BADGE": "Destruktiv", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Konto deaktivieren", + "REVERSIBLE_BADGE": "Rückgängig machbar", + "DESCRIPTION": "Pausieren Sie Ihr Konto vorübergehend. Sie können es jederzeit durch erneutes Anmelden reaktivieren.", + "EFFECTS_TITLE": "Was passiert, wenn Sie deaktivieren", + "EFFECT_1": "Ihr Profil und Ihre Einträge werden aus dem Verzeichnis ausgeblendet.", + "EFFECT_2": "Sie werden von allen aktiven Sitzungen abgemeldet.", + "EFFECT_3": "Geplante E-Mails und Benachrichtigungen werden pausiert.", + "EFFECT_4": "Ihre Daten sind sicher und es wird nichts gelöscht.", + "CTA_HELPER": "Sie können jederzeit reaktivieren.", + "CTA": "Konto deaktivieren…", + "CONFIRM_MODAL": { + "TITLE": "Ihr Konto deaktivieren", + "SUBTITLE": "Ihre Daten bleiben erhalten — Sie können jederzeit reaktivieren.", + "EFFECTS_HEADING": "Was Deaktivierung bedeutet", + "EFFECT_1": "Ihr Profil und Ihre Einträge werden aus dem Verzeichnis ausgeblendet.", + "EFFECT_2": "Sie werden von allen aktiven Sitzungen abgemeldet.", + "EFFECT_3": "Geplante E-Mails und Benachrichtigungen werden pausiert.", + "EFFECT_4": "Ihre Daten sind sicher und es wird nichts gelöscht.", + "REACTIVATION_NOTE": "Um zu reaktivieren, melden Sie sich einfach erneut mit Ihren Zugangsdaten an.", + "CONFIRMATION_HEADING": "Bestätigen Sie, dass Sie es wirklich sind", + "PASSWORD_LABEL": "Ihr aktuelles Passwort", + "PASSWORD_PLACEHOLDER": "Passwort eingeben", + "CANCEL_CTA": "Abbrechen", + "CONFIRM_CTA": "Konto deaktivieren", + "DEACTIVATING": "Wird deaktiviert…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Passwort ist erforderlich.", + "PASSWORD_TOO_SHORT": "Das Passwort muss mindestens 8 Zeichen lang sein." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Konto deaktiviert", + "TITLE": "Ihr Konto reaktivieren", + "DESCRIPTION": "Stellen Sie den vollen Zugang zu Ihrem Konto wieder her und machen Sie Ihr Profil wieder sichtbar.", + "RESTORE_TITLE": "Was die Reaktivierung wiederherstellt", + "RESTORE_1": "Ihr Profil und Ihre Einträge werden wieder im Verzeichnis sichtbar sein.", + "RESTORE_2": "Alle Benachrichtigungen und geplanten E-Mails werden wieder aufgenommen.", + "RESTORE_3": "Alle Ihre Daten, Einstellungen und der Verlauf sind vollständig intakt.", + "CTA_HELPER": "Die Reaktivierung erfolgt sofort — keine Genehmigung erforderlich.", + "CTA": "Konto reaktivieren", + "REACTIVATING": "Wird reaktiviert…", + "SUCCESS_TOAST": "Ihr Konto wurde erfolgreich reaktiviert." + }, "DELETE_ACCOUNT": { "TITLE": "Dieses Konto löschen", "DESCRIPTION": "Entfernen Sie dieses Konto und alles, was damit verbunden ist, dauerhaft. Dies kann nicht rückgängig gemacht werden.", diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 158a5751b..013cb61f4 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -814,6 +814,51 @@ "INTRO_LABEL": "Heads up", "INTRO_BODY": "Every action in this section is permanent. We'll always ask you to confirm before anything is removed.", "IRREVERSIBLE_BADGE": "Destructive", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Deactivate account", + "REVERSIBLE_BADGE": "Reversible", + "DESCRIPTION": "Temporarily pause your account. You can reactivate it at any time by signing back in.", + "EFFECTS_TITLE": "What happens when you deactivate", + "EFFECT_1": "Your profile and listings will be hidden from the directory.", + "EFFECT_2": "You will be signed out of all active sessions.", + "EFFECT_3": "Scheduled emails and notifications will be paused.", + "EFFECT_4": "Your data is kept safe and nothing is deleted.", + "CTA_HELPER": "You can reactivate at any time.", + "CTA": "Deactivate account…", + "CONFIRM_MODAL": { + "TITLE": "Deactivate your account", + "SUBTITLE": "Your data will be kept — you can reactivate at any time.", + "EFFECTS_HEADING": "What deactivation means", + "EFFECT_1": "Your profile and listings will be hidden from the directory.", + "EFFECT_2": "You will be signed out of all active sessions.", + "EFFECT_3": "Scheduled emails and notifications will be paused.", + "EFFECT_4": "Your data is kept safe and nothing is deleted.", + "REACTIVATION_NOTE": "To reactivate, simply sign back in with your credentials.", + "CONFIRMATION_HEADING": "Confirm it's really you", + "PASSWORD_LABEL": "Your current password", + "PASSWORD_PLACEHOLDER": "Enter your password", + "CANCEL_CTA": "Cancel", + "CONFIRM_CTA": "Deactivate account", + "DEACTIVATING": "Deactivating…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Password is required.", + "PASSWORD_TOO_SHORT": "Password must be at least 8 characters." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Account deactivated", + "TITLE": "Reactivate your account", + "DESCRIPTION": "Restore full access to your account and make your profile visible again.", + "RESTORE_TITLE": "What reactivation restores", + "RESTORE_1": "Your profile and listings will be visible in the directory again.", + "RESTORE_2": "All notifications and scheduled emails will resume.", + "RESTORE_3": "All your data, settings, and history are fully intact.", + "CTA_HELPER": "Reactivation is immediate — no approval required.", + "CTA": "Reactivate account", + "REACTIVATING": "Reactivating…", + "SUCCESS_TOAST": "Your account has been reactivated successfully." + }, "DELETE_ACCOUNT": { "TITLE": "Delete this account", "DESCRIPTION": "Permanently remove this account and everything associated with it. This cannot be undone.", diff --git a/apps/web/messages/es.json b/apps/web/messages/es.json index 4a3c9b831..4eb4d9aab 100644 --- a/apps/web/messages/es.json +++ b/apps/web/messages/es.json @@ -673,6 +673,51 @@ "INTRO_LABEL": "Atención", "INTRO_BODY": "Cada acción de esta sección es permanente. Siempre te pediremos que confirmes antes de eliminar algo.", "IRREVERSIBLE_BADGE": "Destructivo", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Desactivar cuenta", + "REVERSIBLE_BADGE": "Reversible", + "DESCRIPTION": "Pausa temporalmente tu cuenta. Puedes reactivarla en cualquier momento volviendo a iniciar sesión.", + "EFFECTS_TITLE": "Qué ocurre cuando desactivas", + "EFFECT_1": "Tu perfil y listados quedarán ocultos del directorio.", + "EFFECT_2": "Serás desconectado de todas las sesiones activas.", + "EFFECT_3": "Los correos y notificaciones programados quedarán en pausa.", + "EFFECT_4": "Tus datos se guardan de forma segura y no se elimina nada.", + "CTA_HELPER": "Puedes reactivar en cualquier momento.", + "CTA": "Desactivar cuenta…", + "CONFIRM_MODAL": { + "TITLE": "Desactivar tu cuenta", + "SUBTITLE": "Tus datos se conservarán — puedes reactivar en cualquier momento.", + "EFFECTS_HEADING": "Qué significa la desactivación", + "EFFECT_1": "Tu perfil y listados quedarán ocultos del directorio.", + "EFFECT_2": "Serás desconectado de todas las sesiones activas.", + "EFFECT_3": "Los correos y notificaciones programados quedarán en pausa.", + "EFFECT_4": "Tus datos se guardan de forma segura y no se elimina nada.", + "REACTIVATION_NOTE": "Para reactivar, simplemente inicia sesión de nuevo con tus credenciales.", + "CONFIRMATION_HEADING": "Confirma que realmente eres tú", + "PASSWORD_LABEL": "Tu contraseña actual", + "PASSWORD_PLACEHOLDER": "Introduce tu contraseña", + "CANCEL_CTA": "Cancelar", + "CONFIRM_CTA": "Desactivar cuenta", + "DEACTIVATING": "Desactivando…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "La contraseña es obligatoria.", + "PASSWORD_TOO_SHORT": "La contraseña debe tener al menos 8 caracteres." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Cuenta desactivada", + "TITLE": "Reactivar tu cuenta", + "DESCRIPTION": "Restaura el acceso completo a tu cuenta y haz que tu perfil sea visible de nuevo.", + "RESTORE_TITLE": "Qué restaura la reactivación", + "RESTORE_1": "Tu perfil y listados serán visibles de nuevo en el directorio.", + "RESTORE_2": "Se reanudarán todas las notificaciones y correos programados.", + "RESTORE_3": "Todos tus datos, configuraciones e historial están completamente intactos.", + "CTA_HELPER": "La reactivación es inmediata — no se requiere aprobación.", + "CTA": "Reactivar cuenta", + "REACTIVATING": "Reactivando…", + "SUCCESS_TOAST": "Tu cuenta ha sido reactivada con éxito." + }, "DELETE_ACCOUNT": { "TITLE": "Eliminar esta cuenta", "DESCRIPTION": "Elimina permanentemente esta cuenta y todo lo asociado a ella. Esta acción no se puede deshacer.", diff --git a/apps/web/messages/fr.json b/apps/web/messages/fr.json index f78552abf..712da12c4 100644 --- a/apps/web/messages/fr.json +++ b/apps/web/messages/fr.json @@ -672,6 +672,51 @@ "INTRO_LABEL": "Attention", "INTRO_BODY": "Chaque action de cette section est permanente. Nous vous demanderons toujours de confirmer avant toute suppression.", "IRREVERSIBLE_BADGE": "Destructif", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Désactiver le compte", + "REVERSIBLE_BADGE": "Réversible", + "DESCRIPTION": "Mettez temporairement votre compte en pause. Vous pouvez le réactiver à tout moment en vous reconnectant.", + "EFFECTS_TITLE": "Ce qui se passe lors de la désactivation", + "EFFECT_1": "Votre profil et vos annonces seront masqués de l'annuaire.", + "EFFECT_2": "Vous serez déconnecté de toutes les sessions actives.", + "EFFECT_3": "Les e-mails et notifications planifiés seront mis en pause.", + "EFFECT_4": "Vos données sont conservées en sécurité et rien n'est supprimé.", + "CTA_HELPER": "Vous pouvez réactiver à tout moment.", + "CTA": "Désactiver le compte…", + "CONFIRM_MODAL": { + "TITLE": "Désactiver votre compte", + "SUBTITLE": "Vos données seront conservées — vous pouvez réactiver à tout moment.", + "EFFECTS_HEADING": "Ce que signifie la désactivation", + "EFFECT_1": "Votre profil et vos annonces seront masqués de l'annuaire.", + "EFFECT_2": "Vous serez déconnecté de toutes les sessions actives.", + "EFFECT_3": "Les e-mails et notifications planifiés seront mis en pause.", + "EFFECT_4": "Vos données sont conservées en sécurité et rien n'est supprimé.", + "REACTIVATION_NOTE": "Pour réactiver, reconnectez-vous simplement avec vos identifiants.", + "CONFIRMATION_HEADING": "Confirmez que c'est bien vous", + "PASSWORD_LABEL": "Votre mot de passe actuel", + "PASSWORD_PLACEHOLDER": "Entrez votre mot de passe", + "CANCEL_CTA": "Annuler", + "CONFIRM_CTA": "Désactiver le compte", + "DEACTIVATING": "Désactivation…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Le mot de passe est requis.", + "PASSWORD_TOO_SHORT": "Le mot de passe doit comporter au moins 8 caractères." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Compte désactivé", + "TITLE": "Réactiver votre compte", + "DESCRIPTION": "Restaurez l'accès complet à votre compte et rendez votre profil à nouveau visible.", + "RESTORE_TITLE": "Ce que la réactivation restaure", + "RESTORE_1": "Votre profil et vos annonces seront à nouveau visibles dans l'annuaire.", + "RESTORE_2": "Toutes les notifications et e-mails planifiés reprendront.", + "RESTORE_3": "Toutes vos données, paramètres et historiques sont entièrement intacts.", + "CTA_HELPER": "La réactivation est immédiate — aucune approbation requise.", + "CTA": "Réactiver le compte", + "REACTIVATING": "Réactivation…", + "SUCCESS_TOAST": "Votre compte a été réactivé avec succès." + }, "DELETE_ACCOUNT": { "TITLE": "Supprimer ce compte", "DESCRIPTION": "Supprimez définitivement ce compte et tout ce qui lui est associé. Cette action est irréversible.", diff --git a/apps/web/messages/he.json b/apps/web/messages/he.json index d17c44977..7743167fb 100644 --- a/apps/web/messages/he.json +++ b/apps/web/messages/he.json @@ -670,6 +670,51 @@ "INTRO_LABEL": "שימו לב", "INTRO_BODY": "כל פעולה באזור זה היא קבועה. נבקש מכם אישור לפני שמשהו יוסר.", "IRREVERSIBLE_BADGE": "הרסני", + "DEACTIVATE_ACCOUNT": { + "TITLE": "השבת חשבון", + "REVERSIBLE_BADGE": "הפיך", + "DESCRIPTION": "השהה את חשבונך זמנית. תוכל להפעילו מחדש בכל עת על ידי כניסה מחדש.", + "EFFECTS_TITLE": "מה קורה כשמשביתים", + "EFFECT_1": "הפרופיל והמודעות שלך יוסתרו מהמדריך.", + "EFFECT_2": "תנותק מכל הפגישות הפעילות.", + "EFFECT_3": "הודעות דוא\"ל והתראות מתוזמנות יושהו.", + "EFFECT_4": "הנתונים שלך שמורים בבטחה ולא נמחק דבר.", + "CTA_HELPER": "תוכל להפעיל מחדש בכל עת.", + "CTA": "השבת חשבון…", + "CONFIRM_MODAL": { + "TITLE": "השבת את חשבונך", + "SUBTITLE": "הנתונים שלך יישמרו — תוכל להפעיל מחדש בכל עת.", + "EFFECTS_HEADING": "מה משמעות ההשבתה", + "EFFECT_1": "הפרופיל והמודעות שלך יוסתרו מהמדריך.", + "EFFECT_2": "תנותק מכל הפגישות הפעילות.", + "EFFECT_3": "הודעות דוא\"ל והתראות מתוזמנות יושהו.", + "EFFECT_4": "הנתונים שלך שמורים בבטחה ולא נמחק דבר.", + "REACTIVATION_NOTE": "להפעלה מחדש, פשוט היכנס שוב עם הפרטים שלך.", + "CONFIRMATION_HEADING": "אשר שזה באמת אתה", + "PASSWORD_LABEL": "הסיסמה הנוכחית שלך", + "PASSWORD_PLACEHOLDER": "הכנס את הסיסמה", + "CANCEL_CTA": "ביטול", + "CONFIRM_CTA": "השבת חשבון", + "DEACTIVATING": "מבצע השבתה…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "נדרשת סיסמה.", + "PASSWORD_TOO_SHORT": "הסיסמה חייבת להכיל לפחות 8 תווים." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "החשבון מושבת", + "TITLE": "הפעל מחדש את חשבונך", + "DESCRIPTION": "שחזר גישה מלאה לחשבונך והפוך את הפרופיל שלך לגלוי שוב.", + "RESTORE_TITLE": "מה הפעלה מחדש משחזרת", + "RESTORE_1": "הפרופיל והמודעות שלך יהיו גלויים שוב במדריך.", + "RESTORE_2": "כל ההתראות ודוא\"ל מתוזמנים יחודשו.", + "RESTORE_3": "כל הנתונים, ההגדרות וההיסטוריה שלך שלמים לחלוטין.", + "CTA_HELPER": "ההפעלה מחדש מיידית — לא נדרש אישור.", + "CTA": "הפעל מחדש חשבון", + "REACTIVATING": "מפעיל מחדש…", + "SUCCESS_TOAST": "חשבונך הופעל מחדש בהצלחה." + }, "DELETE_ACCOUNT": { "TITLE": "מחיקת חשבון זה", "DESCRIPTION": "הסרה לצמיתות של חשבון זה וכל מה שמשויך אליו. לא ניתן לבטל פעולה זו.", diff --git a/apps/web/messages/hi.json b/apps/web/messages/hi.json index a0d0cb3b5..3042f4a0c 100644 --- a/apps/web/messages/hi.json +++ b/apps/web/messages/hi.json @@ -671,6 +671,51 @@ "INTRO_LABEL": "ध्यान दें", "INTRO_BODY": "इस सेक्शन में हर क्रिया स्थायी है। कुछ भी हटाने से पहले हम हमेशा आपसे पुष्टि करने के लिए कहेंगे।", "IRREVERSIBLE_BADGE": "विनाशकारी", + "DEACTIVATE_ACCOUNT": { + "TITLE": "खाता निष्क्रिय करें", + "REVERSIBLE_BADGE": "पूर्ववत करने योग्य", + "DESCRIPTION": "अपने खाते को अस्थायी रूप से रोकें। आप किसी भी समय वापस साइन इन करके इसे पुनः सक्रिय कर सकते हैं।", + "EFFECTS_TITLE": "निष्क्रिय करने पर क्या होता है", + "EFFECT_1": "आपकी प्रोफ़ाइल और लिस्टिंग डायरेक्टरी से छुपा दी जाएगी।", + "EFFECT_2": "आपको सभी सक्रिय सत्रों से साइन आउट कर दिया जाएगा।", + "EFFECT_3": "निर्धारित ईमेल और सूचनाएं रोक दी जाएंगी।", + "EFFECT_4": "आपका डेटा सुरक्षित रखा जाएगा और कुछ भी हटाया नहीं जाएगा।", + "CTA_HELPER": "आप किसी भी समय पुनः सक्रिय कर सकते हैं।", + "CTA": "खाता निष्क्रिय करें…", + "CONFIRM_MODAL": { + "TITLE": "अपना खाता निष्क्रिय करें", + "SUBTITLE": "आपका डेटा सुरक्षित रहेगा — आप किसी भी समय पुनः सक्रिय कर सकते हैं।", + "EFFECTS_HEADING": "निष्क्रियता का अर्थ", + "EFFECT_1": "आपकी प्रोफ़ाइल और लिस्टिंग डायरेक्टरी से छुपा दी जाएगी।", + "EFFECT_2": "आपको सभी सक्रिय सत्रों से साइन आउट कर दिया जाएगा।", + "EFFECT_3": "निर्धारित ईमेल और सूचनाएं रोक दी जाएंगी।", + "EFFECT_4": "आपका डेटा सुरक्षित रखा जाएगा और कुछ भी हटाया नहीं जाएगा।", + "REACTIVATION_NOTE": "पुनः सक्रिय करने के लिए, बस अपनी साख के साथ वापस साइन इन करें।", + "CONFIRMATION_HEADING": "पुष्टि करें कि यह वाकई आप हैं", + "PASSWORD_LABEL": "आपका वर्तमान पासवर्ड", + "PASSWORD_PLACEHOLDER": "पासवर्ड दर्ज करें", + "CANCEL_CTA": "रद्द करें", + "CONFIRM_CTA": "खाता निष्क्रिय करें", + "DEACTIVATING": "निष्क्रिय किया जा रहा है…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "पासवर्ड आवश्यक है।", + "PASSWORD_TOO_SHORT": "पासवर्ड कम से कम 8 अक्षरों का होना चाहिए।" + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "खाता निष्क्रिय है", + "TITLE": "अपना खाता पुनः सक्रिय करें", + "DESCRIPTION": "अपने खाते तक पूर्ण पहुंच पुनः प्राप्त करें और अपनी प्रोफ़ाइल को फिर से दृश्यमान बनाएं।", + "RESTORE_TITLE": "पुनः सक्रियण क्या पुनर्स्थापित करता है", + "RESTORE_1": "आपकी प्रोफ़ाइल और लिस्टिंग डायरेक्टरी में फिर से दिखाई देगी।", + "RESTORE_2": "सभी सूचनाएं और निर्धारित ईमेल फिर से शुरू होंगे।", + "RESTORE_3": "आपका सारा डेटा, सेटिंग्स और इतिहास पूरी तरह से सुरक्षित है।", + "CTA_HELPER": "पुनः सक्रियण तत्काल है — कोई अनुमोदन आवश्यक नहीं है।", + "CTA": "खाता पुनः सक्रिय करें", + "REACTIVATING": "पुनः सक्रिय किया जा रहा है…", + "SUCCESS_TOAST": "आपका खाता सफलतापूर्वक पुनः सक्रिय हो गया है।" + }, "DELETE_ACCOUNT": { "TITLE": "इस खाते को हटाएं", "DESCRIPTION": "इस खाते और इससे जुड़ी हर चीज़ को स्थायी रूप से हटा दें। इसे पूर्ववत नहीं किया जा सकता।", diff --git a/apps/web/messages/id.json b/apps/web/messages/id.json index 74b53e656..90bdde954 100644 --- a/apps/web/messages/id.json +++ b/apps/web/messages/id.json @@ -749,6 +749,51 @@ "INTRO_LABEL": "Perhatian", "INTRO_BODY": "Setiap tindakan di bagian ini bersifat permanen. Kami akan selalu meminta konfirmasi Anda sebelum sesuatu dihapus.", "IRREVERSIBLE_BADGE": "Destruktif", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Nonaktifkan akun", + "REVERSIBLE_BADGE": "Dapat dipulihkan", + "DESCRIPTION": "Jeda akun Anda untuk sementara. Anda dapat mengaktifkannya kembali kapan saja dengan masuk kembali.", + "EFFECTS_TITLE": "Apa yang terjadi saat Anda menonaktifkan", + "EFFECT_1": "Profil dan daftar Anda akan disembunyikan dari direktori.", + "EFFECT_2": "Anda akan keluar dari semua sesi aktif.", + "EFFECT_3": "Email dan notifikasi terjadwal akan dijeda.", + "EFFECT_4": "Data Anda tersimpan aman dan tidak ada yang dihapus.", + "CTA_HELPER": "Anda dapat mengaktifkan kembali kapan saja.", + "CTA": "Nonaktifkan akun…", + "CONFIRM_MODAL": { + "TITLE": "Nonaktifkan akun Anda", + "SUBTITLE": "Data Anda akan disimpan — Anda dapat mengaktifkan kembali kapan saja.", + "EFFECTS_HEADING": "Apa arti penonaktifan", + "EFFECT_1": "Profil dan daftar Anda akan disembunyikan dari direktori.", + "EFFECT_2": "Anda akan keluar dari semua sesi aktif.", + "EFFECT_3": "Email dan notifikasi terjadwal akan dijeda.", + "EFFECT_4": "Data Anda tersimpan aman dan tidak ada yang dihapus.", + "REACTIVATION_NOTE": "Untuk mengaktifkan kembali, cukup masuk kembali dengan kredensial Anda.", + "CONFIRMATION_HEADING": "Konfirmasi bahwa ini benar-benar Anda", + "PASSWORD_LABEL": "Kata sandi Anda saat ini", + "PASSWORD_PLACEHOLDER": "Masukkan kata sandi", + "CANCEL_CTA": "Batal", + "CONFIRM_CTA": "Nonaktifkan akun", + "DEACTIVATING": "Menonaktifkan…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Kata sandi diperlukan.", + "PASSWORD_TOO_SHORT": "Kata sandi harus terdiri dari minimal 8 karakter." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Akun dinonaktifkan", + "TITLE": "Aktifkan kembali akun Anda", + "DESCRIPTION": "Pulihkan akses penuh ke akun Anda dan jadikan profil Anda terlihat lagi.", + "RESTORE_TITLE": "Apa yang dipulihkan aktivasi ulang", + "RESTORE_1": "Profil dan daftar Anda akan terlihat kembali di direktori.", + "RESTORE_2": "Semua notifikasi dan email terjadwal akan dilanjutkan.", + "RESTORE_3": "Semua data, pengaturan, dan riwayat Anda sepenuhnya utuh.", + "CTA_HELPER": "Aktivasi ulang berlangsung segera — tidak perlu persetujuan.", + "CTA": "Aktifkan kembali akun", + "REACTIVATING": "Mengaktifkan kembali…", + "SUCCESS_TOAST": "Akun Anda telah berhasil diaktifkan kembali." + }, "DELETE_ACCOUNT": { "TITLE": "Hapus akun ini", "DESCRIPTION": "Hapus akun ini dan semua yang terkait dengannya secara permanen. Tindakan ini tidak dapat dibatalkan.", diff --git a/apps/web/messages/it.json b/apps/web/messages/it.json index 588b7d2b0..b4f00c0ab 100644 --- a/apps/web/messages/it.json +++ b/apps/web/messages/it.json @@ -671,6 +671,51 @@ "INTRO_LABEL": "Attenzione", "INTRO_BODY": "Ogni azione in questa sezione è permanente. Ti chiederemo sempre una conferma prima di rimuovere qualcosa.", "IRREVERSIBLE_BADGE": "Distruttivo", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Disattiva account", + "REVERSIBLE_BADGE": "Reversibile", + "DESCRIPTION": "Sospendi temporaneamente il tuo account. Puoi riattivarlo in qualsiasi momento effettuando nuovamente l'accesso.", + "EFFECTS_TITLE": "Cosa succede quando disattivi", + "EFFECT_1": "Il tuo profilo e gli annunci saranno nascosti dalla directory.", + "EFFECT_2": "Verrai disconnesso da tutte le sessioni attive.", + "EFFECT_3": "Le email e le notifiche programmate verranno sospese.", + "EFFECT_4": "I tuoi dati sono al sicuro e non viene eliminato nulla.", + "CTA_HELPER": "Puoi riattivare in qualsiasi momento.", + "CTA": "Disattiva account…", + "CONFIRM_MODAL": { + "TITLE": "Disattiva il tuo account", + "SUBTITLE": "I tuoi dati saranno conservati — puoi riattivare in qualsiasi momento.", + "EFFECTS_HEADING": "Cosa significa la disattivazione", + "EFFECT_1": "Il tuo profilo e gli annunci saranno nascosti dalla directory.", + "EFFECT_2": "Verrai disconnesso da tutte le sessioni attive.", + "EFFECT_3": "Le email e le notifiche programmate verranno sospese.", + "EFFECT_4": "I tuoi dati sono al sicuro e non viene eliminato nulla.", + "REACTIVATION_NOTE": "Per riattivare, accedi semplicemente di nuovo con le tue credenziali.", + "CONFIRMATION_HEADING": "Conferma che sei davvero tu", + "PASSWORD_LABEL": "La tua password attuale", + "PASSWORD_PLACEHOLDER": "Inserisci la password", + "CANCEL_CTA": "Annulla", + "CONFIRM_CTA": "Disattiva account", + "DEACTIVATING": "Disattivazione in corso…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "La password è obbligatoria.", + "PASSWORD_TOO_SHORT": "La password deve contenere almeno 8 caratteri." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Account disattivato", + "TITLE": "Riattiva il tuo account", + "DESCRIPTION": "Ripristina l'accesso completo al tuo account e rendi di nuovo visibile il tuo profilo.", + "RESTORE_TITLE": "Cosa ripristina la riattivazione", + "RESTORE_1": "Il tuo profilo e gli annunci saranno di nuovo visibili nella directory.", + "RESTORE_2": "Tutte le notifiche e le email programmate riprenderanno.", + "RESTORE_3": "Tutti i tuoi dati, impostazioni e cronologia sono completamente intatti.", + "CTA_HELPER": "La riattivazione è immediata — nessuna approvazione richiesta.", + "CTA": "Riattiva account", + "REACTIVATING": "Riattivazione in corso…", + "SUCCESS_TOAST": "Il tuo account è stato riattivato con successo." + }, "DELETE_ACCOUNT": { "TITLE": "Elimina questo account", "DESCRIPTION": "Rimuovi definitivamente questo account e tutto ciò che è associato ad esso. Questa azione non può essere annullata.", diff --git a/apps/web/messages/ja.json b/apps/web/messages/ja.json index 630cee2de..82150eb21 100644 --- a/apps/web/messages/ja.json +++ b/apps/web/messages/ja.json @@ -671,6 +671,51 @@ "INTRO_LABEL": "ご注意ください", "INTRO_BODY": "このセクションの操作はすべて永久的なものです。何かを削除する前には、必ず確認を求めます。", "IRREVERSIBLE_BADGE": "破壊的操作", + "DEACTIVATE_ACCOUNT": { + "TITLE": "アカウントを無効化", + "REVERSIBLE_BADGE": "元に戻せます", + "DESCRIPTION": "アカウントを一時的に停止します。いつでもサインインし直すことで再有効化できます。", + "EFFECTS_TITLE": "無効化したときに起こること", + "EFFECT_1": "プロフィールとリストはディレクトリから非表示になります。", + "EFFECT_2": "すべてのアクティブなセッションからサインアウトされます。", + "EFFECT_3": "スケジュールされたメールと通知は一時停止されます。", + "EFFECT_4": "データは安全に保管され、削除されるものはありません。", + "CTA_HELPER": "いつでも再有効化できます。", + "CTA": "アカウントを無効化…", + "CONFIRM_MODAL": { + "TITLE": "アカウントを無効化する", + "SUBTITLE": "データは保持されます — いつでも再有効化できます。", + "EFFECTS_HEADING": "無効化が意味すること", + "EFFECT_1": "プロフィールとリストはディレクトリから非表示になります。", + "EFFECT_2": "すべてのアクティブなセッションからサインアウトされます。", + "EFFECT_3": "スケジュールされたメールと通知は一時停止されます。", + "EFFECT_4": "データは安全に保管され、削除されるものはありません。", + "REACTIVATION_NOTE": "再有効化するには、認証情報でサインインし直してください。", + "CONFIRMATION_HEADING": "本人確認", + "PASSWORD_LABEL": "現在のパスワード", + "PASSWORD_PLACEHOLDER": "パスワードを入力", + "CANCEL_CTA": "キャンセル", + "CONFIRM_CTA": "アカウントを無効化", + "DEACTIVATING": "無効化中…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "パスワードは必須です。", + "PASSWORD_TOO_SHORT": "パスワードは8文字以上である必要があります。" + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "アカウントが無効化されています", + "TITLE": "アカウントを再有効化する", + "DESCRIPTION": "アカウントへの完全なアクセスを回復し、プロフィールを再び表示します。", + "RESTORE_TITLE": "再有効化で回復するもの", + "RESTORE_1": "プロフィールとリストがディレクトリに再び表示されます。", + "RESTORE_2": "すべての通知とスケジュールされたメールが再開されます。", + "RESTORE_3": "すべてのデータ、設定、履歴は完全に保持されています。", + "CTA_HELPER": "再有効化は即時です — 承認は不要です。", + "CTA": "アカウントを再有効化", + "REACTIVATING": "再有効化中…", + "SUCCESS_TOAST": "アカウントが正常に再有効化されました。" + }, "DELETE_ACCOUNT": { "TITLE": "このアカウントを削除する", "DESCRIPTION": "このアカウントとそれに関連するすべての情報を完全に削除します。この操作は取り消せません。", diff --git a/apps/web/messages/ko.json b/apps/web/messages/ko.json index 43d65a419..7d5140a41 100644 --- a/apps/web/messages/ko.json +++ b/apps/web/messages/ko.json @@ -749,6 +749,51 @@ "INTRO_LABEL": "주의", "INTRO_BODY": "이 섹션의 모든 작업은 영구적입니다. 무언가를 삭제하기 전에 항상 확인을 요청합니다.", "IRREVERSIBLE_BADGE": "파괴적 작업", + "DEACTIVATE_ACCOUNT": { + "TITLE": "계정 비활성화", + "REVERSIBLE_BADGE": "복원 가능", + "DESCRIPTION": "계정을 일시적으로 중지합니다. 언제든지 다시 로그인하여 재활성화할 수 있습니다.", + "EFFECTS_TITLE": "비활성화 시 발생하는 일", + "EFFECT_1": "프로필과 목록이 디렉토리에서 숨겨집니다.", + "EFFECT_2": "모든 활성 세션에서 로그아웃됩니다.", + "EFFECT_3": "예약된 이메일과 알림이 일시 중지됩니다.", + "EFFECT_4": "데이터는 안전하게 보관되며 아무것도 삭제되지 않습니다.", + "CTA_HELPER": "언제든지 재활성화할 수 있습니다.", + "CTA": "계정 비활성화…", + "CONFIRM_MODAL": { + "TITLE": "계정을 비활성화합니다", + "SUBTITLE": "데이터는 보존됩니다 — 언제든지 재활성화할 수 있습니다.", + "EFFECTS_HEADING": "비활성화의 의미", + "EFFECT_1": "프로필과 목록이 디렉토리에서 숨겨집니다.", + "EFFECT_2": "모든 활성 세션에서 로그아웃됩니다.", + "EFFECT_3": "예약된 이메일과 알림이 일시 중지됩니다.", + "EFFECT_4": "데이터는 안전하게 보관되며 아무것도 삭제되지 않습니다.", + "REACTIVATION_NOTE": "재활성화하려면 자격 증명으로 다시 로그인하기만 하면 됩니다.", + "CONFIRMATION_HEADING": "본인 확인", + "PASSWORD_LABEL": "현재 비밀번호", + "PASSWORD_PLACEHOLDER": "비밀번호 입력", + "CANCEL_CTA": "취소", + "CONFIRM_CTA": "계정 비활성화", + "DEACTIVATING": "비활성화 중…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "비밀번호가 필요합니다.", + "PASSWORD_TOO_SHORT": "비밀번호는 최소 8자여야 합니다." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "계정이 비활성화됨", + "TITLE": "계정 재활성화", + "DESCRIPTION": "계정에 대한 전체 액세스를 복원하고 프로필을 다시 표시합니다.", + "RESTORE_TITLE": "재활성화로 복원되는 항목", + "RESTORE_1": "프로필과 목록이 디렉토리에 다시 표시됩니다.", + "RESTORE_2": "모든 알림과 예약된 이메일이 재개됩니다.", + "RESTORE_3": "모든 데이터, 설정 및 기록이 완전히 보존됩니다.", + "CTA_HELPER": "재활성화는 즉시 이루어집니다 — 승인이 필요하지 않습니다.", + "CTA": "계정 재활성화", + "REACTIVATING": "재활성화 중…", + "SUCCESS_TOAST": "계정이 성공적으로 재활성화되었습니다." + }, "DELETE_ACCOUNT": { "TITLE": "이 계정 삭제", "DESCRIPTION": "이 계정과 관련된 모든 항목을 영구적으로 제거합니다. 이 작업은 취소할 수 없습니다.", diff --git a/apps/web/messages/nl.json b/apps/web/messages/nl.json index dc6cdb0dd..9654b668a 100644 --- a/apps/web/messages/nl.json +++ b/apps/web/messages/nl.json @@ -671,6 +671,51 @@ "INTRO_LABEL": "Let op", "INTRO_BODY": "Elke actie in deze sectie is permanent. We vragen je altijd om bevestiging voordat er iets wordt verwijderd.", "IRREVERSIBLE_BADGE": "Destructief", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Account deactiveren", + "REVERSIBLE_BADGE": "Omkeerbaar", + "DESCRIPTION": "Pauzeer uw account tijdelijk. U kunt het op elk moment opnieuw activeren door u opnieuw aan te melden.", + "EFFECTS_TITLE": "Wat er gebeurt wanneer u deactiveert", + "EFFECT_1": "Uw profiel en vermeldingen worden verborgen uit de directory.", + "EFFECT_2": "U wordt afgemeld bij alle actieve sessies.", + "EFFECT_3": "Geplande e-mails en meldingen worden gepauzeerd.", + "EFFECT_4": "Uw gegevens worden veilig bewaard en er wordt niets verwijderd.", + "CTA_HELPER": "U kunt op elk moment opnieuw activeren.", + "CTA": "Account deactiveren…", + "CONFIRM_MODAL": { + "TITLE": "Uw account deactiveren", + "SUBTITLE": "Uw gegevens worden bewaard — u kunt op elk moment opnieuw activeren.", + "EFFECTS_HEADING": "Wat deactivering betekent", + "EFFECT_1": "Uw profiel en vermeldingen worden verborgen uit de directory.", + "EFFECT_2": "U wordt afgemeld bij alle actieve sessies.", + "EFFECT_3": "Geplande e-mails en meldingen worden gepauzeerd.", + "EFFECT_4": "Uw gegevens worden veilig bewaard en er wordt niets verwijderd.", + "REACTIVATION_NOTE": "Om opnieuw te activeren, meldt u zich gewoon opnieuw aan met uw inloggegevens.", + "CONFIRMATION_HEADING": "Bevestig dat u het echt bent", + "PASSWORD_LABEL": "Uw huidige wachtwoord", + "PASSWORD_PLACEHOLDER": "Voer uw wachtwoord in", + "CANCEL_CTA": "Annuleren", + "CONFIRM_CTA": "Account deactiveren", + "DEACTIVATING": "Deactiveren…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Wachtwoord is vereist.", + "PASSWORD_TOO_SHORT": "Wachtwoord moet minimaal 8 tekens bevatten." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Account gedeactiveerd", + "TITLE": "Uw account opnieuw activeren", + "DESCRIPTION": "Herstel volledige toegang tot uw account en maak uw profiel opnieuw zichtbaar.", + "RESTORE_TITLE": "Wat reactivering herstelt", + "RESTORE_1": "Uw profiel en vermeldingen zijn weer zichtbaar in de directory.", + "RESTORE_2": "Alle meldingen en geplande e-mails worden hervat.", + "RESTORE_3": "Al uw gegevens, instellingen en geschiedenis zijn volledig intact.", + "CTA_HELPER": "Reactivering is onmiddellijk — geen goedkeuring vereist.", + "CTA": "Account opnieuw activeren", + "REACTIVATING": "Opnieuw activeren…", + "SUCCESS_TOAST": "Uw account is succesvol opnieuw geactiveerd." + }, "DELETE_ACCOUNT": { "TITLE": "Dit account verwijderen", "DESCRIPTION": "Verwijder dit account en alles wat erbij hoort permanent. Dit kan niet ongedaan worden gemaakt.", diff --git a/apps/web/messages/pl.json b/apps/web/messages/pl.json index d0e358695..d7ebc0905 100644 --- a/apps/web/messages/pl.json +++ b/apps/web/messages/pl.json @@ -671,6 +671,51 @@ "INTRO_LABEL": "Uwaga", "INTRO_BODY": "Każda czynność w tej sekcji jest trwała. Zawsze poprosimy o potwierdzenie przed usunięciem czegokolwiek.", "IRREVERSIBLE_BADGE": "Destrukcyjne", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Dezaktywuj konto", + "REVERSIBLE_BADGE": "Odwracalne", + "DESCRIPTION": "Tymczasowo wstrzymaj swoje konto. Możesz je ponownie aktywować w dowolnym momencie, logując się ponownie.", + "EFFECTS_TITLE": "Co się dzieje po dezaktywacji", + "EFFECT_1": "Twój profil i ogłoszenia zostaną ukryte z katalogu.", + "EFFECT_2": "Zostaniesz wylogowany ze wszystkich aktywnych sesji.", + "EFFECT_3": "Zaplanowane e-maile i powiadomienia zostaną wstrzymane.", + "EFFECT_4": "Twoje dane są bezpiecznie przechowywane i nic nie zostanie usunięte.", + "CTA_HELPER": "Możesz ponownie aktywować w dowolnym momencie.", + "CTA": "Dezaktywuj konto…", + "CONFIRM_MODAL": { + "TITLE": "Dezaktywuj swoje konto", + "SUBTITLE": "Twoje dane zostaną zachowane — możesz ponownie aktywować w dowolnym momencie.", + "EFFECTS_HEADING": "Co oznacza dezaktywacja", + "EFFECT_1": "Twój profil i ogłoszenia zostaną ukryte z katalogu.", + "EFFECT_2": "Zostaniesz wylogowany ze wszystkich aktywnych sesji.", + "EFFECT_3": "Zaplanowane e-maile i powiadomienia zostaną wstrzymane.", + "EFFECT_4": "Twoje dane są bezpiecznie przechowywane i nic nie zostanie usunięte.", + "REACTIVATION_NOTE": "Aby ponownie aktywować, po prostu zaloguj się ponownie swoimi danymi.", + "CONFIRMATION_HEADING": "Potwierdź, że to naprawdę Ty", + "PASSWORD_LABEL": "Twoje aktualne hasło", + "PASSWORD_PLACEHOLDER": "Wprowadź hasło", + "CANCEL_CTA": "Anuluj", + "CONFIRM_CTA": "Dezaktywuj konto", + "DEACTIVATING": "Dezaktywowanie…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Hasło jest wymagane.", + "PASSWORD_TOO_SHORT": "Hasło musi mieć co najmniej 8 znaków." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Konto zdezaktywowane", + "TITLE": "Reaktywuj swoje konto", + "DESCRIPTION": "Przywróć pełny dostęp do konta i spraw, aby Twój profil był ponownie widoczny.", + "RESTORE_TITLE": "Co przywraca reaktywacja", + "RESTORE_1": "Twój profil i ogłoszenia będą ponownie widoczne w katalogu.", + "RESTORE_2": "Wszystkie powiadomienia i zaplanowane e-maile zostaną wznowione.", + "RESTORE_3": "Wszystkie Twoje dane, ustawienia i historia są w pełni nienaruszone.", + "CTA_HELPER": "Reaktywacja jest natychmiastowa — nie jest wymagana żadna zgoda.", + "CTA": "Reaktywuj konto", + "REACTIVATING": "Reaktywowanie…", + "SUCCESS_TOAST": "Twoje konto zostało pomyślnie reaktywowane." + }, "DELETE_ACCOUNT": { "TITLE": "Usuń to konto", "DESCRIPTION": "Trwale usuń to konto i wszystko, co jest z nim powiązane. Tej czynności nie można odwrócić.", diff --git a/apps/web/messages/pt.json b/apps/web/messages/pt.json index 28179d716..12acbb331 100644 --- a/apps/web/messages/pt.json +++ b/apps/web/messages/pt.json @@ -671,6 +671,51 @@ "INTRO_LABEL": "Atenção", "INTRO_BODY": "Cada ação nesta seção é permanente. Sempre pediremos confirmação antes de remover algo.", "IRREVERSIBLE_BADGE": "Destrutivo", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Desativar conta", + "REVERSIBLE_BADGE": "Reversível", + "DESCRIPTION": "Pause temporariamente sua conta. Você pode reativá-la a qualquer momento fazendo login novamente.", + "EFFECTS_TITLE": "O que acontece ao desativar", + "EFFECT_1": "Seu perfil e listagens serão ocultados do diretório.", + "EFFECT_2": "Você será desconectado de todas as sessões ativas.", + "EFFECT_3": "Emails e notificações agendados serão pausados.", + "EFFECT_4": "Seus dados ficam seguros e nada é excluído.", + "CTA_HELPER": "Você pode reativar a qualquer momento.", + "CTA": "Desativar conta…", + "CONFIRM_MODAL": { + "TITLE": "Desativar sua conta", + "SUBTITLE": "Seus dados serão mantidos — você pode reativar a qualquer momento.", + "EFFECTS_HEADING": "O que significa a desativação", + "EFFECT_1": "Seu perfil e listagens serão ocultados do diretório.", + "EFFECT_2": "Você será desconectado de todas as sessões ativas.", + "EFFECT_3": "Emails e notificações agendados serão pausados.", + "EFFECT_4": "Seus dados ficam seguros e nada é excluído.", + "REACTIVATION_NOTE": "Para reativar, basta fazer login novamente com suas credenciais.", + "CONFIRMATION_HEADING": "Confirme que é realmente você", + "PASSWORD_LABEL": "Sua senha atual", + "PASSWORD_PLACEHOLDER": "Digite sua senha", + "CANCEL_CTA": "Cancelar", + "CONFIRM_CTA": "Desativar conta", + "DEACTIVATING": "Desativando…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "A senha é obrigatória.", + "PASSWORD_TOO_SHORT": "A senha deve ter pelo menos 8 caracteres." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Conta desativada", + "TITLE": "Reativar sua conta", + "DESCRIPTION": "Restaure o acesso completo à sua conta e torne seu perfil visível novamente.", + "RESTORE_TITLE": "O que a reativação restaura", + "RESTORE_1": "Seu perfil e listagens estarão visíveis no diretório novamente.", + "RESTORE_2": "Todas as notificações e emails agendados serão retomados.", + "RESTORE_3": "Todos os seus dados, configurações e histórico estão completamente intactos.", + "CTA_HELPER": "A reativação é imediata — nenhuma aprovação necessária.", + "CTA": "Reativar conta", + "REACTIVATING": "Reativando…", + "SUCCESS_TOAST": "Sua conta foi reativada com sucesso." + }, "DELETE_ACCOUNT": { "TITLE": "Excluir esta conta", "DESCRIPTION": "Remova permanentemente esta conta e tudo o que está associado a ela. Esta ação não pode ser desfeita.", diff --git a/apps/web/messages/ru.json b/apps/web/messages/ru.json index 699560557..9860ac422 100644 --- a/apps/web/messages/ru.json +++ b/apps/web/messages/ru.json @@ -749,6 +749,51 @@ "INTRO_LABEL": "Внимание", "INTRO_BODY": "Каждое действие в этом разделе является окончательным. Мы всегда будем запрашивать подтверждение перед удалением чего-либо.", "IRREVERSIBLE_BADGE": "Деструктивно", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Деактивировать аккаунт", + "REVERSIBLE_BADGE": "Обратимо", + "DESCRIPTION": "Временно приостановите свой аккаунт. Вы можете реактивировать его в любое время, снова войдя в систему.", + "EFFECTS_TITLE": "Что происходит при деактивации", + "EFFECT_1": "Ваш профиль и объявления будут скрыты из каталога.", + "EFFECT_2": "Вы будете выйдены из всех активных сессий.", + "EFFECT_3": "Запланированные письма и уведомления будут приостановлены.", + "EFFECT_4": "Ваши данные хранятся в безопасности, ничего не удаляется.", + "CTA_HELPER": "Вы можете реактивировать в любое время.", + "CTA": "Деактивировать аккаунт…", + "CONFIRM_MODAL": { + "TITLE": "Деактивировать ваш аккаунт", + "SUBTITLE": "Ваши данные будут сохранены — вы можете реактивировать в любое время.", + "EFFECTS_HEADING": "Что означает деактивация", + "EFFECT_1": "Ваш профиль и объявления будут скрыты из каталога.", + "EFFECT_2": "Вы будете выйдены из всех активных сессий.", + "EFFECT_3": "Запланированные письма и уведомления будут приостановлены.", + "EFFECT_4": "Ваши данные хранятся в безопасности, ничего не удаляется.", + "REACTIVATION_NOTE": "Для реактивации просто снова войдите в систему с вашими учётными данными.", + "CONFIRMATION_HEADING": "Подтвердите, что это действительно вы", + "PASSWORD_LABEL": "Ваш текущий пароль", + "PASSWORD_PLACEHOLDER": "Введите пароль", + "CANCEL_CTA": "Отмена", + "CONFIRM_CTA": "Деактивировать аккаунт", + "DEACTIVATING": "Деактивация…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Пароль обязателен.", + "PASSWORD_TOO_SHORT": "Пароль должен содержать не менее 8 символов." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Аккаунт деактивирован", + "TITLE": "Реактивировать ваш аккаунт", + "DESCRIPTION": "Восстановите полный доступ к аккаунту и сделайте свой профиль снова видимым.", + "RESTORE_TITLE": "Что восстанавливает реактивация", + "RESTORE_1": "Ваш профиль и объявления снова будут видны в каталоге.", + "RESTORE_2": "Все уведомления и запланированные письма возобновятся.", + "RESTORE_3": "Все ваши данные, настройки и история полностью сохранены.", + "CTA_HELPER": "Реактивация происходит немедленно — одобрение не требуется.", + "CTA": "Реактивировать аккаунт", + "REACTIVATING": "Реактивация…", + "SUCCESS_TOAST": "Ваш аккаунт был успешно реактивирован." + }, "DELETE_ACCOUNT": { "TITLE": "Удалить этот аккаунт", "DESCRIPTION": "Безвозвратно удалить этот аккаунт и всё, что с ним связано. Это действие невозможно отменить.", diff --git a/apps/web/messages/th.json b/apps/web/messages/th.json index c93ccacea..6ea9093c1 100644 --- a/apps/web/messages/th.json +++ b/apps/web/messages/th.json @@ -671,6 +671,51 @@ "INTRO_LABEL": "ข้อควรทราบ", "INTRO_BODY": "การดำเนินการทุกอย่างในส่วนนี้เป็นการดำเนินการแบบถาวร เราจะขอให้คุณยืนยันก่อนที่จะลบสิ่งใดเสมอ", "IRREVERSIBLE_BADGE": "การดำเนินการที่ทำลายข้อมูล", + "DEACTIVATE_ACCOUNT": { + "TITLE": "ปิดใช้งานบัญชี", + "REVERSIBLE_BADGE": "สามารถย้อนกลับได้", + "DESCRIPTION": "หยุดบัญชีของคุณชั่วคราว คุณสามารถเปิดใช้งานอีกครั้งได้ตลอดเวลาโดยการเข้าสู่ระบบใหม่", + "EFFECTS_TITLE": "สิ่งที่เกิดขึ้นเมื่อคุณปิดใช้งาน", + "EFFECT_1": "โปรไฟล์และรายการของคุณจะถูกซ่อนจากไดเรกทอรี", + "EFFECT_2": "คุณจะถูกออกจากระบบจากเซสชันที่ใช้งานอยู่ทั้งหมด", + "EFFECT_3": "อีเมลและการแจ้งเตือนที่กำหนดไว้จะถูกหยุดชั่วคราว", + "EFFECT_4": "ข้อมูลของคุณได้รับการเก็บรักษาอย่างปลอดภัยและไม่มีอะไรถูกลบ", + "CTA_HELPER": "คุณสามารถเปิดใช้งานอีกครั้งได้ตลอดเวลา", + "CTA": "ปิดใช้งานบัญชี…", + "CONFIRM_MODAL": { + "TITLE": "ปิดใช้งานบัญชีของคุณ", + "SUBTITLE": "ข้อมูลของคุณจะถูกเก็บไว้ — คุณสามารถเปิดใช้งานอีกครั้งได้ตลอดเวลา", + "EFFECTS_HEADING": "ความหมายของการปิดใช้งาน", + "EFFECT_1": "โปรไฟล์และรายการของคุณจะถูกซ่อนจากไดเรกทอรี", + "EFFECT_2": "คุณจะถูกออกจากระบบจากเซสชันที่ใช้งานอยู่ทั้งหมด", + "EFFECT_3": "อีเมลและการแจ้งเตือนที่กำหนดไว้จะถูกหยุดชั่วคราว", + "EFFECT_4": "ข้อมูลของคุณได้รับการเก็บรักษาอย่างปลอดภัยและไม่มีอะไรถูกลบ", + "REACTIVATION_NOTE": "หากต้องการเปิดใช้งานอีกครั้ง เพียงเข้าสู่ระบบด้วยข้อมูลประจำตัวของคุณ", + "CONFIRMATION_HEADING": "ยืนยันว่าเป็นคุณจริงๆ", + "PASSWORD_LABEL": "รหัสผ่านปัจจุบันของคุณ", + "PASSWORD_PLACEHOLDER": "ป้อนรหัสผ่าน", + "CANCEL_CTA": "ยกเลิก", + "CONFIRM_CTA": "ปิดใช้งานบัญชี", + "DEACTIVATING": "กำลังปิดใช้งาน…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "จำเป็นต้องมีรหัสผ่าน", + "PASSWORD_TOO_SHORT": "รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร" + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "บัญชีถูกปิดใช้งาน", + "TITLE": "เปิดใช้งานบัญชีของคุณอีกครั้ง", + "DESCRIPTION": "กู้คืนการเข้าถึงบัญชีของคุณอย่างเต็มรูปแบบและทำให้โปรไฟล์ของคุณมองเห็นได้อีกครั้ง", + "RESTORE_TITLE": "สิ่งที่การเปิดใช้งานอีกครั้งจะกู้คืน", + "RESTORE_1": "โปรไฟล์และรายการของคุณจะมองเห็นได้อีกครั้งในไดเรกทอรี", + "RESTORE_2": "การแจ้งเตือนและอีเมลที่กำหนดไว้ทั้งหมดจะกลับมาทำงาน", + "RESTORE_3": "ข้อมูล การตั้งค่า และประวัติทั้งหมดของคุณยังคงสมบูรณ์", + "CTA_HELPER": "การเปิดใช้งานอีกครั้งเป็นไปทันที — ไม่ต้องรับการอนุมัติ", + "CTA": "เปิดใช้งานบัญชีอีกครั้ง", + "REACTIVATING": "กำลังเปิดใช้งานอีกครั้ง…", + "SUCCESS_TOAST": "บัญชีของคุณได้รับการเปิดใช้งานอีกครั้งเรียบร้อยแล้ว" + }, "DELETE_ACCOUNT": { "TITLE": "ลบบัญชีนี้", "DESCRIPTION": "ลบบัญชีนี้และทุกสิ่งที่เกี่ยวข้องกับบัญชีนี้อย่างถาวร การดำเนินการนี้ไม่สามารถยกเลิกได้", diff --git a/apps/web/messages/tr.json b/apps/web/messages/tr.json index a0467975a..83b6afa8c 100644 --- a/apps/web/messages/tr.json +++ b/apps/web/messages/tr.json @@ -749,6 +749,51 @@ "INTRO_LABEL": "Uyarı", "INTRO_BODY": "Bu bölümdeki her işlem kalıcıdır. Herhangi bir şey kaldırılmadan önce sizden her zaman onay isteyeceğiz.", "IRREVERSIBLE_BADGE": "Yıkıcı", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Hesabı devre dışı bırak", + "REVERSIBLE_BADGE": "Geri alınabilir", + "DESCRIPTION": "Hesabınızı geçici olarak duraklatın. İstediğiniz zaman tekrar giriş yaparak yeniden etkinleştirebilirsiniz.", + "EFFECTS_TITLE": "Devre dışı bıraktığınızda ne olur", + "EFFECT_1": "Profiliniz ve listeleriniz dizinden gizlenir.", + "EFFECT_2": "Tüm aktif oturumlardan çıkış yapılır.", + "EFFECT_3": "Zamanlanmış e-postalar ve bildirimler duraklatılır.", + "EFFECT_4": "Verileriniz güvenle saklanır ve hiçbir şey silinmez.", + "CTA_HELPER": "İstediğiniz zaman yeniden etkinleştirebilirsiniz.", + "CTA": "Hesabı devre dışı bırak…", + "CONFIRM_MODAL": { + "TITLE": "Hesabınızı devre dışı bırakın", + "SUBTITLE": "Verileriniz korunacak — istediğiniz zaman yeniden etkinleştirebilirsiniz.", + "EFFECTS_HEADING": "Devre dışı bırakmanın anlamı", + "EFFECT_1": "Profiliniz ve listeleriniz dizinden gizlenir.", + "EFFECT_2": "Tüm aktif oturumlardan çıkış yapılır.", + "EFFECT_3": "Zamanlanmış e-postalar ve bildirimler duraklatılır.", + "EFFECT_4": "Verileriniz güvenle saklanır ve hiçbir şey silinmez.", + "REACTIVATION_NOTE": "Yeniden etkinleştirmek için kimlik bilgilerinizle tekrar giriş yapmanız yeterlidir.", + "CONFIRMATION_HEADING": "Gerçekten siz olduğunuzu onaylayın", + "PASSWORD_LABEL": "Mevcut şifreniz", + "PASSWORD_PLACEHOLDER": "Şifrenizi girin", + "CANCEL_CTA": "İptal", + "CONFIRM_CTA": "Hesabı devre dışı bırak", + "DEACTIVATING": "Devre dışı bırakılıyor…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Şifre gereklidir.", + "PASSWORD_TOO_SHORT": "Şifre en az 8 karakter olmalıdır." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Hesap devre dışı", + "TITLE": "Hesabınızı yeniden etkinleştirin", + "DESCRIPTION": "Hesabınıza tam erişimi geri yükleyin ve profilinizi yeniden görünür hale getirin.", + "RESTORE_TITLE": "Yeniden etkinleştirmenin geri yükledikleri", + "RESTORE_1": "Profiliniz ve listeleriniz dizinde yeniden görünür olacak.", + "RESTORE_2": "Tüm bildirimler ve zamanlanmış e-postalar devam edecek.", + "RESTORE_3": "Tüm verileriniz, ayarlarınız ve geçmişiniz tamamen sağlamdır.", + "CTA_HELPER": "Yeniden etkinleştirme anında gerçekleşir — onay gerekmez.", + "CTA": "Hesabı yeniden etkinleştir", + "REACTIVATING": "Yeniden etkinleştiriliyor…", + "SUCCESS_TOAST": "Hesabınız başarıyla yeniden etkinleştirildi." + }, "DELETE_ACCOUNT": { "TITLE": "Bu hesabı sil", "DESCRIPTION": "Bu hesabı ve onunla ilişkili her şeyi kalıcı olarak kaldırın. Bu işlem geri alınamaz.", diff --git a/apps/web/messages/uk.json b/apps/web/messages/uk.json index d87712861..d4a58aa91 100644 --- a/apps/web/messages/uk.json +++ b/apps/web/messages/uk.json @@ -671,6 +671,51 @@ "INTRO_LABEL": "Зверніть увагу", "INTRO_BODY": "Кожна дія в цьому розділі є остаточною. Ми завжди попросимо вас підтвердити, перш ніж щось буде видалено.", "IRREVERSIBLE_BADGE": "Деструктивно", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Деактивувати обліковий запис", + "REVERSIBLE_BADGE": "Оборотно", + "DESCRIPTION": "Тимчасово зупиніть свій обліковий запис. Ви можете реактивувати його в будь-який час, повторно увійшовши в систему.", + "EFFECTS_TITLE": "Що відбувається при деактивації", + "EFFECT_1": "Ваш профіль та оголошення будуть приховані з каталогу.", + "EFFECT_2": "Ви будете виведені з усіх активних сесій.", + "EFFECT_3": "Заплановані листи та сповіщення будуть призупинені.", + "EFFECT_4": "Ваші дані зберігаються в безпеці, нічого не видаляється.", + "CTA_HELPER": "Ви можете реактивувати в будь-який час.", + "CTA": "Деактивувати обліковий запис…", + "CONFIRM_MODAL": { + "TITLE": "Деактивувати ваш обліковий запис", + "SUBTITLE": "Ваші дані будуть збережені — ви можете реактивувати в будь-який час.", + "EFFECTS_HEADING": "Що означає деактивація", + "EFFECT_1": "Ваш профіль та оголошення будуть приховані з каталогу.", + "EFFECT_2": "Ви будете виведені з усіх активних сесій.", + "EFFECT_3": "Заплановані листи та сповіщення будуть призупинені.", + "EFFECT_4": "Ваші дані зберігаються в безпеці, нічого не видаляється.", + "REACTIVATION_NOTE": "Щоб реактивувати, просто увійдіть знову з вашими обліковими даними.", + "CONFIRMATION_HEADING": "Підтвердьте, що це справді ви", + "PASSWORD_LABEL": "Ваш поточний пароль", + "PASSWORD_PLACEHOLDER": "Введіть пароль", + "CANCEL_CTA": "Скасувати", + "CONFIRM_CTA": "Деактивувати обліковий запис", + "DEACTIVATING": "Деактивація…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Пароль обов'язковий.", + "PASSWORD_TOO_SHORT": "Пароль повинен містити не менше 8 символів." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Обліковий запис деактивовано", + "TITLE": "Реактивувати ваш обліковий запис", + "DESCRIPTION": "Відновіть повний доступ до облікового запису та зробіть свій профіль знову видимим.", + "RESTORE_TITLE": "Що відновлює реактивація", + "RESTORE_1": "Ваш профіль та оголошення знову будуть видні в каталозі.", + "RESTORE_2": "Усі сповіщення та заплановані листи відновляться.", + "RESTORE_3": "Усі ваші дані, налаштування та історія повністю збережені.", + "CTA_HELPER": "Реактивація відбувається негайно — схвалення не потрібно.", + "CTA": "Реактивувати обліковий запис", + "REACTIVATING": "Реактивація…", + "SUCCESS_TOAST": "Ваш обліковий запис було успішно реактивовано." + }, "DELETE_ACCOUNT": { "TITLE": "Видалити цей обліковий запис", "DESCRIPTION": "Остаточно видалити цей обліковий запис і все, що з ним пов'язано. Цю дію неможливо скасувати.", diff --git a/apps/web/messages/vi.json b/apps/web/messages/vi.json index 6bc8349fb..7fb698531 100644 --- a/apps/web/messages/vi.json +++ b/apps/web/messages/vi.json @@ -749,6 +749,51 @@ "INTRO_LABEL": "Lưu ý", "INTRO_BODY": "Mọi hành động trong phần này đều là vĩnh viễn. Chúng tôi sẽ luôn yêu cầu bạn xác nhận trước khi xóa bất cứ thứ gì.", "IRREVERSIBLE_BADGE": "Hành động phá hủy", + "DEACTIVATE_ACCOUNT": { + "TITLE": "Vô hiệu hóa tài khoản", + "REVERSIBLE_BADGE": "Có thể hoàn tác", + "DESCRIPTION": "Tạm dừng tài khoản của bạn. Bạn có thể kích hoạt lại bất cứ lúc nào bằng cách đăng nhập lại.", + "EFFECTS_TITLE": "Điều gì xảy ra khi bạn vô hiệu hóa", + "EFFECT_1": "Hồ sơ và danh sách của bạn sẽ bị ẩn khỏi thư mục.", + "EFFECT_2": "Bạn sẽ đăng xuất khỏi tất cả các phiên đang hoạt động.", + "EFFECT_3": "Email và thông báo đã lên lịch sẽ bị tạm dừng.", + "EFFECT_4": "Dữ liệu của bạn được bảo quản an toàn và không có gì bị xóa.", + "CTA_HELPER": "Bạn có thể kích hoạt lại bất cứ lúc nào.", + "CTA": "Vô hiệu hóa tài khoản…", + "CONFIRM_MODAL": { + "TITLE": "Vô hiệu hóa tài khoản của bạn", + "SUBTITLE": "Dữ liệu của bạn sẽ được giữ lại — bạn có thể kích hoạt lại bất cứ lúc nào.", + "EFFECTS_HEADING": "Vô hiệu hóa có nghĩa là gì", + "EFFECT_1": "Hồ sơ và danh sách của bạn sẽ bị ẩn khỏi thư mục.", + "EFFECT_2": "Bạn sẽ đăng xuất khỏi tất cả các phiên đang hoạt động.", + "EFFECT_3": "Email và thông báo đã lên lịch sẽ bị tạm dừng.", + "EFFECT_4": "Dữ liệu của bạn được bảo quản an toàn và không có gì bị xóa.", + "REACTIVATION_NOTE": "Để kích hoạt lại, chỉ cần đăng nhập lại bằng thông tin đăng nhập của bạn.", + "CONFIRMATION_HEADING": "Xác nhận đây thực sự là bạn", + "PASSWORD_LABEL": "Mật khẩu hiện tại của bạn", + "PASSWORD_PLACEHOLDER": "Nhập mật khẩu", + "CANCEL_CTA": "Hủy", + "CONFIRM_CTA": "Vô hiệu hóa tài khoản", + "DEACTIVATING": "Đang vô hiệu hóa…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "Mật khẩu là bắt buộc.", + "PASSWORD_TOO_SHORT": "Mật khẩu phải có ít nhất 8 ký tự." + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "Tài khoản đã bị vô hiệu hóa", + "TITLE": "Kích hoạt lại tài khoản của bạn", + "DESCRIPTION": "Khôi phục toàn quyền truy cập vào tài khoản của bạn và làm cho hồ sơ của bạn hiển thị trở lại.", + "RESTORE_TITLE": "Những gì kích hoạt lại sẽ khôi phục", + "RESTORE_1": "Hồ sơ và danh sách của bạn sẽ hiển thị trở lại trong thư mục.", + "RESTORE_2": "Tất cả thông báo và email đã lên lịch sẽ được tiếp tục.", + "RESTORE_3": "Tất cả dữ liệu, cài đặt và lịch sử của bạn đều nguyên vẹn hoàn toàn.", + "CTA_HELPER": "Kích hoạt lại ngay lập tức — không cần phê duyệt.", + "CTA": "Kích hoạt lại tài khoản", + "REACTIVATING": "Đang kích hoạt lại…", + "SUCCESS_TOAST": "Tài khoản của bạn đã được kích hoạt lại thành công." + }, "DELETE_ACCOUNT": { "TITLE": "Xóa tài khoản này", "DESCRIPTION": "Xóa vĩnh viễn tài khoản này và mọi thứ liên quan đến nó. Hành động này không thể hoàn tác.", diff --git a/apps/web/messages/zh.json b/apps/web/messages/zh.json index ab3648223..86b0b9a98 100644 --- a/apps/web/messages/zh.json +++ b/apps/web/messages/zh.json @@ -682,6 +682,51 @@ "INTRO_LABEL": "请注意", "INTRO_BODY": "本区域中的每个操作都是永久性的。在删除任何内容之前,我们都会要求您确认。", "IRREVERSIBLE_BADGE": "破坏性操作", + "DEACTIVATE_ACCOUNT": { + "TITLE": "停用账户", + "REVERSIBLE_BADGE": "可撤销", + "DESCRIPTION": "暂时暂停您的账户。您可以随时通过重新登录来重新激活它。", + "EFFECTS_TITLE": "停用时会发生什么", + "EFFECT_1": "您的个人资料和列表将从目录中隐藏。", + "EFFECT_2": "您将从所有活动会话中退出。", + "EFFECT_3": "预定的电子邮件和通知将暂停。", + "EFFECT_4": "您的数据安全保存,不会删除任何内容。", + "CTA_HELPER": "您可以随时重新激活。", + "CTA": "停用账户…", + "CONFIRM_MODAL": { + "TITLE": "停用您的账户", + "SUBTITLE": "您的数据将被保留 — 您可以随时重新激活。", + "EFFECTS_HEADING": "停用意味着什么", + "EFFECT_1": "您的个人资料和列表将从目录中隐藏。", + "EFFECT_2": "您将从所有活动会话中退出。", + "EFFECT_3": "预定的电子邮件和通知将暂停。", + "EFFECT_4": "您的数据安全保存,不会删除任何内容。", + "REACTIVATION_NOTE": "要重新激活,只需使用您的凭据重新登录即可。", + "CONFIRMATION_HEADING": "确认您的身份", + "PASSWORD_LABEL": "您当前的密码", + "PASSWORD_PLACEHOLDER": "输入密码", + "CANCEL_CTA": "取消", + "CONFIRM_CTA": "停用账户", + "DEACTIVATING": "停用中…" + }, + "ERRORS": { + "PASSWORD_REQUIRED": "密码为必填项。", + "PASSWORD_TOO_SHORT": "密码必须至少8个字符。" + } + }, + "REACTIVATE_ACCOUNT": { + "STATUS_BADGE": "账户已停用", + "TITLE": "重新激活您的账户", + "DESCRIPTION": "恢复对账户的完全访问权限,让您的个人资料再次可见。", + "RESTORE_TITLE": "重新激活将恢复什么", + "RESTORE_1": "您的个人资料和列表将再次在目录中可见。", + "RESTORE_2": "所有通知和预定电子邮件将恢复。", + "RESTORE_3": "您的所有数据、设置和历史记录完全完好无损。", + "CTA_HELPER": "重新激活是即时的 — 无需审批。", + "CTA": "重新激活账户", + "REACTIVATING": "重新激活中…", + "SUCCESS_TOAST": "您的账户已成功重新激活。" + }, "DELETE_ACCOUNT": { "TITLE": "删除此账户", "DESCRIPTION": "永久删除此账户及与其关联的所有内容。此操作无法撤销。", diff --git a/apps/web/types/next-auth.d.ts b/apps/web/types/next-auth.d.ts index a4d15d3d0..f89aad923 100644 --- a/apps/web/types/next-auth.d.ts +++ b/apps/web/types/next-auth.d.ts @@ -1,6 +1,6 @@ import { DefaultSession, DefaultUser } from 'next-auth'; -// Extend NextAuth session and user types to include isAdmin +// Extend NextAuth session and user types to include isAdmin and isDeactivated declare module 'next-auth' { interface Session { @@ -11,6 +11,8 @@ declare module 'next-auth' { isAdmin?: boolean; customerId?: string; tenantId?: string; + /** True when the user's account has a non-null deactivatedAt timestamp. */ + isDeactivated?: boolean; } & DefaultSession['user']; } interface User extends DefaultUser { @@ -30,5 +32,7 @@ declare module 'next-auth/jwt' { isAdmin?: boolean; customerId?: string; tenantId?: string; + /** True when the user's account has a non-null deactivatedAt timestamp. */ + isDeactivated?: boolean; } }