diff --git a/apps/web/src/apps/auth/login/components/android-icon.tsx b/apps/web/src/apps/auth/login/components/android-icon.tsx
new file mode 100644
index 0000000..17110c7
--- /dev/null
+++ b/apps/web/src/apps/auth/login/components/android-icon.tsx
@@ -0,0 +1,6 @@
+export const AndroidIcon = (props: any) => (
+
+);
diff --git a/apps/web/src/apps/auth/login/components/chrome-icon.tsx b/apps/web/src/apps/auth/login/components/chrome-icon.tsx
new file mode 100644
index 0000000..1143373
--- /dev/null
+++ b/apps/web/src/apps/auth/login/components/chrome-icon.tsx
@@ -0,0 +1,16 @@
+export const ChromeIcon = (props: any) => (
+
+);
diff --git a/apps/web/src/apps/auth/login/components/google-icon.tsx b/apps/web/src/apps/auth/login/components/google-icon.tsx
new file mode 100644
index 0000000..ddb1a8f
--- /dev/null
+++ b/apps/web/src/apps/auth/login/components/google-icon.tsx
@@ -0,0 +1,20 @@
+export const GoogleIcon = (props: any) => (
+
+);
diff --git a/apps/web/src/apps/auth/login/components/icon-circle.tsx b/apps/web/src/apps/auth/login/components/icon-circle.tsx
new file mode 100644
index 0000000..c39efa7
--- /dev/null
+++ b/apps/web/src/apps/auth/login/components/icon-circle.tsx
@@ -0,0 +1,12 @@
+export const IconCircle = ({ x, y, children }: { x: number; y: number; children: React.ReactNode }) => (
+
+
+ {/* 1. Glossy & Glassmorphic Orbs */}
+
+
+
+
+
+ Write Better {/* Wrapper luar KHUSUS untuk Drop Shadow */}
+
+ {/* Elemen dalam KHUSUS untuk Gradien Teks */}
+
+ Everywhere
+
+
+
+
+ {/* Graphics Area */}
+
+ {/* Concentric Rings */}
+
+
+
+
+
+ {/* Center Logo */}
+
+
+ {/* Glowing backdrop */}
+
+ {/* Custom P/Pen Logo from reference */}
+
+
+
+
+ {/* Orbiting Icons */}
+
+ {/* Ring 1 (r=100) */}
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Ring 2 (r=150) */}
+
+
+
+
+
+
+
+
+ y!
+
+
+
+ {/* Ring 3 (r=210) */}
+
+
+
+
+ {/* Ring 4 (r=270) */}
+
+
+
+
+
+
+
+
+ Compatible with{' '}
+ Gmail, Outlook Web, LinkedIn and most web editors for a
+ smooth writing experience anywhere online.
+
+
+ {/* Pagination Indicators */}
+
+
+ );
+}
diff --git a/apps/web/src/apps/auth/login/components/ring.tsx b/apps/web/src/apps/auth/login/components/ring.tsx
new file mode 100644
index 0000000..fb9e283
--- /dev/null
+++ b/apps/web/src/apps/auth/login/components/ring.tsx
@@ -0,0 +1,6 @@
+export const Ring = ({ size }: { size: number }) => (
+
+);
diff --git a/apps/web/src/apps/auth/login/index.tsx b/apps/web/src/apps/auth/login/index.tsx
index 4667697..0642ffd 100644
--- a/apps/web/src/apps/auth/login/index.tsx
+++ b/apps/web/src/apps/auth/login/index.tsx
@@ -1,104 +1,115 @@
-import { TextInput, PasswordInput, Checkbox, Button, Divider } from '@repo/ui/components';
-import { Mail, Lock, LogIn, Apple, Globe, MessageCircle } from 'lucide-react';
+import {
+ FieldTextInput,
+ FieldPasswordInput,
+ Checkbox,
+ Button,
+ Divider,
+ Select,
+ Image,
+ notifications,
+} from '@repo/ui/components';
+import { Lock, Apple, LogInIcon, UserRound, Globe } from 'lucide-react';
+import { GoogleIcon } from './components/google-icon';
+import { MicrosoftIcon } from './components/microsoft-icon';
+import { RightSection } from './components/right-section';
+import { useTranslation, registerModuleNamespace } from '@repo/core-i18n';
+import { AppStorageKey, appStorage } from '../../../core/storage/local';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { createLoginSchema } from './validators/login.validator';
+import { useMemo, useState } from 'react';
-const GoogleIcon = (props: any) => (
-
-);
+import loginEn from './languages/en/login.json';
+import loginId from './languages/id/login.json';
+import { CommonRemoteDataServices } from '@repo/core-api/data-services';
+import { apiClient } from '../../../core/lib/api-client';
+import { initiateAuthSession } from '../../../core/lib/auth.helper';
-const MicrosoftIcon = (props: any) => (
-
-);
+registerModuleNamespace('auth-login', {
+ en: loginEn,
+ id: loginId,
+});
-const AndroidIcon = (props: any) => (
-
-);
-
-const ChromeIcon = (props: any) => (
-
-);
-
-const Ring = ({ size }: { size: number }) => (
-
-);
-
-const IconCircle = ({ x, y, children }: { x: number; y: number; children: React.ReactNode }) => (
-
- {children}
-
-);
+export const authDataService = new CommonRemoteDataServices(apiClient, {});
export default function LoginPage() {
+ const { t, i18n } = useTranslation('auth-login');
+ const [loadingLogin, setLoadingLogin] = useState
(false);
+
+ const validator = useMemo(() => {
+ return createLoginSchema(t);
+ }, [t, i18n.language]);
+
+ const formControl = useForm({
+ resolver: zodResolver(validator),
+ defaultValues: { username: '', password: '' },
+ });
+ const { control } = formControl;
+
+ const onSubmit = async (data: any) => {
+ setLoadingLogin(true);
+ try {
+ const response = await authDataService.customRequest({ url: '/api/v1/auth', method: 'POST', data });
+ await initiateAuthSession(response.data);
+ } catch (error: any) {
+ const message = error?.response?.data?.message;
+ notifications.show({
+ title: t('common:notifications.errorTitle'),
+ message: message ?? error?.message ?? 'Login failed',
+ color: 'red',
+ });
+ } finally {
+ setLoadingLogin(false);
+ }
+ };
+
return (
{/* Left Column - Login Form */}
- {/* Logo (Top Left) */}
-
- {/* Image Logo (Uncomment and set src to your logo path) */}
- {/*

*/}
+
+ {/* Logo (Top Left) */}
+
+
+
- {/* Text Logo (Remove if using image) */}
-
- E
+ {/* Language Switcher (Top Right) */}
+
+
+
-
Eigen
-
+
{/* Header */}
-
- Login to your account!
+
+ {t('title')}
-
- Enter your registered email address and password to login!
-
+
{t('description')}
{/* Form */}
-
-
+
{/* Right Column - Graphic */}
-
- {/* 1. Glossy & Glassmorphic Orbs */}
-
-
-
-
- Write Better{' '}
-
- Everywhere
-
-
-
- {/* Graphics Area */}
-
- {/* Concentric Rings */}
-
-
-
-
-
- {/* Center Logo */}
-
-
- {/* Glowing backdrop */}
-
- {/* Custom P/Pen Logo from reference */}
-
-
-
-
- {/* Orbiting Icons */}
-
- {/* Ring 1 (r=100) */}
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Ring 2 (r=150) */}
-
-
-
-
-
-
-
-
- y!
-
-
-
- {/* Ring 3 (r=210) */}
-
-
-
-
- {/* Ring 4 (r=270) */}
-
-
-
-
-
-
-
-
- Compatible with{' '}
- Gmail, Outlook Web, LinkedIn and most web editors for
- a smooth writing experience anywhere online.
-
-
- {/* Pagination Indicators */}
-
+
+
);
diff --git a/apps/web/src/apps/auth/login/languages/en/login.json b/apps/web/src/apps/auth/login/languages/en/login.json
new file mode 100644
index 0000000..fe8feb5
--- /dev/null
+++ b/apps/web/src/apps/auth/login/languages/en/login.json
@@ -0,0 +1,12 @@
+{
+ "title": "Login to your account!",
+ "description": "Enter your registered username and password to login!",
+ "username_label": "Username",
+ "username_placeholder": "eg. administrator",
+ "password_label": "Password",
+ "password_placeholder": "********",
+ "remember_me": "Remember me",
+ "forgot_password": "Forgot Password ?",
+ "login_button": "Login",
+ "or_login_with": "Or login with"
+}
\ No newline at end of file
diff --git a/apps/web/src/apps/auth/login/languages/id/login.json b/apps/web/src/apps/auth/login/languages/id/login.json
new file mode 100644
index 0000000..7fff63a
--- /dev/null
+++ b/apps/web/src/apps/auth/login/languages/id/login.json
@@ -0,0 +1,12 @@
+{
+ "title": "Login ke akun Anda!",
+ "description": "Masukkan username dan password yang terdaftar untuk login!",
+ "username_label": "Username",
+ "username_placeholder": "cth. administrator",
+ "password_label": "Password",
+ "password_placeholder": "********",
+ "remember_me": "Ingat saya",
+ "forgot_password": "Lupa Kata Sandi ?",
+ "login_button": "Masuk",
+ "or_login_with": "Atau login dengan"
+}
\ No newline at end of file
diff --git a/apps/web/src/apps/auth/login/validators/login.validator.ts b/apps/web/src/apps/auth/login/validators/login.validator.ts
new file mode 100644
index 0000000..6b8f478
--- /dev/null
+++ b/apps/web/src/apps/auth/login/validators/login.validator.ts
@@ -0,0 +1,9 @@
+import { z } from 'zod';
+import { compose, required } from '@repo/ui/validators';
+
+export const createLoginSchema = (t: any) => {
+ return z.object({
+ username: compose(z.string(), required(t('username_label'))),
+ password: compose(z.string(), required(t('password_label'))),
+ });
+};
diff --git a/packages/brand/public/logo-primary.svg b/packages/brand/public/logo-primary.svg
new file mode 100644
index 0000000..4328d48
--- /dev/null
+++ b/packages/brand/public/logo-primary.svg
@@ -0,0 +1,6 @@
+