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

View File

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

View File

@@ -2,7 +2,7 @@ import { useTranslate } from "@probo/i18n";
import { Button, Field, useToast } from "@probo/ui"; import { Button, Field, useToast } from "@probo/ui";
import type { FormEventHandler } from "react"; import type { FormEventHandler } from "react";
import { Link, useNavigate } from "react-router"; import { Link, useNavigate } from "react-router";
import { clearRelayStore } from "/providers/RelayProviders"; import { buildEndpoint, clearRelayStore } from "/providers/RelayProviders";
export default function LoginPage() { export default function LoginPage() {
const { __ } = useTranslate(); const { __ } = useTranslate();
@@ -15,7 +15,7 @@ export default function LoginPage() {
const email = formData.get("email")?.toString(); const email = formData.get("email")?.toString();
const password = formData.get("password")?.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", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",

View File

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