fix(user): satisfy biome check for oauth/payment UI

This commit is contained in:
ppanel-web 2026-02-08 08:49:17 +00:00
parent b32ba55ab8
commit 6e3ef8ab7e
5 changed files with 54 additions and 49 deletions

View File

@ -1,28 +1,29 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OAuth Redirect</title> <title>OAuth Redirect</title>
<meta http-equiv="refresh" content="0; url=/#/auth" /> <meta http-equiv="refresh" content="0; url=/#/auth">
<script> <script>
(function () { "use strict";
try {
// Providers redirect to a path without URL fragments (#). Our app uses hash routing. // Providers redirect to a path without URL fragments (#). Our app uses hash routing.
// Bridge /oauth/<provider>[/]?... -> /#/oauth/<provider>?... // Bridge /oauth/<provider>[/]?... -> /#/oauth/<provider>?...
var path = window.location.pathname || "/"; (() => {
try {
// Normalize trailing slash so /oauth/google/ and /oauth/google both map to the same route. // Normalize trailing slash so /oauth/google/ and /oauth/google both map to the same route.
let path = window.location.pathname || "/";
path = path.replace(/\/$/, ""); path = path.replace(/\/$/, "");
var search = window.location.search || "";
var target = "/#" + path + search; const search = window.location.search || "";
const target = `/#${path}${search}`;
window.location.replace(target); window.location.replace(target);
} catch (e) { } catch (_e) {
window.location.replace("/#/auth"); window.location.replace("/#/auth");
} }
})(); })();
</script> </script>
</head> </head>
<body> <body>Redirecting…</body>
Redirecting…
</body>
</html> </html>

View File

@ -1,27 +1,28 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OAuth Redirect</title> <title>OAuth Redirect</title>
<meta http-equiv="refresh" content="0; url=/#/auth" /> <meta http-equiv="refresh" content="0; url=/#/auth">
<script> <script>
(function () { "use strict";
try {
// Providers redirect to a path without URL fragments (#). Our app uses hash routing. // Providers redirect to a path without URL fragments (#). Our app uses hash routing.
// Bridge /oauth/<provider>[/]?... -> /#/oauth/<provider>?... // Bridge /oauth/<provider>[/]?... -> /#/oauth/<provider>?...
var path = window.location.pathname || "/"; (() => {
try {
let path = window.location.pathname || "/";
path = path.replace(/\/$/, ""); path = path.replace(/\/$/, "");
var search = window.location.search || "";
var target = "/#" + path + search; const search = window.location.search || "";
const target = `/#${path}${search}`;
window.location.replace(target); window.location.replace(target);
} catch (e) { } catch (_e) {
window.location.replace("/#/auth"); window.location.replace("/#/auth");
} }
})(); })();
</script> </script>
</head> </head>
<body> <body>Redirecting…</body>
Redirecting…
</body>
</html> </html>

View File

@ -9,7 +9,7 @@ import {
import { cn } from "@workspace/ui/lib/utils"; import { cn } from "@workspace/ui/lib/utils";
import { getAvailablePaymentMethods } from "@workspace/ui/services/user/portal"; import { getAvailablePaymentMethods } from "@workspace/ui/services/user/portal";
import type React from "react"; import type React from "react";
import React, { memo } from "react"; import { memo, useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
interface PaymentMethodsProps { interface PaymentMethodsProps {
@ -37,7 +37,7 @@ const PaymentMethods: React.FC<PaymentMethodsProps> = ({
// Only set a default when the current value is not a valid option. // Only set a default when the current value is not a valid option.
// This avoids resetting the user's selection on refetch (common on mobile). // This avoids resetting the user's selection on refetch (common on mobile).
// Prefer non-balance methods when possible. // Prefer non-balance methods when possible.
React.useEffect(() => { useEffect(() => {
if (!data || data.length === 0) return; if (!data || data.length === 0) return;
const valid = data.some((m) => String(m.id) === String(value)); const valid = data.some((m) => String(m.id) === String(value));
if (valid) return; if (valid) return;

View File

@ -18,6 +18,7 @@ import { useGlobalStore } from "@/stores/global";
export default function Announcement({ type }: { type: "popup" | "pinned" }) { export default function Announcement({ type }: { type: "popup" | "pinned" }) {
const { t } = useTranslation("dashboard"); const { t } = useTranslation("dashboard");
const { user } = useGlobalStore(); const { user } = useGlobalStore();
const [open, setOpen] = useState(false);
const { data } = useQuery({ const { data } = useQuery({
queryKey: ["announcement", type], queryKey: ["announcement", type],
@ -38,14 +39,12 @@ export default function Announcement({ type }: { type: "popup" | "pinned" }) {
enabled: !!user, enabled: !!user,
}); });
if (!data) return null;
const [open, setOpen] = useState(false);
useEffect(() => { useEffect(() => {
if (type === "popup" && !!data) setOpen(true); if (type === "popup" && !!data) setOpen(true);
}, [data, type]); }, [data, type]);
if (!data) return null;
if (type === "popup") { if (type === "popup") {
return ( return (
<Dialog onOpenChange={setOpen} open={open}> <Dialog onOpenChange={setOpen} open={open}>

View File

@ -276,7 +276,9 @@ const CheckoutForm: React.FC<Omit<StripePaymentProps, "publishable_key">> = ({
const stripe = useStripe(); const stripe = useStripe();
const [errorMessage, setErrorMessage] = useState<string | null>(null); const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null); const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);
const [qrCodeImageDataUrl, setQrCodeImageDataUrl] = useState<string | null>(null); const [qrCodeImageDataUrl, setQrCodeImageDataUrl] = useState<string | null>(
null
);
const [isSubmitted, setIsSubmitted] = useState(false); const [isSubmitted, setIsSubmitted] = useState(false);
const { t } = useTranslation("payment"); const { t } = useTranslation("payment");
const qrCodeMap: Record<string, string> = { const qrCodeMap: Record<string, string> = {
@ -342,7 +344,7 @@ const CheckoutForm: React.FC<Omit<StripePaymentProps, "publishable_key">> = ({
const imageDataUrl = wechat?.image_data_url; // data:image/png;base64,... const imageDataUrl = wechat?.image_data_url; // data:image/png;base64,...
setQrCodeUrl(data || null); setQrCodeUrl(data || null);
setQrCodeImageDataUrl(!data ? imageDataUrl || null : null); setQrCodeImageDataUrl(data ? null : imageDataUrl || null);
} }
} }
} catch (_error) { } catch (_error) {
@ -362,11 +364,13 @@ const CheckoutForm: React.FC<Omit<StripePaymentProps, "publishable_key">> = ({
<> <>
{qrCodeImageDataUrl ? ( {qrCodeImageDataUrl ? (
<img <img
alt={qrCodeMap[method] || t(`qrcode.${method}`, `Scan with ${method}`)} alt={
qrCodeMap[method] || t(`qrcode.${method}`, `Scan with ${method}`)
}
className="mx-auto h-[208px] w-[208px]" className="mx-auto h-[208px] w-[208px]"
height={208}
src={qrCodeImageDataUrl} src={qrCodeImageDataUrl}
width={208} width={208}
height={208}
/> />
) : ( ) : (
<QRCodeCanvas <QRCodeCanvas