diff --git a/apps/main/src/lib/routes.tsx b/apps/main/src/lib/routes.tsx
index 63fa415..f128ede 100644
--- a/apps/main/src/lib/routes.tsx
+++ b/apps/main/src/lib/routes.tsx
@@ -11,6 +11,7 @@ import { FeedbackPage } from "../pages/feedback";
import { JoinPage } from "../pages/join";
import { LegalNoticePage } from "../pages/legal-notice";
import { LoginPage } from "../pages/login";
+import { LoginV2Page } from "../pages/login-v2";
import { NotFoundPage } from "../pages/NotFoundPage";
// import NotesPage from "../pages/notes"; // Notes feature temporarily hidden
import { OAuthSigninPage } from "../pages/oauth-signin";
@@ -19,6 +20,7 @@ import { PrivacyPolicyPage } from "../pages/privacy-policy";
import { ResetPasswordPage } from "../pages/reset-password";
import SettingsPage from "../pages/settings";
import { SignUpPage } from "../pages/signup";
+import { SignUpV2Page } from "../pages/signup-v2";
import { TabloPage } from "../pages/tablo";
import { TabloDetailsPage } from "../pages/tablo-details";
import { TablosPage } from "../pages/tablos";
@@ -96,7 +98,10 @@ export const routes: RouteObject[] = [
{
path: "events",
element: ,
- children: [{ index: true }, { path: "create", element: }],
+ children: [
+ { index: true },
+ { path: "create", element: },
+ ],
},
{
path: "tasks",
@@ -163,10 +168,18 @@ export const routes: RouteObject[] = [
path: "login",
element: ,
},
+ {
+ path: "login-v2",
+ element: ,
+ },
{
path: "signup",
element: ,
},
+ {
+ path: "signup-v2",
+ element: ,
+ },
{
path: "reset-password",
element: ,
diff --git a/apps/main/src/locales/en/auth.json b/apps/main/src/locales/en/auth.json
index 2931e11..0bdd438 100644
--- a/apps/main/src/locales/en/auth.json
+++ b/apps/main/src/locales/en/auth.json
@@ -13,7 +13,13 @@
"forgotPassword": "Forgot password?",
"loginButton": "Log in",
"noAccount": "Don't have an account?",
- "signupLink": "Sign up"
+ "signupLink": "Sign up",
+ "newExperienceLink": "Take a look at the new login experience",
+ "asideTitle": "Centralize your projects with Xtablo",
+ "asideDescription": "Plan tasks, events and collaboration from one clean workspace.",
+ "feature1": "Track every deadline with shared timelines",
+ "feature2": "Keep files, discussions and decisions in context",
+ "feature3": "Give your team one reliable source of truth"
},
"signup": {
"title": "Create an Xtablo account",
@@ -29,6 +35,11 @@
"signupButton": "Create my account",
"alreadyAccount": "Already have an account?",
"loginLink": "Log in",
+ "asideTitle": "Build a calmer workflow from day one",
+ "asideDescription": "Create your workspace, invite your team, and keep every project in sync.",
+ "feature1": "Shared task boards with clear ownership",
+ "feature2": "Planning views for milestones and deadlines",
+ "feature3": "Fast collaboration around files and updates",
"termsAccept": "I accept the",
"termsLink": "legal notice",
"termsAnd": "and the",
diff --git a/apps/main/src/locales/fr/auth.json b/apps/main/src/locales/fr/auth.json
index 6cf4069..23c45b4 100644
--- a/apps/main/src/locales/fr/auth.json
+++ b/apps/main/src/locales/fr/auth.json
@@ -13,7 +13,13 @@
"forgotPassword": "Mot de passe oublié ?",
"loginButton": "Se connecter",
"noAccount": "Pas encore de compte ?",
- "signupLink": "S'inscrire"
+ "signupLink": "S'inscrire",
+ "newExperienceLink": "Découvrez la nouvelle expérience de connexion",
+ "asideTitle": "Centralisez vos projets avec Xtablo",
+ "asideDescription": "Planifiez tâches, événements et collaboration depuis un espace unique.",
+ "feature1": "Suivez chaque échéance avec des timelines partagées",
+ "feature2": "Gardez fichiers, discussions et décisions au même endroit",
+ "feature3": "Donnez à votre équipe une source unique de vérité"
},
"signup": {
"title": "Créer un compte Xtablo",
@@ -29,6 +35,11 @@
"signupButton": "Créer mon compte",
"alreadyAccount": "Déjà un compte ?",
"loginLink": "Se connecter",
+ "asideTitle": "Créez un workflow plus serein dès le premier jour",
+ "asideDescription": "Créez votre espace, invitez votre équipe et synchronisez vos projets.",
+ "feature1": "Des tableaux de tâches partagés avec des responsabilités claires",
+ "feature2": "Des vues planning pour les jalons et échéances",
+ "feature3": "Une collaboration rapide autour des fichiers et mises à jour",
"termsAccept": "J'accepte les",
"termsLink": "mentions légales",
"termsAnd": "et la",
diff --git a/apps/main/src/pages/login-v2.tsx b/apps/main/src/pages/login-v2.tsx
new file mode 100644
index 0000000..0940d22
--- /dev/null
+++ b/apps/main/src/pages/login-v2.tsx
@@ -0,0 +1,278 @@
+import { AnimatedBackground } from "@ui/components/AnimatedBackground";
+import { LoginWithGoogle } from "@ui/components/BrandButtons/LoginWithGoogle";
+import { useTheme } from "@xtablo/shared/contexts/ThemeContext";
+import { Button } from "@xtablo/ui/components/button";
+import { FieldError } from "@xtablo/ui/components/field";
+import { Input } from "@xtablo/ui/components/input";
+import { Label } from "@xtablo/ui/components/label";
+import {
+ ArrowLeftIcon,
+ MonitorIcon,
+ MoonIcon,
+ SparklesIcon,
+ SunIcon,
+} from "lucide-react";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+import { Link, useSearchParams } from "react-router-dom";
+import { useLoginEmail } from "../hooks/auth";
+
+export function LoginV2Page() {
+ const [searchParams] = useSearchParams();
+ const emailParam = searchParams.get("email");
+ const { t } = useTranslation(["auth", "common"]);
+ const redirectUrl = localStorage.getItem("redirectUrl");
+ const {
+ mutate: login,
+ isPending,
+ errors,
+ } = useLoginEmail({
+ redirectUrl: redirectUrl ?? null,
+ });
+
+ const [formData, setFormData] = useState({
+ email: emailParam ?? "",
+ password: "",
+ });
+
+ const { theme, setTheme } = useTheme();
+
+ const toggleTheme = () => {
+ if (theme === "light") {
+ setTheme("dark");
+ } else if (theme === "dark") {
+ setTheme("system");
+ } else {
+ setTheme("light");
+ }
+ };
+
+ const getThemeIcon = () => {
+ switch (theme) {
+ case "light":
+ return ;
+ case "dark":
+ return ;
+ case "system":
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ const onSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ login({
+ email: formData.email,
+ password: formData.password,
+ });
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+

+

+
+
+
+ {t("auth:login.title")}
+
+
+ {t("auth:login.noAccount")}{" "}
+
+ {t("auth:login.signupLink")}
+
+
+
+
+
+
+
+
+
+ {t("auth:common.orContinue")}
+
+
+
+
+
+
+
+
+ © {new Date().getFullYear()} Xtablo. {t("auth:common.backHome")}
+
+
+
+ {t("auth:signup.termsLink")}
+
+ |
+
+ {t("auth:signup.privacyLink")}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/main/src/pages/login.tsx b/apps/main/src/pages/login.tsx
index 3a25a4e..ae54648 100644
--- a/apps/main/src/pages/login.tsx
+++ b/apps/main/src/pages/login.tsx
@@ -50,7 +50,7 @@ export function LoginPage() {
const rotateY = ((x - centerX) / centerX) * 1;
setTransform(
- `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.002, 1.002, 1.002)`
+ `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.002, 1.002, 1.002)`,
);
setIsHovered(true);
};
@@ -101,7 +101,7 @@ export function LoginPage() {
ref={cardRef}
className={twMerge(
"w-full max-w-lg rounded-2xl relative",
- "transition-transform duration-200 ease-out will-change-transform"
+ "transition-transform duration-200 ease-out will-change-transform",
)}
style={{ transform }}
onMouseMove={handleMouseMove}
@@ -116,7 +116,7 @@ export function LoginPage() {
"relative w-full h-full p-8 bg-card/80 backdrop-blur-md rounded-2xl border border-border z-10 transition-shadow duration-200",
isHovered
? "shadow-[0_15px_35px_rgba(0,0,0,0.15)] dark:shadow-[0_15px_35px_rgba(0,0,0,0.3)]"
- : "shadow-xl shadow-black/10 dark:shadow-black/25"
+ : "shadow-xl shadow-black/10 dark:shadow-black/25",
)}
>
@@ -124,7 +124,12 @@ export function LoginPage() {
href="https://www.xtablo.com"
className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground transition-colors"
>
-