Merge branch 'main' into enterprise-license
commit
db7e31a84c
|
@ -8,14 +8,13 @@ import {
|
||||||
} from "@heroicons/react/outline";
|
} from "@heroicons/react/outline";
|
||||||
import { ChevronLeftIcon } from "@heroicons/react/solid";
|
import { ChevronLeftIcon } from "@heroicons/react/solid";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { InstallAppButton } from "@calcom/app-store/components";
|
import { InstallAppButton } from "@calcom/app-store/components";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { App as AppType } from "@calcom/types/App";
|
import { App as AppType } from "@calcom/types/App";
|
||||||
import { Button } from "@calcom/ui";
|
import { Button } from "@calcom/ui";
|
||||||
|
|
||||||
//import NavTabs from "@components/NavTabs";
|
|
||||||
import Shell from "@components/Shell";
|
import Shell from "@components/Shell";
|
||||||
import Badge from "@components/ui/Badge";
|
import Badge from "@components/ui/Badge";
|
||||||
|
|
||||||
|
@ -60,7 +59,29 @@ export default function App({
|
||||||
currency: "USD",
|
currency: "USD",
|
||||||
useGrouping: false,
|
useGrouping: false,
|
||||||
}).format(price);
|
}).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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Shell large>
|
<Shell large>
|
||||||
|
@ -83,7 +104,7 @@ export default function App({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 sm:mt-0 sm:text-right">
|
<div className="mt-4 sm:mt-0 sm:text-right">
|
||||||
{isGlobal ? (
|
{isGlobal || installedApp ? (
|
||||||
<Button color="secondary" disabled title="This app is globally installed">
|
<Button color="secondary" disabled title="This app is globally installed">
|
||||||
{t("installed")}
|
{t("installed")}
|
||||||
</Button>
|
</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 (
|
return (
|
||||||
<Shell heading={t("bookings")} subtitle={t("bookings_description")}>
|
<Shell heading={t("bookings")} subtitle={t("bookings_description")}>
|
||||||
<WipeMyCalActionButton trpc={trpc} />
|
<WipeMyCalActionButton trpc={trpc} bookingStatus={status} bookingsEmpty={isEmpty} />
|
||||||
<BookingsShell>
|
<BookingsShell>
|
||||||
<div className="-mx-4 flex flex-col sm:mx-auto">
|
<div className="-mx-4 flex flex-col sm:mx-auto">
|
||||||
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
<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_title_attendee": "Request to reschedule your booking",
|
||||||
"request_reschedule_subtitle": "{{organizer}} has cancelled the booking and requested you to pick another time.",
|
"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_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",
|
"reschedule_reason": "Reason for reschedule",
|
||||||
"hi_user_name": "Hi {{name}}",
|
"hi_user_name": "Hi {{name}}",
|
||||||
"ics_event_title": "{{eventType}} with {{name}}",
|
"ics_event_title": "{{eventType}} with {{name}}",
|
||||||
|
@ -89,7 +89,7 @@
|
||||||
"meeting_url": "Meeting URL",
|
"meeting_url": "Meeting URL",
|
||||||
"meeting_request_rejected": "Your meeting request has been rejected",
|
"meeting_request_rejected": "Your meeting request has been rejected",
|
||||||
"rescheduled_event_type_subject": "Rescheduled: {{eventType}} with {{name}} at {{date}}",
|
"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}}",
|
"rejected_event_type_with_organizer": "Rejected: {{eventType}} with {{organizer}} on {{date}}",
|
||||||
"hi": "Hi",
|
"hi": "Hi",
|
||||||
"join_team": "Join team",
|
"join_team": "Join team",
|
||||||
|
@ -735,7 +735,7 @@
|
||||||
"personal_note": "Name this key",
|
"personal_note": "Name this key",
|
||||||
"personal_note_placeholder": "E.g. Development",
|
"personal_note_placeholder": "E.g. Development",
|
||||||
"api_key_no_note": "Nameless API key",
|
"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",
|
"edit_api_key": "Edit API key",
|
||||||
"never_expire_key": "Never expires",
|
"never_expire_key": "Never expires",
|
||||||
"delete_api_key": "Revoke API key",
|
"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 stateStr = encodeURIComponent(JSON.stringify(state));
|
||||||
const searchParams = `?state=${stateStr}`;
|
const searchParams = `?state=${stateStr}`;
|
||||||
|
|
||||||
const res = await fetch(`/api/integrations/${appName}/add` + searchParams);
|
const res = await fetch(`/api/integrations/${appName}/add` + searchParams);
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error("Something went wrong");
|
throw new Error("Something went wrong");
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
window.location.href = json.url;
|
window.location.href = json.url;
|
||||||
}, options);
|
}, options);
|
||||||
|
|
|
@ -38,5 +38,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
}
|
}
|
||||||
return res.status(500);
|
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 {
|
interface IWipeMyCalActionButtonProps {
|
||||||
trpc: any;
|
trpc: any;
|
||||||
|
bookingsEmpty: boolean;
|
||||||
|
bookingStatus: "upcoming" | "past" | "cancelled";
|
||||||
}
|
}
|
||||||
|
|
||||||
const WipeMyCalActionButton = (props: IWipeMyCalActionButtonProps) => {
|
const WipeMyCalActionButton = (props: IWipeMyCalActionButtonProps) => {
|
||||||
const { trpc } = props;
|
const { trpc, bookingsEmpty, bookingStatus } = props;
|
||||||
|
|
||||||
const [openDialog, setOpenDialog] = useState(false);
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
const { isSuccess, isLoading, data } = trpc.useQuery(["viewer.integrations"]);
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{data &&
|
{data && isSuccess && !isLoading && credentialId && (
|
||||||
isSuccess &&
|
<>
|
||||||
!isLoading &&
|
<ConfirmDialog trpc={trpc} isOpenDialog={openDialog} setIsOpenDialog={setOpenDialog} />
|
||||||
data?.other?.items.find((item: { type: string }) => item.type === "wipemycal_other") && (
|
<Button onClick={() => setOpenDialog(true)}>Wipe Today</Button>
|
||||||
<>
|
</>
|
||||||
<ConfirmDialog trpc={trpc} isOpenDialog={openDialog} setIsOpenDialog={setOpenDialog} />
|
)}
|
||||||
<Button onClick={() => setOpenDialog(true)}>Wipe Today</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue