Fix api url contain undefined

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2025-06-13 15:59:28 -07:00
parent dfbc2aade1
commit 40f88fb812
5 changed files with 91 additions and 29 deletions

View File

@@ -33,6 +33,7 @@ import { Suspense } from "react";
import { useToast } from "@probo/ui";
import { ErrorBoundary } from "react-error-boundary";
import { PageError } from "/components/PageError";
import { buildEndpoint } from "/providers/RelayProviders";
const MainLayoutQuery = graphql`
query MainLayoutQuery {
@@ -156,7 +157,7 @@ function UserDropdown() {
) => {
e.preventDefault();
fetch(import.meta.env.VITE_API_URL + "/api/console/v1/auth/logout", {
fetch(buildEndpoint("/api/console/v1/auth/logout"), {
method: "DELETE",
headers: {
"Content-Type": "application/json",

View File

@@ -6,9 +6,10 @@ import {
Card,
Markdown,
IconCheckmark1,
IconCircleProgress
IconCircleProgress,
} from "@probo/ui";
import { ProgressBar } from "../components/documentSigning/ProgressBar";
import { buildEndpoint } from "/providers/RelayProviders";
type Document = {
document_version_id: string;
@@ -30,12 +31,15 @@ export default function DocumentSigningRequestsPage() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [signingData, setSigningData] = useState<DocumentSigningResponse | null>(null);
const [signingData, setSigningData] =
useState<DocumentSigningResponse | null>(null);
const [currentDocIndex, setCurrentDocIndex] = useState(0);
useEffect(() => {
if (!token) {
setError(__("Missing signing token. Please check your URL and try again."));
setError(
__("Missing signing token. Please check your URL and try again.")
);
setLoading(false);
return;
}
@@ -43,14 +47,14 @@ export default function DocumentSigningRequestsPage() {
async function fetchDocuments() {
try {
const response = await fetch(
`${import.meta.env.VITE_API_URL}/api/console/v1/documents/signing-requests`,
buildEndpoint("/api/console/v1/documents/signing-requests"),
{
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
},
}
);
if (!response.ok) {
@@ -71,7 +75,7 @@ export default function DocumentSigningRequestsPage() {
});
} catch (err) {
setError(
err instanceof Error ? err.message : __("An unknown error occurred"),
err instanceof Error ? err.message : __("An unknown error occurred")
);
} finally {
setLoading(false);
@@ -88,14 +92,16 @@ export default function DocumentSigningRequestsPage() {
try {
const response = await fetch(
`${import.meta.env.VITE_API_URL}/api/console/v1/documents/signing-requests/${docToSign.document_version_id}/sign`,
buildEndpoint(
`/api/console/v1/documents/signing-requests/${docToSign.document_version_id}/sign`
),
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
},
}
);
if (!response.ok) {
@@ -117,7 +123,9 @@ export default function DocumentSigningRequestsPage() {
setCurrentDocIndex(currentDocIndex + 1);
}
} catch (err) {
setError(err instanceof Error ? err.message : __("Failed to sign document"));
setError(
err instanceof Error ? err.message : __("Failed to sign document")
);
}
};
@@ -146,7 +154,9 @@ export default function DocumentSigningRequestsPage() {
<div className="flex items-center gap-4">
<IconCircleProgress size={24} className="text-txt-accent" />
<div>
<h1 className="text-xl font-semibold">{__("Loading Signing Requests")}</h1>
<h1 className="text-xl font-semibold">
{__("Loading Signing Requests")}
</h1>
<p className="text-txt-tertiary">
{__("Please wait while we fetch your documents...")}
</p>
@@ -164,7 +174,9 @@ export default function DocumentSigningRequestsPage() {
<title>{__("Error")}</title>
<div className="flex justify-center items-center min-h-screen">
<Card padded className="w-full max-w-3xl">
<h1 className="text-xl font-semibold text-red-600 mb-2">{__("Error")}</h1>
<h1 className="text-xl font-semibold text-red-600 mb-2">
{__("Error")}
</h1>
<p className="text-txt-tertiary mb-4">{error}</p>
<Button onClick={() => window.location.reload()}>
{__("Try Again")}
@@ -181,9 +193,13 @@ export default function DocumentSigningRequestsPage() {
<title>{__("No Documents to Sign")}</title>
<div className="flex justify-center items-center min-h-screen">
<Card padded className="w-full max-w-3xl">
<h1 className="text-xl font-semibold mb-2">{__("No Documents to Sign")}</h1>
<h1 className="text-xl font-semibold mb-2">
{__("No Documents to Sign")}
</h1>
<p className="text-txt-tertiary">
{__("There are no documents requiring your signature at this time.")}
{__(
"There are no documents requiring your signature at this time."
)}
</p>
</Card>
</div>
@@ -200,15 +216,19 @@ export default function DocumentSigningRequestsPage() {
<title>{__("Document Signing")}</title>
<div className="container mx-auto py-10 space-y-6">
<div className="space-y-4">
<h1 className="text-3xl font-bold">{__("Document Signing Request")}</h1>
<h1 className="text-3xl font-bold">
{__("Document Signing Request")}
</h1>
<p className="text-txt-tertiary">
{__("From")} {signingData.requesterName} {__("at")} {signingData.requesterOrganization}
{__("From")} {signingData.requesterName} {__("at")}{" "}
{signingData.requesterOrganization}
</p>
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm text-txt-tertiary">
{getSignedCount()} {__("of")} {signingData.documents.length} {__("documents signed")}
{getSignedCount()} {__("of")} {signingData.documents.length}{" "}
{__("documents signed")}
</span>
<span className="text-sm font-medium">
{Math.round(getProgressPercentage())}%
@@ -223,7 +243,8 @@ export default function DocumentSigningRequestsPage() {
<div>
<h2 className="text-xl font-semibold">{currentDoc.title}</h2>
<p className="text-txt-tertiary">
{__("Document")} {currentDocIndex + 1} {__("of")} {signingData.documents.length}
{__("Document")} {currentDocIndex + 1} {__("of")}{" "}
{signingData.documents.length}
</p>
</div>

View File

@@ -2,7 +2,7 @@ import { useTranslate } from "@probo/i18n";
import { Button, Field, useToast } from "@probo/ui";
import type { FormEventHandler } from "react";
import { Link, useNavigate } from "react-router";
import { clearRelayStore } from "/providers/RelayProviders";
import { buildEndpoint, clearRelayStore } from "/providers/RelayProviders";
export default function LoginPage() {
const { __ } = useTranslate();
@@ -15,7 +15,7 @@ export default function LoginPage() {
const email = formData.get("email")?.toString();
const password = formData.get("password")?.toString();
fetch(import.meta.env.VITE_API_URL + "/api/console/v1/auth/login", {
fetch(buildEndpoint("/api/console/v1/auth/login"), {
method: "POST",
headers: {
"Content-Type": "application/json",

View File

@@ -3,6 +3,7 @@ import { Link, useNavigate } from "react-router";
import { Button, Field, useToast } from "@probo/ui";
import { useTranslate } from "@probo/i18n";
import { useMutation } from "@tanstack/react-query";
import { buildEndpoint } from "/providers/RelayProviders";
interface RegisterData {
email: string;
@@ -20,7 +21,7 @@ export default function RegisterPage() {
const registerUser = async (data: RegisterData) => {
const response = await fetch(
`${import.meta.env.VITE_API_URL}/api/console/v1/auth/register`,
buildEndpoint("/api/console/v1/auth/register"),
{
method: "POST",
headers: {
@@ -33,7 +34,10 @@ export default function RegisterPage() {
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
return { success: false, error: errorData.message || __("Registration failed") };
return {
success: false,
error: errorData.message || __("Registration failed"),
};
}
return { success: true, data: await response.json() };
@@ -103,7 +107,9 @@ export default function RegisterPage() {
type="text"
placeholder={__("John Doe")}
value={fullName}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setFullName(e.target.value)}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setFullName(e.target.value)
}
required
/>
@@ -112,7 +118,9 @@ export default function RegisterPage() {
type="email"
placeholder={__("name@example.com")}
value={email}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setEmail(e.target.value)
}
required
/>
@@ -121,19 +129,30 @@ export default function RegisterPage() {
type="password"
placeholder="••••••••"
value={password}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPassword(e.target.value)}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setPassword(e.target.value)
}
required
/>
<Button type="submit" className="w-full" disabled={registerMutation.isPending}>
{registerMutation.isPending ? __("Creating account...") : __("Sign up with email")}
<Button
type="submit"
className="w-full"
disabled={registerMutation.isPending}
>
{registerMutation.isPending
? __("Creating account...")
: __("Sign up with email")}
</Button>
</form>
<div className="text-center">
<p className="text-sm text-txt-tertiary">
{__("Already have an account?")}{" "}
<Link to="/login" className="underline text-txt-primary hover:text-txt-secondary">
<Link
to="/login"
className="underline text-txt-primary hover:text-txt-secondary"
>
{__("Log in here")}
</Link>
</p>

View File

@@ -23,6 +23,27 @@ export class InternalServerError extends Error {
}
}
export function buildEndpoint(path: string): string {
const host = import.meta.env.VITE_API_URL;
if (!host) {
return path;
}
const formattedHost =
host.startsWith("http://") || host.startsWith("https://")
? host
: `https://${host}`;
const url = new URL(formattedHost);
if (path) {
url.pathname = path.startsWith("/") ? path : `/${path}`;
}
return url.toString();
}
const hasUnauthenticatedError = (error: GraphQLError) =>
error.extensions?.code == "UNAUTHENTICATED";
@@ -79,7 +100,7 @@ const fetchRelay: FetchFunction = async (
}
const response = await fetch(
import.meta.env.VITE_API_URL + "/api/console/v1/query",
buildEndpoint("/api/console/v1/query"),
requestInit
);