* refactor: move Segment, BookerLayoutSelector, CreateLicenseKeyForm from packages/features to apps/web/modules Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: update relative imports to absolute paths in moved files Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: move useBookingLocation and blocklist components from packages/features to apps/web/modules Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: move EmbedTabs and embed hooks from packages/features to apps/web/modules Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix * migrate useBookerUrl * migrate embed tabs * fix * fix * fix * mv ThemeLabel * mv settings headers to webn * update imports * clean up --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
173 lines
5.9 KiB
TypeScript
173 lines
5.9 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
|
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|
import { trpc } from "@calcom/trpc/react";
|
|
|
|
import { OAuthClientsAdminSkeleton } from "./oauth-clients-admin-skeleton";
|
|
import { showToast } from "@calcom/ui/components/toast";
|
|
import SettingsHeader from "@calcom/web/modules/settings/components/SettingsHeader";
|
|
|
|
import type { OAuthClientCreateFormValues } from "../oauth/create/OAuthClientCreateModal";
|
|
import { OAuthClientCreateDialog } from "../oauth/create/OAuthClientCreateModal";
|
|
import { OAuthClientPreviewDialog } from "../oauth/create/OAuthClientPreviewDialog";
|
|
import { OAuthClientDetailsDialog, type OAuthClientDetails } from "../oauth/view/OAuthClientDetailsDialog";
|
|
import { OAuthClientsList } from "../oauth/OAuthClientsList";
|
|
|
|
export default function OAuthClientsAdminView() {
|
|
const { t } = useLocale();
|
|
const utils = trpc.useUtils();
|
|
const [isCreatingClient, setIsCreatingClient] = useState(false);
|
|
const [createdClient, setCreatedClient] = useState<OAuthClientDetails | null>(null);
|
|
const [selectedClient, setSelectedClient] = useState<OAuthClientDetails | null>(null);
|
|
|
|
const { data: pendingClients, isLoading: isPendingClientsLoading } = trpc.viewer.oAuth.listClients.useQuery({
|
|
status: "PENDING",
|
|
});
|
|
const { data: rejectedClients, isLoading: isRejectedClientsLoading } = trpc.viewer.oAuth.listClients.useQuery({
|
|
status: "REJECTED",
|
|
});
|
|
const { data: approvedClients, isLoading: isApprovedClientsLoading } = trpc.viewer.oAuth.listClients.useQuery({
|
|
status: "APPROVED",
|
|
});
|
|
|
|
const createMutation = trpc.viewer.oAuth.createClient.useMutation({
|
|
onSuccess: async (data) => {
|
|
setCreatedClient({
|
|
clientId: data.clientId,
|
|
clientSecret: data.clientSecret,
|
|
name: data.name,
|
|
purpose: data.purpose ?? "",
|
|
status: data.status,
|
|
redirectUri: data.redirectUri,
|
|
logo: data.logo || null,
|
|
});
|
|
showToast(t("oauth_client_created"), "success");
|
|
utils.viewer.oAuth.listClients.invalidate();
|
|
},
|
|
onError: (error) => {
|
|
showToast(`${t("oauth_client_create_error")}: ${error.message}`, "error");
|
|
},
|
|
});
|
|
|
|
const updateStatusMutation = trpc.viewer.oAuth.updateClient.useMutation({
|
|
onSuccess: async (data) => {
|
|
showToast(
|
|
t("oauth_client_status_updated", { name: data.name, status: data.status }),
|
|
"success"
|
|
);
|
|
|
|
setSelectedClient((prev) => {
|
|
if (!prev) return prev;
|
|
if (prev.clientId !== data.clientId) return prev;
|
|
return {
|
|
...prev,
|
|
status: data.status,
|
|
rejectionReason: data.rejectionReason,
|
|
};
|
|
});
|
|
|
|
utils.viewer.oAuth.listClients.invalidate();
|
|
},
|
|
onError: (error) => {
|
|
showToast(`${t("oauth_client_status_update_error")}: ${error.message}`, "error");
|
|
},
|
|
});
|
|
|
|
const handleCreateClient = (values: OAuthClientCreateFormValues) => {
|
|
createMutation.mutate({
|
|
name: values.name,
|
|
purpose: values.purpose,
|
|
redirectUri: values.redirectUri,
|
|
websiteUrl: values.websiteUrl,
|
|
logo: values.logo,
|
|
enablePkce: values.enablePkce,
|
|
});
|
|
};
|
|
|
|
const handleCloseDialog = () => {
|
|
setIsCreatingClient(false);
|
|
setCreatedClient(null);
|
|
};
|
|
|
|
const handleCloseClientDialog = () => {
|
|
setSelectedClient(null);
|
|
};
|
|
|
|
const handleApprove = (clientId: string) => {
|
|
updateStatusMutation.mutate({ clientId, status: "APPROVED" });
|
|
};
|
|
|
|
const handleReject = (input: { clientId: string; rejectionReason: string }) => {
|
|
updateStatusMutation.mutate({ clientId: input.clientId, status: "REJECTED", rejectionReason: input.rejectionReason });
|
|
};
|
|
|
|
if (isPendingClientsLoading || isRejectedClientsLoading || isApprovedClientsLoading) {
|
|
return <OAuthClientsAdminSkeleton />;
|
|
}
|
|
|
|
return (
|
|
<SettingsHeader
|
|
title={t("oauth_clients_admin")}
|
|
description={t("oauth_clients_admin_description")}>
|
|
<div className="space-y-10">
|
|
<div className="space-y-3" data-testid="oauth-client-admin-pending-section">
|
|
<h2 className="text-emphasis text-base font-semibold">{t("pending")}</h2>
|
|
<OAuthClientsList
|
|
clients={pendingClients ?? []}
|
|
onSelectClient={(client) => setSelectedClient(client)}
|
|
showStatus
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-3" data-testid="oauth-client-admin-rejected-section">
|
|
<h2 className="text-emphasis text-base font-semibold">{t("rejected")}</h2>
|
|
<OAuthClientsList
|
|
clients={rejectedClients ?? []}
|
|
onSelectClient={(client) => setSelectedClient(client)}
|
|
showStatus
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-3" data-testid="oauth-client-admin-approved-section">
|
|
<h2 className="text-emphasis text-base font-semibold">{t("approved")}</h2>
|
|
<OAuthClientsList
|
|
clients={approvedClients ?? []}
|
|
onSelectClient={(client) => setSelectedClient(client)}
|
|
showStatus
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{createdClient ? (
|
|
<OAuthClientPreviewDialog
|
|
open={isCreatingClient}
|
|
onOpenChange={setIsCreatingClient}
|
|
title={t("oauth_client_created")}
|
|
description={t("oauth_client_created_description")}
|
|
client={createdClient}
|
|
onClose={handleCloseDialog}
|
|
/>
|
|
) : (
|
|
<OAuthClientCreateDialog
|
|
open={isCreatingClient}
|
|
onOpenChange={setIsCreatingClient}
|
|
isSubmitting={createMutation.isPending}
|
|
onSubmit={handleCreateClient}
|
|
onClose={handleCloseDialog}
|
|
/>
|
|
)}
|
|
|
|
<OAuthClientDetailsDialog
|
|
open={Boolean(selectedClient)}
|
|
onOpenChange={(open) => !open && handleCloseClientDialog()}
|
|
client={selectedClient}
|
|
onApprove={handleApprove}
|
|
onReject={handleReject}
|
|
isStatusChangePending={updateStatusMutation.isPending}
|
|
/>
|
|
</SettingsHeader>
|
|
);
|
|
}
|