Fix api url contain undefined
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user