feat: add devMode configuration to runtime settings and update LoginPage for theme management
This commit is contained in:
@@ -7,19 +7,23 @@ import { useTranslations } from "next-intl";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useAuthStore } from "@/stores/auth-store";
|
||||
import { useThemeStore } from "@/stores/theme-store";
|
||||
import { useConfig } from "@/hooks/use-config";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Mail, AlertCircle, Loader2, X, Info, Eye, EyeOff, LogIn } from "lucide-react";
|
||||
import { Mail, AlertCircle, Loader2, X, Info, Eye, EyeOff, LogIn, Sun, Moon, Monitor } from "lucide-react";
|
||||
import { discoverOAuth, type OAuthMetadata } from "@/lib/oauth/discovery";
|
||||
import { generateCodeVerifier, generateCodeChallenge, generateState } from "@/lib/oauth/pkce";
|
||||
import { OAUTH_SCOPES } from "@/lib/oauth/tokens";
|
||||
|
||||
const APP_VERSION = "1.1.2";
|
||||
|
||||
export default function LoginPage() {
|
||||
const router = useRouter();
|
||||
const t = useTranslations("login");
|
||||
const params = useParams();
|
||||
const { login, isLoading, error, clearError, isAuthenticated } = useAuthStore();
|
||||
const { appName, jmapServerUrl: serverUrl, oauthEnabled, oauthClientId, oauthIssuerUrl, rememberMeEnabled, isLoading: configLoading, error: configError } = useConfig();
|
||||
const { theme, setTheme, initializeTheme } = useThemeStore();
|
||||
const { appName, jmapServerUrl: serverUrl, oauthEnabled, oauthClientId, oauthIssuerUrl, rememberMeEnabled, devMode, isLoading: configLoading, error: configError } = useConfig();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
username: "",
|
||||
@@ -46,6 +50,10 @@ export default function LoginPage() {
|
||||
const totpInputRef = useRef<HTMLInputElement>(null);
|
||||
const prevError = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
initializeTheme();
|
||||
}, [initializeTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (serverUrl) {
|
||||
document.title = appName;
|
||||
@@ -295,8 +303,34 @@ export default function LoginPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleDevLogin = async () => {
|
||||
const success = await login(serverUrl, "dev@localhost", "dev");
|
||||
if (success) {
|
||||
router.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
const themeIcon = theme === 'light' ? Sun : theme === 'dark' ? Moon : Monitor;
|
||||
const ThemeIcon = themeIcon;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-background via-background to-muted/20">
|
||||
<div className="min-h-screen flex flex-col items-center justify-center bg-gradient-to-br from-background via-background to-muted/20 relative">
|
||||
{/* Theme toggle - top right */}
|
||||
<div className="absolute top-4 right-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const next = theme === 'light' ? 'dark' : theme === 'dark' ? 'system' : 'light';
|
||||
setTheme(next);
|
||||
}}
|
||||
className="p-2.5 rounded-lg bg-secondary/60 hover:bg-secondary border border-border/50 text-muted-foreground hover:text-foreground transition-colors"
|
||||
aria-label={`Theme: ${theme}`}
|
||||
title={`Theme: ${theme}`}
|
||||
>
|
||||
<ThemeIcon className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="w-full max-w-sm mx-auto px-4">
|
||||
{/* Logo */}
|
||||
<div className="text-center mb-12">
|
||||
@@ -342,7 +376,33 @@ export default function LoginPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Login Form */}
|
||||
{/* Dev Mode: One-click login */}
|
||||
{devMode ? (
|
||||
<div className="space-y-4">
|
||||
<Button
|
||||
type="button"
|
||||
className="w-full h-12 font-medium text-base bg-primary hover:bg-primary/90 transition-all duration-200 shadow-lg shadow-primary/20"
|
||||
onClick={handleDevLogin}
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="flex items-center gap-2">
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
{t("signing_in")}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<LogIn className="w-4 h-4" />
|
||||
{t("sign_in")}
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
<p className="text-center text-xs text-muted-foreground">
|
||||
Dev mode — logging in as dev@localhost
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
/* Login Form */
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className={cn("space-y-4", shakeError && "animate-shake")}
|
||||
@@ -525,6 +585,12 @@ export default function LoginPage() {
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Version number - bottom center */}
|
||||
<div className="absolute bottom-4 text-xs text-muted-foreground/50">
|
||||
v{APP_VERSION}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -22,5 +22,6 @@ export async function GET() {
|
||||
oauthClientId: process.env.OAUTH_CLIENT_ID || '',
|
||||
oauthIssuerUrl: process.env.OAUTH_ISSUER_URL || '',
|
||||
rememberMeEnabled: !!process.env.SESSION_SECRET,
|
||||
devMode: process.env.DEV_MOCK_JMAP === 'true',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ interface ConfigData {
|
||||
oauthClientId: string;
|
||||
oauthIssuerUrl: string;
|
||||
rememberMeEnabled: boolean;
|
||||
devMode: boolean;
|
||||
}
|
||||
|
||||
interface AppConfig extends ConfigData {
|
||||
@@ -65,6 +66,7 @@ export function useConfig(): AppConfig {
|
||||
oauthClientId: configCache?.oauthClientId || '',
|
||||
oauthIssuerUrl: configCache?.oauthIssuerUrl || '',
|
||||
rememberMeEnabled: configCache?.rememberMeEnabled || false,
|
||||
devMode: configCache?.devMode || false,
|
||||
isLoading: !configCache,
|
||||
error: null,
|
||||
});
|
||||
@@ -79,6 +81,7 @@ export function useConfig(): AppConfig {
|
||||
oauthClientId: configCache.oauthClientId,
|
||||
oauthIssuerUrl: configCache.oauthIssuerUrl,
|
||||
rememberMeEnabled: configCache.rememberMeEnabled,
|
||||
devMode: configCache.devMode,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
@@ -94,6 +97,7 @@ export function useConfig(): AppConfig {
|
||||
oauthClientId: data.oauthClientId,
|
||||
oauthIssuerUrl: data.oauthIssuerUrl,
|
||||
rememberMeEnabled: data.rememberMeEnabled,
|
||||
devMode: data.devMode,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user