diff --git a/frontend/src/pages/Admin/Invitations/NewInviteModal/index.jsx b/frontend/src/pages/Admin/Invitations/NewInviteModal/index.jsx
index 102200dc6cd..8340ef083c6 100644
--- a/frontend/src/pages/Admin/Invitations/NewInviteModal/index.jsx
+++ b/frontend/src/pages/Admin/Invitations/NewInviteModal/index.jsx
@@ -1,9 +1,10 @@
import React, { useEffect, useState } from "react";
-import { X } from "@phosphor-icons/react";
+import { X, Copy, Check } from "@phosphor-icons/react";
import Admin from "@/models/admin";
import Workspace from "@/models/workspace";
+import showToast from "@/utils/toast";
-export default function NewInviteModal({ closeModal }) {
+export default function NewInviteModal({ closeModal, onSuccess }) {
const [invite, setInvite] = useState(null);
const [error, setError] = useState(null);
const [copied, setCopied] = useState(false);
@@ -18,15 +19,22 @@ export default function NewInviteModal({ closeModal }) {
role: null,
workspaceIds: selectedWorkspaceIds,
});
- if (!!newInvite) setInvite(newInvite);
+ if (!!newInvite) {
+ setInvite(newInvite);
+ onSuccess();
+ }
setError(error);
};
+
const copyInviteLink = () => {
if (!invite) return false;
window.navigator.clipboard.writeText(
`${window.location.origin}/accept-invite/${invite.code}`
);
setCopied(true);
+ showToast("Invite link copied to clipboard", "success", {
+ clear: true,
+ });
};
const handleWorkspaceSelection = (workspaceId) => {
@@ -79,12 +87,30 @@ export default function NewInviteModal({ closeModal }) {
{error &&
Error: {error}
}
{invite && (
-
+
+
+
+ {copied ? (
+
+ ) : (
+
+ )}
+
+
)}
After creation you will be able to copy the invite and send it
@@ -126,13 +152,13 @@ export default function NewInviteModal({ closeModal }) {
)}
-
+
{!invite ? (
<>
Cancel
@@ -145,12 +171,11 @@ export default function NewInviteModal({ closeModal }) {
>
) : (
- {copied ? "Copied Link" : "Copy Invite Link"}
+ Close
)}
diff --git a/frontend/src/pages/Admin/Invitations/index.jsx b/frontend/src/pages/Admin/Invitations/index.jsx
index b79c55d5fe8..d7f9d5ed28a 100644
--- a/frontend/src/pages/Admin/Invitations/index.jsx
+++ b/frontend/src/pages/Admin/Invitations/index.jsx
@@ -4,7 +4,6 @@ import { isMobile } from "react-device-detect";
import * as Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css";
import { EnvelopeSimple } from "@phosphor-icons/react";
-import usePrefersDarkMode from "@/hooks/usePrefersDarkMode";
import Admin from "@/models/admin";
import InviteRow from "./InviteRow";
import NewInviteModal from "./NewInviteModal";
@@ -14,6 +13,18 @@ import CTAButton from "@/components/lib/CTAButton";
export default function AdminInvites() {
const { isOpen, openModal, closeModal } = useModal();
+ const [loading, setLoading] = useState(true);
+ const [invites, setInvites] = useState([]);
+
+ const fetchInvites = async () => {
+ const _invites = await Admin.invites();
+ setInvites(_invites);
+ setLoading(false);
+ };
+
+ useEffect(() => {
+ fetchInvites();
+ }, []);
return (
@@ -44,71 +55,58 @@ export default function AdminInvites() {
-
+ {loading ? (
+
+ ) : (
+
+
+
+
+ Status
+
+
+ Accepted By
+
+
+ Created By
+
+
+ Created
+
+
+ {" "}
+
+
+
+
+ {invites.length === 0 ? (
+
+
+ No invitations found
+
+
+ ) : (
+ invites.map((invite) => (
+
+ ))
+ )}
+
+
+ )}
-
+
);
}
-
-function InvitationsContainer() {
- const darkMode = usePrefersDarkMode();
- const [loading, setLoading] = useState(true);
- const [invites, setInvites] = useState([]);
-
- useEffect(() => {
- async function fetchInvites() {
- const _invites = await Admin.invites();
- setInvites(_invites);
- setLoading(false);
- }
- fetchInvites();
- }, []);
-
- if (loading) {
- return (
-
- );
- }
-
- return (
-
-
-
-
- Status
-
-
- Accepted By
-
-
- Created By
-
-
- Created
-
-
- {" "}
-
-
-
-
- {invites.map((invite) => (
-
- ))}
-
-
- );
-}
diff --git a/frontend/src/pages/GeneralSettings/ApiKeys/NewApiKeyModal/index.jsx b/frontend/src/pages/GeneralSettings/ApiKeys/NewApiKeyModal/index.jsx
index 8ca766d49f6..8e0248b5553 100644
--- a/frontend/src/pages/GeneralSettings/ApiKeys/NewApiKeyModal/index.jsx
+++ b/frontend/src/pages/GeneralSettings/ApiKeys/NewApiKeyModal/index.jsx
@@ -1,11 +1,12 @@
import React, { useEffect, useState } from "react";
-import { X } from "@phosphor-icons/react";
+import { X, Copy, Check } from "@phosphor-icons/react";
import Admin from "@/models/admin";
import paths from "@/utils/paths";
import { userFromStorage } from "@/utils/request";
import System from "@/models/system";
+import showToast from "@/utils/toast";
-export default function NewApiKeyModal({ closeModal }) {
+export default function NewApiKeyModal({ closeModal, onSuccess }) {
const [apiKey, setApiKey] = useState(null);
const [error, setError] = useState(null);
const [copied, setCopied] = useState(false);
@@ -17,14 +18,22 @@ export default function NewApiKeyModal({ closeModal }) {
const Model = !!user ? Admin : System;
const { apiKey: newApiKey, error } = await Model.generateApiKey();
- if (!!newApiKey) setApiKey(newApiKey);
+ if (!!newApiKey) {
+ setApiKey(newApiKey);
+ onSuccess();
+ }
setError(error);
};
+
const copyApiKey = () => {
if (!apiKey) return false;
window.navigator.clipboard.writeText(apiKey.secret);
setCopied(true);
+ showToast("API key copied to clipboard", "success", {
+ clear: true,
+ });
};
+
useEffect(() => {
function resetStatus() {
if (!copied) return false;
@@ -57,12 +66,30 @@ export default function NewApiKeyModal({ closeModal }) {
{error &&
Error: {error}
}
{apiKey && (
-
+
+
+
+ {copied ? (
+
+ ) : (
+
+ )}
+
+
)}
Once created the API key can be used to programmatically access
@@ -77,13 +104,13 @@ export default function NewApiKeyModal({ closeModal }) {
Read the API documentation →
-
+
{!apiKey ? (
<>
Cancel
@@ -91,17 +118,16 @@ export default function NewApiKeyModal({ closeModal }) {
type="submit"
className="transition-all duration-300 bg-white text-black hover:opacity-60 px-4 py-2 rounded-lg text-sm"
>
- Create API key
+ Create API Key
>
) : (
- {copied ? "Copied API key" : "Copy API key"}
+ Close
)}
diff --git a/frontend/src/pages/GeneralSettings/ApiKeys/index.jsx b/frontend/src/pages/GeneralSettings/ApiKeys/index.jsx
index b3bffdfcf16..9c2e3c2a61c 100644
--- a/frontend/src/pages/GeneralSettings/ApiKeys/index.jsx
+++ b/frontend/src/pages/GeneralSettings/ApiKeys/index.jsx
@@ -18,6 +18,25 @@ import { useTranslation } from "react-i18next";
export default function AdminApiKeys() {
const { isOpen, openModal, closeModal } = useModal();
const { t } = useTranslation();
+ const [loading, setLoading] = useState(true);
+ const [apiKeys, setApiKeys] = useState([]);
+
+ const fetchExistingKeys = async () => {
+ const user = userFromStorage();
+ const Model = !!user ? Admin : System;
+ const { apiKeys: foundKeys } = await Model.getApiKeys();
+ setApiKeys(foundKeys);
+ setLoading(false);
+ };
+
+ useEffect(() => {
+ fetchExistingKeys();
+ }, []);
+
+ const removeApiKey = (id) => {
+ setApiKeys((prevKeys) => prevKeys.filter((apiKey) => apiKey.id !== id));
+ };
+
return (
@@ -54,70 +73,62 @@ export default function AdminApiKeys() {
-
+ {loading ? (
+
+ ) : (
+
+
+
+
+ {t("api.table.key")}
+
+
+ {t("api.table.by")}
+
+
+ {t("api.table.created")}
+
+
+ {" "}
+
+
+
+
+ {apiKeys.length === 0 ? (
+
+
+ No API keys found
+
+
+ ) : (
+ apiKeys.map((apiKey) => (
+
+ ))
+ )}
+
+
+ )}
-
+
);
}
-
-function ApiKeysContainer() {
- const [loading, setLoading] = useState(true);
- const [apiKeys, setApiKeys] = useState([]);
- const { t } = useTranslation();
-
- useEffect(() => {
- async function fetchExistingKeys() {
- const user = userFromStorage();
- const Model = !!user ? Admin : System;
- const { apiKeys: foundKeys } = await Model.getApiKeys();
- setApiKeys(foundKeys);
- setLoading(false);
- }
- fetchExistingKeys();
- }, []);
-
- if (loading) {
- return (
-
- );
- }
-
- return (
-
-
-
-
- {t("api.table.key")}
-
-
- {t("api.table.by")}
-
-
- {t("api.table.created")}
-
-
- {" "}
-
-
-
-
- {apiKeys.map((apiKey) => (
-
- ))}
-
-
- );
-}