Clean up frontend files and read me translations
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {HomepageAuth} from "@/ui/Desktop/Homepage/HomepageAuth.tsx";
|
||||
import {HomepageUpdateLog} from "@/ui/Desktop/Homepage/HompageUpdateLog.tsx";
|
||||
import {HomepageAlertManager} from "@/ui/Desktop/Homepage/HomepageAlertManager.tsx";
|
||||
import {Button} from "@/components/ui/button.tsx";
|
||||
import { getUserInfo, getDatabaseHealth, setCookie, getCookie } from "@/ui/main-axios.ts";
|
||||
import {getUserInfo, getDatabaseHealth, getCookie} from "@/ui/main-axios.ts";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
interface HomepageProps {
|
||||
@@ -15,22 +14,19 @@ interface HomepageProps {
|
||||
}
|
||||
|
||||
export function Homepage({
|
||||
onSelectView,
|
||||
isAuthenticated,
|
||||
authLoading,
|
||||
onAuthSuccess,
|
||||
isTopbarOpen
|
||||
}: HomepageProps): React.ReactElement {
|
||||
const {t} = useTranslation();
|
||||
const [loggedIn, setLoggedIn] = useState(isAuthenticated);
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [username, setUsername] = useState<string | null>(null);
|
||||
const [userId, setUserId] = useState<string | null>(null);
|
||||
const [dbError, setDbError] = useState<string | null>(null);
|
||||
|
||||
// Calculate margins based on topbar state (same logic as AppView.tsx)
|
||||
const topMarginPx = isTopbarOpen ? 74 : 26;
|
||||
const leftMarginPx = 26; // Assuming sidebar is collapsed for homepage
|
||||
const leftMarginPx = 26;
|
||||
const bottomMarginPx = 8;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -83,7 +79,7 @@ export function Homepage({
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
<div
|
||||
className="w-full h-full flex items-center justify-center"
|
||||
style={{
|
||||
marginLeft: leftMarginPx,
|
||||
|
||||
@@ -4,7 +4,7 @@ import {Button} from "@/components/ui/button.tsx";
|
||||
import {Badge} from "@/components/ui/badge.tsx";
|
||||
import {X, ExternalLink, AlertTriangle, Info, CheckCircle, AlertCircle} from "lucide-react";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import type { TermixAlert } from '../../../types/index.js';
|
||||
import type {TermixAlert} from '../../../types/index.js';
|
||||
|
||||
interface AlertCardProps {
|
||||
alert: TermixAlert;
|
||||
@@ -56,7 +56,7 @@ const getTypeBadgeVariant = (type?: string) => {
|
||||
|
||||
export function HomepageAlertCard({alert, onDismiss, onClose}: AlertCardProps): React.ReactElement {
|
||||
const {t} = useTranslation();
|
||||
|
||||
|
||||
if (!alert) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {HomepageAlertCard} from "./HomepageAlertCard.tsx";
|
||||
import {Button} from "@/components/ui/button.tsx";
|
||||
import { getUserAlerts, dismissAlert } from "@/ui/main-axios.ts";
|
||||
import {getUserAlerts, dismissAlert} from "@/ui/main-axios.ts";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import type { TermixAlert } from '../../../types/index.js';
|
||||
import type {TermixAlert} from '../../../types/index.js';
|
||||
|
||||
interface AlertManagerProps {
|
||||
userId: string | null;
|
||||
@@ -49,7 +49,6 @@ export function HomepageAlertManager({userId, loggedIn}: AlertManagerProps): Rea
|
||||
setAlerts(sortedAlerts);
|
||||
setCurrentAlertIndex(0);
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch user alerts:', err);
|
||||
const {toast} = await import('sonner');
|
||||
toast.error(t('homepage.failedToLoadAlerts'));
|
||||
setError(t('homepage.failedToLoadAlerts'));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, {useState, useEffect} from "react";
|
||||
import {Eye, EyeOff} from "lucide-react";
|
||||
import {cn} from "@/lib/utils.ts";
|
||||
import {Button} from "@/components/ui/button.tsx";
|
||||
import {Input} from "@/components/ui/input.tsx";
|
||||
@@ -24,9 +23,8 @@ import {
|
||||
getCookie,
|
||||
getServerConfig,
|
||||
isElectron,
|
||||
type ServerConfig
|
||||
} from "../../main-axios.ts";
|
||||
import {ServerConfig as ServerConfigComponent} from "@/ui/Desktop/ElectronOnly/ServerConfig.tsx";
|
||||
import {ServerConfig as ServerConfigComponent} from "@/ui/Desktop/Electron Only/ServerConfig.tsx";
|
||||
|
||||
interface HomepageAuthProps extends React.ComponentProps<"div"> {
|
||||
setLoggedIn: (loggedIn: boolean) => void;
|
||||
@@ -61,14 +59,14 @@ export function HomepageAuth({
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [oidcLoading, setOidcLoading] = useState(false);
|
||||
const [visibility, setVisibility] = useState({
|
||||
password: false,
|
||||
signupConfirm: false,
|
||||
resetNew: false,
|
||||
resetConfirm: false
|
||||
});
|
||||
password: false,
|
||||
signupConfirm: false,
|
||||
resetNew: false,
|
||||
resetConfirm: false
|
||||
});
|
||||
const toggleVisibility = (field: keyof typeof visibility) => {
|
||||
setVisibility(prev => ({ ...prev, [field]: !prev[field] }));
|
||||
};
|
||||
setVisibility(prev => ({...prev, [field]: !prev[field]}));
|
||||
};
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [internalLoggedIn, setInternalLoggedIn] = useState(false);
|
||||
@@ -83,7 +81,7 @@ export function HomepageAuth({
|
||||
const [tempToken, setTempToken] = useState("");
|
||||
const [resetLoading, setResetLoading] = useState(false);
|
||||
const [resetSuccess, setResetSuccess] = useState(false);
|
||||
|
||||
|
||||
const [totpRequired, setTotpRequired] = useState(false);
|
||||
const [totpCode, setTotpCode] = useState("");
|
||||
const [totpTempToken, setTotpTempToken] = useState("");
|
||||
@@ -159,23 +157,23 @@ export function HomepageAuth({
|
||||
await registerUser(localUsername, password);
|
||||
res = await loginUser(localUsername, password);
|
||||
}
|
||||
|
||||
|
||||
if (res.requires_totp) {
|
||||
setTotpRequired(true);
|
||||
setTotpTempToken(res.temp_token);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!res || !res.token) {
|
||||
throw new Error(t('errors.noTokenReceived'));
|
||||
}
|
||||
|
||||
|
||||
setCookie("jwt", res.token);
|
||||
[meRes] = await Promise.all([
|
||||
getUserInfo(),
|
||||
]);
|
||||
|
||||
|
||||
setInternalLoggedIn(true);
|
||||
setLoggedIn(true);
|
||||
setIsAdmin(!!meRes.is_admin);
|
||||
@@ -300,17 +298,17 @@ export function HomepageAuth({
|
||||
|
||||
setError(null);
|
||||
setTotpLoading(true);
|
||||
|
||||
|
||||
try {
|
||||
const res = await verifyTOTPLogin(totpTempToken, totpCode);
|
||||
|
||||
|
||||
if (!res || !res.token) {
|
||||
throw new Error(t('errors.noTokenReceived'));
|
||||
}
|
||||
|
||||
|
||||
setCookie("jwt", res.token);
|
||||
const meRes = await getUserInfo();
|
||||
|
||||
|
||||
setInternalLoggedIn(true);
|
||||
setLoggedIn(true);
|
||||
setIsAdmin(!!meRes.is_admin);
|
||||
@@ -408,58 +406,51 @@ export function HomepageAuth({
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Check if we need to show server config for Electron
|
||||
const [showServerConfig, setShowServerConfig] = useState<boolean | null>(null);
|
||||
const [currentServerUrl, setCurrentServerUrl] = useState<string>('');
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const checkServerConfig = async () => {
|
||||
if (isElectron()) {
|
||||
try {
|
||||
const config = await getServerConfig();
|
||||
console.log('Desktop HomepageAuth - Server config check:', config);
|
||||
setCurrentServerUrl(config?.serverUrl || '');
|
||||
setShowServerConfig(!config || !config.serverUrl);
|
||||
} catch (error) {
|
||||
console.log('Desktop HomepageAuth - No server config found, showing config screen');
|
||||
setShowServerConfig(true);
|
||||
}
|
||||
} else {
|
||||
setShowServerConfig(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
checkServerConfig();
|
||||
}, []);
|
||||
|
||||
|
||||
if (showServerConfig === null) {
|
||||
// Still checking
|
||||
return (
|
||||
<div
|
||||
className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md ${className || ''}`}
|
||||
{...props}
|
||||
>
|
||||
<div className="flex items-center justify-center h-32">
|
||||
<div className="w-6 h-6 border-2 border-primary border-t-transparent rounded-full animate-spin" />
|
||||
<div className="w-6 h-6 border-2 border-primary border-t-transparent rounded-full animate-spin"/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (showServerConfig) {
|
||||
console.log('Desktop HomepageAuth - SHOWING SERVER CONFIG SCREEN');
|
||||
return (
|
||||
<div
|
||||
className={`w-[420px] max-w-full p-6 flex flex-col bg-dark-bg border-2 border-dark-border rounded-md ${className || ''}`}
|
||||
{...props}
|
||||
>
|
||||
<ServerConfigComponent
|
||||
<ServerConfigComponent
|
||||
onServerConfigured={() => {
|
||||
console.log('Server configured, reloading page');
|
||||
window.location.reload();
|
||||
}}
|
||||
onCancel={() => {
|
||||
console.log('Cancelled server config, going back to login');
|
||||
setShowServerConfig(false);
|
||||
}}
|
||||
isFirstTime={!currentServerUrl}
|
||||
@@ -509,7 +500,7 @@ export function HomepageAuth({
|
||||
<h2 className="text-xl font-bold mb-1">{t('auth.twoFactorAuth')}</h2>
|
||||
<p className="text-muted-foreground">{t('auth.enterCode')}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label htmlFor="totp-code">{t('auth.verifyCode')}</Label>
|
||||
<Input
|
||||
@@ -527,7 +518,7 @@ export function HomepageAuth({
|
||||
{t('auth.backupCode')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
className="w-full h-11 text-base font-semibold"
|
||||
@@ -536,7 +527,7 @@ export function HomepageAuth({
|
||||
>
|
||||
{totpLoading ? Spinner : t('auth.verifyCode')}
|
||||
</Button>
|
||||
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
@@ -553,7 +544,7 @@ export function HomepageAuth({
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{(!internalLoggedIn && (!authLoading || !getCookie("jwt")) && !totpRequired) && (
|
||||
<>
|
||||
<div className="flex gap-2 mb-6">
|
||||
@@ -758,29 +749,30 @@ export function HomepageAuth({
|
||||
</div>
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label htmlFor="new-password">{t('auth.newPassword')}</Label>
|
||||
<PasswordInput
|
||||
id="new-password"
|
||||
required
|
||||
className="h-11 text-base focus:ring-2 focus:ring-primary/50 transition-all duration-200"
|
||||
value={newPassword}
|
||||
onChange={e => setNewPassword(e.target.value)}
|
||||
disabled={resetLoading}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label htmlFor="confirm-password">{t('auth.confirmNewPassword')}</Label>
|
||||
<PasswordInput
|
||||
id="confirm-password"
|
||||
required
|
||||
className="h-11 text-base focus:ring-2 focus:ring-primary/50 transition-all duration-200"
|
||||
value={confirmPassword}
|
||||
onChange={e => setConfirmPassword(e.target.value)}
|
||||
disabled={resetLoading}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
<Label htmlFor="new-password">{t('auth.newPassword')}</Label>
|
||||
<PasswordInput
|
||||
id="new-password"
|
||||
required
|
||||
className="h-11 text-base focus:ring-2 focus:ring-primary/50 transition-all duration-200"
|
||||
value={newPassword}
|
||||
onChange={e => setNewPassword(e.target.value)}
|
||||
disabled={resetLoading}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label
|
||||
htmlFor="confirm-password">{t('auth.confirmNewPassword')}</Label>
|
||||
<PasswordInput
|
||||
id="confirm-password"
|
||||
required
|
||||
className="h-11 text-base focus:ring-2 focus:ring-primary/50 transition-all duration-200"
|
||||
value={confirmPassword}
|
||||
onChange={e => setConfirmPassword(e.target.value)}
|
||||
disabled={resetLoading}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
className="w-full h-11 text-base font-semibold"
|
||||
@@ -823,26 +815,26 @@ export function HomepageAuth({
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label htmlFor="password">{t('common.password')}</Label>
|
||||
<PasswordInput
|
||||
id="password"
|
||||
required
|
||||
className="h-11 text-base"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
disabled={loading || internalLoggedIn}/>
|
||||
</div>
|
||||
{tab === "signup" && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label htmlFor="signup-confirm-password">{t('common.confirmPassword')}</Label>
|
||||
<PasswordInput
|
||||
id="signup-confirm-password"
|
||||
required
|
||||
className="h-11 text-base"
|
||||
value={signupConfirmPassword}
|
||||
onChange={e => setSignupConfirmPassword(e.target.value)}
|
||||
disabled={loading || internalLoggedIn}/>
|
||||
</div>
|
||||
<Label htmlFor="password">{t('common.password')}</Label>
|
||||
<PasswordInput
|
||||
id="password"
|
||||
required
|
||||
className="h-11 text-base"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
disabled={loading || internalLoggedIn}/>
|
||||
</div>
|
||||
{tab === "signup" && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label htmlFor="signup-confirm-password">{t('common.confirmPassword')}</Label>
|
||||
<PasswordInput
|
||||
id="signup-confirm-password"
|
||||
required
|
||||
className="h-11 text-base"
|
||||
value={signupConfirmPassword}
|
||||
onChange={e => setSignupConfirmPassword(e.target.value)}
|
||||
disabled={loading || internalLoggedIn}/>
|
||||
</div>
|
||||
)}
|
||||
<Button type="submit" className="w-full h-11 mt-2 text-base font-semibold"
|
||||
disabled={loading || internalLoggedIn}>
|
||||
@@ -863,13 +855,13 @@ export function HomepageAuth({
|
||||
)}
|
||||
</form>
|
||||
)}
|
||||
|
||||
|
||||
<div className="mt-6 pt-4 border-t border-dark-border space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label className="text-sm text-muted-foreground">{t('common.language')}</Label>
|
||||
</div>
|
||||
<LanguageSwitcher />
|
||||
<LanguageSwitcher/>
|
||||
</div>
|
||||
{isElectron() && currentServerUrl && (
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {Alert, AlertDescription, AlertTitle} from "@/components/ui/alert.tsx";
|
||||
import {Button} from "@/components/ui/button.tsx";
|
||||
import {Separator} from "@/components/ui/separator.tsx";
|
||||
import { getReleasesRSS, getVersionInfo } from "@/ui/main-axios.ts";
|
||||
import {getReleasesRSS, getVersionInfo} from "@/ui/main-axios.ts";
|
||||
import {useTranslation} from "react-i18next";
|
||||
|
||||
interface HomepageUpdateLogProps extends React.ComponentProps<"div"> {
|
||||
@@ -90,7 +89,8 @@ export function HomepageUpdateLog({loggedIn}: HomepageUpdateLogProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-[400px] h-[600px] flex flex-col border-2 border-dark-border rounded-lg bg-dark-bg p-4 shadow-lg">
|
||||
<div
|
||||
className="w-[400px] h-[600px] flex flex-col border-2 border-dark-border rounded-lg bg-dark-bg p-4 shadow-lg">
|
||||
<div>
|
||||
<h3 className="text-lg font-bold mb-3 text-white">{t('common.updatesAndReleases')}</h3>
|
||||
|
||||
@@ -100,7 +100,7 @@ export function HomepageUpdateLog({loggedIn}: HomepageUpdateLogProps) {
|
||||
<Alert className="bg-dark-bg-darker border-dark-border text-white">
|
||||
<AlertTitle className="text-white">{t('common.updateAvailable')}</AlertTitle>
|
||||
<AlertDescription className="text-gray-300">
|
||||
{t('common.newVersionAvailable', { version: versionInfo.version })}
|
||||
{t('common.newVersionAvailable', {version: versionInfo.version})}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user