mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2025-12-19 05:37:43 +00:00
some S3 compatible storage systems have a region parameter (#524)
* some S3 compatible storage systems have a region parameter * wip * wip * replace current toastMethod by new toastProvider * wip * fix failing testcase * wip
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
import { Icons } from "src/components/icons";
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
import { useToast } from "../toast/toast-provider";
|
||||
|
||||
type ClipBoardProps = {
|
||||
id?: string;
|
||||
@@ -13,6 +13,7 @@ type ClipBoardProps = {
|
||||
const hasClipboard = typeof navigator.clipboard !== "undefined";
|
||||
|
||||
export const ClipBoard = ({ text, id = "", name = "" }: ClipBoardProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const handleClick = () => {
|
||||
navigator.clipboard
|
||||
.writeText(text)
|
||||
|
||||
@@ -2,15 +2,18 @@ import React from "react";
|
||||
import { H1 } from "@jambonz/ui-kit";
|
||||
|
||||
import { RequireAuth } from "./require-auth";
|
||||
import { ToastProvider } from "./toast/toast-provider";
|
||||
|
||||
/** Wrapper to pass different auth contexts */
|
||||
const RequireAuthTestWrapper = () => {
|
||||
return (
|
||||
<RequireAuth>
|
||||
<div className="auth-div">
|
||||
<H1>Protected Route</H1>
|
||||
</div>
|
||||
</RequireAuth>
|
||||
<ToastProvider>
|
||||
<RequireAuth>
|
||||
<div className="auth-div">
|
||||
<H1>Protected Route</H1>
|
||||
</div>
|
||||
</RequireAuth>
|
||||
</ToastProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,14 +2,15 @@ import React, { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { useAuth } from "src/router/auth";
|
||||
import { toastError } from "src/store";
|
||||
import { ROUTE_LOGIN } from "src/router/routes";
|
||||
import { MSG_MUST_LOGIN } from "src/constants";
|
||||
import { useToast } from "./toast/toast-provider";
|
||||
|
||||
/**
|
||||
* Wrapper component that enforces valid authorization to the app
|
||||
*/
|
||||
export const RequireAuth = ({ children }: { children: React.ReactNode }) => {
|
||||
const { toastError } = useToast();
|
||||
const { authorized } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
||||
96
src/components/toast/toast-provider.tsx
Normal file
96
src/components/toast/toast-provider.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from "react";
|
||||
import { Toast } from "./index";
|
||||
import type { IMessage, Toast as ToastProps } from "src/store/types";
|
||||
import { TOAST_TIME } from "src/constants";
|
||||
|
||||
// Define the context type
|
||||
interface ToastContextType {
|
||||
toastSuccess: (message: IMessage) => void;
|
||||
toastError: (message: IMessage) => void;
|
||||
}
|
||||
|
||||
// Create the context with a default value
|
||||
const ToastContext = createContext<ToastContextType | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Provider component that makes toast functionality available to any
|
||||
* nested components that call useToast().
|
||||
*/
|
||||
export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [toast, setToast] = useState<ToastProps | null>(null);
|
||||
const timeoutRef = useRef<number | null>(null);
|
||||
|
||||
// Clear any existing toasts and timeouts
|
||||
const clearToast = useCallback(() => {
|
||||
setToast(null);
|
||||
if (timeoutRef.current !== null) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Show a toast with the specified type and message
|
||||
const showToast = useCallback(
|
||||
(type: "success" | "error", message: IMessage) => {
|
||||
clearToast();
|
||||
|
||||
setToast({ type, message });
|
||||
|
||||
// Auto-hide after specified time
|
||||
timeoutRef.current = window.setTimeout(() => {
|
||||
setToast(null);
|
||||
}, TOAST_TIME);
|
||||
},
|
||||
[clearToast],
|
||||
);
|
||||
|
||||
// Exposed methods
|
||||
const toastSuccess = useCallback(
|
||||
(message: IMessage) => {
|
||||
showToast("success", message);
|
||||
},
|
||||
[showToast],
|
||||
);
|
||||
|
||||
const toastError = useCallback(
|
||||
(message: IMessage) => {
|
||||
showToast("error", message);
|
||||
},
|
||||
[showToast],
|
||||
);
|
||||
|
||||
// Context value
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
toastSuccess,
|
||||
toastError,
|
||||
}),
|
||||
[toastSuccess, toastError],
|
||||
);
|
||||
|
||||
return (
|
||||
<ToastContext.Provider value={contextValue}>
|
||||
{children}
|
||||
{toast && <Toast type={toast.type} message={toast.message} />}
|
||||
</ToastContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useToast = () => {
|
||||
const context = useContext(ToastContext);
|
||||
|
||||
if (context === undefined) {
|
||||
throw new Error("useToast must be used within a ToastProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
@@ -1,12 +1,12 @@
|
||||
import React, { useState } from "react";
|
||||
import { P, Button } from "@jambonz/ui-kit";
|
||||
|
||||
import { toastSuccess, toastError } from "src/store";
|
||||
import { useApiData, postApiKey, deleteApiKey } from "src/api";
|
||||
import { Modal, ModalClose, Obscure, ClipBoard, Section } from "src/components";
|
||||
import { getHumanDateTime, hasLength } from "src/utils";
|
||||
|
||||
import type { ApiKey, TokenResponse } from "src/api/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type ApiKeyProps = {
|
||||
path: string;
|
||||
@@ -18,6 +18,7 @@ type ApiKeyProps = {
|
||||
};
|
||||
|
||||
export const ApiKeys = ({ path, post, label }: ApiKeyProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const [apiKeys, apiKeysRefetcher] = useApiData<ApiKey[]>(path);
|
||||
const [deleteKey, setDeleteKey] = useState<ApiKey | null>(null);
|
||||
const [addedKey, setAddedKey] = useState<TokenResponse | null>(null);
|
||||
|
||||
@@ -5,12 +5,7 @@ import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import { Icons, ModalForm } from "src/components";
|
||||
import { naviTop, naviByo } from "./items";
|
||||
import { UserMe } from "../user-me";
|
||||
import {
|
||||
useSelectState,
|
||||
useDispatch,
|
||||
toastSuccess,
|
||||
toastError,
|
||||
} from "src/store";
|
||||
import { useSelectState, useDispatch } from "src/store";
|
||||
import {
|
||||
getActiveSP,
|
||||
removeAccountFilter,
|
||||
@@ -26,6 +21,7 @@ import { Scope, UserData } from "src/store/types";
|
||||
import { USER_ADMIN } from "src/api/constants";
|
||||
import { ROUTE_LOGIN } from "src/router/routes";
|
||||
import { Lcr } from "src/api/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type CommonProps = {
|
||||
handleMenu: () => void;
|
||||
@@ -67,6 +63,7 @@ export const Navi = ({
|
||||
handleMenu,
|
||||
handleLogout,
|
||||
}: NaviProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const user = useSelectState("user");
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useParams } from "react-router-dom";
|
||||
|
||||
import { ApiKeys } from "src/containers/internal/api-keys";
|
||||
import { useApiData } from "src/api";
|
||||
import { toastError, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import { AccountForm } from "./form";
|
||||
|
||||
import type { Account, Application, Limit, TtsCache } from "src/api/types";
|
||||
@@ -14,8 +14,10 @@ import {
|
||||
} from "src/router/routes";
|
||||
import { useScopedRedirect } from "src/utils";
|
||||
import { Scope } from "src/store/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const EditAccount = () => {
|
||||
const { toastError } = useToast();
|
||||
const params = useParams();
|
||||
const user = useSelectState("user");
|
||||
const [data, refetch, error] = useApiData<Account>(
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from "react";
|
||||
import { P, Button, ButtonGroup, MS, Icon, H1 } from "@jambonz/ui-kit";
|
||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import {
|
||||
putAccount,
|
||||
postAccount,
|
||||
@@ -75,6 +75,7 @@ import { EditBoard } from "src/components/editboard";
|
||||
import { ModalLoader } from "src/components/modal";
|
||||
import { useAuth } from "src/router/auth";
|
||||
import { Scope } from "src/store/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type AccountFormProps = {
|
||||
apps?: Application[];
|
||||
@@ -89,6 +90,7 @@ export const AccountForm = ({
|
||||
account,
|
||||
ttsCache,
|
||||
}: AccountFormProps) => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const user = useSelectState("user");
|
||||
@@ -289,6 +291,7 @@ export const AccountForm = ({
|
||||
endpoint: endpoint,
|
||||
access_key_id: bucketAccessKeyId,
|
||||
secret_access_key: bucketSecretAccessKey,
|
||||
...(bucketRegion && { region: bucketRegion }),
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -437,6 +440,9 @@ export const AccountForm = ({
|
||||
access_key_id: bucketAccessKeyId || null,
|
||||
secret_access_key: bucketSecretAccessKey || null,
|
||||
...(hasLength(bucketTags) && { tags: bucketTags }),
|
||||
...(bucketRegion && {
|
||||
region: bucketRegion,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
...(!bucketCredentialChecked && {
|
||||
@@ -550,6 +556,10 @@ export const AccountForm = ({
|
||||
setBucketRegion(tmpBucketRegion);
|
||||
} else if (account.data.bucket_credential?.region) {
|
||||
setBucketRegion(account.data.bucket_credential?.region);
|
||||
} else if (
|
||||
account.data.bucket_credential?.vendor === BUCKET_VENDOR_S3_COMPATIBLE
|
||||
) {
|
||||
setBucketRegion("");
|
||||
}
|
||||
|
||||
if (tmpAzureConnectionString) {
|
||||
@@ -583,9 +593,7 @@ export const AccountForm = ({
|
||||
JSON.parse(account.data.bucket_credential?.service_key),
|
||||
);
|
||||
}
|
||||
setInitialCheckRecordAllCall(
|
||||
hasValue(bucketVendor) && bucketVendor.length !== 0,
|
||||
);
|
||||
setInitialCheckRecordAllCall(hasValue(account.data.bucket_credential));
|
||||
}
|
||||
}, [account]);
|
||||
|
||||
@@ -1102,6 +1110,18 @@ export const AccountForm = ({
|
||||
onChange={(e) => {
|
||||
setBucketVendor(e.target.value);
|
||||
setTmpBucketVendor(e.target.value);
|
||||
if (
|
||||
e.target.value === BUCKET_VENDOR_AWS &&
|
||||
!regions?.aws.find((r) => r.value === bucketRegion)
|
||||
) {
|
||||
setBucketRegion("us-east-1");
|
||||
setTmpBucketRegion("us-east-1");
|
||||
} else if (
|
||||
e.target.value === BUCKET_VENDOR_S3_COMPATIBLE
|
||||
) {
|
||||
setBucketRegion("");
|
||||
setTmpBucketRegion("");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -1122,6 +1142,17 @@ export const AccountForm = ({
|
||||
setTmpEndpoint(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="endpoint">Region (Optional)</label>
|
||||
<input
|
||||
id="aws_compatible_region"
|
||||
type="text"
|
||||
name="aws_compatible_region"
|
||||
value={bucketRegion}
|
||||
onChange={(e) => {
|
||||
setBucketRegion(e.target.value);
|
||||
setTmpBucketRegion(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<label htmlFor="bucket_name">
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useServiceProviderData, deleteAccount } from "src/api";
|
||||
import { ROUTE_INTERNAL_ACCOUNTS } from "src/router/routes";
|
||||
import { Section, Icons, Spinner, SearchFilter } from "src/components";
|
||||
import { DeleteAccount } from "./delete";
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import {
|
||||
hasLength,
|
||||
hasValue,
|
||||
@@ -17,8 +17,10 @@ import { USER_ACCOUNT } from "src/api/constants";
|
||||
|
||||
import { Scope } from "src/store/types";
|
||||
import type { Account } from "src/api/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const Accounts = () => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const [accounts, refetch] = useServiceProviderData<Account[]>("Accounts");
|
||||
const [account, setAccount] = useState<Account | null>(null);
|
||||
|
||||
@@ -10,11 +10,13 @@ import { postSubscriptions, useApiData } from "src/api";
|
||||
import { CurrentUserData, Subscription } from "src/api/types";
|
||||
import { Section } from "src/components";
|
||||
import { ROUTE_INTERNAL_ACCOUNTS } from "src/router/routes";
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import { PaymentMethod } from "@stripe/stripe-js";
|
||||
import { ModalLoader } from "src/components/modal";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const ManagePaymentForm = () => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const stripe = useStripe();
|
||||
const elements = useElements();
|
||||
|
||||
@@ -19,10 +19,11 @@ import {
|
||||
useStripe,
|
||||
} from "@stripe/react-stripe-js";
|
||||
import { PaymentMethod } from "@stripe/stripe-js";
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
import { ModalLoader } from "src/components/modal";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
const SubscriptionForm = () => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const [userData] = useApiData<CurrentUserData>("Users/me");
|
||||
const [priceInfo] = useApiData<PriceInfo[]>("/Prices");
|
||||
const [userStripeInfo] = useApiData<StripeCustomerId>("/StripeCustomerId");
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
PER_PAGE_SELECTION,
|
||||
USER_ACCOUNT,
|
||||
} from "src/api/constants";
|
||||
import { toastError, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import { hasLength, hasValue } from "src/utils";
|
||||
import {
|
||||
AccountFilter,
|
||||
@@ -27,8 +27,10 @@ import {
|
||||
setLocation,
|
||||
} from "src/store/localStore";
|
||||
import AlertDetailItem from "./alert-detail-item";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const Alerts = () => {
|
||||
const { toastError } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
const [accountSid, setAccountSid] = useState("");
|
||||
|
||||
@@ -3,15 +3,17 @@ import { H1 } from "@jambonz/ui-kit";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { useApiData } from "src/api";
|
||||
import { toastError, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import { ApplicationForm } from "./form";
|
||||
|
||||
import type { Application } from "src/api/types";
|
||||
import { useScopedRedirect } from "src/utils/use-scoped-redirect";
|
||||
import { Scope } from "src/store/types";
|
||||
import { ROUTE_INTERNAL_APPLICATIONS } from "src/router/routes";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const EditApplication = () => {
|
||||
const { toastError } = useToast();
|
||||
const params = useParams();
|
||||
const user = useSelectState("user");
|
||||
const [data, refetch, error] = useApiData<Application>(
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Button, ButtonGroup, MS } from "@jambonz/ui-kit";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import { ClipBoard, Section, Tooltip } from "src/components";
|
||||
import {
|
||||
Selector,
|
||||
@@ -57,12 +57,14 @@ import { hasLength, isUserAccountScope, useRedirect } from "src/utils";
|
||||
import { setAccountFilter, setLocation } from "src/store/localStore";
|
||||
import SpeechProviderSelection from "./speech-selection";
|
||||
import ObscureInput from "src/components/obscure-input";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type ApplicationFormProps = {
|
||||
application?: UseApiDataMap<Application>;
|
||||
};
|
||||
|
||||
export const ApplicationForm = ({ application }: ApplicationFormProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const user = useSelectState("user");
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
SelectFilter,
|
||||
} from "src/components";
|
||||
import { DeleteApplication } from "./delete";
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import { isUserAccountScope, hasLength, hasValue } from "src/utils";
|
||||
|
||||
import type { Application, Account } from "src/api/types";
|
||||
@@ -29,8 +29,10 @@ import { ScopedAccess } from "src/components/scoped-access";
|
||||
import { Scope } from "src/store/types";
|
||||
import { PER_PAGE_SELECTION, USER_ACCOUNT } from "src/api/constants";
|
||||
import { getAccountFilter, setLocation } from "src/store/localStore";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const Applications = () => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
const [accountSid, setAccountSid] = useState("");
|
||||
|
||||
@@ -10,7 +10,8 @@ import {
|
||||
} from "src/api/types";
|
||||
import { Selector } from "src/components/forms";
|
||||
import { SelectorOption } from "src/components/forms/selector";
|
||||
import { toastError, useSelectState } from "src/store";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
import { useSelectState } from "src/store";
|
||||
import { hasLength } from "src/utils";
|
||||
import {
|
||||
ELEVENLABS_LANG_EN,
|
||||
@@ -82,6 +83,7 @@ export const SpeechProviderSelection = ({
|
||||
sttLabelOptions,
|
||||
sttLabel: [recogLabel, setRecogLabel],
|
||||
}: SpeechProviderSelectionProbs) => {
|
||||
const { toastError } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const [
|
||||
synthesisSupportedLanguagesAndVoices,
|
||||
|
||||
@@ -3,15 +3,17 @@ import { H1 } from "@jambonz/ui-kit";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { useApiData } from "src/api";
|
||||
import { toastError, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import { CarrierForm } from "./form";
|
||||
|
||||
import { Carrier, SipGateway, SmppGateway } from "src/api/types";
|
||||
import { useScopedRedirect } from "src/utils/use-scoped-redirect";
|
||||
import { ROUTE_INTERNAL_CARRIERS } from "src/router/routes";
|
||||
import { Scope } from "src/store/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const EditCarrier = () => {
|
||||
const { toastError } = useToast();
|
||||
const params = useParams();
|
||||
const user = useSelectState("user");
|
||||
const [data, refetch, error] = useApiData<Carrier>(
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
} from "src/components/forms";
|
||||
import { MSG_REQUIRED_FIELDS } from "src/constants";
|
||||
import { ROUTE_INTERNAL_CARRIERS } from "src/router/routes";
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import {
|
||||
checkSelectOptions,
|
||||
getIpValidationType,
|
||||
@@ -67,6 +67,7 @@ import {
|
||||
} from "src/api/types";
|
||||
import { setAccountFilter, setLocation } from "src/store/localStore";
|
||||
import { RegisterStatus } from "./register-status";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type CarrierFormProps = {
|
||||
carrier?: UseApiDataMap<Carrier>;
|
||||
@@ -79,6 +80,7 @@ export const CarrierForm = ({
|
||||
carrierSipGateways,
|
||||
carrierSmppGateways,
|
||||
}: CarrierFormProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const user = useSelectState("user");
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
useApiData,
|
||||
useServiceProviderData,
|
||||
} from "src/api";
|
||||
import { toastSuccess, toastError, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import { ROUTE_INTERNAL_CARRIERS } from "src/router/routes";
|
||||
import {
|
||||
AccountFilter,
|
||||
@@ -43,8 +43,10 @@ import type {
|
||||
} from "src/api/types";
|
||||
import { Scope } from "src/store/types";
|
||||
import { getAccountFilter, setLocation } from "src/store/localStore";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const Carriers = () => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const [userData] = useApiData<CurrentUserData>("Users/me");
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
|
||||
@@ -6,9 +6,9 @@ import {
|
||||
getPcap,
|
||||
getServiceProviderPcap,
|
||||
} from "src/api";
|
||||
import { toastError } from "src/store";
|
||||
|
||||
import type { DownloadedBlob } from "src/api/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type PcapButtonProps = {
|
||||
accountSid: string;
|
||||
@@ -21,6 +21,7 @@ export const PcapButton = ({
|
||||
serviceProviderSid,
|
||||
sipCallId,
|
||||
}: PcapButtonProps) => {
|
||||
const { toastError } = useToast();
|
||||
const [pcap, setPcap] = useState<DownloadedBlob>();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -3,11 +3,12 @@ import React, { useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useApiData } from "src/api";
|
||||
import { Client } from "src/api/types";
|
||||
import { toastError } from "src/store";
|
||||
import ClientsForm from "./form";
|
||||
import { ROUTE_INTERNAL_CLIENTS } from "src/router/routes";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const ClientsEdit = () => {
|
||||
const { toastError } = useToast();
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [data, refetch, error] = useApiData<Client>(
|
||||
|
||||
@@ -13,16 +13,18 @@ import { Section, Tooltip } from "src/components";
|
||||
import { AccountSelect, Message, Passwd } from "src/components/forms";
|
||||
import { MSG_REQUIRED_FIELDS } from "src/constants";
|
||||
import { ROUTE_INTERNAL_CLIENTS } from "src/router/routes";
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import ClientsDelete from "./delete";
|
||||
import { hasValue } from "src/utils";
|
||||
import { IMessage } from "src/store/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type ClientsFormProps = {
|
||||
client?: UseApiDataMap<Client>;
|
||||
};
|
||||
|
||||
export const ClientsForm = ({ client }: ClientsFormProps) => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -12,14 +12,16 @@ import {
|
||||
Spinner,
|
||||
} from "src/components";
|
||||
import { ROUTE_INTERNAL_CLIENTS } from "src/router/routes";
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import { Scope } from "src/store/types";
|
||||
import { hasLength, hasValue, useFilteredResults } from "src/utils";
|
||||
import ClientsDelete from "./delete";
|
||||
import { USER_ACCOUNT } from "src/api/constants";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
import { getAccountFilter } from "src/store/localStore";
|
||||
|
||||
export const Clients = () => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const [userData] = useApiData<CurrentUserData>("Users/me");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
|
||||
@@ -4,8 +4,8 @@ import Card from "./card";
|
||||
import { hasLength } from "src/utils";
|
||||
import update from "immutability-helper";
|
||||
import { deleteLcrRoute } from "src/api";
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
import { SelectorOption } from "src/components/forms/selector";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type ContainerProps = {
|
||||
lcrRoute: [LcrRoute[], React.Dispatch<React.SetStateAction<LcrRoute[]>>];
|
||||
@@ -16,6 +16,7 @@ export const Container = ({
|
||||
lcrRoute: [lcrRoutes, setLcrRoutes],
|
||||
carrierSelectorOptions,
|
||||
}: ContainerProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const moveCard = (dragIndex: number, hoverIndex: number) => {
|
||||
setLcrRoutes((prevCards) =>
|
||||
update(prevCards, {
|
||||
|
||||
@@ -2,12 +2,7 @@ import React, { useEffect, useMemo, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { Button, ButtonGroup, Icon, MS, MXS } from "@jambonz/ui-kit";
|
||||
import { Icons, Section } from "src/components";
|
||||
import {
|
||||
toastError,
|
||||
toastSuccess,
|
||||
useDispatch,
|
||||
useSelectState,
|
||||
} from "src/store";
|
||||
import { useDispatch, useSelectState } from "src/store";
|
||||
import { MSG_REQUIRED_FIELDS } from "src/constants";
|
||||
import { setLocation } from "src/store/localStore";
|
||||
import { AccountSelect, Message, Selector } from "src/components/forms";
|
||||
@@ -35,6 +30,7 @@ import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import Container from "./container";
|
||||
import { hasValue } from "src/utils";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type LcrFormProps = {
|
||||
lcrDataMap?: UseApiDataMap<Lcr>;
|
||||
@@ -56,6 +52,7 @@ export const LcrForm = ({ lcrDataMap, lcrRouteDataMap }: LcrFormProps) => {
|
||||
],
|
||||
};
|
||||
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from "src/components";
|
||||
import { ScopedAccess } from "src/components/scoped-access";
|
||||
import { ROUTE_INTERNAL_LEST_COST_ROUTING } from "src/router/routes";
|
||||
import { toastSuccess, toastError, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
// import { getAccountFilter, setLocation } from "src/store/localStore";
|
||||
import { Scope } from "src/store/types";
|
||||
import {
|
||||
@@ -25,8 +25,10 @@ import {
|
||||
} from "src/utils";
|
||||
import { USER_ACCOUNT } from "src/api/constants";
|
||||
import DeleteLcr from "./delete";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const Lcrs = () => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const user = useSelectState("user");
|
||||
useScopedRedirect(
|
||||
Scope.admin,
|
||||
|
||||
@@ -3,12 +3,13 @@ import { H1 } from "@jambonz/ui-kit";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { useApiData } from "src/api";
|
||||
import { toastError } from "src/store";
|
||||
import { MsTeamsTenantForm } from "./form";
|
||||
|
||||
import type { MSTeamsTenant } from "src/api/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const EditMsTeamsTenant = () => {
|
||||
const { toastError } = useToast();
|
||||
const params = useParams();
|
||||
const [data, refetch, error] = useApiData<MSTeamsTenant>(
|
||||
`MicrosoftTeamsTenants/${params.ms_teams_tenant_sid}`,
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
ApplicationSelect,
|
||||
} from "src/components/forms";
|
||||
import { MSG_REQUIRED_FIELDS } from "src/constants";
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import {
|
||||
ROUTE_INTERNAL_ACCOUNTS,
|
||||
ROUTE_INTERNAL_MS_TEAMS_TENANTS,
|
||||
@@ -28,6 +28,7 @@ import type {
|
||||
MSTeamsTenant,
|
||||
UseApiDataMap,
|
||||
} from "src/api/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type MsTeamsTenantFormProps = {
|
||||
msTeamsTenant?: UseApiDataMap<MSTeamsTenant>;
|
||||
@@ -36,6 +37,7 @@ type MsTeamsTenantFormProps = {
|
||||
export const MsTeamsTenantForm = ({
|
||||
msTeamsTenant,
|
||||
}: MsTeamsTenantFormProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
withAccessControl,
|
||||
useFilteredResults,
|
||||
} from "src/utils";
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
import {
|
||||
Icons,
|
||||
Section,
|
||||
@@ -29,8 +28,10 @@ import { DeleteMsTeamsTenant } from "./delete";
|
||||
|
||||
import type { Account, MSTeamsTenant, Application } from "src/api/types";
|
||||
import type { ACLGetIMessage } from "src/utils/with-access-control";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const MSTeamsTenants = () => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const [msTeamsTenant, setMsTeamsTenant] = useState<MSTeamsTenant | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
@@ -3,12 +3,13 @@ import { H1 } from "@jambonz/ui-kit";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { useApiData } from "src/api";
|
||||
import { toastError } from "src/store";
|
||||
import { PhoneNumberForm } from "./form";
|
||||
|
||||
import type { PhoneNumber } from "src/api/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const EditPhoneNumber = () => {
|
||||
const { toastError } = useToast();
|
||||
const params = useParams();
|
||||
const [data, refetch, error] = useApiData<PhoneNumber>(
|
||||
`PhoneNumbers/${params.phone_number_sid}`,
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
ROUTE_INTERNAL_CARRIERS,
|
||||
ROUTE_INTERNAL_PHONE_NUMBERS,
|
||||
} from "src/router/routes";
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
import { hasLength, useRedirect } from "src/utils";
|
||||
|
||||
import type {
|
||||
@@ -31,12 +30,14 @@ import type {
|
||||
UseApiDataMap,
|
||||
} from "src/api/types";
|
||||
import { setAccountFilter, setLocation } from "src/store/localStore";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type PhoneNumberFormProps = {
|
||||
phoneNumber?: UseApiDataMap<PhoneNumber>;
|
||||
};
|
||||
|
||||
export const PhoneNumberForm = ({ phoneNumber }: PhoneNumberFormProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
const [applications] = useServiceProviderData<Application[]>("Applications");
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
putPhoneNumber,
|
||||
useServiceProviderData,
|
||||
} from "src/api";
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import {
|
||||
Icons,
|
||||
Section,
|
||||
@@ -32,8 +32,10 @@ import { PER_PAGE_SELECTION, USER_ACCOUNT } from "src/api/constants";
|
||||
import { ScopedAccess } from "src/components/scoped-access";
|
||||
import { Scope } from "src/store/types";
|
||||
import { getAccountFilter, setLocation } from "src/store/localStore";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const PhoneNumbers = () => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
|
||||
@@ -3,9 +3,9 @@ import React, { useEffect, useState } from "react";
|
||||
import { getRecentCallLog } from "src/api";
|
||||
import { RecentCall } from "src/api/types";
|
||||
import { Icons, Spinner } from "src/components";
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
import { hasValue } from "src/utils";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
dayjs.extend(utc);
|
||||
|
||||
type CallSystemLogsProps = {
|
||||
@@ -29,6 +29,7 @@ const formatLog = (log: string): string => {
|
||||
};
|
||||
|
||||
export default function CallSystemLogs({ call }: CallSystemLogsProps) {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const [logs, setLogs] = useState<string[] | null>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
PER_PAGE_SELECTION,
|
||||
USER_ACCOUNT,
|
||||
} from "src/api/constants";
|
||||
import { toastError, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import {
|
||||
Section,
|
||||
AccountFilter,
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
getQueryFilter,
|
||||
setLocation,
|
||||
} from "src/store/localStore";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
const directionSelection = [
|
||||
{ name: "either", value: "io" },
|
||||
@@ -42,6 +43,7 @@ const statusSelection = [
|
||||
];
|
||||
|
||||
export const RecentCalls = () => {
|
||||
const { toastError } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
const [accountSid, setAccountSid] = useState("");
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { getPcap } from "src/api";
|
||||
import { toastError } from "src/store";
|
||||
|
||||
import type { DownloadedBlob, RecentCall } from "src/api/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type PcapButtonProps = {
|
||||
call: RecentCall;
|
||||
};
|
||||
|
||||
export const PcapButton = ({ call }: PcapButtonProps) => {
|
||||
const { toastError } = useToast();
|
||||
const [pcap, setPcap] = useState<DownloadedBlob | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -22,13 +22,14 @@ import {
|
||||
getSpansByNameRegex,
|
||||
getSpansFromJaegerRoot,
|
||||
} from "./utils";
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type PlayerProps = {
|
||||
call: RecentCall;
|
||||
};
|
||||
|
||||
export const Player = ({ call }: PlayerProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const { recording_url, call_sid } = call;
|
||||
const url =
|
||||
recording_url && recording_url.startsWith("http://")
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
SystemInformation,
|
||||
TtsCache,
|
||||
} from "src/api/types";
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
import { Selector } from "src/components/forms";
|
||||
import { hasValue, isvalidIpv4OrCidr } from "src/utils";
|
||||
import {
|
||||
@@ -22,8 +21,10 @@ import {
|
||||
PASSWORD_MIN,
|
||||
} from "src/api/constants";
|
||||
import { Modal } from "src/components";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const AdminSettings = () => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const [passwordSettings, passwordSettingsFetcher] =
|
||||
useApiData<PasswordSettings>("PasswordSettings");
|
||||
const [systemInformation, systemInformationFetcher] =
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { P, Button, ButtonGroup } from "@jambonz/ui-kit";
|
||||
|
||||
import { useDispatch, toastSuccess, toastError } from "src/store";
|
||||
import { useDispatch } from "src/store";
|
||||
import { hasLength } from "src/utils";
|
||||
import {
|
||||
putServiceProvider,
|
||||
@@ -15,6 +15,7 @@ import { Checkzone, LocalLimits } from "src/components/forms";
|
||||
import { withSelectState } from "src/utils";
|
||||
import type { Limit, ServiceProvider } from "src/api/types";
|
||||
import { removeActiveSP } from "src/store/localStore";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export type ServiceProviderSettingsProps = {
|
||||
serviceProviders: ServiceProvider[];
|
||||
@@ -25,6 +26,7 @@ export const ServiceProviderSettings = ({
|
||||
serviceProviders,
|
||||
currentServiceProvider,
|
||||
}: ServiceProviderSettingsProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const dispatch = useDispatch();
|
||||
const [limits, refetchLimits] = useServiceProviderData<Limit[]>("Limits");
|
||||
const [name, setName] = useState("");
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { H1 } from "@jambonz/ui-kit";
|
||||
|
||||
import { useApiData } from "src/api";
|
||||
import { toastError, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import { SpeechServiceForm } from "./form";
|
||||
|
||||
import type { SpeechCredential } from "src/api/types";
|
||||
@@ -11,6 +11,7 @@ import { useScopedRedirect } from "src/utils/use-scoped-redirect";
|
||||
import { Scope } from "src/store/types";
|
||||
import { ROUTE_INTERNAL_SPEECH } from "src/router/routes";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const EditSpeechService = () => {
|
||||
const params = useParams();
|
||||
@@ -18,6 +19,7 @@ export const EditSpeechService = () => {
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
const [url, setUrl] = useState("");
|
||||
const [data, refetch, error] = useApiData<SpeechCredential>(url);
|
||||
const { toastError } = useToast();
|
||||
|
||||
useScopedRedirect(
|
||||
Scope.account,
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
Checkzone,
|
||||
Message,
|
||||
} from "src/components/forms";
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import {
|
||||
deleteGoogleCustomVoice,
|
||||
getGoogleCustomVoices,
|
||||
@@ -91,12 +91,14 @@ import {
|
||||
GOOGLE_CUSTOM_VOICES_REPORTED_USAGE,
|
||||
VERBIO_STT_MODELS,
|
||||
} from "src/api/constants";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type SpeechServiceFormProps = {
|
||||
credential?: UseApiDataMap<SpeechCredential>;
|
||||
};
|
||||
|
||||
export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const user = useSelectState("user");
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Link } from "react-router-dom";
|
||||
|
||||
import { USER_ACCOUNT } from "src/api/constants";
|
||||
import { AccountFilter, Icons, Section, Spinner } from "src/components";
|
||||
import { useSelectState, toastError, toastSuccess } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import {
|
||||
deleteSpeechService,
|
||||
useServiceProviderData,
|
||||
@@ -26,8 +26,10 @@ import { ScopedAccess } from "src/components/scoped-access";
|
||||
import { Scope } from "src/store/types";
|
||||
import { getAccountFilter, setLocation } from "src/store/localStore";
|
||||
import { VENDOR_CUSTOM } from "src/vendor";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const SpeechServices = () => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const user = useSelectState("user");
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
const [apiUrl, setApiUrl] = useState("");
|
||||
|
||||
@@ -5,11 +5,12 @@ import { useParams } from "react-router-dom";
|
||||
import { UserForm } from "./form";
|
||||
import { useApiData } from "src/api";
|
||||
import { User } from "src/api/types";
|
||||
import { toastError } from "src/store";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const EditUser = () => {
|
||||
const params = useParams();
|
||||
const [data, refetch, error] = useApiData<User>(`Users/${params.user_sid}`);
|
||||
const { toastError } = useToast();
|
||||
|
||||
/** Handle error toast at top level... */
|
||||
useEffect(() => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
import { Button, ButtonGroup, MS } from "@jambonz/ui-kit";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
|
||||
import { toastError, toastSuccess, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
import {
|
||||
deleteUser,
|
||||
postFetch,
|
||||
@@ -38,12 +38,14 @@ import type {
|
||||
} from "src/api/types";
|
||||
import type { IMessage } from "src/store/types";
|
||||
import { setAccountFilter, setLocation } from "src/store/localStore";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type UserFormProps = {
|
||||
user?: UseApiDataMap<User>;
|
||||
};
|
||||
|
||||
export const UserForm = ({ user }: UserFormProps) => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const { signout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const currentUser = useSelectState("user");
|
||||
|
||||
@@ -8,9 +8,10 @@ import { useNavigate } from "react-router-dom";
|
||||
import { MSG_SOMETHING_WRONG } from "src/constants";
|
||||
|
||||
import { ROUTE_LOGIN } from "src/router/routes";
|
||||
import { toastSuccess } from "src/store";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const ForgotPassword = () => {
|
||||
const { toastSuccess } = useToast();
|
||||
const [message, setMessage] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react";
|
||||
import { Button, H1 } from "@jambonz/ui-kit";
|
||||
import { useLocation, Navigate, Link } from "react-router-dom";
|
||||
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
import { getToken, parseJwt, useAuth } from "src/router/auth";
|
||||
import {
|
||||
SESS_FLASH_MSG,
|
||||
@@ -26,8 +25,10 @@ import { v4 as uuid } from "uuid";
|
||||
import { setLocationBeforeOauth, setOauthState } from "src/store/localStore";
|
||||
import { getGithubOauthUrl, getGoogleOauthUrl } from "./utils";
|
||||
import { UserData } from "src/store/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const Login = () => {
|
||||
const { toastSuccess, toastError } = useToast();
|
||||
const state = uuid();
|
||||
setOauthState(state);
|
||||
setLocationBeforeOauth("/sign-in");
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
BASE_URL,
|
||||
} from "src/api/constants";
|
||||
import { Spinner } from "src/components";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
import { setToken } from "src/router/auth";
|
||||
import {
|
||||
ROUTE_INTERNAL_ACCOUNTS,
|
||||
@@ -15,7 +16,6 @@ import {
|
||||
ROUTE_REGISTER,
|
||||
ROUTE_REGISTER_SUB_DOMAIN,
|
||||
} from "src/router/routes";
|
||||
import { toastError } from "src/store";
|
||||
import {
|
||||
getLocationBeforeOauth,
|
||||
getOauthState,
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
} from "src/store/localStore";
|
||||
|
||||
export const OauthCallback = () => {
|
||||
const { toastError } = useToast();
|
||||
const queryParams = new URLSearchParams(location.search);
|
||||
const code = queryParams.get("code");
|
||||
const newState = queryParams.get("state");
|
||||
|
||||
@@ -7,10 +7,11 @@ import { Passwd } from "src/components/forms";
|
||||
import { ROUTE_LOGIN, ROUTE_REGISTER_EMAIL_VERIFY } from "src/router/routes";
|
||||
import { generateActivationCode } from "./utils";
|
||||
import { setToken } from "src/router/auth";
|
||||
import { toastError } from "src/store";
|
||||
import { setRootDomain } from "src/store/localStore";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
export const RegisterEmail = () => {
|
||||
const { toastError } = useToast();
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -2,15 +2,16 @@ import { Button, H1, MS } from "@jambonz/ui-kit";
|
||||
import React, { useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { putActivationCode } from "src/api";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
import { getToken, parseJwt } from "src/router/auth";
|
||||
import {
|
||||
ROUTE_REGISTER_EMAIL,
|
||||
ROUTE_REGISTER_SUB_DOMAIN,
|
||||
} from "src/router/routes";
|
||||
import { toastError } from "src/store";
|
||||
import { UserData } from "src/store/types";
|
||||
|
||||
export const EmailVerify = () => {
|
||||
const { toastError } = useToast();
|
||||
const [code, setCode] = useState("");
|
||||
const userData: UserData = parseJwt(getToken());
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -3,11 +3,12 @@ import React, { useEffect, useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { postChangepassword, postSignIn } from "src/api";
|
||||
import { Message, Passwd } from "src/components/forms";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
import { setToken } from "src/router/auth";
|
||||
import { ROUTE_LOGIN } from "src/router/routes";
|
||||
import { toastError, toastSuccess } from "src/store";
|
||||
|
||||
export const ResetPassword = () => {
|
||||
const { toastError, toastSuccess } = useToast();
|
||||
const params = useParams();
|
||||
const resetId = params.id;
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
|
||||
17
src/main.tsx
17
src/main.tsx
@@ -7,17 +7,20 @@ import { AuthProvider } from "./router/auth";
|
||||
import { Router } from "./router";
|
||||
|
||||
import "./styles/index.scss";
|
||||
import { ToastProvider } from "./components/toast/toast-provider";
|
||||
|
||||
const root: Element = document.getElementById("root")!;
|
||||
|
||||
createRoot(root).render(
|
||||
<React.StrictMode>
|
||||
<StateProvider>
|
||||
<BrowserRouter>
|
||||
<AuthProvider>
|
||||
<Router />
|
||||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
</StateProvider>
|
||||
<ToastProvider>
|
||||
<StateProvider>
|
||||
<BrowserRouter>
|
||||
<AuthProvider>
|
||||
<Router />
|
||||
</AuthProvider>
|
||||
</BrowserRouter>
|
||||
</StateProvider>
|
||||
</ToastProvider>
|
||||
</React.StrictMode>,
|
||||
);
|
||||
|
||||
@@ -25,12 +25,12 @@ import {
|
||||
import type { UserLogin } from "src/api/types";
|
||||
import { ENABLE_HOSTED_SYSTEM, USER_ACCOUNT } from "src/api/constants";
|
||||
import type { UserData } from "src/store/types";
|
||||
import { toastError } from "src/store";
|
||||
import {
|
||||
clearLocalStorage,
|
||||
removeLocationBeforeOauth,
|
||||
removeOauthState,
|
||||
} from "src/store/localStore";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
interface SignIn {
|
||||
(username: string, password: string): Promise<UserLogin>;
|
||||
@@ -94,6 +94,7 @@ export const parseJwt = (token: string) => {
|
||||
* Provider hook that creates auth object and handles state
|
||||
*/
|
||||
export const useProvideAuth = (): AuthStateContext => {
|
||||
const { toastError } = useToast();
|
||||
let token = getToken();
|
||||
let userData: UserData;
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useReducer, useContext } from "react";
|
||||
|
||||
import { TOAST_TIME } from "src/constants";
|
||||
import {
|
||||
genericAction,
|
||||
userAsyncAction,
|
||||
@@ -11,8 +10,6 @@ import {
|
||||
} from "./actions";
|
||||
|
||||
import type {
|
||||
IMessage,
|
||||
Toast,
|
||||
State,
|
||||
Action,
|
||||
MiddleWare,
|
||||
@@ -49,22 +46,12 @@ const reducer: React.Reducer<State, Action<keyof State>> = (state, action) => {
|
||||
}
|
||||
};
|
||||
|
||||
let toastTimeout: number;
|
||||
|
||||
/** Async middlewares */
|
||||
/** Proxies dispatch to reducer */
|
||||
const middleware: MiddleWare = (dispatch) => {
|
||||
/** This generic implementation enforces global dispatch type-safety */
|
||||
return <Type extends keyof State>(action: Action<Type>) => {
|
||||
switch (action.type) {
|
||||
case "toast":
|
||||
if (toastTimeout) {
|
||||
clearTimeout(toastTimeout);
|
||||
}
|
||||
toastTimeout = setTimeout(() => {
|
||||
dispatch({ type: "toast" });
|
||||
}, TOAST_TIME);
|
||||
return dispatch(action);
|
||||
case "user":
|
||||
return userAsyncAction().then((payload) => {
|
||||
dispatch({ ...action, payload });
|
||||
@@ -106,28 +93,6 @@ export const useDispatch = (): GlobalDispatch => {
|
||||
return globalDispatch;
|
||||
};
|
||||
|
||||
/** Toast dispatch helpers to make component code less cumbersome */
|
||||
const toastDispatch = (payload: Toast) => {
|
||||
globalDispatch({
|
||||
type: "toast",
|
||||
payload,
|
||||
});
|
||||
};
|
||||
|
||||
export const toastError = (msg: IMessage) => {
|
||||
toastDispatch({
|
||||
type: "error",
|
||||
message: msg,
|
||||
});
|
||||
};
|
||||
|
||||
export const toastSuccess = (msg: IMessage) => {
|
||||
toastDispatch({
|
||||
type: "success",
|
||||
message: msg,
|
||||
});
|
||||
};
|
||||
|
||||
/** Wrapper hook for state context */
|
||||
export const useStateContext = () => {
|
||||
const { state } = useContext(StateContext);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { toastError } from "src/store";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
import type { IMessage } from "src/store/types";
|
||||
|
||||
@@ -11,6 +10,7 @@ export const useRedirect = <Type>(
|
||||
message: IMessage,
|
||||
) => {
|
||||
const navigate = useNavigate();
|
||||
const { toastError } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
if (collection && !collection.length) {
|
||||
|
||||
@@ -7,8 +7,9 @@ import {
|
||||
SpeechCredential,
|
||||
User,
|
||||
} from "src/api/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
import { toastError, useSelectState } from "src/store";
|
||||
import { useSelectState } from "src/store";
|
||||
|
||||
import { IMessage, Scope, UserData } from "src/store/types";
|
||||
|
||||
@@ -21,6 +22,7 @@ export const useScopedRedirect = (
|
||||
) => {
|
||||
const navigate = useNavigate();
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
const { toastError } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
@@ -47,5 +49,5 @@ export const useScopedRedirect = (
|
||||
|
||||
navigate(redirect);
|
||||
}
|
||||
}, [user, currentServiceProvider, data]);
|
||||
}, [user, currentServiceProvider, data, toastError]);
|
||||
};
|
||||
|
||||
@@ -2,11 +2,12 @@ import React, { useEffect } from "react";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { toastError, useSelectState, useAccessControl } from "src/store";
|
||||
import { useSelectState, useAccessControl } from "src/store";
|
||||
import { ROUTE_INTERNAL_SETTINGS } from "src/router/routes";
|
||||
|
||||
import type { ACL, IMessage } from "src/store/types";
|
||||
import type { ServiceProvider } from "src/api/types";
|
||||
import { useToast } from "src/components/toast/toast-provider";
|
||||
|
||||
type PassthroughProps = {
|
||||
[key: string]: unknown;
|
||||
@@ -22,6 +23,7 @@ export const withAccessControl = (
|
||||
) => {
|
||||
return function WithAccessControl(Component: React.ComponentType) {
|
||||
return function ComponentWithAccessControl(props: PassthroughProps) {
|
||||
const { toastError } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const hasPermission = useAccessControl(acl);
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
|
||||
Reference in New Issue
Block a user