mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-05-12 04:40:09 +02:00
Cleanup user management
This commit is contained in:
parent
54c2547d89
commit
b07fdbf1d1
@ -10,8 +10,6 @@ from fastapi import Depends
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from danswer.auth.invited_users import get_invited_users
|
||||
from danswer.auth.invited_users import write_invited_users
|
||||
from danswer.auth.users import current_admin_user
|
||||
from danswer.configs.app_configs import GENERATIVE_MODEL_ACCESS_CHECK_FREQ
|
||||
from danswer.configs.app_configs import TOKEN_BUDGET_GLOBALLY_ENABLED
|
||||
@ -28,7 +26,6 @@ from danswer.db.feedback import update_document_boost
|
||||
from danswer.db.feedback import update_document_hidden
|
||||
from danswer.db.index_attempt import cancel_indexing_attempts_for_connector
|
||||
from danswer.db.models import User
|
||||
from danswer.db.users import get_user_by_email
|
||||
from danswer.document_index.document_index_utils import get_both_index_names
|
||||
from danswer.document_index.factory import get_default_document_index
|
||||
from danswer.dynamic_configs.factory import get_dynamic_config_store
|
||||
@ -40,7 +37,6 @@ from danswer.server.documents.models import ConnectorCredentialPairIdentifier
|
||||
from danswer.server.manage.models import BoostDoc
|
||||
from danswer.server.manage.models import BoostUpdateRequest
|
||||
from danswer.server.manage.models import HiddenUpdateRequest
|
||||
from danswer.server.manage.models import UserByEmail
|
||||
from danswer.utils.logger import setup_logger
|
||||
|
||||
router = APIRouter(prefix="/manage")
|
||||
@ -235,66 +231,3 @@ def update_token_budget_settings(
|
||||
# Store the settings in the dynamic config store
|
||||
get_dynamic_config_store().store(TOKEN_BUDGET_SETTINGS, settings_json)
|
||||
return {"message": "Token budget settings updated successfully."}
|
||||
|
||||
|
||||
@router.put("/admin/users")
|
||||
def bulk_invite_users(
|
||||
emails: list[str] = Body(..., embed=True),
|
||||
_: User | None = Depends(current_admin_user),
|
||||
) -> int:
|
||||
all_emails = list(set(emails) | set(get_invited_users()))
|
||||
return write_invited_users(all_emails)
|
||||
|
||||
|
||||
@router.patch("/admin/remove-invited-user")
|
||||
def remove_invited_user(
|
||||
user_email: UserByEmail,
|
||||
_: User | None = Depends(current_admin_user),
|
||||
) -> int:
|
||||
user_emails = get_invited_users()
|
||||
remaining_users = [user for user in user_emails if user != user_email.user_email]
|
||||
return write_invited_users(remaining_users)
|
||||
|
||||
|
||||
@router.patch("/admin/deactivate-user")
|
||||
def deactivate_user(
|
||||
user_email: UserByEmail,
|
||||
current_user: User = Depends(current_admin_user),
|
||||
db_session: Session = Depends(get_session),
|
||||
) -> None:
|
||||
if current_user.email == user_email.user_email:
|
||||
raise HTTPException(status_code=400, detail="You cannot deactivate yourself")
|
||||
|
||||
user_to_deactivate = get_user_by_email(
|
||||
email=user_email.user_email, db_session=db_session
|
||||
)
|
||||
|
||||
if not user_to_deactivate:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
if user_to_deactivate.is_active is False:
|
||||
logger.warning("{} is already deactivated".format(user_to_deactivate.email))
|
||||
|
||||
user_to_deactivate.is_active = False
|
||||
db_session.add(user_to_deactivate)
|
||||
db_session.commit()
|
||||
|
||||
|
||||
@router.patch("/admin/activate-user")
|
||||
def activate_user(
|
||||
user_email: UserByEmail,
|
||||
_: User | None = Depends(current_admin_user),
|
||||
db_session: Session = Depends(get_session),
|
||||
) -> None:
|
||||
user_to_activate = get_user_by_email(
|
||||
email=user_email.user_email, db_session=db_session
|
||||
)
|
||||
if not user_to_activate:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
if user_to_activate.is_active is True:
|
||||
logger.warning("{} is already activated".format(user_to_activate.email))
|
||||
|
||||
user_to_activate.is_active = True
|
||||
db_session.add(user_to_activate)
|
||||
db_session.commit()
|
||||
|
@ -1,6 +1,7 @@
|
||||
import re
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi import Body
|
||||
from fastapi import Depends
|
||||
from fastapi import HTTPException
|
||||
from fastapi import status
|
||||
@ -9,6 +10,7 @@ from sqlalchemy import update
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from danswer.auth.invited_users import get_invited_users
|
||||
from danswer.auth.invited_users import write_invited_users
|
||||
from danswer.auth.noauth_user import fetch_no_auth_user
|
||||
from danswer.auth.noauth_user import set_no_auth_user_preferences
|
||||
from danswer.auth.schemas import UserRole
|
||||
@ -17,6 +19,7 @@ from danswer.auth.users import current_admin_user
|
||||
from danswer.auth.users import current_user
|
||||
from danswer.auth.users import optional_user
|
||||
from danswer.configs.app_configs import AUTH_TYPE
|
||||
from danswer.configs.app_configs import VALID_EMAIL_DOMAINS
|
||||
from danswer.configs.constants import AuthType
|
||||
from danswer.db.engine import get_session
|
||||
from danswer.db.models import User
|
||||
@ -30,9 +33,14 @@ from danswer.server.manage.models import UserRoleResponse
|
||||
from danswer.server.models import FullUserSnapshot
|
||||
from danswer.server.models import InvitedUserSnapshot
|
||||
from danswer.server.models import MinimalUserSnapshot
|
||||
from danswer.utils.logger import setup_logger
|
||||
|
||||
logger = setup_logger()
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
USERS_PAGE_SIZE = 10
|
||||
|
||||
|
||||
@ -104,16 +112,94 @@ def list_all_users(
|
||||
)
|
||||
for user in users
|
||||
][accepted_page * USERS_PAGE_SIZE : (accepted_page + 1) * USERS_PAGE_SIZE],
|
||||
invited=[
|
||||
InvitedUserSnapshot(email=email)
|
||||
for email in invited_emails
|
||||
if email not in accepted_emails
|
||||
][invited_page * USERS_PAGE_SIZE : (invited_page + 1) * USERS_PAGE_SIZE],
|
||||
invited=[InvitedUserSnapshot(email=email) for email in invited_emails][
|
||||
invited_page * USERS_PAGE_SIZE : (invited_page + 1) * USERS_PAGE_SIZE
|
||||
],
|
||||
accepted_pages=accepted_count // USERS_PAGE_SIZE + 1,
|
||||
invited_pages=invited_count // USERS_PAGE_SIZE + 1,
|
||||
)
|
||||
|
||||
|
||||
@router.put("/manage/admin/users")
|
||||
def bulk_invite_users(
|
||||
emails: list[str] = Body(..., embed=True),
|
||||
current_user: User | None = Depends(current_admin_user),
|
||||
) -> int:
|
||||
if current_user is None:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Auth is disabled, cannot invite users"
|
||||
)
|
||||
|
||||
all_emails = list(set(emails) | set(get_invited_users()))
|
||||
return write_invited_users(all_emails)
|
||||
|
||||
|
||||
@router.patch("/manage/admin/remove-invited-user")
|
||||
def remove_invited_user(
|
||||
user_email: UserByEmail,
|
||||
_: User | None = Depends(current_admin_user),
|
||||
) -> int:
|
||||
user_emails = get_invited_users()
|
||||
remaining_users = [user for user in user_emails if user != user_email.user_email]
|
||||
return write_invited_users(remaining_users)
|
||||
|
||||
|
||||
@router.patch("/manage/admin/deactivate-user")
|
||||
def deactivate_user(
|
||||
user_email: UserByEmail,
|
||||
current_user: User | None = Depends(current_admin_user),
|
||||
db_session: Session = Depends(get_session),
|
||||
) -> None:
|
||||
if current_user is None:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Auth is disabled, cannot deactivate user"
|
||||
)
|
||||
|
||||
if current_user.email == user_email.user_email:
|
||||
raise HTTPException(status_code=400, detail="You cannot deactivate yourself")
|
||||
|
||||
user_to_deactivate = get_user_by_email(
|
||||
email=user_email.user_email, db_session=db_session
|
||||
)
|
||||
|
||||
if not user_to_deactivate:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
if user_to_deactivate.is_active is False:
|
||||
logger.warning("{} is already deactivated".format(user_to_deactivate.email))
|
||||
|
||||
user_to_deactivate.is_active = False
|
||||
db_session.add(user_to_deactivate)
|
||||
db_session.commit()
|
||||
|
||||
|
||||
@router.patch("/manage/admin/activate-user")
|
||||
def activate_user(
|
||||
user_email: UserByEmail,
|
||||
_: User | None = Depends(current_admin_user),
|
||||
db_session: Session = Depends(get_session),
|
||||
) -> None:
|
||||
user_to_activate = get_user_by_email(
|
||||
email=user_email.user_email, db_session=db_session
|
||||
)
|
||||
if not user_to_activate:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
if user_to_activate.is_active is True:
|
||||
logger.warning("{} is already activated".format(user_to_activate.email))
|
||||
|
||||
user_to_activate.is_active = True
|
||||
db_session.add(user_to_activate)
|
||||
db_session.commit()
|
||||
|
||||
|
||||
@router.get("/manage/admin/valid-domains")
|
||||
def get_valid_domains(
|
||||
_: User | None = Depends(current_admin_user),
|
||||
) -> list[str]:
|
||||
return VALID_EMAIL_DOMAINS
|
||||
|
||||
|
||||
"""Endpoints for all"""
|
||||
|
||||
|
||||
|
@ -4,19 +4,9 @@ import SignedUpUserTable from "@/components/admin/users/SignedUpUserTable";
|
||||
import { SearchBar } from "@/components/search/SearchBar";
|
||||
import { useState } from "react";
|
||||
import { FiPlusSquare } from "react-icons/fi";
|
||||
import Link from "next/link";
|
||||
import { Modal } from "@/components/Modal";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableHeaderCell,
|
||||
TableBody,
|
||||
TableCell,
|
||||
Button,
|
||||
Text,
|
||||
} from "@tremor/react";
|
||||
import { Button, Text } from "@tremor/react";
|
||||
import { LoadingAnimation } from "@/components/Loading";
|
||||
import { AdminPageTitle } from "@/components/admin/Title";
|
||||
import { usePopup, PopupSpec } from "@/components/admin/connectors/Popup";
|
||||
@ -24,11 +14,43 @@ import { UsersIcon } from "@/components/icons/icons";
|
||||
import { errorHandlingFetcher } from "@/lib/fetcher";
|
||||
import { type User, UserStatus } from "@/lib/types";
|
||||
import useSWR, { mutate } from "swr";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
import { ErrorCallout } from "@/components/ErrorCallout";
|
||||
import { HidableSection } from "@/app/admin/assistants/HidableSection";
|
||||
import BulkAdd from "@/components/admin/users/BulkAdd";
|
||||
|
||||
const ValidDomainsDisplay = ({ validDomains }: { validDomains: string[] }) => {
|
||||
if (!validDomains.length) {
|
||||
return (
|
||||
<div className="text-sm">
|
||||
No invited users. Anyone can sign up with a valid email address. To
|
||||
restrict access you can:
|
||||
<div className="flex flex-wrap ml-2 mt-1">
|
||||
(1) Invite users above. Once a user has been invited, only emails that
|
||||
have explicitly been invited will be able to sign-up.
|
||||
</div>
|
||||
<div className="mt-1 ml-2">
|
||||
(2) Set the{" "}
|
||||
<b className="font-mono w-fit h-fit">VALID_EMAIL_DOMAINS</b>{" "}
|
||||
environment variable to a comma separated list of email domains. This
|
||||
will restrict access to users with email addresses from these domains.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="text-sm">
|
||||
No invited users. Anyone with an email address with any of the following
|
||||
domains can sign up: <i>{validDomains.join(", ")}</i>.
|
||||
<div className="mt-2">
|
||||
To further restrict access you can invite users above. Once a user has
|
||||
been invited, only emails that have explicitly been invited will be able
|
||||
to sign-up.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface UsersResponse {
|
||||
accepted: User[];
|
||||
invited: User[];
|
||||
@ -46,11 +68,18 @@ const UsersTables = ({
|
||||
const [invitedPage, setInvitedPage] = useState(1);
|
||||
const [acceptedPage, setAcceptedPage] = useState(1);
|
||||
const { data, isLoading, mutate, error } = useSWR<UsersResponse>(
|
||||
`/api/manage/users?q=${encodeURI(q)}&accepted_page=${acceptedPage - 1}&invited_page=${invitedPage - 1}`,
|
||||
`/api/manage/users?q=${encodeURI(q)}&accepted_page=${
|
||||
acceptedPage - 1
|
||||
}&invited_page=${invitedPage - 1}`,
|
||||
errorHandlingFetcher
|
||||
);
|
||||
const {
|
||||
data: validDomains,
|
||||
isLoading: isLoadingDomains,
|
||||
error: domainsError,
|
||||
} = useSWR<string[]>("/api/manage/admin/valid-domains", errorHandlingFetcher);
|
||||
|
||||
if (isLoading) {
|
||||
if (isLoading || isLoadingDomains) {
|
||||
return <LoadingAnimation text="Loading" />;
|
||||
}
|
||||
|
||||
@ -63,18 +92,45 @@ const UsersTables = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (domainsError || !validDomains) {
|
||||
return (
|
||||
<ErrorCallout
|
||||
errorTitle="Error loading valid domains"
|
||||
errorMsg={domainsError?.info?.detail}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const { accepted, invited, accepted_pages, invited_pages } = data;
|
||||
|
||||
// remove users that are already accepted
|
||||
const finalInvited = invited.filter(
|
||||
(user) => !accepted.map((u) => u.email).includes(user.email)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<InvitedUserTable
|
||||
users={invited}
|
||||
setPopup={setPopup}
|
||||
currentPage={invitedPage}
|
||||
onPageChange={setInvitedPage}
|
||||
totalPages={invited_pages}
|
||||
mutate={mutate}
|
||||
/>
|
||||
<HidableSection sectionTitle="Invited Users">
|
||||
{invited.length > 0 ? (
|
||||
finalInvited.length > 0 ? (
|
||||
<InvitedUserTable
|
||||
users={finalInvited}
|
||||
setPopup={setPopup}
|
||||
currentPage={invitedPage}
|
||||
onPageChange={setInvitedPage}
|
||||
totalPages={invited_pages}
|
||||
mutate={mutate}
|
||||
/>
|
||||
) : (
|
||||
<div className="text-sm">
|
||||
To invite additional teammates, use the <b>Invite Users</b> button
|
||||
above!
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<ValidDomainsDisplay validDomains={validDomains} />
|
||||
)}
|
||||
</HidableSection>
|
||||
<SignedUpUserTable
|
||||
users={accepted}
|
||||
setPopup={setPopup}
|
||||
@ -129,6 +185,13 @@ const AddUserButton = ({
|
||||
type: "success",
|
||||
});
|
||||
};
|
||||
const onFailure = async (res: Response) => {
|
||||
const error = (await res.json()).detail;
|
||||
setPopup({
|
||||
message: `Failed to invite users - ${error}`,
|
||||
type: "error",
|
||||
});
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Button className="w-fit" onClick={() => setModal(true)}>
|
||||
@ -143,7 +206,7 @@ const AddUserButton = ({
|
||||
<Text className="font-medium text-base">
|
||||
Add the email addresses to import, separated by whitespaces.
|
||||
</Text>
|
||||
<BulkAdd onSuccess={onSuccess} />
|
||||
<BulkAdd onSuccess={onSuccess} onFailure={onFailure} />
|
||||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
|
@ -1,6 +1,21 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@tremor/react";
|
||||
import Link from "next/link";
|
||||
import { FiLogIn } from "react-icons/fi";
|
||||
|
||||
const Page = () => {
|
||||
return (
|
||||
<div>Unable to login, please try again and/or contact an administrator</div>
|
||||
<div className="flex flex-col items-center justify-center h-screen">
|
||||
<div className="font-bold">
|
||||
Unable to login, please try again and/or contact an administrator.
|
||||
</div>
|
||||
<Link href="/auth/login" className="w-fit">
|
||||
<Button className="mt-4" size="xs" icon={FiLogIn}>
|
||||
Back to login
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -7,8 +7,8 @@ export interface PopupSpec {
|
||||
|
||||
export const Popup: React.FC<PopupSpec> = ({ message, type }) => (
|
||||
<div
|
||||
className={`fixed bottom-4 left-4 p-4 rounded-md shadow-lg text-white z-30 ${
|
||||
type === "success" ? "bg-green-500" : "bg-red-500"
|
||||
className={`fixed bottom-4 left-4 p-4 rounded-md shadow-lg text-white z-[100] ${
|
||||
type === "success" ? "bg-green-500" : "bg-error"
|
||||
}`}
|
||||
>
|
||||
{message}
|
||||
|
@ -1,13 +1,8 @@
|
||||
"use client";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
import { RobotIcon } from "@/components/icons/icons";
|
||||
|
||||
import { withFormik, FormikProps, FormikErrors, Form, Field } from "formik";
|
||||
|
||||
import { BackButton } from "@/components/BackButton";
|
||||
import { Card } from "@tremor/react";
|
||||
import { AdminPageTitle } from "@/components/admin/Title";
|
||||
import { fetchAssistantEditorInfoSS } from "@/lib/assistants/fetchPersonaEditorInfoSS";
|
||||
import { Button, Text } from "@tremor/react";
|
||||
import { Button } from "@tremor/react";
|
||||
|
||||
const WHITESPACE_SPLIT = /\s+/;
|
||||
const EMAIL_REGEX = /[^@]+@[^.]+\.[^.]/;
|
||||
@ -24,6 +19,7 @@ const addUsers = async (url: string, { arg }: { arg: Array<string> }) => {
|
||||
|
||||
interface FormProps {
|
||||
onSuccess: () => void;
|
||||
onFailure: (res: Response) => void;
|
||||
}
|
||||
|
||||
interface FormValues {
|
||||
@ -77,13 +73,15 @@ const AddUserForm = withFormik<FormProps, FormValues>({
|
||||
await addUsers("/api/manage/admin/users", { arg: emails }).then((res) => {
|
||||
if (res.ok) {
|
||||
formikBag.props.onSuccess();
|
||||
} else {
|
||||
formikBag.props.onFailure(res);
|
||||
}
|
||||
});
|
||||
},
|
||||
})(AddUserFormRenderer);
|
||||
|
||||
const BulkAdd = ({ onSuccess }: FormProps) => {
|
||||
return <AddUserForm onSuccess={onSuccess} />;
|
||||
const BulkAdd = ({ onSuccess, onFailure }: FormProps) => {
|
||||
return <AddUserForm onSuccess={onSuccess} onFailure={onFailure} />;
|
||||
};
|
||||
|
||||
export default BulkAdd;
|
||||
|
@ -68,43 +68,41 @@ const InvitedUserTable = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<HidableSection sectionTitle="Invited Users">
|
||||
<>
|
||||
{totalPages > 1 ? (
|
||||
<CenteredPageSelector
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
onPageChange={onPageChange}
|
||||
/>
|
||||
) : null}
|
||||
<Table className="overflow-visible">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell>Email</TableHeaderCell>
|
||||
<TableHeaderCell>
|
||||
<div className="flex justify-end">Actions</div>
|
||||
</TableHeaderCell>
|
||||
<>
|
||||
<Table className="overflow-visible">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell>Email</TableHeaderCell>
|
||||
<TableHeaderCell>
|
||||
<div className="flex justify-end">Actions</div>
|
||||
</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{users.map((user) => (
|
||||
<TableRow key={user.email}>
|
||||
<TableCell>{user.email}</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex justify-end">
|
||||
<RemoveUserButton
|
||||
user={user}
|
||||
onSuccess={onRemovalSuccess}
|
||||
onError={onRemovalError}
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{users.map((user) => (
|
||||
<TableRow key={user.email}>
|
||||
<TableCell>{user.email}</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex justify-end">
|
||||
<RemoveUserButton
|
||||
user={user}
|
||||
onSuccess={onRemovalSuccess}
|
||||
onError={onRemovalError}
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</>
|
||||
</HidableSection>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{totalPages > 1 ? (
|
||||
<CenteredPageSelector
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
onPageChange={onPageChange}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
TableCell,
|
||||
Button,
|
||||
} from "@tremor/react";
|
||||
import { PageSelector } from "@/components/PageSelector";
|
||||
|
||||
interface Props {
|
||||
users: Array<User>;
|
||||
@ -45,6 +44,7 @@ const PromoterButton = ({
|
||||
className="w-min"
|
||||
onClick={() => trigger({ user_email: user.email })}
|
||||
disabled={isMutating}
|
||||
size="xs"
|
||||
>
|
||||
{promote ? "Promote" : "Demote"} to {promote ? "Admin" : "Basic"} User
|
||||
</Button>
|
||||
@ -54,28 +54,38 @@ const PromoterButton = ({
|
||||
const DeactivaterButton = ({
|
||||
user,
|
||||
deactivate,
|
||||
onSuccess,
|
||||
onError,
|
||||
setPopup,
|
||||
mutate,
|
||||
}: {
|
||||
user: User;
|
||||
deactivate: boolean;
|
||||
onSuccess: () => void;
|
||||
onError: (message: string) => void;
|
||||
setPopup: (spec: PopupSpec) => void;
|
||||
mutate: () => void;
|
||||
}) => {
|
||||
const { trigger, isMutating } = useSWRMutation(
|
||||
deactivate
|
||||
? "/api/manage/admin/deactivate-user"
|
||||
: "/api/manage/admin/activate-user",
|
||||
userMutationFetcher,
|
||||
{ onSuccess, onError }
|
||||
{
|
||||
onSuccess: () => {
|
||||
mutate();
|
||||
setPopup({
|
||||
message: `User ${deactivate ? "deactivated" : "activated"}!`,
|
||||
type: "success",
|
||||
});
|
||||
},
|
||||
onError: (errorMsg) => setPopup({ message: errorMsg, type: "error" }),
|
||||
}
|
||||
);
|
||||
return (
|
||||
<Button
|
||||
className="w-min"
|
||||
onClick={() => trigger({ user_email: user.email })}
|
||||
disabled={isMutating}
|
||||
size="xs"
|
||||
>
|
||||
{deactivate ? "Deactivate" : "Activate"} Access
|
||||
{deactivate ? "Deactivate" : "Activate"}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@ -116,34 +126,8 @@ const SignedUpUserTable = ({
|
||||
onError(`Unable to demote admin - ${errorMsg}`);
|
||||
};
|
||||
|
||||
const onDeactivateSuccess = () => {
|
||||
mutate();
|
||||
setPopup({
|
||||
message: "User deactivated!",
|
||||
type: "success",
|
||||
});
|
||||
};
|
||||
const onDeactivateError = (errorMsg: string) => {
|
||||
setPopup({
|
||||
message: `Unable to deactivate user - ${errorMsg}`,
|
||||
type: "error",
|
||||
});
|
||||
};
|
||||
const onActivateSuccess = () => {
|
||||
mutate();
|
||||
setPopup({
|
||||
message: "User activate!",
|
||||
type: "success",
|
||||
});
|
||||
};
|
||||
const onActivateError = (errorMsg: string) => {
|
||||
setPopup({
|
||||
message: `Unable to activate user - ${errorMsg}`,
|
||||
type: "error",
|
||||
});
|
||||
};
|
||||
return (
|
||||
<HidableSection sectionTitle="Signed Up Users">
|
||||
<HidableSection sectionTitle="Current Users">
|
||||
<>
|
||||
{totalPages > 1 ? (
|
||||
<CenteredPageSelector
|
||||
@ -186,8 +170,8 @@ const SignedUpUserTable = ({
|
||||
<DeactivaterButton
|
||||
user={user}
|
||||
deactivate={user.status === UserStatus.live}
|
||||
onSuccess={onDeactivateSuccess}
|
||||
onError={onDeactivateError}
|
||||
setPopup={setPopup}
|
||||
mutate={mutate}
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
@ -12,8 +12,8 @@ const userMutationFetcher = async (
|
||||
}),
|
||||
}).then(async (res) => {
|
||||
if (res.ok) return res.json();
|
||||
const text = await res.text();
|
||||
throw Error(text);
|
||||
const errorDetail = (await res.json()).detail;
|
||||
throw Error(errorDetail);
|
||||
});
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user