Merge feat/F4-account-dashboard: auth + account dashboard (F4)
Some checks failed
build-prerelease / Test (push) Blocked by required conditions
build-prerelease / Resolve version stamps + change detection (push) Successful in 47s
build-prerelease / Build neuron-blackwell (push) Has been skipped
build-prerelease / Build neuron-ampere (push) Has been skipped
build-prerelease / Build neuron-ada (push) Has been skipped
build-prerelease / Package helexa-neuron-ada RPM (push) Has been skipped
build-prerelease / Package helexa-neuron-ampere RPM (push) Has been skipped
build-prerelease / Package helexa-neuron-blackwell RPM (push) Has been skipped
build-prerelease / Lint (fmt + clippy) (push) Successful in 3m16s
build-prerelease / Build cortex binary (push) Has been skipped
build-prerelease / Build helexa-bench binary (push) Has been skipped
build-prerelease / Package cortex RPM (push) Has been skipped
build-prerelease / Package helexa-bench RPM (push) Has been skipped
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Has been cancelled
Some checks failed
build-prerelease / Test (push) Blocked by required conditions
build-prerelease / Resolve version stamps + change detection (push) Successful in 47s
build-prerelease / Build neuron-blackwell (push) Has been skipped
build-prerelease / Build neuron-ampere (push) Has been skipped
build-prerelease / Build neuron-ada (push) Has been skipped
build-prerelease / Package helexa-neuron-ada RPM (push) Has been skipped
build-prerelease / Package helexa-neuron-ampere RPM (push) Has been skipped
build-prerelease / Package helexa-neuron-blackwell RPM (push) Has been skipped
build-prerelease / Lint (fmt + clippy) (push) Successful in 3m16s
build-prerelease / Build cortex binary (push) Has been skipped
build-prerelease / Build helexa-bench binary (push) Has been skipped
build-prerelease / Package cortex RPM (push) Has been skipped
build-prerelease / Package helexa-bench RPM (push) Has been skipped
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Has been cancelled
This commit is contained in:
@@ -23,7 +23,7 @@ const ROOT = path.resolve(
|
||||
const RESOURCES_DIR = path.join(ROOT, "src", "i18n", "resources");
|
||||
|
||||
// Namespaces to validate.
|
||||
const NAMESPACES = ["common", "mission", "chat"];
|
||||
const NAMESPACES = ["common", "mission", "chat", "account"];
|
||||
|
||||
// Languages to validate should track SUPPORTED_LANGUAGES in src/i18n/languages.ts.
|
||||
// NOTE: This list is intentionally narrower than SUPPORTED_LANGUAGES and does not
|
||||
|
||||
@@ -1,26 +1,58 @@
|
||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||
import ThemeProvider from "./layout/ThemeProvider";
|
||||
import AuthProvider from "./auth/AuthProvider";
|
||||
import RequireAuth from "./auth/RequireAuth";
|
||||
import Header from "./components/Header";
|
||||
import Footer from "./components/Footer";
|
||||
import Mission from "./pages/Mission";
|
||||
import Chat from "./pages/Chat";
|
||||
import Login from "./pages/auth/Login";
|
||||
import Register from "./pages/auth/Register";
|
||||
import VerifyEmail from "./pages/auth/VerifyEmail";
|
||||
import RequestReset from "./pages/auth/RequestReset";
|
||||
import ResetPassword from "./pages/auth/ResetPassword";
|
||||
import Dashboard from "./pages/account/Dashboard";
|
||||
import ApiKeys from "./pages/account/ApiKeys";
|
||||
import "./App.css";
|
||||
|
||||
// Composition root: theme + router + layout shell. `/` is the chat
|
||||
// workspace (F3, anonymous for now); `/mission` (F2) is the EU-sovereignty
|
||||
// narrative; the auth/account routes (F4) land next.
|
||||
// Composition root: theme → router → auth → layout shell. `/` is the chat
|
||||
// workspace (F3); `/mission` the EU-sovereignty narrative (F2); the auth +
|
||||
// account routes (F4) follow, with /account guarded.
|
||||
export default function App() {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<BrowserRouter>
|
||||
<div className="d-flex flex-column min-vh-100">
|
||||
<Header />
|
||||
<Routes>
|
||||
<Route path="/" element={<Chat />} />
|
||||
<Route path="/mission" element={<Mission />} />
|
||||
</Routes>
|
||||
<Footer />
|
||||
</div>
|
||||
<AuthProvider>
|
||||
<div className="d-flex flex-column min-vh-100">
|
||||
<Header />
|
||||
<Routes>
|
||||
<Route path="/" element={<Chat />} />
|
||||
<Route path="/mission" element={<Mission />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
<Route path="/verify" element={<VerifyEmail />} />
|
||||
<Route path="/forgot" element={<RequestReset />} />
|
||||
<Route path="/reset" element={<ResetPassword />} />
|
||||
<Route
|
||||
path="/account"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<Dashboard />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/account/keys"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<ApiKeys />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
<Footer />
|
||||
</div>
|
||||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
213
helexa.ai/src/api/account.ts
Normal file
213
helexa.ai/src/api/account.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
// Account API client over helexa-upstream's /web/v1 (B4/B5). The browser
|
||||
// calls a same-origin `/api` prefix (vite-proxied in dev, nginx-routed in
|
||||
// prod). A MockAccountApi behind VITE_USE_MOCK_ACCOUNT_API lets the
|
||||
// dashboard be built/demoed before the upstream service is reachable.
|
||||
|
||||
import {
|
||||
ApiError,
|
||||
type AccountBalance,
|
||||
type ApiKeySummary,
|
||||
type CreatedKey,
|
||||
type Session,
|
||||
} from "./types";
|
||||
|
||||
export interface AccountApi {
|
||||
register(email: string, password: string, fingerprint?: string): Promise<void>;
|
||||
verify(token: string): Promise<void>;
|
||||
login(email: string, password: string): Promise<Session>;
|
||||
requestReset(email: string): Promise<void>;
|
||||
confirmReset(token: string, newPassword: string): Promise<void>;
|
||||
account(token: string): Promise<AccountBalance>;
|
||||
listKeys(token: string): Promise<ApiKeySummary[]>;
|
||||
createKey(
|
||||
token: string,
|
||||
label: string,
|
||||
limitKind: "percent" | "hardcap",
|
||||
limitValue: number,
|
||||
): Promise<CreatedKey>;
|
||||
archiveKey(token: string, id: string): Promise<void>;
|
||||
updateKeyLimit(
|
||||
token: string,
|
||||
id: string,
|
||||
limitKind: "percent" | "hardcap",
|
||||
limitValue: number,
|
||||
): Promise<void>;
|
||||
redeem(token: string, code: string): Promise<AccountBalance>;
|
||||
}
|
||||
|
||||
const BASE = (import.meta.env.VITE_ACCOUNT_BASE_URL || "/api").replace(/\/$/, "");
|
||||
|
||||
async function call<T>(
|
||||
path: string,
|
||||
init: RequestInit & { token?: string } = {},
|
||||
): Promise<T> {
|
||||
const headers: Record<string, string> = { "content-type": "application/json" };
|
||||
if (init.token) headers.authorization = `Bearer ${init.token}`;
|
||||
let resp: Response;
|
||||
try {
|
||||
resp = await fetch(`${BASE}${path}`, { ...init, headers });
|
||||
} catch {
|
||||
throw new ApiError(0, "network_error", "Could not reach the account service.");
|
||||
}
|
||||
if (resp.status === 204) return undefined as T;
|
||||
let body: unknown = null;
|
||||
try {
|
||||
body = await resp.json();
|
||||
} catch {
|
||||
/* empty body */
|
||||
}
|
||||
if (!resp.ok) {
|
||||
const err = (body as { error?: { code?: string; message?: string } })?.error;
|
||||
throw new ApiError(resp.status, err?.code ?? "error", err?.message ?? "Request failed.");
|
||||
}
|
||||
return body as T;
|
||||
}
|
||||
|
||||
class RealAccountApi implements AccountApi {
|
||||
async register(email: string, password: string, fingerprint?: string) {
|
||||
await call("/register", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ email, password, fingerprint }),
|
||||
});
|
||||
}
|
||||
async verify(token: string) {
|
||||
await call("/verify", { method: "POST", body: JSON.stringify({ token }) });
|
||||
}
|
||||
login(email: string, password: string) {
|
||||
return call<Session>("/login", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ email, password }),
|
||||
});
|
||||
}
|
||||
async requestReset(email: string) {
|
||||
await call("/password-reset/request", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ email }),
|
||||
});
|
||||
}
|
||||
async confirmReset(token: string, newPassword: string) {
|
||||
await call("/password-reset/confirm", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ token, new_password: newPassword }),
|
||||
});
|
||||
}
|
||||
account(token: string) {
|
||||
return call<AccountBalance>("/account", { token });
|
||||
}
|
||||
listKeys(token: string) {
|
||||
return call<{ keys: ApiKeySummary[] }>("/keys", { token }).then((r) => r.keys);
|
||||
}
|
||||
createKey(token: string, label: string, limit_kind: "percent" | "hardcap", limit_value: number) {
|
||||
return call<CreatedKey>("/keys", {
|
||||
method: "POST",
|
||||
token,
|
||||
body: JSON.stringify({ label, limit_kind, limit_value }),
|
||||
});
|
||||
}
|
||||
async archiveKey(token: string, id: string) {
|
||||
await call(`/keys/${id}/archive`, { method: "POST", token, body: "{}" });
|
||||
}
|
||||
async updateKeyLimit(
|
||||
token: string,
|
||||
id: string,
|
||||
limit_kind: "percent" | "hardcap",
|
||||
limit_value: number,
|
||||
) {
|
||||
await call(`/keys/${id}/limit`, {
|
||||
method: "PATCH",
|
||||
token,
|
||||
body: JSON.stringify({ limit_kind, limit_value }),
|
||||
});
|
||||
}
|
||||
redeem(token: string, code: string) {
|
||||
return call<AccountBalance>("/redeem", {
|
||||
method: "POST",
|
||||
token,
|
||||
body: JSON.stringify({ code }),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mock (VITE_USE_MOCK_ACCOUNT_API) ────────────────────────────────
|
||||
// Minimal in-memory account so the dashboard is fully developable offline.
|
||||
|
||||
class MockAccountApi implements AccountApi {
|
||||
private total = 1_000_000;
|
||||
private spent = 0;
|
||||
private reserved = 0;
|
||||
private keys: ApiKeySummary[] = [];
|
||||
private seq = 1;
|
||||
|
||||
async register() {}
|
||||
async verify() {}
|
||||
async login(): Promise<Session> {
|
||||
return { token: "mock-token", expires_in: 604800 };
|
||||
}
|
||||
async requestReset() {}
|
||||
async confirmReset() {}
|
||||
async account(): Promise<AccountBalance> {
|
||||
return {
|
||||
account_id: "mock-account",
|
||||
allocation_total: this.total,
|
||||
allocation_spent: this.spent,
|
||||
allocation_reserved: this.reserved,
|
||||
};
|
||||
}
|
||||
async listKeys(): Promise<ApiKeySummary[]> {
|
||||
return [...this.keys];
|
||||
}
|
||||
async createKey(
|
||||
_t: string,
|
||||
label: string,
|
||||
limit_kind: "percent" | "hardcap",
|
||||
limit_value: number,
|
||||
): Promise<CreatedKey> {
|
||||
const id = `mock-${this.seq++}`;
|
||||
const prefix = `sk-helexa-mock${this.seq}`;
|
||||
this.keys.push({
|
||||
id,
|
||||
prefix,
|
||||
label,
|
||||
status: "active",
|
||||
limit_kind,
|
||||
limit_value,
|
||||
spent: 0,
|
||||
reserved: 0,
|
||||
created_at: new Date().toISOString(),
|
||||
});
|
||||
return { id, key: `${prefix}-RAWSECRETSHOWNONCE`, prefix, limit_kind, limit_value };
|
||||
}
|
||||
async archiveKey(_t: string, id: string) {
|
||||
const k = this.keys.find((x) => x.id === id);
|
||||
if (k) k.status = "archived";
|
||||
}
|
||||
async updateKeyLimit(
|
||||
_t: string,
|
||||
id: string,
|
||||
limit_kind: "percent" | "hardcap",
|
||||
limit_value: number,
|
||||
) {
|
||||
const k = this.keys.find((x) => x.id === id);
|
||||
if (k) {
|
||||
k.limit_kind = limit_kind;
|
||||
k.limit_value = limit_value;
|
||||
}
|
||||
}
|
||||
async redeem(_t: string, code: string): Promise<AccountBalance> {
|
||||
if (!code.startsWith("helexa-topup-")) {
|
||||
throw new ApiError(400, "bad_request", "invalid or already-redeemed code");
|
||||
}
|
||||
this.total += 500_000;
|
||||
return this.account();
|
||||
}
|
||||
}
|
||||
|
||||
let instance: AccountApi | null = null;
|
||||
export function accountApi(): AccountApi {
|
||||
if (!instance) {
|
||||
instance = import.meta.env.VITE_USE_MOCK_ACCOUNT_API
|
||||
? new MockAccountApi()
|
||||
: new RealAccountApi();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
45
helexa.ai/src/api/types.ts
Normal file
45
helexa.ai/src/api/types.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
// Wire types for the helexa-upstream /web/v1 account API (B4/B5).
|
||||
|
||||
export interface ApiKeySummary {
|
||||
id: string;
|
||||
prefix: string;
|
||||
label: string;
|
||||
status: "active" | "archived";
|
||||
limit_kind: "percent" | "hardcap";
|
||||
limit_value: number;
|
||||
spent: number;
|
||||
reserved: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface CreatedKey {
|
||||
id: string;
|
||||
/** Raw secret — shown exactly once at creation. */
|
||||
key: string;
|
||||
prefix: string;
|
||||
limit_kind: "percent" | "hardcap";
|
||||
limit_value: number;
|
||||
}
|
||||
|
||||
export interface AccountBalance {
|
||||
account_id: string;
|
||||
allocation_total: number;
|
||||
allocation_spent: number;
|
||||
allocation_reserved: number;
|
||||
}
|
||||
|
||||
export interface Session {
|
||||
token: string;
|
||||
expires_in: number;
|
||||
}
|
||||
|
||||
/** Typed error carrying the backend's machine-readable code. */
|
||||
export class ApiError extends Error {
|
||||
code: string;
|
||||
status: number;
|
||||
constructor(status: number, code: string, message: string) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
53
helexa.ai/src/auth/AuthProvider.tsx
Normal file
53
helexa.ai/src/auth/AuthProvider.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useState, type ReactNode } from "react";
|
||||
import { accountApi } from "../api/account";
|
||||
import { claimAnonymousData } from "../data/repositories";
|
||||
import { getFingerprint } from "../lib/fingerprint";
|
||||
import { AuthContext } from "./context";
|
||||
|
||||
const TOKEN_KEY = "helexa.token";
|
||||
const EMAIL_KEY = "helexa.email";
|
||||
|
||||
export default function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [token, setToken] = useState<string | null>(() =>
|
||||
localStorage.getItem(TOKEN_KEY),
|
||||
);
|
||||
const [email, setEmail] = useState<string | null>(() =>
|
||||
localStorage.getItem(EMAIL_KEY),
|
||||
);
|
||||
|
||||
async function login(em: string, password: string): Promise<void> {
|
||||
const api = accountApi();
|
||||
const session = await api.login(em, password);
|
||||
localStorage.setItem(TOKEN_KEY, session.token);
|
||||
localStorage.setItem(EMAIL_KEY, em);
|
||||
setToken(session.token);
|
||||
setEmail(em);
|
||||
// Claim anonymous local history into the account (stays client-side).
|
||||
try {
|
||||
const acct = await api.account(session.token);
|
||||
await claimAnonymousData(acct.account_id);
|
||||
} catch {
|
||||
/* non-fatal */
|
||||
}
|
||||
}
|
||||
|
||||
async function register(em: string, password: string): Promise<void> {
|
||||
const fingerprint = await getFingerprint();
|
||||
await accountApi().register(em, password, fingerprint);
|
||||
}
|
||||
|
||||
function logout(): void {
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
localStorage.removeItem(EMAIL_KEY);
|
||||
setToken(null);
|
||||
setEmail(null);
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
value={{ token, email, status: token ? "authed" : "anon", login, register, logout }}
|
||||
>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
14
helexa.ai/src/auth/RequireAuth.tsx
Normal file
14
helexa.ai/src/auth/RequireAuth.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { type ReactNode } from "react";
|
||||
import { Navigate, useLocation } from "react-router-dom";
|
||||
import { useAuth } from "./context";
|
||||
|
||||
/** Route guard: redirect unauthenticated users to /login?next=…. */
|
||||
export default function RequireAuth({ children }: { children: ReactNode }) {
|
||||
const { status } = useAuth();
|
||||
const location = useLocation();
|
||||
if (status !== "authed") {
|
||||
const next = encodeURIComponent(location.pathname + location.search);
|
||||
return <Navigate to={`/login?next=${next}`} replace />;
|
||||
}
|
||||
return <>{children}</>;
|
||||
}
|
||||
23
helexa.ai/src/auth/context.ts
Normal file
23
helexa.ai/src/auth/context.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
export interface AuthContextValue {
|
||||
token: string | null;
|
||||
email: string | null;
|
||||
status: "anon" | "authed";
|
||||
login: (email: string, password: string) => Promise<void>;
|
||||
register: (email: string, password: string) => Promise<void>;
|
||||
logout: () => void;
|
||||
}
|
||||
|
||||
export const AuthContext = createContext<AuthContextValue>({
|
||||
token: null,
|
||||
email: null,
|
||||
status: "anon",
|
||||
login: async () => {},
|
||||
register: async () => {},
|
||||
logout: () => {},
|
||||
});
|
||||
|
||||
export function useAuth(): AuthContextValue {
|
||||
return useContext(AuthContext);
|
||||
}
|
||||
@@ -6,11 +6,12 @@ import { useTheme } from "../layout/theme";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AUTONYM_MAP, type LanguageCode, isRtlLanguage } from "../i18n/languages";
|
||||
import { getLanguageOptionsByUsage } from "../i18n/translation-priority";
|
||||
import { useAuth } from "../auth/context";
|
||||
|
||||
/**
|
||||
* Top navigation: brand, primary routes (chat at `/`, `/mission`), an
|
||||
* auth-aware cluster (stubbed until F4 wires sessions), the theme toggle,
|
||||
* and the language selector.
|
||||
* auth-aware cluster (Account/Sign out when signed in, else Sign in/up),
|
||||
* the theme toggle, and the language selector.
|
||||
*
|
||||
* The language picker is ordered by **estimated usage**
|
||||
* (getLanguageOptionsByUsage), not alphabetically — a deliberate choice that
|
||||
@@ -21,6 +22,7 @@ import { getLanguageOptionsByUsage } from "../i18n/translation-priority";
|
||||
const Header: React.FC = () => {
|
||||
const { theme, toggleTheme } = useTheme();
|
||||
const { t, i18n } = useTranslation("common");
|
||||
const { status, logout } = useAuth();
|
||||
|
||||
const currentLanguage: LanguageCode = (i18n.language.split("-")[0] ||
|
||||
"en") as LanguageCode;
|
||||
@@ -75,13 +77,31 @@ const Header: React.FC = () => {
|
||||
</Nav>
|
||||
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
{/* Auth cluster — plain links until F4 wires session state. */}
|
||||
<NavLink to="/login" className="nav-link">
|
||||
{t("nav.login")}
|
||||
</NavLink>
|
||||
<NavLink to="/register" className="nav-link">
|
||||
{t("nav.register")}
|
||||
</NavLink>
|
||||
{/* Auth-aware cluster. */}
|
||||
{status === "authed" ? (
|
||||
<>
|
||||
<NavLink to="/account" className="nav-link">
|
||||
{t("nav.account")}
|
||||
</NavLink>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline-secondary"
|
||||
onClick={logout}
|
||||
className="me-1"
|
||||
>
|
||||
{t("nav.logout")}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<NavLink to="/login" className="nav-link">
|
||||
{t("nav.login")}
|
||||
</NavLink>
|
||||
<NavLink to="/register" className="nav-link">
|
||||
{t("nav.register")}
|
||||
</NavLink>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Button
|
||||
size="sm"
|
||||
|
||||
@@ -13,131 +13,163 @@ import ruCommon from "./resources/ru/common.json";
|
||||
import enMission from "./resources/en/mission.json";
|
||||
import ruMission from "./resources/ru/mission.json";
|
||||
import enChat from "./resources/en/chat.json";
|
||||
import enAccount from "./resources/en/account.json";
|
||||
import ruChat from "./resources/ru/chat.json";
|
||||
import ruAccount from "./resources/ru/account.json";
|
||||
|
||||
// Scandinavian & Nordic languages
|
||||
import daCommon from "./resources/da/common.json";
|
||||
import daMission from "./resources/da/mission.json";
|
||||
import daChat from "./resources/da/chat.json";
|
||||
import daAccount from "./resources/da/account.json";
|
||||
|
||||
import fiCommon from "./resources/fi/common.json";
|
||||
import fiMission from "./resources/fi/mission.json";
|
||||
import fiChat from "./resources/fi/chat.json";
|
||||
import fiAccount from "./resources/fi/account.json";
|
||||
|
||||
import noCommon from "./resources/no/common.json";
|
||||
import noMission from "./resources/no/mission.json";
|
||||
import noChat from "./resources/no/chat.json";
|
||||
import noAccount from "./resources/no/account.json";
|
||||
|
||||
import svCommon from "./resources/sv/common.json";
|
||||
import svMission from "./resources/sv/mission.json";
|
||||
import svChat from "./resources/sv/chat.json";
|
||||
import svAccount from "./resources/sv/account.json";
|
||||
|
||||
import bgCommon from "./resources/bg/common.json";
|
||||
import bgMission from "./resources/bg/mission.json";
|
||||
import bgChat from "./resources/bg/chat.json";
|
||||
import bgAccount from "./resources/bg/account.json";
|
||||
|
||||
import etCommon from "./resources/et/common.json";
|
||||
import etMission from "./resources/et/mission.json";
|
||||
import etChat from "./resources/et/chat.json";
|
||||
import etAccount from "./resources/et/account.json";
|
||||
|
||||
// African & MENA languages
|
||||
import swCommon from "./resources/sw/common.json";
|
||||
import swMission from "./resources/sw/mission.json";
|
||||
import swChat from "./resources/sw/chat.json";
|
||||
import swAccount from "./resources/sw/account.json";
|
||||
|
||||
import arCommon from "./resources/ar/common.json";
|
||||
import arMission from "./resources/ar/mission.json";
|
||||
import arChat from "./resources/ar/chat.json";
|
||||
import arAccount from "./resources/ar/account.json";
|
||||
|
||||
import faCommon from "./resources/fa/common.json";
|
||||
import faMission from "./resources/fa/mission.json";
|
||||
import faChat from "./resources/fa/chat.json";
|
||||
import faAccount from "./resources/fa/account.json";
|
||||
|
||||
import haCommon from "./resources/ha/common.json";
|
||||
import haMission from "./resources/ha/mission.json";
|
||||
import haChat from "./resources/ha/chat.json";
|
||||
import haAccount from "./resources/ha/account.json";
|
||||
|
||||
import amCommon from "./resources/am/common.json";
|
||||
import amMission from "./resources/am/mission.json";
|
||||
import amChat from "./resources/am/chat.json";
|
||||
import amAccount from "./resources/am/account.json";
|
||||
|
||||
import yoCommon from "./resources/yo/common.json";
|
||||
import yoMission from "./resources/yo/mission.json";
|
||||
import yoChat from "./resources/yo/chat.json";
|
||||
import yoAccount from "./resources/yo/account.json";
|
||||
|
||||
import zuCommon from "./resources/zu/common.json";
|
||||
import zuMission from "./resources/zu/mission.json";
|
||||
import zuChat from "./resources/zu/chat.json";
|
||||
import zuAccount from "./resources/zu/account.json";
|
||||
|
||||
// Darija (Moroccan Arabic)
|
||||
import maCommon from "./resources/ma/common.json";
|
||||
import maMission from "./resources/ma/mission.json";
|
||||
import maChat from "./resources/ma/chat.json";
|
||||
import maAccount from "./resources/ma/account.json";
|
||||
|
||||
// European / other languages
|
||||
import esCommon from "./resources/es/common.json";
|
||||
import esMission from "./resources/es/mission.json";
|
||||
import esChat from "./resources/es/chat.json";
|
||||
import esAccount from "./resources/es/account.json";
|
||||
|
||||
import frCommon from "./resources/fr/common.json";
|
||||
import frMission from "./resources/fr/mission.json";
|
||||
import frChat from "./resources/fr/chat.json";
|
||||
import frAccount from "./resources/fr/account.json";
|
||||
|
||||
import deCommon from "./resources/de/common.json";
|
||||
import deMission from "./resources/de/mission.json";
|
||||
import deChat from "./resources/de/chat.json";
|
||||
import deAccount from "./resources/de/account.json";
|
||||
|
||||
import elCommon from "./resources/el/common.json";
|
||||
import elMission from "./resources/el/mission.json";
|
||||
import elChat from "./resources/el/chat.json";
|
||||
import elAccount from "./resources/el/account.json";
|
||||
|
||||
import itCommon from "./resources/it/common.json";
|
||||
import itMission from "./resources/it/mission.json";
|
||||
import itChat from "./resources/it/chat.json";
|
||||
import itAccount from "./resources/it/account.json";
|
||||
|
||||
import heCommon from "./resources/he/common.json";
|
||||
import heMission from "./resources/he/mission.json";
|
||||
import heChat from "./resources/he/chat.json";
|
||||
import heAccount from "./resources/he/account.json";
|
||||
|
||||
import ptCommon from "./resources/pt/common.json";
|
||||
import ptMission from "./resources/pt/mission.json";
|
||||
import ptChat from "./resources/pt/chat.json";
|
||||
import ptAccount from "./resources/pt/account.json";
|
||||
|
||||
import roCommon from "./resources/ro/common.json";
|
||||
import roMission from "./resources/ro/mission.json";
|
||||
import roChat from "./resources/ro/chat.json";
|
||||
import roAccount from "./resources/ro/account.json";
|
||||
|
||||
import kaCommon from "./resources/ka/common.json";
|
||||
import kaMission from "./resources/ka/mission.json";
|
||||
import kaChat from "./resources/ka/chat.json";
|
||||
import kaAccount from "./resources/ka/account.json";
|
||||
|
||||
import trCommon from "./resources/tr/common.json";
|
||||
import trMission from "./resources/tr/mission.json";
|
||||
import trChat from "./resources/tr/chat.json";
|
||||
import trAccount from "./resources/tr/account.json";
|
||||
|
||||
import plCommon from "./resources/pl/common.json";
|
||||
import plMission from "./resources/pl/mission.json";
|
||||
import plChat from "./resources/pl/chat.json";
|
||||
import plAccount from "./resources/pl/account.json";
|
||||
|
||||
import ukCommon from "./resources/uk/common.json";
|
||||
import ukMission from "./resources/uk/mission.json";
|
||||
import ukChat from "./resources/uk/chat.json";
|
||||
import ukAccount from "./resources/uk/account.json";
|
||||
|
||||
import nlCommon from "./resources/nl/common.json";
|
||||
import nlMission from "./resources/nl/mission.json";
|
||||
import nlChat from "./resources/nl/chat.json";
|
||||
import nlAccount from "./resources/nl/account.json";
|
||||
|
||||
import srCommon from "./resources/sr/common.json";
|
||||
import srMission from "./resources/sr/mission.json";
|
||||
import srChat from "./resources/sr/chat.json";
|
||||
import srAccount from "./resources/sr/account.json";
|
||||
|
||||
import kkCommon from "./resources/kk/common.json";
|
||||
import kkMission from "./resources/kk/mission.json";
|
||||
import kkChat from "./resources/kk/chat.json";
|
||||
import kkAccount from "./resources/kk/account.json";
|
||||
|
||||
import uzCommon from "./resources/uz/common.json";
|
||||
import uzMission from "./resources/uz/mission.json";
|
||||
import uzChat from "./resources/uz/chat.json";
|
||||
import uzAccount from "./resources/uz/account.json";
|
||||
|
||||
/**
|
||||
* Application translation resources, split by language and namespace.
|
||||
@@ -151,41 +183,49 @@ const resources: Resource = {
|
||||
common: enCommon,
|
||||
mission: enMission,
|
||||
chat: enChat,
|
||||
account: enAccount,
|
||||
},
|
||||
ru: {
|
||||
common: ruCommon,
|
||||
mission: ruMission,
|
||||
chat: ruChat,
|
||||
account: ruAccount,
|
||||
},
|
||||
bg: {
|
||||
common: bgCommon,
|
||||
mission: bgMission,
|
||||
chat: bgChat,
|
||||
account: bgAccount,
|
||||
},
|
||||
da: {
|
||||
common: daCommon,
|
||||
mission: daMission,
|
||||
chat: daChat,
|
||||
account: daAccount,
|
||||
},
|
||||
et: {
|
||||
common: etCommon,
|
||||
mission: etMission,
|
||||
chat: etChat,
|
||||
account: etAccount,
|
||||
},
|
||||
fi: {
|
||||
common: fiCommon,
|
||||
mission: fiMission,
|
||||
chat: fiChat,
|
||||
account: fiAccount,
|
||||
},
|
||||
kk: {
|
||||
common: kkCommon,
|
||||
mission: kkMission,
|
||||
chat: kkChat,
|
||||
account: kkAccount,
|
||||
},
|
||||
uz: {
|
||||
common: uzCommon,
|
||||
mission: uzMission,
|
||||
chat: uzChat,
|
||||
account: uzAccount,
|
||||
},
|
||||
|
||||
// African & MENA languages (LTR unless marked RTL via isRtlLanguage)
|
||||
@@ -193,41 +233,49 @@ const resources: Resource = {
|
||||
common: swCommon,
|
||||
mission: swMission,
|
||||
chat: swChat,
|
||||
account: swAccount,
|
||||
},
|
||||
ar: {
|
||||
common: arCommon,
|
||||
mission: arMission,
|
||||
chat: arChat,
|
||||
account: arAccount,
|
||||
},
|
||||
fa: {
|
||||
common: faCommon,
|
||||
mission: faMission,
|
||||
chat: faChat,
|
||||
account: faAccount,
|
||||
},
|
||||
ha: {
|
||||
common: haCommon,
|
||||
mission: haMission,
|
||||
chat: haChat,
|
||||
account: haAccount,
|
||||
},
|
||||
am: {
|
||||
common: amCommon,
|
||||
mission: amMission,
|
||||
chat: amChat,
|
||||
account: amAccount,
|
||||
},
|
||||
yo: {
|
||||
common: yoCommon,
|
||||
mission: yoMission,
|
||||
chat: yoChat,
|
||||
account: yoAccount,
|
||||
},
|
||||
zu: {
|
||||
common: zuCommon,
|
||||
mission: zuMission,
|
||||
chat: zuChat,
|
||||
account: zuAccount,
|
||||
},
|
||||
ma: {
|
||||
common: maCommon,
|
||||
mission: maMission,
|
||||
chat: maChat,
|
||||
account: maAccount,
|
||||
},
|
||||
|
||||
// European & other languages
|
||||
@@ -235,81 +283,97 @@ const resources: Resource = {
|
||||
common: esCommon,
|
||||
mission: esMission,
|
||||
chat: esChat,
|
||||
account: esAccount,
|
||||
},
|
||||
fr: {
|
||||
common: frCommon,
|
||||
mission: frMission,
|
||||
chat: frChat,
|
||||
account: frAccount,
|
||||
},
|
||||
de: {
|
||||
common: deCommon,
|
||||
mission: deMission,
|
||||
chat: deChat,
|
||||
account: deAccount,
|
||||
},
|
||||
el: {
|
||||
common: elCommon,
|
||||
mission: elMission,
|
||||
chat: elChat,
|
||||
account: elAccount,
|
||||
},
|
||||
it: {
|
||||
common: itCommon,
|
||||
mission: itMission,
|
||||
chat: itChat,
|
||||
account: itAccount,
|
||||
},
|
||||
he: {
|
||||
common: heCommon,
|
||||
mission: heMission,
|
||||
chat: heChat,
|
||||
account: heAccount,
|
||||
},
|
||||
pt: {
|
||||
common: ptCommon,
|
||||
mission: ptMission,
|
||||
chat: ptChat,
|
||||
account: ptAccount,
|
||||
},
|
||||
ro: {
|
||||
common: roCommon,
|
||||
mission: roMission,
|
||||
chat: roChat,
|
||||
account: roAccount,
|
||||
},
|
||||
ka: {
|
||||
common: kaCommon,
|
||||
mission: kaMission,
|
||||
chat: kaChat,
|
||||
account: kaAccount,
|
||||
},
|
||||
tr: {
|
||||
common: trCommon,
|
||||
mission: trMission,
|
||||
chat: trChat,
|
||||
account: trAccount,
|
||||
},
|
||||
pl: {
|
||||
common: plCommon,
|
||||
mission: plMission,
|
||||
chat: plChat,
|
||||
account: plAccount,
|
||||
},
|
||||
uk: {
|
||||
common: ukCommon,
|
||||
mission: ukMission,
|
||||
chat: ukChat,
|
||||
account: ukAccount,
|
||||
},
|
||||
nl: {
|
||||
common: nlCommon,
|
||||
mission: nlMission,
|
||||
chat: nlChat,
|
||||
account: nlAccount,
|
||||
},
|
||||
sr: {
|
||||
common: srCommon,
|
||||
mission: srMission,
|
||||
chat: srChat,
|
||||
account: srAccount,
|
||||
},
|
||||
no: {
|
||||
common: noCommon,
|
||||
mission: noMission,
|
||||
chat: noChat,
|
||||
account: noAccount,
|
||||
},
|
||||
sv: {
|
||||
common: svCommon,
|
||||
mission: svMission,
|
||||
chat: svChat,
|
||||
account: svAccount,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -335,7 +399,7 @@ i18n.use(initReactI18next).init({
|
||||
lng: browserLang,
|
||||
fallbackLng: "en",
|
||||
supportedLngs: SUPPORTED_LANGUAGES,
|
||||
ns: ["common", "mission", "chat"],
|
||||
ns: ["common", "mission", "chat", "account"],
|
||||
defaultNS: "common",
|
||||
// Because we control the keys and interpolate only simple values.
|
||||
interpolation: {
|
||||
|
||||
69
helexa.ai/src/i18n/resources/am/account.json
Normal file
69
helexa.ai/src/i18n/resources/am/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/ar/account.json
Normal file
69
helexa.ai/src/i18n/resources/ar/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/bg/account.json
Normal file
69
helexa.ai/src/i18n/resources/bg/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/da/account.json
Normal file
69
helexa.ai/src/i18n/resources/da/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/de/account.json
Normal file
69
helexa.ai/src/i18n/resources/de/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/el/account.json
Normal file
69
helexa.ai/src/i18n/resources/el/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/en/account.json
Normal file
69
helexa.ai/src/i18n/resources/en/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/es/account.json
Normal file
69
helexa.ai/src/i18n/resources/es/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/et/account.json
Normal file
69
helexa.ai/src/i18n/resources/et/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/fa/account.json
Normal file
69
helexa.ai/src/i18n/resources/fa/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/fi/account.json
Normal file
69
helexa.ai/src/i18n/resources/fi/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/fr/account.json
Normal file
69
helexa.ai/src/i18n/resources/fr/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/ha/account.json
Normal file
69
helexa.ai/src/i18n/resources/ha/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/he/account.json
Normal file
69
helexa.ai/src/i18n/resources/he/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/it/account.json
Normal file
69
helexa.ai/src/i18n/resources/it/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/ka/account.json
Normal file
69
helexa.ai/src/i18n/resources/ka/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/kk/account.json
Normal file
69
helexa.ai/src/i18n/resources/kk/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/ma/account.json
Normal file
69
helexa.ai/src/i18n/resources/ma/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/nl/account.json
Normal file
69
helexa.ai/src/i18n/resources/nl/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/no/account.json
Normal file
69
helexa.ai/src/i18n/resources/no/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/pl/account.json
Normal file
69
helexa.ai/src/i18n/resources/pl/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/pt/account.json
Normal file
69
helexa.ai/src/i18n/resources/pt/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/ro/account.json
Normal file
69
helexa.ai/src/i18n/resources/ro/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/ru/account.json
Normal file
69
helexa.ai/src/i18n/resources/ru/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/sr/account.json
Normal file
69
helexa.ai/src/i18n/resources/sr/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/sv/account.json
Normal file
69
helexa.ai/src/i18n/resources/sv/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/sw/account.json
Normal file
69
helexa.ai/src/i18n/resources/sw/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/tr/account.json
Normal file
69
helexa.ai/src/i18n/resources/tr/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/uk/account.json
Normal file
69
helexa.ai/src/i18n/resources/uk/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/uz/account.json
Normal file
69
helexa.ai/src/i18n/resources/uz/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/yo/account.json
Normal file
69
helexa.ai/src/i18n/resources/yo/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
69
helexa.ai/src/i18n/resources/zu/account.json
Normal file
69
helexa.ai/src/i18n/resources/zu/account.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"login": {
|
||||
"title": "Sign in",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign in",
|
||||
"noAccount": "No account? Sign up"
|
||||
},
|
||||
"register": {
|
||||
"title": "Create your account",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"submit": "Sign up",
|
||||
"haveAccount": "Already have an account? Sign in",
|
||||
"checkEmail": "Almost there — check your email to verify your account."
|
||||
},
|
||||
"verify": {
|
||||
"verifying": "Verifying…",
|
||||
"ok": "Email verified. You can now sign in.",
|
||||
"failed": "This verification link is invalid or has expired.",
|
||||
"toLogin": "Go to sign in"
|
||||
},
|
||||
"reset": {
|
||||
"requestTitle": "Reset your password",
|
||||
"email": "Email",
|
||||
"requestSubmit": "Send reset link",
|
||||
"requestDone": "If that email has an account, a reset link is on its way.",
|
||||
"confirmTitle": "Choose a new password",
|
||||
"newPassword": "New password",
|
||||
"confirmSubmit": "Set password",
|
||||
"ok": "Password updated. You can now sign in."
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Account",
|
||||
"balance": "Allocation",
|
||||
"total": "Total",
|
||||
"spent": "Spent",
|
||||
"reserved": "Reserved",
|
||||
"remaining": "Remaining",
|
||||
"manageKeys": "Manage API keys",
|
||||
"redeemTitle": "Redeem a top-up code",
|
||||
"redeemPlaceholder": "helexa-topup-…",
|
||||
"redeem": "Redeem",
|
||||
"redeemed": "Code redeemed.",
|
||||
"logout": "Sign out"
|
||||
},
|
||||
"keys": {
|
||||
"title": "API keys",
|
||||
"create": "Create key",
|
||||
"label": "Label",
|
||||
"limitKind": "Limit",
|
||||
"percent": "% of allocation",
|
||||
"hardcap": "Hard cap (tokens)",
|
||||
"value": "Value",
|
||||
"none": "No keys yet.",
|
||||
"createdTitle": "Your new API key",
|
||||
"createdWarn": "Copy it now — you won't see it again.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"archive": "Archive",
|
||||
"save": "Save",
|
||||
"status": "Status",
|
||||
"usage": "Used"
|
||||
},
|
||||
"error": {
|
||||
"generic": "Something went wrong.",
|
||||
"unauthorized": "Please sign in again."
|
||||
}
|
||||
}
|
||||
159
helexa.ai/src/pages/account/ApiKeys.tsx
Normal file
159
helexa.ai/src/pages/account/ApiKeys.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Alert, Badge, Button, Container, Form, Modal, Table } from "react-bootstrap";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAuth } from "../../auth/context";
|
||||
import { accountApi } from "../../api/account";
|
||||
import { ApiError, type ApiKeySummary, type CreatedKey } from "../../api/types";
|
||||
|
||||
type LimitKind = "percent" | "hardcap";
|
||||
|
||||
export default function ApiKeys() {
|
||||
const { t } = useTranslation("account");
|
||||
const { token, logout } = useAuth();
|
||||
const [keys, setKeys] = useState<ApiKeySummary[]>([]);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Create-key form state.
|
||||
const [label, setLabel] = useState("");
|
||||
const [limitKind, setLimitKind] = useState<LimitKind>("percent");
|
||||
const [limitValue, setLimitValue] = useState(100);
|
||||
const [created, setCreated] = useState<CreatedKey | null>(null);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
if (!token) return;
|
||||
try {
|
||||
setKeys(await accountApi().listKeys(token));
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError && err.status === 401) logout();
|
||||
else setError(t("error.generic"));
|
||||
}
|
||||
}, [token, logout, t]);
|
||||
|
||||
useEffect(() => {
|
||||
// load() is async; setState happens after await, not synchronously.
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
void load();
|
||||
}, [load]);
|
||||
|
||||
async function create(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
if (!token) return;
|
||||
setError(null);
|
||||
try {
|
||||
const key = await accountApi().createKey(token, label, limitKind, limitValue);
|
||||
setCreated(key);
|
||||
setLabel("");
|
||||
await load();
|
||||
} catch (err) {
|
||||
setError(err instanceof ApiError ? err.message : t("error.generic"));
|
||||
}
|
||||
}
|
||||
|
||||
async function archive(id: string) {
|
||||
if (!token) return;
|
||||
await accountApi().archiveKey(token, id);
|
||||
await load();
|
||||
}
|
||||
|
||||
return (
|
||||
<Container className="py-5 flex-grow-1" style={{ maxWidth: 860 }}>
|
||||
<h1 className="h3 mb-4">{t("keys.title")}</h1>
|
||||
{error && <Alert variant="warning">{error}</Alert>}
|
||||
|
||||
<Form onSubmit={create} className="surface-elevated p-3 rounded-3 mb-4">
|
||||
<div className="row g-2 align-items-end">
|
||||
<div className="col">
|
||||
<Form.Label className="small">{t("keys.label")}</Form.Label>
|
||||
<Form.Control value={label} onChange={(e) => setLabel(e.target.value)} />
|
||||
</div>
|
||||
<div className="col">
|
||||
<Form.Label className="small">{t("keys.limitKind")}</Form.Label>
|
||||
<Form.Select
|
||||
value={limitKind}
|
||||
onChange={(e) => setLimitKind(e.target.value as LimitKind)}
|
||||
>
|
||||
<option value="percent">{t("keys.percent")}</option>
|
||||
<option value="hardcap">{t("keys.hardcap")}</option>
|
||||
</Form.Select>
|
||||
</div>
|
||||
<div className="col">
|
||||
<Form.Label className="small">{t("keys.value")}</Form.Label>
|
||||
<Form.Control
|
||||
type="number"
|
||||
min={0}
|
||||
value={limitValue}
|
||||
onChange={(e) => setLimitValue(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<Button type="submit">{t("keys.create")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
{keys.length === 0 ? (
|
||||
<p className="text-muted">{t("keys.none")}</p>
|
||||
) : (
|
||||
<Table responsive hover>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t("keys.label")}</th>
|
||||
<th>Prefix</th>
|
||||
<th>{t("keys.limitKind")}</th>
|
||||
<th>{t("keys.usage")}</th>
|
||||
<th>{t("keys.status")}</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{keys.map((k) => (
|
||||
<tr key={k.id}>
|
||||
<td>{k.label || "—"}</td>
|
||||
<td>
|
||||
<code>{k.prefix}…</code>
|
||||
</td>
|
||||
<td>
|
||||
{k.limit_kind === "percent" ? `${k.limit_value}%` : k.limit_value.toLocaleString()}
|
||||
</td>
|
||||
<td>{k.spent.toLocaleString()}</td>
|
||||
<td>
|
||||
<Badge bg={k.status === "active" ? "success" : "secondary"}>{k.status}</Badge>
|
||||
</td>
|
||||
<td className="text-end">
|
||||
{k.status === "active" && (
|
||||
<Button size="sm" variant="outline-danger" onClick={() => void archive(k.id)}>
|
||||
{t("keys.archive")}
|
||||
</Button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
)}
|
||||
|
||||
{/* The raw key is shown exactly once. */}
|
||||
<Modal show={!!created} onHide={() => setCreated(null)} centered>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title className="h6">{t("keys.createdTitle")}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Alert variant="warning" className="py-2">{t("keys.createdWarn")}</Alert>
|
||||
<div className="d-flex gap-2">
|
||||
<Form.Control readOnly value={created?.key ?? ""} />
|
||||
<Button
|
||||
variant="outline-secondary"
|
||||
onClick={() => {
|
||||
if (created) void navigator.clipboard.writeText(created.key);
|
||||
setCopied(true);
|
||||
}}
|
||||
>
|
||||
{copied ? t("keys.copied") : t("keys.copy")}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
104
helexa.ai/src/pages/account/Dashboard.tsx
Normal file
104
helexa.ai/src/pages/account/Dashboard.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Alert, Button, Card, Container, Form, ProgressBar } from "react-bootstrap";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAuth } from "../../auth/context";
|
||||
import { accountApi } from "../../api/account";
|
||||
import { ApiError, type AccountBalance } from "../../api/types";
|
||||
|
||||
export default function Dashboard() {
|
||||
const { t } = useTranslation("account");
|
||||
const { token, logout } = useAuth();
|
||||
const [balance, setBalance] = useState<AccountBalance | null>(null);
|
||||
const [code, setCode] = useState("");
|
||||
const [msg, setMsg] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
if (!token) return;
|
||||
try {
|
||||
setBalance(await accountApi().account(token));
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError && err.status === 401) logout();
|
||||
else setError(t("error.generic"));
|
||||
}
|
||||
}, [token, logout, t]);
|
||||
|
||||
useEffect(() => {
|
||||
// load() is async; setState happens after await, not synchronously.
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
void load();
|
||||
}, [load]);
|
||||
|
||||
async function redeem(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
if (!token) return;
|
||||
setError(null);
|
||||
setMsg(null);
|
||||
try {
|
||||
setBalance(await accountApi().redeem(token, code.trim()));
|
||||
setCode("");
|
||||
setMsg(t("dashboard.redeemed"));
|
||||
} catch (err) {
|
||||
setError(err instanceof ApiError ? err.message : t("error.generic"));
|
||||
}
|
||||
}
|
||||
|
||||
const remaining = balance
|
||||
? balance.allocation_total - balance.allocation_spent - balance.allocation_reserved
|
||||
: 0;
|
||||
const pct = balance && balance.allocation_total > 0
|
||||
? Math.round(((balance.allocation_spent + balance.allocation_reserved) / balance.allocation_total) * 100)
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<Container className="py-5 flex-grow-1" style={{ maxWidth: 720 }}>
|
||||
<div className="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 className="h3 mb-0">{t("dashboard.title")}</h1>
|
||||
<Button variant="outline-secondary" size="sm" onClick={logout}>
|
||||
{t("dashboard.logout")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Card className="surface-elevated mb-4">
|
||||
<Card.Body>
|
||||
<Card.Title className="h6 text-uppercase text-muted">
|
||||
{t("dashboard.balance")}
|
||||
</Card.Title>
|
||||
{balance && (
|
||||
<>
|
||||
<ProgressBar now={pct} className="my-3" />
|
||||
<div className="d-flex justify-content-between small">
|
||||
<span>{t("dashboard.total")}: {balance.allocation_total.toLocaleString()}</span>
|
||||
<span>{t("dashboard.spent")}: {balance.allocation_spent.toLocaleString()}</span>
|
||||
<span>{t("dashboard.reserved")}: {balance.allocation_reserved.toLocaleString()}</span>
|
||||
<span>{t("dashboard.remaining")}: {remaining.toLocaleString()}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<Link to="/account/keys" className="btn btn-primary btn-sm mt-3">
|
||||
{t("dashboard.manageKeys")}
|
||||
</Link>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
|
||||
<Card className="surface-elevated">
|
||||
<Card.Body>
|
||||
<Card.Title className="h6">{t("dashboard.redeemTitle")}</Card.Title>
|
||||
{msg && <Alert variant="success" className="py-2">{msg}</Alert>}
|
||||
{error && <Alert variant="warning" className="py-2">{error}</Alert>}
|
||||
<Form onSubmit={redeem} className="d-flex gap-2">
|
||||
<Form.Control
|
||||
value={code}
|
||||
placeholder={t("dashboard.redeemPlaceholder")}
|
||||
onChange={(e) => setCode(e.target.value)}
|
||||
/>
|
||||
<Button type="submit" disabled={!code.trim()}>
|
||||
{t("dashboard.redeem")}
|
||||
</Button>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
64
helexa.ai/src/pages/auth/Login.tsx
Normal file
64
helexa.ai/src/pages/auth/Login.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useState } from "react";
|
||||
import { Link, useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { Alert, Button, Container, Form } from "react-bootstrap";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAuth } from "../../auth/context";
|
||||
import { ApiError } from "../../api/types";
|
||||
|
||||
export default function Login() {
|
||||
const { t } = useTranslation("account");
|
||||
const { login } = useAuth();
|
||||
const nav = useNavigate();
|
||||
const [params] = useSearchParams();
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
async function submit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
try {
|
||||
await login(email, password);
|
||||
nav(params.get("next") || "/account", { replace: true });
|
||||
} catch (err) {
|
||||
setError(err instanceof ApiError ? err.message : t("error.generic"));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Container className="py-5 flex-grow-1" style={{ maxWidth: 420 }}>
|
||||
<h1 className="h3 mb-4">{t("login.title")}</h1>
|
||||
{error && <Alert variant="warning">{error}</Alert>}
|
||||
<Form onSubmit={submit}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>{t("login.email")}</Form.Label>
|
||||
<Form.Control
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>{t("login.password")}</Form.Label>
|
||||
<Form.Control
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
<Button type="submit" disabled={busy} className="w-100">
|
||||
{t("login.submit")}
|
||||
</Button>
|
||||
</Form>
|
||||
<p className="mt-3 small">
|
||||
<Link to="/register">{t("login.noAccount")}</Link>
|
||||
</p>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
70
helexa.ai/src/pages/auth/Register.tsx
Normal file
70
helexa.ai/src/pages/auth/Register.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Alert, Button, Container, Form } from "react-bootstrap";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAuth } from "../../auth/context";
|
||||
import { ApiError } from "../../api/types";
|
||||
|
||||
export default function Register() {
|
||||
const { t } = useTranslation("account");
|
||||
const { register } = useAuth();
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [done, setDone] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
async function submit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
try {
|
||||
await register(email, password);
|
||||
setDone(true);
|
||||
} catch (err) {
|
||||
setError(err instanceof ApiError ? err.message : t("error.generic"));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Container className="py-5 flex-grow-1" style={{ maxWidth: 420 }}>
|
||||
<h1 className="h3 mb-4">{t("register.title")}</h1>
|
||||
{done ? (
|
||||
<Alert variant="success">{t("register.checkEmail")}</Alert>
|
||||
) : (
|
||||
<>
|
||||
{error && <Alert variant="warning">{error}</Alert>}
|
||||
<Form onSubmit={submit}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>{t("register.email")}</Form.Label>
|
||||
<Form.Control
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>{t("register.password")}</Form.Label>
|
||||
<Form.Control
|
||||
type="password"
|
||||
minLength={8}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
<Button type="submit" disabled={busy} className="w-100">
|
||||
{t("register.submit")}
|
||||
</Button>
|
||||
</Form>
|
||||
<p className="mt-3 small">
|
||||
<Link to="/login">{t("register.haveAccount")}</Link>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
48
helexa.ai/src/pages/auth/RequestReset.tsx
Normal file
48
helexa.ai/src/pages/auth/RequestReset.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useState } from "react";
|
||||
import { Alert, Button, Container, Form } from "react-bootstrap";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { accountApi } from "../../api/account";
|
||||
|
||||
export default function RequestReset() {
|
||||
const { t } = useTranslation("account");
|
||||
const [email, setEmail] = useState("");
|
||||
const [done, setDone] = useState(false);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
async function submit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
setBusy(true);
|
||||
// Always succeeds from the UI's view (no account enumeration).
|
||||
try {
|
||||
await accountApi().requestReset(email);
|
||||
} catch {
|
||||
/* swallow */
|
||||
}
|
||||
setDone(true);
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container className="py-5 flex-grow-1" style={{ maxWidth: 420 }}>
|
||||
<h1 className="h3 mb-4">{t("reset.requestTitle")}</h1>
|
||||
{done ? (
|
||||
<Alert variant="info">{t("reset.requestDone")}</Alert>
|
||||
) : (
|
||||
<Form onSubmit={submit}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>{t("reset.email")}</Form.Label>
|
||||
<Form.Control
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
<Button type="submit" disabled={busy} className="w-100">
|
||||
{t("reset.requestSubmit")}
|
||||
</Button>
|
||||
</Form>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
64
helexa.ai/src/pages/auth/ResetPassword.tsx
Normal file
64
helexa.ai/src/pages/auth/ResetPassword.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useState } from "react";
|
||||
import { Link, useSearchParams } from "react-router-dom";
|
||||
import { Alert, Button, Container, Form } from "react-bootstrap";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { accountApi } from "../../api/account";
|
||||
import { ApiError } from "../../api/types";
|
||||
|
||||
export default function ResetPassword() {
|
||||
const { t } = useTranslation("account");
|
||||
const [params] = useSearchParams();
|
||||
const [password, setPassword] = useState("");
|
||||
const [done, setDone] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
async function submit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
const token = params.get("token");
|
||||
if (!token) {
|
||||
setError(t("verify.failed"));
|
||||
return;
|
||||
}
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
try {
|
||||
await accountApi().confirmReset(token, password);
|
||||
setDone(true);
|
||||
} catch (err) {
|
||||
setError(err instanceof ApiError ? err.message : t("error.generic"));
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Container className="py-5 flex-grow-1" style={{ maxWidth: 420 }}>
|
||||
<h1 className="h3 mb-4">{t("reset.confirmTitle")}</h1>
|
||||
{done ? (
|
||||
<Alert variant="success">
|
||||
{t("reset.ok")} <Link to="/login">{t("verify.toLogin")}</Link>
|
||||
</Alert>
|
||||
) : (
|
||||
<>
|
||||
{error && <Alert variant="warning">{error}</Alert>}
|
||||
<Form onSubmit={submit}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>{t("reset.newPassword")}</Form.Label>
|
||||
<Form.Control
|
||||
type="password"
|
||||
minLength={8}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</Form.Group>
|
||||
<Button type="submit" disabled={busy} className="w-100">
|
||||
{t("reset.confirmSubmit")}
|
||||
</Button>
|
||||
</Form>
|
||||
</>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
40
helexa.ai/src/pages/auth/VerifyEmail.tsx
Normal file
40
helexa.ai/src/pages/auth/VerifyEmail.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, useSearchParams } from "react-router-dom";
|
||||
import { Alert, Container, Spinner } from "react-bootstrap";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { accountApi } from "../../api/account";
|
||||
|
||||
export default function VerifyEmail() {
|
||||
const { t } = useTranslation("account");
|
||||
const [params] = useSearchParams();
|
||||
const [state, setState] = useState<"verifying" | "ok" | "failed">("verifying");
|
||||
|
||||
useEffect(() => {
|
||||
const token = params.get("token");
|
||||
// Keep all setState in async callbacks (no synchronous setState in the
|
||||
// effect body): a missing token resolves to a rejected promise.
|
||||
const run = token ? accountApi().verify(token) : Promise.reject(new Error("no token"));
|
||||
run.then(() => setState("ok")).catch(() => setState("failed"));
|
||||
}, [params]);
|
||||
|
||||
return (
|
||||
<Container className="py-5 flex-grow-1" style={{ maxWidth: 480 }}>
|
||||
{state === "verifying" && (
|
||||
<p>
|
||||
<Spinner size="sm" className="me-2" />
|
||||
{t("verify.verifying")}
|
||||
</p>
|
||||
)}
|
||||
{state === "ok" && (
|
||||
<Alert variant="success">
|
||||
{t("verify.ok")} <Link to="/login">{t("verify.toLogin")}</Link>
|
||||
</Alert>
|
||||
)}
|
||||
{state === "failed" && (
|
||||
<Alert variant="warning">
|
||||
{t("verify.failed")} <Link to="/login">{t("verify.toLogin")}</Link>
|
||||
</Alert>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user