Merge branch 'main' into enterprise-license
commit
db7e31a84c
|
@ -8,14 +8,13 @@ import {
|
|||
} from "@heroicons/react/outline";
|
||||
import { ChevronLeftIcon } from "@heroicons/react/solid";
|
||||
import Link from "next/link";
|
||||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { InstallAppButton } from "@calcom/app-store/components";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { App as AppType } from "@calcom/types/App";
|
||||
import { Button } from "@calcom/ui";
|
||||
|
||||
//import NavTabs from "@components/NavTabs";
|
||||
import Shell from "@components/Shell";
|
||||
import Badge from "@components/ui/Badge";
|
||||
|
||||
|
@ -60,7 +59,29 @@ export default function App({
|
|||
currency: "USD",
|
||||
useGrouping: false,
|
||||
}).format(price);
|
||||
|
||||
const [installedApp, setInstalledApp] = useState(false);
|
||||
useEffect(() => {
|
||||
async function getInstalledApp(appCredentialType: string) {
|
||||
const queryParam = new URLSearchParams();
|
||||
queryParam.set("app-credential-type", appCredentialType);
|
||||
try {
|
||||
const result = await fetch(`/api/app-store/installed?${queryParam.toString()}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (result.status === 200) {
|
||||
setInstalledApp(true);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.log(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
getInstalledApp(type);
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<Shell large>
|
||||
|
@ -83,7 +104,7 @@ export default function App({
|
|||
</div>
|
||||
|
||||
<div className="mt-4 sm:mt-0 sm:text-right">
|
||||
{isGlobal ? (
|
||||
{isGlobal || installedApp ? (
|
||||
<Button color="secondary" disabled title="This app is globally installed">
|
||||
{t("installed")}
|
||||
</Button>
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { getSession } from "@lib/auth";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
req.session = await getSession({ req });
|
||||
if (req.method === "GET" && req.session && req.session.user.id && req.query) {
|
||||
const { "app-credential-type": appCredentialType } = req.query;
|
||||
if (!appCredentialType && Array.isArray(appCredentialType)) {
|
||||
return res.status(400);
|
||||
}
|
||||
|
||||
const userId = req.session.user.id;
|
||||
try {
|
||||
const installedApp = await prisma?.credential.findFirst({
|
||||
where: {
|
||||
type: appCredentialType as string,
|
||||
userId: userId,
|
||||
},
|
||||
});
|
||||
|
||||
if (installedApp && !!installedApp.key) {
|
||||
res.status(200);
|
||||
} else {
|
||||
res.status(404);
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(500);
|
||||
}
|
||||
} else {
|
||||
res.status(400);
|
||||
}
|
||||
res.end();
|
||||
}
|
|
@ -46,7 +46,7 @@ export default function Bookings() {
|
|||
|
||||
return (
|
||||
<Shell heading={t("bookings")} subtitle={t("bookings_description")}>
|
||||
<WipeMyCalActionButton trpc={trpc} />
|
||||
<WipeMyCalActionButton trpc={trpc} bookingStatus={status} bookingsEmpty={isEmpty} />
|
||||
<BookingsShell>
|
||||
<div className="-mx-4 flex flex-col sm:mx-auto">
|
||||
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
"request_reschedule_title_attendee": "Request to reschedule your booking",
|
||||
"request_reschedule_subtitle": "{{organizer}} has cancelled the booking and requested you to pick another time.",
|
||||
"request_reschedule_title_organizer": "You have requested {{attendee}} to reschedule",
|
||||
"request_reschedule_subtitle_organizer": "You have cancelled the booking and {{attendee}} should be pick a new booking time with you.",
|
||||
"request_reschedule_subtitle_organizer": "You have cancelled the booking and {{attendee}} should pick a new booking time with you.",
|
||||
"reschedule_reason": "Reason for reschedule",
|
||||
"hi_user_name": "Hi {{name}}",
|
||||
"ics_event_title": "{{eventType}} with {{name}}",
|
||||
|
@ -89,7 +89,7 @@
|
|||
"meeting_url": "Meeting URL",
|
||||
"meeting_request_rejected": "Your meeting request has been rejected",
|
||||
"rescheduled_event_type_subject": "Rescheduled: {{eventType}} with {{name}} at {{date}}",
|
||||
"requested_to_reschedule_subject_attendee": "Action Required Reschedule: Please book a new to time for {{eventType}} with {{name}}",
|
||||
"requested_to_reschedule_subject_attendee": "Action Required Reschedule: Please book a new time for {{eventType}} with {{name}}",
|
||||
"rejected_event_type_with_organizer": "Rejected: {{eventType}} with {{organizer}} on {{date}}",
|
||||
"hi": "Hi",
|
||||
"join_team": "Join team",
|
||||
|
@ -735,7 +735,7 @@
|
|||
"personal_note": "Name this key",
|
||||
"personal_note_placeholder": "E.g. Development",
|
||||
"api_key_no_note": "Nameless API key",
|
||||
"api_key_never_expires":"This API key has no expiration date",
|
||||
"api_key_never_expires": "This API key has no expiration date",
|
||||
"edit_api_key": "Edit API key",
|
||||
"never_expire_key": "Never expires",
|
||||
"delete_api_key": "Revoke API key",
|
||||
|
|
|
@ -12,10 +12,13 @@ function useAddAppMutation(type: App["type"], options?: Parameters<typeof useMut
|
|||
};
|
||||
const stateStr = encodeURIComponent(JSON.stringify(state));
|
||||
const searchParams = `?state=${stateStr}`;
|
||||
|
||||
const res = await fetch(`/api/integrations/${appName}/add` + searchParams);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error("Something went wrong");
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
window.location.href = json.url;
|
||||
}, options);
|
||||
|
|
|
@ -38,5 +38,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
}
|
||||
return res.status(500);
|
||||
}
|
||||
return res.redirect("/apps/installed");
|
||||
|
||||
return res.status(200).json({ url: "/apps/installed" });
|
||||
}
|
||||
|
|
|
@ -6,25 +6,32 @@ import { ConfirmDialog } from "./confirmDialog";
|
|||
|
||||
interface IWipeMyCalActionButtonProps {
|
||||
trpc: any;
|
||||
bookingsEmpty: boolean;
|
||||
bookingStatus: "upcoming" | "past" | "cancelled";
|
||||
}
|
||||
|
||||
const WipeMyCalActionButton = (props: IWipeMyCalActionButtonProps) => {
|
||||
const { trpc } = props;
|
||||
|
||||
const { trpc, bookingsEmpty, bookingStatus } = props;
|
||||
const [openDialog, setOpenDialog] = useState(false);
|
||||
const { isSuccess, isLoading, data } = trpc.useQuery(["viewer.integrations"]);
|
||||
|
||||
if (bookingStatus !== "upcoming" || bookingsEmpty) {
|
||||
return <></>;
|
||||
}
|
||||
const wipeMyCalCredentials: { credentialIds: number[] } = data?.other?.items.find(
|
||||
(item: { type: string }) => item.type === "wipemycal_other"
|
||||
);
|
||||
|
||||
const [credentialId] = wipeMyCalCredentials?.credentialIds || [false];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{data &&
|
||||
isSuccess &&
|
||||
!isLoading &&
|
||||
data?.other?.items.find((item: { type: string }) => item.type === "wipemycal_other") && (
|
||||
<>
|
||||
<ConfirmDialog trpc={trpc} isOpenDialog={openDialog} setIsOpenDialog={setOpenDialog} />
|
||||
<Button onClick={() => setOpenDialog(true)}>Wipe Today</Button>
|
||||
</>
|
||||
)}
|
||||
{data && isSuccess && !isLoading && credentialId && (
|
||||
<>
|
||||
<ConfirmDialog trpc={trpc} isOpenDialog={openDialog} setIsOpenDialog={setOpenDialog} />
|
||||
<Button onClick={() => setOpenDialog(true)}>Wipe Today</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue