diff --git a/src/app/(auth)/forgot/page.tsx b/src/app/(auth)/forgot/page.tsx index a839da1..26d696a 100644 --- a/src/app/(auth)/forgot/page.tsx +++ b/src/app/(auth)/forgot/page.tsx @@ -1,45 +1,15 @@ "use client"; -import { useRouter } from "next/navigation"; import { AuthForm } from "@/features/auth/AuthForm"; -import { authStrings } from "@/features/auth/strings"; +import { AuthHeader } from "@/features/auth/AuthHeader"; // Ports ios/Mercury/Features/Auth/ForgotPasswordView.swift: back chevron, // MERCURY wordmark, explanatory subtitle (rendered by AuthForm), the email // field, and the "Сэргээх холбоос авах" CTA. export default function ForgotPasswordPage() { - const router = useRouter(); - return ( <> - - -
- -

- {authStrings.wordmark} -

+
diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index 0278dc7..7546473 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -2,7 +2,7 @@ import { useRouter } from "next/navigation"; import { AuthForm } from "@/features/auth/AuthForm"; -import { authStrings } from "@/features/auth/strings"; +import { AuthHeader } from "@/features/auth/AuthHeader"; // Ports ios/Mercury/Features/Auth/LoginView.swift: back chevron, MERCURY // wordmark, then the login form (email + password + forgot link + CTAs). @@ -11,34 +11,7 @@ export default function LoginPage() { return ( <> - - -
- -

- {authStrings.wordmark} -

+
diff --git a/src/app/(auth)/register/page.tsx b/src/app/(auth)/register/page.tsx index c5f6429..8111ec0 100644 --- a/src/app/(auth)/register/page.tsx +++ b/src/app/(auth)/register/page.tsx @@ -2,7 +2,7 @@ import { useRouter } from "next/navigation"; import { AuthForm } from "@/features/auth/AuthForm"; -import { authStrings } from "@/features/auth/strings"; +import { AuthHeader } from "@/features/auth/AuthHeader"; // Ports ios/Mercury/Features/Auth/RegisterView.swift: back chevron, MERCURY // wordmark, then the registration form (email + password + confirm + CTAs). @@ -11,34 +11,7 @@ export default function RegisterPage() { return ( <> - - -
- -

- {authStrings.wordmark} -

+
diff --git a/src/features/auth/AuthForm.tsx b/src/features/auth/AuthForm.tsx index cc8ffe3..5b01498 100644 --- a/src/features/auth/AuthForm.tsx +++ b/src/features/auth/AuthForm.tsx @@ -137,8 +137,8 @@ export function AuthForm({ if (step === "twoFactor") { return ( -
-

+ +

{authStrings.twoFactor.subtitle}

@@ -162,84 +162,88 @@ export function AuthForm({ } return ( - + {mode === "forgot" && ( -

+

{authStrings.forgot.subtitle}

)} - - - - - {mode !== "forgot" && ( - +
+ - )} - {mode === "register" && ( - - - - )} + {mode !== "forgot" && ( + + + + )} - {mode === "login" && ( - - )} + {mode === "register" && ( + + + + )} + + {mode === "login" && ( + + )} +
{infoMessage && ( -

+

{infoMessage}

)} - - {mode === "login" - ? authStrings.login.submit - : mode === "register" - ? authStrings.register.submit - : authStrings.forgot.submit} - +
+ + {mode === "login" + ? authStrings.login.submit + : mode === "register" + ? authStrings.register.submit + : authStrings.forgot.submit} + - {mode === "login" && ( - - {authStrings.login.registerLink} - - )} - {mode === "register" && ( - - {authStrings.register.loginLink} - - )} + {mode === "login" && ( + + {authStrings.login.registerLink} + + )} + {mode === "register" && ( + + {authStrings.register.loginLink} + + )} +
); } @@ -247,7 +251,7 @@ export function AuthForm({ function FormError({ message }: { message?: string }) { if (!message) return null; return ( -

+

{message}

); diff --git a/src/features/auth/AuthHeader.tsx b/src/features/auth/AuthHeader.tsx new file mode 100644 index 0000000..36a73e3 --- /dev/null +++ b/src/features/auth/AuthHeader.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { Icon } from "@/ds"; +import { authStrings } from "./strings"; + +/** + * Shared header for the three auth screens (login / register / forgot): a + * back chevron pinned top-left, then a MERCURY wordmark lockup — a tinted + * monogram badge stacked above the bold, letter-spaced wordmark — centered + * below it. Purely presentational; navigation still goes through + * `router.back()`, unchanged from the markup this replaces in each page. + */ +export function AuthHeader() { + const router = useRouter(); + + return ( +
+ + +
+ + M + +

+ {authStrings.wordmark} +

+
+
+ ); +} diff --git a/src/features/profile/CategoriesManager.tsx b/src/features/profile/CategoriesManager.tsx index f3275e9..b85d5f6 100644 --- a/src/features/profile/CategoriesManager.tsx +++ b/src/features/profile/CategoriesManager.tsx @@ -15,6 +15,8 @@ import { Skeleton, } from "@seed-design/react"; import { MercuryButton } from "@/ds/MercuryButton"; +import { EmptyState, Icon, IconChip } from "@/ds"; +import { categoryStyle } from "@/ds/categoryStyle"; import { useCategories } from "@/api/hooks/reads"; import { useCategoryMutations } from "@/api/hooks/mutations"; import type { Category } from "@/api/schemas"; @@ -105,9 +107,9 @@ export function CategoriesManager({ onBack }: CategoriesManagerProps) { type="button" onClick={onBack} aria-label={profileStrings.categories.back} - style={{ background: "none", border: "none", fontSize: 20, cursor: "pointer" }} + style={{ display: "grid", placeItems: "center", background: "none", border: "none", cursor: "pointer", color: "var(--seed-color-fg-neutral)" }} > - ← + )}

{profileStrings.categories.title}

@@ -124,7 +126,7 @@ export function CategoriesManager({ onBack }: CategoriesManagerProps) { )} {!isLoading && groups.length === 0 && ( -

{profileStrings.categories.empty}

+ )} {groups.map((group) => ( @@ -132,6 +134,7 @@ export function CategoriesManager({ onBack }: CategoriesManagerProps) { setSheet({ mode: "edit", category: { name: group.name, kind: "expense", depth: 0, icon: group.icon } })} onDelete={() => setDeleteTarget(group.name)} @@ -204,20 +207,23 @@ export function CategoriesManager({ onBack }: CategoriesManagerProps) { function CategoryRow({ icon, name, + kind, heading, onEdit, onDelete, }: { icon?: string | null; name: string; + kind?: string; heading?: boolean; onEdit: () => void; onDelete: () => void; }) { + const style = categoryStyle(name, kind === "income"); return ( -
- {icon || "🏷️"} - {name} +
+ + {style.name} @@ -315,9 +323,9 @@ function CategoryEditor({ type="button" onClick={onCancel} aria-label={profileStrings.categories.cancel} - style={{ background: "none", border: "none", fontSize: 20, cursor: "pointer" }} + style={{ display: "grid", placeItems: "center", background: "none", border: "none", cursor: "pointer", color: "var(--seed-color-fg-neutral)" }} > - ← +

{editing ? profileStrings.categories.edit : profileStrings.categories.add} diff --git a/src/features/profile/ConnectedBanks.tsx b/src/features/profile/ConnectedBanks.tsx index 4d2bde4..6ab63c0 100644 --- a/src/features/profile/ConnectedBanks.tsx +++ b/src/features/profile/ConnectedBanks.tsx @@ -2,6 +2,7 @@ import { useConnections } from "@/api/hooks/reads"; import { Skeleton } from "@seed-design/react"; +import { EmptyState, IconChip, SectionHeader } from "@/ds"; import { profileStrings } from "./strings"; /** @@ -11,68 +12,79 @@ import { profileStrings } from "./strings"; */ export function ConnectedBanks() { const { data: connections, isLoading } = useConnections(); + const empty = !isLoading && (!connections || connections.length === 0); return (
-

- {profileStrings.banks.title} -

+
+ +
{isLoading && ( -
+
)} - {!isLoading && (!connections || connections.length === 0) && ( -

- {profileStrings.banks.empty} -

+ {empty && ( + )} {!isLoading && connections && connections.length > 0 && ( -
    - {connections.map((c) => ( -
  • -
    - {c.bank} - - {c.username} - -
    - {c.courierManaged && ( - - {profileStrings.banks.courierManaged} - - )} -
  • - ))} -
- )} + <> +
    + {connections.map((c) => ( +
  • +
    + +
    + + {c.bank} + + {c.username} +
    +
    + {c.courierManaged && ( + + {profileStrings.banks.courierManaged} + + )} +
  • + ))} +
-

- {profileStrings.banks.manageNote} -

+

+ {profileStrings.banks.manageNote} +

+ + )}
); } diff --git a/src/features/profile/ProfileView.tsx b/src/features/profile/ProfileView.tsx index d167e98..b63bd7d 100644 --- a/src/features/profile/ProfileView.tsx +++ b/src/features/profile/ProfileView.tsx @@ -3,7 +3,8 @@ import * as React from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; -import { Card, HideAmountsToggle, MercuryButton } from "@/ds"; +import { Card, HideAmountsToggle, Icon, IconChip, MercuryButton } from "@/ds"; +import type { IconName } from "@/ds"; import { useMe, useSettings } from "@/api/hooks/reads"; import { profileStrings } from "./strings"; import { SettingsForm } from "./SettingsForm"; @@ -24,6 +25,7 @@ export function ProfileView() { const emailLocal = me?.email ? me.email.split("@")[0] : ""; const displayName = settings?.holderName || emailLocal || me?.email || ""; + const initial = (displayName || me?.email || "?").trim().charAt(0).toUpperCase() || "?"; async function handleLogout() { setLoggingOut(true); @@ -45,23 +47,57 @@ export function ProfileView() {

- -
- {displayName} + + + {initial} + +
+ + {displayName} + {me?.email && ( - - {profileStrings.account.handlePrefix} - {emailLocal} + + {me.email} )}
- -