Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.local-dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ NEXT_PUBLIC_API_BASE_URL=https://backend.ideologicalatlas.com
NEXT_PUBLIC_API_VERSION=v1
NEXT_PUBLIC_GOOGLE_CLIENT_ID=CHANGE-ME
NEXT_PUBLIC_HERO_IMAGE_URL=https://lh3.googleusercontent.com/aida-public/AB6AXuBl8rwoOPJmg5qcAJKw4Vc6naau5P9eLyJ11DFXm2z8frKF2DuZpaVtUU0tL_UAYE1-Gg3dtYJgWrWlU_8kCc87rTxzi3e-c6ywBJA1hpaaPmgc3hKuIOpw3Qfv3euB3XL9at_gn3qn5xy3pyMMkJDpTLV2gMQc4T4FV5HMpZdZWzkQW17SYW4cfF3k9iyZ3LbX4Tdlh4RTI92ZQxhbAMjigy44BQhG-ULKSVmLIkOkxN7NYTEX_vir3J4LmvlsDq5UrY5E_VTLT60
NEXT_PUBLIC_GITHUB_URL=https://github.com/Ideological-Atlas
568 changes: 272 additions & 296 deletions messages/en.json

Large diffs are not rendered by default.

471 changes: 218 additions & 253 deletions messages/es.json

Large diffs are not rendered by default.

16 changes: 11 additions & 5 deletions src/app/[locale]/(auth)/forgot-password/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { AuthTemplate } from '@/components/templates/AuthTemplate';
import { ForgotPasswordForm } from '@/components/organisms/ForgotPasswordForm';
import { ForgotPasswordForm } from '@/components/organisms/auth/ForgotPasswordForm';
import { getTranslations } from 'next-intl/server';
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Recuperar Contraseña | Ideological Atlas',
description: 'Solicita un enlace para restablecer tu contraseña.',
};
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Auth' });

return {
title: `${t('forgot_password_title')} | Ideological Atlas`,
description: t('forgot_password_subtitle'),
};
}

export default function ForgotPasswordPage() {
return (
Expand Down
16 changes: 11 additions & 5 deletions src/app/[locale]/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { AuthTemplate } from '@/components/templates/AuthTemplate';
import { LoginForm } from '@/components/organisms/LoginForm';
import { LoginForm } from '@/components/organisms/auth/LoginForm';
import { getTranslations } from 'next-intl/server';
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Iniciar Sesión | Ideological Atlas',
description: 'Inicia sesión para acceder a tu perfil ideológico.',
};
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Auth' });

return {
title: `${t('login_title')} | Ideological Atlas`,
description: t('login_subtitle'),
};
}

export default function LoginPage() {
return (
Expand Down
15 changes: 10 additions & 5 deletions src/app/[locale]/(auth)/register/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { AuthTemplate } from '@/components/templates/AuthTemplate';
import { RegisterForm } from '@/components/organisms/RegisterForm';
import { RegisterForm } from '@/components/organisms/auth/RegisterForm';
import { getTranslations } from 'next-intl/server';
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Crear Cuenta | Ideological Atlas',
description: 'Regístrate para establecer tu perfil ideológico.',
};
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Auth' });
return {
title: `${t('register_title')} | Ideological Atlas`,
description: t('register_subtitle'),
};
}

export default function RegisterPage() {
return (
Expand Down
15 changes: 10 additions & 5 deletions src/app/[locale]/(auth)/reset-password/[token]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { AuthTemplate } from '@/components/templates/AuthTemplate';
import { ResetPasswordForm } from '@/components/organisms/ResetPasswordForm';
import { ResetPasswordForm } from '@/components/organisms/auth/ResetPasswordForm';
import { getTranslations } from 'next-intl/server';
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Restablecer Contraseña | Ideological Atlas',
description: 'Establece una nueva contraseña segura para tu cuenta.',
};
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Auth' });
return {
title: `${t('reset_password_title')} | Ideological Atlas`,
description: t('reset_password_subtitle'),
};
}

export default async function ResetPasswordPage({ params }: { params: Promise<{ token: string }> }) {
const { token } = await params;
Expand Down
14 changes: 9 additions & 5 deletions src/app/[locale]/(auth)/verify/[uuid]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { AuthTemplate } from '@/components/templates/AuthTemplate';
import { VerifyStatus } from '@/components/organisms/VerifyStatus';
import { VerifyStatus } from '@/components/organisms/auth/VerifyStatus';
import { getTranslations } from 'next-intl/server';
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Verificación | Ideological Atlas',
description: 'Verificando tu cuenta de usuario.',
};
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Verify' });
return {
title: `${t('title_loading')} | Ideological Atlas`,
};
}

export default function VerifyPage() {
return (
Expand Down
13 changes: 9 additions & 4 deletions src/app/[locale]/(auth)/welcome/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { AuthTemplate } from '@/components/templates/AuthTemplate';
import { PostRegisterStatus } from '@/components/organisms/PostRegisterStatus';
import { PostRegisterStatus } from '@/components/organisms/auth/PostRegisterStatus';
import { getTranslations } from 'next-intl/server';
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: '¡Bienvenido! | Ideological Atlas',
};
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Auth' });
return {
title: `${t('post_register_title')} | Ideological Atlas`,
};
}

export default function WelcomePage() {
return (
Expand Down
14 changes: 10 additions & 4 deletions src/app/[locale]/answers/[uuid]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { PublicAtlasView } from '@/components/organisms/Atlas/PublicAtlasView';
import { getTranslations } from 'next-intl/server';
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Perfil Ideológico | Ideological Atlas',
description: 'Ver perfil ideológico detallado.',
};
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Atlas' });

return {
title: `${t('public_profile_title')} | Ideological Atlas`,
description: t('public_profile_description'),
};
}

export default async function PublicAnswerPage({ params }: { params: Promise<{ uuid: string }> }) {
const { uuid } = await params;
Expand Down
14 changes: 10 additions & 4 deletions src/app/[locale]/atlas/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { AtlasView } from '@/components/organisms/Atlas/AtlasView';
import { getTranslations } from 'next-intl/server';
import type { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Atlas Ideológico | Ideological Atlas',
description: 'Define tu posición en el espectro.',
};
export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: 'Atlas' });

return {
title: `${t('header_title')} | Ideological Atlas`,
description: t('header_description'),
};
}

export default function AtlasPage() {
return (
Expand Down
63 changes: 10 additions & 53 deletions src/app/[locale]/legal/terms-of-use/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
'use client';

import { useTranslations, useLocale } from 'next-intl';
import Link from 'next/link';
import { useTranslations } from 'next-intl';
import { motion, type Variants } from 'framer-motion';
import { LegalSidebar } from '@/components/molecules/legal/LegalSidebar';
import { LegalSection } from '@/components/molecules/legal/LegalSection';
import { LegalPageHeader } from '@/components/organisms/legal/LegalPageHeader';
import { LegalContactCard } from '@/components/molecules/legal/LegalContactCard';

export default function TermsOfUsePage() {
const t = useTranslations('Terms');
const tCommon = useTranslations('Common');
const locale = useLocale();

const sidebarItems = [
{ id: 'intro', icon: 'info', label: t('sidebar.intro') },
Expand All @@ -29,49 +28,19 @@ export default function TermsOfUsePage() {
},
};

const headerVariants: Variants = {
hidden: { opacity: 0, y: -20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.6, ease: 'easeOut' },
},
};

return (
<div className="bg-background min-h-screen">
<div className="layout-content-container mx-auto w-full max-w-7xl px-4 py-8 sm:px-6 md:py-12 lg:px-8">
<div className="grid grid-cols-1 gap-10 lg:grid-cols-12">
<LegalSidebar items={sidebarItems} />

<main className="lg:col-span-9">
<motion.div
variants={headerVariants}
initial="hidden"
animate="visible"
className="border-border mb-10 flex flex-col gap-6 border-b pb-8"
>
<nav className="text-muted-foreground flex flex-wrap items-center gap-2 text-sm">
<Link href={`/${locale}`} className="hover:text-primary transition-colors">
{tCommon('home')}
</Link>
<span className="material-symbols-outlined text-[16px]">chevron_right</span>
<span className="text-foreground font-medium">{t('title')}</span>
</nav>

<div className="flex flex-col gap-4">
<h1 className="text-foreground text-4xl leading-tight font-black tracking-tight md:text-5xl">
{t('title')}
</h1>
<p className="text-muted-foreground text-lg">{t('subtitle')}</p>
<div className="text-muted-foreground flex flex-wrap items-center gap-2 text-sm font-medium">
<span className="material-symbols-outlined text-[18px]">calendar_month</span>
<span>{t('last_updated')}: 2026-01-23</span>
<span className="mx-2 opacity-50">•</span>
<span>{t('version')}</span>
</div>
</div>
</motion.div>
<LegalPageHeader
title={t('title')}
subtitle={t('subtitle')}
lastUpdated={`${t('last_updated')}: 2026-02-05`}
version={t('version')}
/>

<motion.div
variants={containerVariants}
Expand Down Expand Up @@ -164,19 +133,7 @@ export default function TermsOfUsePage() {
</div>
</LegalSection>

<div className="bg-secondary/20 border-border flex flex-col items-center justify-between gap-4 rounded-xl border p-6 md:flex-row">
<div className="text-center md:text-left">
<p className="text-foreground mb-1 font-bold">{t('contact_community.title')}</p>
<p className="text-muted-foreground text-sm">{t('contact_community.desc')}</p>
</div>
<a
href="mailto:legal@ideologicalatlas.org"
className="bg-card text-primary hover:text-primary-hover border-border flex items-center gap-2 rounded-lg border px-4 py-2 text-sm font-bold shadow-sm transition-colors hover:shadow-md"
>
<span className="material-symbols-outlined text-[18px]">group</span>
{t('contact_community.btn')}
</a>
</div>
<LegalContactCard />
</motion.div>
</main>
</div>
Expand Down
19 changes: 17 additions & 2 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,17 @@
--color-accent-foreground: var(--accent-foreground);
--color-accent-hover: var(--accent-hover);
--color-accent-strong: var(--strong-accent);
--color-accent-strong-hover: var(--strong-accent-hover);

--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-destructive-hover: var(--destructive-hover);

--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);

--color-gold: var(--gold);

--color-card: var(--card);
--color-card-foreground: var(--card-foreground);

Expand Down Expand Up @@ -82,8 +88,11 @@
--destructive: #ef4444;
--destructive-hover: #dc2626;
--destructive-foreground: #ffffff;
--warning: #f59e0b;
--warning-foreground: #ffffff;
--gold: #fbbf24;
--strong-accent: #3476d8;
--strong-accent-hover: #0d34ae;
--strong-accent-hover: #1d4ed8;
--strong-accent-foreground: #ffffff;
--other-user: #8b5cf6;
--other-user-strong: #c8f65c;
Expand Down Expand Up @@ -125,8 +134,11 @@
--destructive: #7f1d1d;
--destructive-hover: #991b1b;
--destructive-foreground: #f8fafc;
--warning: #d97706;
--warning-foreground: #ffffff;
--gold: #fbbf24;
--strong-accent: #3476d8;
--strong-accent-hover: #0d34ae;
--strong-accent-hover: #1d4ed8;
--strong-accent-foreground: #ffffff;
--other-user: #8b5cf6;
--other-user-strong: #c8f65c;
Expand Down Expand Up @@ -188,11 +200,13 @@
::view-transition-new(root) {
animation: wipe-in-from-left 0.7s cubic-bezier(0.25, 1, 0.5, 1);
z-index: 9999;
mix-blend-mode: normal;
}

::view-transition-old(root) {
animation: none;
z-index: 1;
mix-blend-mode: normal;
}

:root.back-transition ::view-transition-new(root) {
Expand All @@ -207,6 +221,7 @@
::view-transition-group(hero-about-image) {
animation-duration: 0.7s;
animation-timing-function: cubic-bezier(0.25, 1, 0.5, 1);
z-index: 10000;
}

.driver-popover.driverjs-theme {
Expand Down
29 changes: 29 additions & 0 deletions src/components/atoms/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { tv, type VariantProps } from 'tailwind-variants';
import { clsx } from 'clsx';

const alertVariants = tv({
base: 'rounded-lg border p-3 text-center text-sm font-medium transition-all',
variants: {
variant: {
default: 'bg-secondary/50 border-border text-foreground',
destructive: 'bg-destructive/10 text-destructive border-destructive/20',
success: 'bg-green-500/10 text-green-600 border-green-500/20 dark:text-green-400',
warning: 'bg-amber-500/10 text-amber-600 border-amber-500/20 dark:text-amber-500',
info: 'bg-blue-500/10 text-blue-500 border-blue-500/20',
},
},
defaultVariants: {
variant: 'default',
},
});

interface AlertProps extends VariantProps<typeof alertVariants> {
children: React.ReactNode;
className?: string;
}

export function Alert({ children, variant, className }: AlertProps) {
if (!children) return null;

return <div className={clsx(alertVariants({ variant }), className)}>{children}</div>;
}
21 changes: 21 additions & 0 deletions src/components/atoms/Portal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use client';

import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';

interface PortalProps {
children: React.ReactNode;
}

export function Portal({ children }: PortalProps) {
const [mounted, setMounted] = useState(false);

useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setMounted(true);
}, []);

if (!mounted) return null;

return createPortal(children, document.body);
}
Loading