chore: recategorize apps (#9306)
* Remove unused code in InstalledAppsLayout * Add new app categories "crm", "conferencing" and "messaging" * Sort getAppCategories entries alphabetically * Fix 404s on new category pages (and remove hardcoded category lists) * Fix admin apps list not showing "no available apps" for new categories * Recategorise apps * Sync seed-app-store categories with config files * Replace unnecessary seed-app-store.config.json with appStoreMetadata * Copy video.svg to conferencing.svg * Add messaging.svg * Remove web3 from getAppCategories (used by installed apps, admin apps) * Fix app-store-cli categories - Add conferencing - Add CRM - Remove video - Remove web3 * Remove outdated web3 comment in seed-app-store * Update apps/web/public/static/locales/en/common.json * Add cron script to keep db apps in sync with app metadata * Add redirect for app category "video" to "conferencing" * Fix up "video" category overrides to apply to conferencing * Fix conferencing apps not showing as a location for non-team users * Restore "installed_app" string for conferencing apps * Make linter happier * Remove my "installed_app_conferencing_description" as this was fixed upstream * Quick tidy up * Add dry-run to syncAppMeta via CRON_ENABLE_APP_SYNC env * Replace console.log with logger in syncAppMeta --------- Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: alannnc <alannnc@gmail.com> Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> Co-authored-by: Omar López <zomars@me.com>pull/9497/head^2
parent
8cfb9902ff
commit
171827f547
|
@ -81,6 +81,10 @@ CALCOM_TELEMETRY_DISABLED=
|
||||||
# ApiKey for cronjobs
|
# ApiKey for cronjobs
|
||||||
CRON_API_KEY='0cc0e6c35519bba620c9360cfe3e68d0'
|
CRON_API_KEY='0cc0e6c35519bba620c9360cfe3e68d0'
|
||||||
|
|
||||||
|
# Whether to automatically keep app metadata in the database in sync with the metadata/config files. When disabled, the
|
||||||
|
# sync runs in a reporting-only dry-run mode.
|
||||||
|
CRON_ENABLE_APP_SYNC=false
|
||||||
|
|
||||||
# Application Key for symmetric encryption and decryption
|
# Application Key for symmetric encryption and decryption
|
||||||
# must be 32 bytes for AES256 encryption algorithm
|
# must be 32 bytes for AES256 encryption algorithm
|
||||||
# You can use: `openssl rand -base64 24` to generate one
|
# You can use: `openssl rand -base64 24` to generate one
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
name: Cron - syncAppMeta
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
# "Scheduled workflows run on the latest commit on the default or base branch."
|
||||||
|
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
|
||||||
|
schedule:
|
||||||
|
# Runs “Every month at 1st (see https://crontab.guru)
|
||||||
|
- cron: "0 0 1 * *"
|
||||||
|
jobs:
|
||||||
|
cron-syncAppMeta:
|
||||||
|
env:
|
||||||
|
APP_URL: ${{ secrets.APP_URL }}
|
||||||
|
CRON_API_KEY: ${{ secrets.CRON_API_KEY }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: cURL request
|
||||||
|
if: ${{ env.APP_URL && env.CRON_API_KEY }}
|
||||||
|
run: |
|
||||||
|
curl ${{ secrets.APP_URL }}/api/cron/syncAppMeta \
|
||||||
|
-X POST \
|
||||||
|
-H 'content-type: application/json' \
|
||||||
|
-H 'authorization: ${{ secrets.CRON_API_KEY }}' \
|
||||||
|
--fail
|
|
@ -11,6 +11,7 @@ env:
|
||||||
INPUT_ENV_GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
|
INPUT_ENV_GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
|
||||||
INPUT_ENV_GOOGLE_LOGIN_ENABLED: true
|
INPUT_ENV_GOOGLE_LOGIN_ENABLED: true
|
||||||
# INPUT_ENV_CRON_API_KEY: xxx
|
# INPUT_ENV_CRON_API_KEY: xxx
|
||||||
|
# INPUT_ENV_CRON_ENABLE_APP_SYNC: true|false
|
||||||
INPUT_ENV_CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
INPUT_ENV_CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||||
INPUT_ENV_NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
|
INPUT_ENV_NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
|
||||||
INPUT_ENV_STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
|
INPUT_ENV_STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
|
||||||
|
|
4
app.json
4
app.json
|
@ -37,6 +37,10 @@
|
||||||
"description": "ApiKey for cronjobs",
|
"description": "ApiKey for cronjobs",
|
||||||
"value": ""
|
"value": ""
|
||||||
},
|
},
|
||||||
|
"CRON_ENABLE_APP_SYNC": {
|
||||||
|
"description": "Whether to automatically keep app metadata in the database in sync with the metadata/config files. When disabled, the sync runs in a reporting-only dry-run mode.",
|
||||||
|
"value": "false"
|
||||||
|
},
|
||||||
"SEND_FEEDBACK_EMAIL": {
|
"SEND_FEEDBACK_EMAIL": {
|
||||||
"description": "Send feedback email",
|
"description": "Send feedback email",
|
||||||
"value": ""
|
"value": ""
|
||||||
|
|
|
@ -117,8 +117,16 @@ export const EventSetupTab = (
|
||||||
const [selectedLocation, setSelectedLocation] = useState<LocationOption | undefined>(undefined);
|
const [selectedLocation, setSelectedLocation] = useState<LocationOption | undefined>(undefined);
|
||||||
const [multipleDuration, setMultipleDuration] = useState(eventType.metadata?.multipleDuration);
|
const [multipleDuration, setMultipleDuration] = useState(eventType.metadata?.multipleDuration);
|
||||||
|
|
||||||
const locationOptions = props.locationOptions.filter((option) => {
|
const locationOptions = props.locationOptions.map((locationOption) => {
|
||||||
return !team ? option.label !== "Conferencing" : true;
|
const options = locationOption.options.filter((option) => {
|
||||||
|
// Skip "Organizer's Default App" for non-team members
|
||||||
|
return !team ? option.label !== t("organizer_default_conferencing_app") : true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...locationOption,
|
||||||
|
options,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const multipleDurationOptions = [5, 10, 15, 20, 25, 30, 45, 50, 60, 75, 80, 90, 120, 180].map((mins) => ({
|
const multipleDurationOptions = [5, 10, 15, 20, 25, 30, 45, 50, 60, 75, 80, 90, 120, 180].map((mins) => ({
|
||||||
|
|
|
@ -493,6 +493,16 @@ const nextConfig = {
|
||||||
destination: "/event-types?openIntercom=true",
|
destination: "/event-types?openIntercom=true",
|
||||||
permanent: true,
|
permanent: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
source: "/apps/categories/video",
|
||||||
|
destination: "/apps/categories/conferencing",
|
||||||
|
permanent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/apps/installed/video",
|
||||||
|
destination: "/apps/installed/conferencing",
|
||||||
|
permanent: true,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (process.env.NEXT_PUBLIC_WEBAPP_URL === "https://app.cal.com") {
|
if (process.env.NEXT_PUBLIC_WEBAPP_URL === "https://app.cal.com") {
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
import { getAppWithMetadata } from "@calcom/app-store/_appRegistry";
|
||||||
|
import logger from "@calcom/lib/logger";
|
||||||
|
import { prisma } from "@calcom/prisma";
|
||||||
|
import type { AppCategories, Prisma } from "@calcom/prisma/client";
|
||||||
|
|
||||||
|
const isDryRun = process.env.CRON_ENABLE_APP_SYNC !== "true";
|
||||||
|
const log = logger.getChildLogger({
|
||||||
|
prefix: ["[api/cron/syncAppMeta]", ...(isDryRun ? ["(dry-run)"] : [])],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* syncAppMeta makes sure any app metadata that has been replicated into the database
|
||||||
|
* remains synchronized with any changes made to the app config files.
|
||||||
|
*/
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
const apiKey = req.headers.authorization || req.query.apiKey;
|
||||||
|
if (process.env.CRON_API_KEY !== apiKey) {
|
||||||
|
res.status(401).json({ message: "Not authenticated" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req.method !== "POST") {
|
||||||
|
res.status(405).json({ message: "Invalid method" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info(`🧐 Checking DB apps are in-sync with app metadata`);
|
||||||
|
|
||||||
|
const dbApps = await prisma.app.findMany();
|
||||||
|
|
||||||
|
for await (const dbApp of dbApps) {
|
||||||
|
const app = await getAppWithMetadata(dbApp);
|
||||||
|
const updates: Prisma.AppUpdateManyMutationInput = {};
|
||||||
|
|
||||||
|
if (!app) {
|
||||||
|
log.warn(`💀 App ${dbApp.slug} (${dbApp.dirName}) no longer exists.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for any changes in the app categories (tolerates changes in ordering)
|
||||||
|
if (
|
||||||
|
dbApp.categories.length !== app.categories.length ||
|
||||||
|
!dbApp.categories.every((category) => app.categories.includes(category))
|
||||||
|
) {
|
||||||
|
updates["categories"] = app.categories as AppCategories[];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbApp.dirName !== (app.dirName ?? app.slug)) {
|
||||||
|
updates["dirName"] = app.dirName ?? app.slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(updates).length > 0) {
|
||||||
|
log.info(`🔨 Updating app ${dbApp.slug} with ${Object.keys(updates).join(", ")}`);
|
||||||
|
if (!isDryRun) {
|
||||||
|
await prisma.app.update({
|
||||||
|
where: { slug: dbApp.slug },
|
||||||
|
data: updates,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info(`✅ App ${dbApp.slug} is up-to-date and correct`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ ok: true });
|
||||||
|
}
|
|
@ -6,11 +6,11 @@ import { AppSettings } from "@calcom/app-store/_components/AppSettings";
|
||||||
import { InstallAppButton } from "@calcom/app-store/components";
|
import { InstallAppButton } from "@calcom/app-store/components";
|
||||||
import type { EventLocationType } from "@calcom/app-store/locations";
|
import type { EventLocationType } from "@calcom/app-store/locations";
|
||||||
import { getEventLocationTypeFromApp } from "@calcom/app-store/locations";
|
import { getEventLocationTypeFromApp } from "@calcom/app-store/locations";
|
||||||
import { InstalledAppVariants } from "@calcom/app-store/utils";
|
|
||||||
import { AppSetDefaultLinkDialog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
|
import { AppSetDefaultLinkDialog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
|
||||||
import DisconnectIntegrationModal from "@calcom/features/apps/components/DisconnectIntegrationModal";
|
import DisconnectIntegrationModal from "@calcom/features/apps/components/DisconnectIntegrationModal";
|
||||||
import { BulkEditDefaultConferencingModal } from "@calcom/features/eventtypes/components/BulkEditDefaultConferencingModal";
|
import { BulkEditDefaultConferencingModal } from "@calcom/features/eventtypes/components/BulkEditDefaultConferencingModal";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
import { AppCategories } from "@calcom/prisma/enums";
|
||||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import type { App } from "@calcom/types/App";
|
import type { App } from "@calcom/types/App";
|
||||||
|
@ -29,11 +29,14 @@ import {
|
||||||
DropdownItem,
|
DropdownItem,
|
||||||
showToast,
|
showToast,
|
||||||
} from "@calcom/ui";
|
} from "@calcom/ui";
|
||||||
|
import type { LucideIcon } from "@calcom/ui/components/icon";
|
||||||
import {
|
import {
|
||||||
BarChart,
|
BarChart,
|
||||||
Calendar,
|
Calendar,
|
||||||
|
Contact,
|
||||||
CreditCard,
|
CreditCard,
|
||||||
Grid,
|
Grid,
|
||||||
|
Mail,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
Plus,
|
Plus,
|
||||||
Share2,
|
Share2,
|
||||||
|
@ -101,8 +104,8 @@ function ConnectOrDisconnectIntegrationMenuItem(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IntegrationsContainerProps {
|
interface IntegrationsContainerProps {
|
||||||
variant?: (typeof InstalledAppVariants)[number];
|
variant?: AppCategories;
|
||||||
exclude?: (typeof InstalledAppVariants)[number][];
|
exclude?: AppCategories[];
|
||||||
handleDisconnect: (credentialId: number) => void;
|
handleDisconnect: (credentialId: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,14 +228,19 @@ const IntegrationsContainer = ({
|
||||||
}: IntegrationsContainerProps): JSX.Element => {
|
}: IntegrationsContainerProps): JSX.Element => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const query = trpc.viewer.integrations.useQuery({ variant, exclude, onlyInstalled: true });
|
const query = trpc.viewer.integrations.useQuery({ variant, exclude, onlyInstalled: true });
|
||||||
const emptyIcon = {
|
|
||||||
|
// TODO: Refactor and reuse getAppCategories?
|
||||||
|
const emptyIcon: Record<AppCategories, LucideIcon> = {
|
||||||
calendar: Calendar,
|
calendar: Calendar,
|
||||||
conferencing: Video,
|
conferencing: Video,
|
||||||
automation: Share2,
|
automation: Share2,
|
||||||
analytics: BarChart,
|
analytics: BarChart,
|
||||||
payment: CreditCard,
|
payment: CreditCard,
|
||||||
web3: BarChart,
|
web3: BarChart, // deprecated
|
||||||
other: Grid,
|
other: Grid,
|
||||||
|
video: Video, // deprecated
|
||||||
|
messaging: Mail,
|
||||||
|
crm: Contact,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -267,9 +275,7 @@ const IntegrationsContainer = ({
|
||||||
className="mb-6"
|
className="mb-6"
|
||||||
actions={
|
actions={
|
||||||
<Button
|
<Button
|
||||||
href={
|
href={variant ? `/apps/categories/${variant}` : "/apps"}
|
||||||
variant ? `/apps/categories/${variant === "conferencing" ? "video" : variant}` : "/apps"
|
|
||||||
}
|
|
||||||
color="secondary"
|
color="secondary"
|
||||||
StartIcon={Plus}>
|
StartIcon={Plus}>
|
||||||
{t("add")}
|
{t("add")}
|
||||||
|
@ -285,7 +291,7 @@ const IntegrationsContainer = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const querySchema = z.object({
|
const querySchema = z.object({
|
||||||
category: z.enum(InstalledAppVariants),
|
category: z.nativeEnum(AppCategories),
|
||||||
});
|
});
|
||||||
|
|
||||||
type querySchemaType = z.infer<typeof querySchema>;
|
type querySchemaType = z.infer<typeof querySchema>;
|
||||||
|
@ -299,13 +305,11 @@ export default function InstalledApps() {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const category = router.query.category as querySchemaType["category"];
|
const category = router.query.category as querySchemaType["category"];
|
||||||
const categoryList: querySchemaType["category"][] = [
|
|
||||||
"payment",
|
const categoryList: AppCategories[] = Object.values(AppCategories).filter((category) => {
|
||||||
"conferencing",
|
// Exclude calendar and other from categoryList, we handle those slightly differently below
|
||||||
"automation",
|
return !(category in { other: null, calendar: null });
|
||||||
"analytics",
|
});
|
||||||
"web3",
|
|
||||||
];
|
|
||||||
|
|
||||||
const [data, updateData] = useReducer(
|
const [data, updateData] = useReducer(
|
||||||
(data: ModalState, partialData: Partial<ModalState>) => ({ ...data, ...partialData }),
|
(data: ModalState, partialData: Partial<ModalState>) => ({ ...data, ...partialData }),
|
||||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 52 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 28 KiB |
|
@ -863,12 +863,14 @@
|
||||||
"free_to_use_apps": "Free",
|
"free_to_use_apps": "Free",
|
||||||
"no_category_apps": "No {{category}} apps",
|
"no_category_apps": "No {{category}} apps",
|
||||||
"no_category_apps_description_calendar": "Add a calendar app to check for conflicts to prevent double bookings",
|
"no_category_apps_description_calendar": "Add a calendar app to check for conflicts to prevent double bookings",
|
||||||
"no_category_apps_description_conferencing": "Try adding a conference app to intergrate video call with your clients",
|
"no_category_apps_description_conferencing": "Try adding a conference app for video calls with your clients",
|
||||||
"no_category_apps_description_payment": "Add a payment app to ease transaction between you and your clients",
|
"no_category_apps_description_payment": "Add a payment app to ease transaction between you and your clients",
|
||||||
"no_category_apps_description_analytics": "Add an analytics app for your booking pages",
|
"no_category_apps_description_analytics": "Add an analytics app for your booking pages",
|
||||||
"no_category_apps_description_automation": "Add an automation app to use",
|
"no_category_apps_description_automation": "Add an automation app to use",
|
||||||
"no_category_apps_description_other": "Add any other type of app to do all sorts of things",
|
"no_category_apps_description_other": "Add any other type of app to do all sorts of things",
|
||||||
"no_category_apps_description_web3": "Add a web3 app for your booking pages",
|
"no_category_apps_description_web3": "Add a web3 app for your booking pages",
|
||||||
|
"no_category_apps_description_messaging": "Add a messaging app to set up custom notifications & reminders",
|
||||||
|
"no_category_apps_description_crm": "Add a CRM app to keep track of who you've met with",
|
||||||
"installed_app_calendar_description": "Set the calendars to check for conflicts to prevent double bookings.",
|
"installed_app_calendar_description": "Set the calendars to check for conflicts to prevent double bookings.",
|
||||||
"installed_app_payment_description": "Configure which payment processing services to use when charging your clients.",
|
"installed_app_payment_description": "Configure which payment processing services to use when charging your clients.",
|
||||||
"installed_app_analytics_description": "Configure which analytics apps to use for your booking pages",
|
"installed_app_analytics_description": "Configure which analytics apps to use for your booking pages",
|
||||||
|
@ -876,6 +878,8 @@
|
||||||
"installed_app_conferencing_description": "Configure which conferencing apps to use",
|
"installed_app_conferencing_description": "Configure which conferencing apps to use",
|
||||||
"installed_app_automation_description": "Configure which automation apps to use",
|
"installed_app_automation_description": "Configure which automation apps to use",
|
||||||
"installed_app_web3_description": "Configure which web3 apps to use for your booking pages",
|
"installed_app_web3_description": "Configure which web3 apps to use for your booking pages",
|
||||||
|
"installed_app_messaging_description": "Configure which messaging apps to use for setting up custom notifications & reminders",
|
||||||
|
"installed_app_crm_description": "Configure which CRM apps to use for keeping track of who you've met with",
|
||||||
"analytics": "Analytics",
|
"analytics": "Analytics",
|
||||||
"empty_installed_apps_headline": "No apps installed",
|
"empty_installed_apps_headline": "No apps installed",
|
||||||
"empty_installed_apps_description": "Apps enable you to enhance your workflow and improve your scheduling life significantly.",
|
"empty_installed_apps_description": "Apps enable you to enhance your workflow and improve your scheduling life significantly.",
|
||||||
|
@ -1286,6 +1290,8 @@
|
||||||
"connect_analytics_apps": "Connect analytics apps",
|
"connect_analytics_apps": "Connect analytics apps",
|
||||||
"connect_other_apps": "Connect other apps",
|
"connect_other_apps": "Connect other apps",
|
||||||
"connect_web3_apps": "Connect web3 apps",
|
"connect_web3_apps": "Connect web3 apps",
|
||||||
|
"connect_messaging_apps": "Connect messaging apps",
|
||||||
|
"connect_crm_apps": "Connect CRM apps",
|
||||||
"current_step_of_total": "Step {{currentStep}} of {{maxSteps}}",
|
"current_step_of_total": "Step {{currentStep}} of {{maxSteps}}",
|
||||||
"add_variable": "Add variable",
|
"add_variable": "Add variable",
|
||||||
"custom_phone_number": "Custom phone number",
|
"custom_phone_number": "Custom phone number",
|
||||||
|
@ -1895,6 +1901,8 @@
|
||||||
"set_up_your_profile_description": "Let people know who you are within {{orgName}}, and when they engage with your public link.",
|
"set_up_your_profile_description": "Let people know who you are within {{orgName}}, and when they engage with your public link.",
|
||||||
"my_profile": "My Profile",
|
"my_profile": "My Profile",
|
||||||
"my_settings": "My Settings",
|
"my_settings": "My Settings",
|
||||||
|
"crm": "CRM",
|
||||||
|
"messaging": "Messaging",
|
||||||
"sender_id_info": "Name or number shown as the sender of an SMS (some countries do not allow alphanumeric sender IDs)",
|
"sender_id_info": "Name or number shown as the sender of an SMS (some countries do not allow alphanumeric sender IDs)",
|
||||||
"google_new_spam_policy": "Google’s new spam policy could prevent you from receiving any email and calendar notifications about this booking.",
|
"google_new_spam_policy": "Google’s new spam policy could prevent you from receiving any email and calendar notifications about this booking.",
|
||||||
"resolve": "Resolve",
|
"resolve": "Resolve",
|
||||||
|
|
|
@ -82,14 +82,17 @@ export const AppForm = ({
|
||||||
label: "Category of App",
|
label: "Category of App",
|
||||||
name: "category",
|
name: "category",
|
||||||
type: "select",
|
type: "select",
|
||||||
|
|
||||||
|
// TODO: Refactor and reuse getAppCategories or type as Record<AppCategories,> to enforce consistency
|
||||||
options: [
|
options: [
|
||||||
{ label: "Calendar", value: "calendar" },
|
// Manually sorted alphabetically
|
||||||
{ label: "Video", value: "video" },
|
|
||||||
{ label: "Payment", value: "payment" },
|
|
||||||
{ label: "Messaging", value: "messaging" },
|
|
||||||
{ label: "Web3", value: "web3" },
|
|
||||||
{ label: "Automation", value: "automation" },
|
|
||||||
{ label: "Analytics", value: "analytics" },
|
{ label: "Analytics", value: "analytics" },
|
||||||
|
{ label: "Automation", value: "automation" },
|
||||||
|
{ label: "Calendar", value: "calendar" },
|
||||||
|
{ label: "Conferencing", value: "conferencing" },
|
||||||
|
{ label: "CRM", value: "crm" },
|
||||||
|
{ label: "Messaging", value: "messaging" },
|
||||||
|
{ label: "Payment", value: "payment" },
|
||||||
{ label: "Other", value: "other" },
|
{ label: "Other", value: "other" },
|
||||||
],
|
],
|
||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { Calendar, Video, CreditCard, Share2, BarChart, Grid } from "@calcom/ui/components/icon";
|
import type { AppCategories } from "@calcom/prisma/enums";
|
||||||
|
import type { LucideIcon } from "@calcom/ui/components/icon";
|
||||||
|
import {
|
||||||
|
Calendar,
|
||||||
|
Video,
|
||||||
|
CreditCard,
|
||||||
|
Share2,
|
||||||
|
BarChart,
|
||||||
|
Grid,
|
||||||
|
Mail,
|
||||||
|
Contact,
|
||||||
|
} from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
function getHref(baseURL: string, category: string, useQueryParam: boolean) {
|
function getHref(baseURL: string, category: string, useQueryParam: boolean) {
|
||||||
const baseUrlParsed = new URL(baseURL, WEBAPP_URL);
|
const baseUrlParsed = new URL(baseURL, WEBAPP_URL);
|
||||||
|
@ -7,8 +18,26 @@ function getHref(baseURL: string, category: string, useQueryParam: boolean) {
|
||||||
return useQueryParam ? `${baseUrlParsed.toString()}` : `${baseURL}/${category}`;
|
return useQueryParam ? `${baseUrlParsed.toString()}` : `${baseURL}/${category}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAppCategories = (baseURL: string, useQueryParam: boolean) => {
|
type AppCategoryEntry = {
|
||||||
|
name: AppCategories;
|
||||||
|
href: string;
|
||||||
|
icon: LucideIcon;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAppCategories = (baseURL: string, useQueryParam: boolean): AppCategoryEntry[] => {
|
||||||
|
// Manually sorted alphabetically, but leaving "Other" at the end
|
||||||
|
// TODO: Refactor and type with Record<AppCategories, AppCategoryEntry> to enforce consistency
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
name: "analytics",
|
||||||
|
href: getHref(baseURL, "analytics", useQueryParam),
|
||||||
|
icon: BarChart,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "automation",
|
||||||
|
href: getHref(baseURL, "automation", useQueryParam),
|
||||||
|
icon: Share2,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "calendar",
|
name: "calendar",
|
||||||
href: getHref(baseURL, "calendar", useQueryParam),
|
href: getHref(baseURL, "calendar", useQueryParam),
|
||||||
|
@ -19,26 +48,21 @@ const getAppCategories = (baseURL: string, useQueryParam: boolean) => {
|
||||||
href: getHref(baseURL, "conferencing", useQueryParam),
|
href: getHref(baseURL, "conferencing", useQueryParam),
|
||||||
icon: Video,
|
icon: Video,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "crm",
|
||||||
|
href: getHref(baseURL, "crm", useQueryParam),
|
||||||
|
icon: Contact,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "messaging",
|
||||||
|
href: getHref(baseURL, "messaging", useQueryParam),
|
||||||
|
icon: Mail,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "payment",
|
name: "payment",
|
||||||
href: getHref(baseURL, "payment", useQueryParam),
|
href: getHref(baseURL, "payment", useQueryParam),
|
||||||
icon: CreditCard,
|
icon: CreditCard,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "automation",
|
|
||||||
href: getHref(baseURL, "automation", useQueryParam),
|
|
||||||
icon: Share2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "analytics",
|
|
||||||
href: getHref(baseURL, "analytics", useQueryParam),
|
|
||||||
icon: BarChart,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "web3",
|
|
||||||
href: getHref(baseURL, "web3", useQueryParam),
|
|
||||||
icon: BarChart,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "other",
|
name: "other",
|
||||||
href: getHref(baseURL, "other", useQueryParam),
|
href: getHref(baseURL, "other", useQueryParam),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
import { InstalledAppVariants } from "../utils";
|
import { AppCategories } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
const variantSchema = z.enum(InstalledAppVariants);
|
const variantSchema = z.nativeEnum(AppCategories);
|
||||||
|
|
||||||
export default function getInstalledAppPath(
|
export default function getInstalledAppPath(
|
||||||
{ variant, slug }: { variant?: string; slug?: string },
|
{ variant, slug }: { variant?: string; slug?: string },
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Cal.com",
|
"publisher": "Cal.com",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"description": "Discover radically unique video calls designed to help hybrid-remote teams create, collaborate and celebrate together.",
|
"description": "Discover radically unique video calls designed to help hybrid-remote teams create, collaborate and celebrate together.",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"description": "Feel connected with your remote team. Team events, new hire onboardings, coffee chats, all on Campfire. No more awkward Zoom calls.\r\r",
|
"description": "Feel connected with your remote team. Team events, new hire onboardings, coffee chats, all on Campfire. No more awkward Zoom calls.\r\r",
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "other",
|
"variant": "other",
|
||||||
"categories": ["other"],
|
"categories": ["crm"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"description": "Close is the inside sales CRM of choice for startups and SMBs. Make more calls, send more emails and close more deals starting today.",
|
"description": "Close is the inside sales CRM of choice for startups and SMBs. Make more calls, send more emails and close more deals starting today.",
|
||||||
|
|
|
@ -9,10 +9,10 @@ export const metadata = {
|
||||||
type: "daily_video",
|
type: "daily_video",
|
||||||
variant: "conferencing",
|
variant: "conferencing",
|
||||||
url: "https://daily.co",
|
url: "https://daily.co",
|
||||||
categories: ["video"],
|
categories: ["conferencing"],
|
||||||
logo: "icon.svg",
|
logo: "icon.svg",
|
||||||
publisher: "Cal.com",
|
publisher: "Cal.com",
|
||||||
category: "video",
|
category: "conferencing",
|
||||||
slug: "daily-video",
|
slug: "daily-video",
|
||||||
title: "Cal Video",
|
title: "Cal Video",
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://discord.com/",
|
"url": "https://discord.com/",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "support@cal.com",
|
"email": "support@cal.com",
|
||||||
"appData": {
|
"appData": {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://github.com/shivamklr",
|
"url": "https://github.com/shivamklr",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Shivam Kalra",
|
"publisher": "Shivam Kalra",
|
||||||
"email": "shivamkalra98@gmail.com",
|
"email": "shivamkalra98@gmail.com",
|
||||||
"appData": {
|
"appData": {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://github.com/Mythie",
|
"url": "https://github.com/Mythie",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Lucas Smith",
|
"publisher": "Lucas Smith",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"description": "Facetime makes it super simple for collaborating teams to jump on a video call.",
|
"description": "Facetime makes it super simple for collaborating teams to jump on a video call.",
|
||||||
|
|
|
@ -8,8 +8,8 @@ export const metadata = {
|
||||||
description: _package.description,
|
description: _package.description,
|
||||||
installed: !!(process.env.GOOGLE_API_CREDENTIALS && validJson(process.env.GOOGLE_API_CREDENTIALS)),
|
installed: !!(process.env.GOOGLE_API_CREDENTIALS && validJson(process.env.GOOGLE_API_CREDENTIALS)),
|
||||||
slug: "google-meet",
|
slug: "google-meet",
|
||||||
category: "video",
|
category: "conferencing",
|
||||||
categories: ["video"],
|
categories: ["conferencing"],
|
||||||
type: "google_video",
|
type: "google_video",
|
||||||
title: "Google Meet",
|
title: "Google Meet",
|
||||||
variant: "conferencing",
|
variant: "conferencing",
|
||||||
|
|
|
@ -11,7 +11,7 @@ export const metadata = {
|
||||||
logo: "icon.svg",
|
logo: "icon.svg",
|
||||||
publisher: "Cal.com",
|
publisher: "Cal.com",
|
||||||
url: "https://hubspot.com/",
|
url: "https://hubspot.com/",
|
||||||
categories: ["other"],
|
categories: ["crm"],
|
||||||
label: "HubSpot CRM",
|
label: "HubSpot CRM",
|
||||||
slug: "hubspot",
|
slug: "hubspot",
|
||||||
title: "HubSpot CRM",
|
title: "HubSpot CRM",
|
||||||
|
|
|
@ -9,11 +9,11 @@ export const metadata = {
|
||||||
installed: true,
|
installed: true,
|
||||||
type: "huddle01_video",
|
type: "huddle01_video",
|
||||||
variant: "conferencing",
|
variant: "conferencing",
|
||||||
categories: ["video", "web3"],
|
categories: ["conferencing"],
|
||||||
logo: "icon.svg",
|
logo: "icon.svg",
|
||||||
publisher: "huddle01.com",
|
publisher: "huddle01.com",
|
||||||
url: "https://huddle01.com",
|
url: "https://huddle01.com",
|
||||||
category: "web3",
|
category: "conferencing",
|
||||||
slug: "huddle01",
|
slug: "huddle01",
|
||||||
title: "Huddle01",
|
title: "Huddle01",
|
||||||
isGlobal: false,
|
isGlobal: false,
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const metadata = {
|
||||||
installed: true,
|
installed: true,
|
||||||
type: "jitsi_video",
|
type: "jitsi_video",
|
||||||
variant: "conferencing",
|
variant: "conferencing",
|
||||||
categories: ["video"],
|
categories: ["conferencing"],
|
||||||
logo: "icon.svg",
|
logo: "icon.svg",
|
||||||
publisher: "Cal.com",
|
publisher: "Cal.com",
|
||||||
url: "https://jitsi.org/",
|
url: "https://jitsi.org/",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "support@cal.com",
|
"email": "support@cal.com",
|
||||||
"appData": {
|
"appData": {
|
||||||
|
|
|
@ -9,8 +9,7 @@
|
||||||
"verified": true,
|
"verified": true,
|
||||||
"rating": 4.3,
|
"rating": 4.3,
|
||||||
"reviews": 69,
|
"reviews": 69,
|
||||||
"category": "video",
|
"categories": ["conferencing"],
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "msteams",
|
"slug": "msteams",
|
||||||
"title": "MS Teams (Requires work/school account)",
|
"title": "MS Teams (Requires work/school account)",
|
||||||
"trending": true,
|
"trending": true,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://ping.gg",
|
"url": "https://ping.gg",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Ping.gg",
|
"publisher": "Ping.gg",
|
||||||
"email": "support@ping.gg",
|
"email": "support@ping.gg",
|
||||||
"description": "Ping.gg makes high quality video collaborations easier than ever. Think 'Zoom for streamers and creators'. Join a call in 3 clicks, manage audio and video like a pro, and copy-paste your guests straight into OBS",
|
"description": "Ping.gg makes high quality video collaborations easier than ever. Think 'Zoom for streamers and creators'. Join a call in 3 clicks, manage audio and video like a pro, and copy-paste your guests straight into OBS",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://github.com/eluce2",
|
"url": "https://github.com/eluce2",
|
||||||
"variant": "other",
|
"variant": "other",
|
||||||
"categories": ["other"],
|
"categories": ["automation"],
|
||||||
"publisher": "Eric Luce",
|
"publisher": "Eric Luce",
|
||||||
"email": "info@restlessmindstech.com",
|
"email": "info@restlessmindstech.com",
|
||||||
"description": "Quickly share your Cal.com meeting links with Raycast",
|
"description": "Quickly share your Cal.com meeting links with Raycast",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon-dark.svg",
|
"logo": "icon-dark.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"description": "Your online recording studio. The easiest way to record podcasts and videos in studio quality from anywhere. All from the browser.",
|
"description": "Your online recording studio. The easiest way to record podcasts and videos in studio quality from anywhere. All from the browser.",
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"logo": "icon-dark.svg",
|
"logo": "icon-dark.svg",
|
||||||
"url": "https://cal.com/resources/feature/routing-forms",
|
"url": "https://cal.com/resources/feature/routing-forms",
|
||||||
"variant": "other",
|
"variant": "other",
|
||||||
"categories": ["other"],
|
"categories": ["automation"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"licenseRequired": true,
|
"licenseRequired": true,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.png",
|
"logo": "icon.png",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "other_calendar",
|
"variant": "other_calendar",
|
||||||
"categories": ["other"],
|
"categories": ["crm"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"description": "Salesforce (Sales Cloud) is a cloud-based application designed to help your salespeople sell smarter and faster by centralizing customer information, logging their interactions with your company, and automating many of the tasks salespeople do every day.",
|
"description": "Salesforce (Sales Cloud) is a cloud-based application designed to help your salespeople sell smarter and faster by centralizing customer information, logging their interactions with your company, and automating many of the tasks salespeople do every day.",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "logo.png",
|
"logo": "logo.png",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "other_calendar",
|
"variant": "other_calendar",
|
||||||
"categories": ["other"],
|
"categories": ["crm"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"description": "SendGrid delivers your transactional and marketing emails through the world's largest cloud-based email delivery platform.",
|
"description": "SendGrid delivers your transactional and marketing emails through the world's largest cloud-based email delivery platform.",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["messaging"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "support@cal.com",
|
"email": "support@cal.com",
|
||||||
"description": "Schedule a chat with your guests or have a Signal Video call.",
|
"description": "Schedule a chat with your guests or have a Signal Video call.",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon-dark.svg",
|
"logo": "icon-dark.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "support@cal.com",
|
"email": "support@cal.com",
|
||||||
"description": "Video meetings made for music.\rCreate your own virtual music classroom, easily.",
|
"description": "Video meetings made for music.\rCreate your own virtual music classroom, easily.",
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://sylaps.com",
|
"url": "https://sylaps.com",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Sylaps Inc",
|
"publisher": "Sylaps Inc",
|
||||||
"email": "support@sylaps.com",
|
"email": "support@sylaps.com",
|
||||||
"description": "Free Audio and Video Conferencing, Online Collaboration, Screen Sharing on web browser, mobile and desktop.",
|
"description": "Free Audio and Video Conferencing, Online Collaboration, Screen Sharing on web browser, mobile and desktop.",
|
||||||
|
|
|
@ -8,9 +8,9 @@ export const metadata = {
|
||||||
type: "tandem_video",
|
type: "tandem_video",
|
||||||
title: "Tandem Video",
|
title: "Tandem Video",
|
||||||
variant: "conferencing",
|
variant: "conferencing",
|
||||||
categories: ["video"],
|
categories: ["conferencing"],
|
||||||
slug: "tandem",
|
slug: "tandem",
|
||||||
category: "video",
|
category: "conferencing",
|
||||||
logo: "icon.svg",
|
logo: "icon.svg",
|
||||||
publisher: "",
|
publisher: "",
|
||||||
url: "",
|
url: "",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["messaging"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "support@cal.com",
|
"email": "support@cal.com",
|
||||||
"description": "Schedule a chat with your guests or have a Telegram Video call.",
|
"description": "Schedule a chat with your guests or have a Telegram Video call.",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://example.com/link",
|
"url": "https://example.com/link",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Cal.com Inc",
|
"publisher": "Cal.com Inc",
|
||||||
"email": "support@cal.com",
|
"email": "support@cal.com",
|
||||||
"appData": {
|
"appData": {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon-dark.svg",
|
"logo": "icon-dark.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "other",
|
"variant": "other",
|
||||||
"categories": ["other"],
|
"categories": ["automation"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"description": "Adds a link to copy Typeform Redirect URL",
|
"description": "Adds a link to copy Typeform Redirect URL",
|
||||||
|
|
|
@ -6,6 +6,7 @@ import type { TFunction } from "next-i18next";
|
||||||
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
|
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
|
||||||
import type { EventLocationType } from "@calcom/app-store/locations";
|
import type { EventLocationType } from "@calcom/app-store/locations";
|
||||||
import { defaultLocations } from "@calcom/app-store/locations";
|
import { defaultLocations } from "@calcom/app-store/locations";
|
||||||
|
import { AppCategories } from "@calcom/prisma/enums";
|
||||||
import type { App, AppMeta } from "@calcom/types/App";
|
import type { App, AppMeta } from "@calcom/types/App";
|
||||||
|
|
||||||
export * from "./_utils/getEventTypeAppData";
|
export * from "./_utils/getEventTypeAppData";
|
||||||
|
@ -37,16 +38,6 @@ const credentialData = Prisma.validator<Prisma.CredentialArgs>()({
|
||||||
|
|
||||||
export type CredentialData = Prisma.CredentialGetPayload<typeof credentialData>;
|
export type CredentialData = Prisma.CredentialGetPayload<typeof credentialData>;
|
||||||
|
|
||||||
export const InstalledAppVariants = [
|
|
||||||
"conferencing",
|
|
||||||
"calendar",
|
|
||||||
"payment",
|
|
||||||
"analytics",
|
|
||||||
"automation",
|
|
||||||
"other",
|
|
||||||
"web3",
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export const ALL_APPS = Object.values(ALL_APPS_MAP);
|
export const ALL_APPS = Object.values(ALL_APPS_MAP);
|
||||||
|
|
||||||
export function getLocationGroupedOptions(integrations: ReturnType<typeof getApps>, t: TFunction) {
|
export function getLocationGroupedOptions(integrations: ReturnType<typeof getApps>, t: TFunction) {
|
||||||
|
@ -58,8 +49,13 @@ export function getLocationGroupedOptions(integrations: ReturnType<typeof getApp
|
||||||
if (app.locationOption) {
|
if (app.locationOption) {
|
||||||
// All apps that are labeled as a locationOption are video apps. Extract the secondary category if available
|
// All apps that are labeled as a locationOption are video apps. Extract the secondary category if available
|
||||||
let category =
|
let category =
|
||||||
app.categories.length >= 2 ? app.categories.find((category) => category !== "video") : app.category;
|
app.categories.length >= 2
|
||||||
if (!category) category = "video";
|
? app.categories.find(
|
||||||
|
(category) =>
|
||||||
|
!([AppCategories.video, AppCategories.conferencing] as string[]).includes(category)
|
||||||
|
)
|
||||||
|
: app.category;
|
||||||
|
if (!category) category = AppCategories.conferencing;
|
||||||
const option = { ...app.locationOption, icon: app.logo, slug: app.slug };
|
const option = { ...app.locationOption, icon: app.logo, slug: app.slug };
|
||||||
if (apps[category]) {
|
if (apps[category]) {
|
||||||
apps[category] = [...apps[category], option];
|
apps[category] = [...apps[category], option];
|
||||||
|
|
|
@ -6,8 +6,8 @@ export const metadata = {
|
||||||
name: "Vital",
|
name: "Vital",
|
||||||
description: _package.description,
|
description: _package.description,
|
||||||
installed: true,
|
installed: true,
|
||||||
category: "other",
|
category: "automation",
|
||||||
categories: ["other"],
|
categories: ["automation"],
|
||||||
logo: "icon.svg",
|
logo: "icon.svg",
|
||||||
label: "Vital",
|
label: "Vital",
|
||||||
publisher: "Vital",
|
publisher: "Vital",
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
"logo": "/icon.ico",
|
"logo": "/icon.ico",
|
||||||
"url": "https://github.com/aar2dee2",
|
"url": "https://github.com/aar2dee2",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "aar2dee2",
|
"publisher": "aar2dee2",
|
||||||
"email": "support@cal.com",
|
"email": "support@cal.com",
|
||||||
"description": "Create meetings with Cisco Webex",
|
"description": "Create meetings with Cisco Webex",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.svg",
|
"logo": "icon.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["messaging"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "support@cal.com",
|
"email": "support@cal.com",
|
||||||
"description": "Schedule a chat with your guests or have a WhatsApp Video call.",
|
"description": "Schedule a chat with your guests or have a WhatsApp Video call.",
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"logo": "icon-dark.svg",
|
"logo": "icon-dark.svg",
|
||||||
"url": "https://cal.com/",
|
"url": "https://cal.com/",
|
||||||
"variant": "conferencing",
|
"variant": "conferencing",
|
||||||
"categories": ["video"],
|
"categories": ["conferencing"],
|
||||||
"publisher": "Cal.com, Inc.",
|
"publisher": "Cal.com, Inc.",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"description": "Whereby makes it super simple for collaborating teams to jump on a video call.",
|
"description": "Whereby makes it super simple for collaborating teams to jump on a video call.",
|
||||||
|
|
|
@ -6,8 +6,8 @@ export const metadata = {
|
||||||
name: _package.name,
|
name: _package.name,
|
||||||
description: _package.description,
|
description: _package.description,
|
||||||
installed: true,
|
installed: true,
|
||||||
category: "other",
|
category: "automation",
|
||||||
categories: ["other"],
|
categories: ["automation"],
|
||||||
// If using static next public folder, can then be referenced from the base URL (/).
|
// If using static next public folder, can then be referenced from the base URL (/).
|
||||||
logo: "icon-dark.svg",
|
logo: "icon-dark.svg",
|
||||||
publisher: "Cal.com",
|
publisher: "Cal.com",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"logo": "icon.png",
|
"logo": "icon.png",
|
||||||
"url": "https://github.com/jatinsandilya",
|
"url": "https://github.com/jatinsandilya",
|
||||||
"variant": "other",
|
"variant": "other",
|
||||||
"categories": ["other"],
|
"categories": ["crm"],
|
||||||
"publisher": "Jatin Sandilya",
|
"publisher": "Jatin Sandilya",
|
||||||
"email": "help@cal.com",
|
"email": "help@cal.com",
|
||||||
"description": "Zoho CRM is a cloud-based application designed to help your salespeople sell smarter and faster by centralizing customer information, logging their interactions with your company, and automating many of the tasks salespeople do every day",
|
"description": "Zoho CRM is a cloud-based application designed to help your salespeople sell smarter and faster by centralizing customer information, logging their interactions with your company, and automating many of the tasks salespeople do every day",
|
||||||
|
|
|
@ -7,12 +7,12 @@ export const metadata = {
|
||||||
name: "Zoom Video",
|
name: "Zoom Video",
|
||||||
description: _package.description,
|
description: _package.description,
|
||||||
type: "zoom_video",
|
type: "zoom_video",
|
||||||
categories: ["video"],
|
categories: ["conferencing"],
|
||||||
variant: "conferencing",
|
variant: "conferencing",
|
||||||
logo: "icon.svg",
|
logo: "icon.svg",
|
||||||
publisher: "Cal.com",
|
publisher: "Cal.com",
|
||||||
url: "https://zoom.us/",
|
url: "https://zoom.us/",
|
||||||
category: "video",
|
category: "conferencing",
|
||||||
slug: "zoom",
|
slug: "zoom",
|
||||||
title: "Zoom Video",
|
title: "Zoom Video",
|
||||||
email: "help@cal.com",
|
email: "help@cal.com",
|
||||||
|
|
|
@ -292,7 +292,7 @@ const AdminAppsListContainer = () => {
|
||||||
|
|
||||||
if (isLoading) return <SkeletonLoader />;
|
if (isLoading) return <SkeletonLoader />;
|
||||||
|
|
||||||
if (!apps) {
|
if (!apps || apps.length === 0) {
|
||||||
return (
|
return (
|
||||||
<EmptyScreen
|
<EmptyScreen
|
||||||
Icon={AlertCircle}
|
Icon={AlertCircle}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
-- AlterEnum
|
||||||
|
-- This migration adds more than one value to an enum.
|
||||||
|
-- With PostgreSQL versions 11 and earlier, this is not possible
|
||||||
|
-- in a single migration. This can be worked around by creating
|
||||||
|
-- multiple migrations, each migration adding only one value to
|
||||||
|
-- the enum.
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TYPE "AppCategories" ADD VALUE 'conferencing';
|
||||||
|
ALTER TYPE "AppCategories" ADD VALUE 'crm';
|
|
@ -614,10 +614,12 @@ enum AppCategories {
|
||||||
messaging
|
messaging
|
||||||
other
|
other
|
||||||
payment
|
payment
|
||||||
video
|
video // deprecated, please use 'conferencing' instead
|
||||||
web3
|
web3 // deprecated, we should no longer have any web3 apps
|
||||||
automation
|
automation
|
||||||
analytics
|
analytics
|
||||||
|
conferencing
|
||||||
|
crm
|
||||||
}
|
}
|
||||||
|
|
||||||
model App {
|
model App {
|
||||||
|
|
|
@ -1,244 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"/*": "This file is deprecated now. No new entry should be added to it.",
|
|
||||||
"dirName": "routing-forms",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "routing-forms",
|
|
||||||
"type": "routing-forms_other"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "whereby",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "whereby",
|
|
||||||
"type": "whereby_video"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "around",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "around",
|
|
||||||
"type": "around_video"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "riverside",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "riverside",
|
|
||||||
"type": "riverside_video"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "typeform",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "typeform",
|
|
||||||
"type": "typeform_other"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "ping",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "ping",
|
|
||||||
"type": "ping_video"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "campfire",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "campfire",
|
|
||||||
"type": "campfire_video"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "raycast",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "raycast",
|
|
||||||
"type": "raycast_other"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "n8n",
|
|
||||||
"categories": ["automation"],
|
|
||||||
"slug": "n8n",
|
|
||||||
"type": "n8n_automation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "exchangecalendar",
|
|
||||||
"categories": ["calendar"],
|
|
||||||
"slug": "exchange",
|
|
||||||
"type": "exchange_calendar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "qr_code",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "qr_code",
|
|
||||||
"type": "qr_code_other"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "weather_in_your_calendar",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "weather_in_your_calendar",
|
|
||||||
"type": "weather_in_your_calendar_other"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "fathom",
|
|
||||||
"categories": ["analytics"],
|
|
||||||
"slug": "fathom",
|
|
||||||
"type": "fathom_analytics"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "plausible",
|
|
||||||
"categories": ["analytics"],
|
|
||||||
"slug": "plausible",
|
|
||||||
"type": "plausible_analytics"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "wordpress",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "wordpress",
|
|
||||||
"type": "wordpress_other"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "ga4",
|
|
||||||
"categories": ["analytics"],
|
|
||||||
"slug": "ga4",
|
|
||||||
"type": "ga4_analytics"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "pipedream",
|
|
||||||
"categories": ["automation"],
|
|
||||||
"slug": "pipedream",
|
|
||||||
"type": "pipedream_automation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "sirius_video",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "sirius_video",
|
|
||||||
"type": "sirius_video_video"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "sendgrid",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "sendgrid",
|
|
||||||
"type": "sendgrid_other_calendar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "closecom",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "closecom",
|
|
||||||
"type": "closecom_other_calendar"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "whatsapp",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "whatsapp",
|
|
||||||
"type": "whatsapp_video"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "telegram",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "telegram",
|
|
||||||
"type": "telegram_video"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "signal",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "signal",
|
|
||||||
"type": "signal_video"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "vimcal",
|
|
||||||
"categories": ["calendar"],
|
|
||||||
"slug": "vimcal",
|
|
||||||
"type": "vimcal_other"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "amie",
|
|
||||||
"categories": ["calendar"],
|
|
||||||
"slug": "amie",
|
|
||||||
"type": "amie_other"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "booking-pages-tag",
|
|
||||||
"categories": ["analytics"],
|
|
||||||
"slug": "booking-pages-tag",
|
|
||||||
"type": "booking-pages-tag_other",
|
|
||||||
"isTemplate": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "event-type-app-card",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "event-type-app-card",
|
|
||||||
"type": "event-type-app-card_other",
|
|
||||||
"isTemplate": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "event-type-location-video-static",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "event-type-location-video-static",
|
|
||||||
"type": "event-type-location-video-static_other",
|
|
||||||
"isTemplate": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "general-app-settings",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "general-app-settings",
|
|
||||||
"type": "general-app-settings_other",
|
|
||||||
"isTemplate": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "link-as-an-app",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "link-as-an-app",
|
|
||||||
"type": "link-as-an-app_other",
|
|
||||||
"isTemplate": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "basic",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "basic",
|
|
||||||
"type": "basic_other",
|
|
||||||
"isTemplate": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "facetime",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "facetime",
|
|
||||||
"type": "facetime_video",
|
|
||||||
"isTemplate": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "zohocrm",
|
|
||||||
"categories": ["other"],
|
|
||||||
"slug": "zohocrm",
|
|
||||||
"type": "zohocrm_other_calendar",
|
|
||||||
"isTemplate": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "webex",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "webex",
|
|
||||||
"type": "webex_video",
|
|
||||||
"isTemplate": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "cron",
|
|
||||||
"categories": ["calendar"],
|
|
||||||
"slug": "cron",
|
|
||||||
"type": "cron_other",
|
|
||||||
"isTemplate": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "gtm",
|
|
||||||
"categories": ["analytics"],
|
|
||||||
"slug": "gtm",
|
|
||||||
"type": "gtm_analytics",
|
|
||||||
"isTemplate": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "discord",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "discord",
|
|
||||||
"type": "discord_video",
|
|
||||||
"isTemplate": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dirName": "sylapsvideo",
|
|
||||||
"categories": ["video"],
|
|
||||||
"slug": "sylapsvideo",
|
|
||||||
"type": "sylaps_video",
|
|
||||||
"isTemplate": false
|
|
||||||
}
|
|
||||||
]
|
|
|
@ -4,10 +4,11 @@
|
||||||
*/
|
*/
|
||||||
import type { Prisma } from "@prisma/client";
|
import type { Prisma } from "@prisma/client";
|
||||||
import dotEnv from "dotenv";
|
import dotEnv from "dotenv";
|
||||||
import fs from "fs";
|
|
||||||
import path from "path";
|
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
|
||||||
|
|
||||||
import prisma from ".";
|
import prisma from ".";
|
||||||
|
import { AppCategories } from "./enums";
|
||||||
|
|
||||||
dotEnv.config({ path: "../../.env.appStore" });
|
dotEnv.config({ path: "../../.env.appStore" });
|
||||||
|
|
||||||
|
@ -235,7 +236,7 @@ export default async function main() {
|
||||||
client_secret,
|
client_secret,
|
||||||
redirect_uris,
|
redirect_uris,
|
||||||
});
|
});
|
||||||
await createApp("google-meet", "googlevideo", ["video"], "google_video", {
|
await createApp("google-meet", "googlevideo", ["conferencing"], "google_video", {
|
||||||
client_id,
|
client_id,
|
||||||
client_secret,
|
client_secret,
|
||||||
redirect_uris,
|
redirect_uris,
|
||||||
|
@ -248,7 +249,7 @@ export default async function main() {
|
||||||
client_id: process.env.MS_GRAPH_CLIENT_ID,
|
client_id: process.env.MS_GRAPH_CLIENT_ID,
|
||||||
client_secret: process.env.MS_GRAPH_CLIENT_SECRET,
|
client_secret: process.env.MS_GRAPH_CLIENT_SECRET,
|
||||||
});
|
});
|
||||||
await createApp("msteams", "office365video", ["video"], "office365_video", {
|
await createApp("msteams", "office365video", ["conferencing"], "office365_video", {
|
||||||
client_id: process.env.MS_GRAPH_CLIENT_ID,
|
client_id: process.env.MS_GRAPH_CLIENT_ID,
|
||||||
client_secret: process.env.MS_GRAPH_CLIENT_SECRET,
|
client_secret: process.env.MS_GRAPH_CLIENT_SECRET,
|
||||||
});
|
});
|
||||||
|
@ -266,45 +267,45 @@ export default async function main() {
|
||||||
}
|
}
|
||||||
// Video apps
|
// Video apps
|
||||||
if (process.env.DAILY_API_KEY) {
|
if (process.env.DAILY_API_KEY) {
|
||||||
await createApp("daily-video", "dailyvideo", ["video"], "daily_video", {
|
await createApp("daily-video", "dailyvideo", ["conferencing"], "daily_video", {
|
||||||
api_key: process.env.DAILY_API_KEY,
|
api_key: process.env.DAILY_API_KEY,
|
||||||
scale_plan: process.env.DAILY_SCALE_PLAN,
|
scale_plan: process.env.DAILY_SCALE_PLAN,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (process.env.TANDEM_CLIENT_ID && process.env.TANDEM_CLIENT_SECRET) {
|
if (process.env.TANDEM_CLIENT_ID && process.env.TANDEM_CLIENT_SECRET) {
|
||||||
await createApp("tandem", "tandemvideo", ["video"], "tandem_video", {
|
await createApp("tandem", "tandemvideo", ["conferencing"], "tandem_video", {
|
||||||
client_id: process.env.TANDEM_CLIENT_ID as string,
|
client_id: process.env.TANDEM_CLIENT_ID as string,
|
||||||
client_secret: process.env.TANDEM_CLIENT_SECRET as string,
|
client_secret: process.env.TANDEM_CLIENT_SECRET as string,
|
||||||
base_url: (process.env.TANDEM_BASE_URL as string) || "https://tandem.chat",
|
base_url: (process.env.TANDEM_BASE_URL as string) || "https://tandem.chat",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (process.env.ZOOM_CLIENT_ID && process.env.ZOOM_CLIENT_SECRET) {
|
if (process.env.ZOOM_CLIENT_ID && process.env.ZOOM_CLIENT_SECRET) {
|
||||||
await createApp("zoom", "zoomvideo", ["video"], "zoom_video", {
|
await createApp("zoom", "zoomvideo", ["conferencing"], "zoom_video", {
|
||||||
client_id: process.env.ZOOM_CLIENT_ID,
|
client_id: process.env.ZOOM_CLIENT_ID,
|
||||||
client_secret: process.env.ZOOM_CLIENT_SECRET,
|
client_secret: process.env.ZOOM_CLIENT_SECRET,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await createApp("jitsi", "jitsivideo", ["video"], "jitsi_video");
|
await createApp("jitsi", "jitsivideo", ["conferencing"], "jitsi_video");
|
||||||
// Other apps
|
// Other apps
|
||||||
if (process.env.HUBSPOT_CLIENT_ID && process.env.HUBSPOT_CLIENT_SECRET) {
|
if (process.env.HUBSPOT_CLIENT_ID && process.env.HUBSPOT_CLIENT_SECRET) {
|
||||||
await createApp("hubspot", "hubspot", ["other"], "hubspot_other_calendar", {
|
await createApp("hubspot", "hubspot", ["crm"], "hubspot_other_calendar", {
|
||||||
client_id: process.env.HUBSPOT_CLIENT_ID,
|
client_id: process.env.HUBSPOT_CLIENT_ID,
|
||||||
client_secret: process.env.HUBSPOT_CLIENT_SECRET,
|
client_secret: process.env.HUBSPOT_CLIENT_SECRET,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (process.env.SALESFORCE_CONSUMER_KEY && process.env.SALESFORCE_CONSUMER_SECRET) {
|
if (process.env.SALESFORCE_CONSUMER_KEY && process.env.SALESFORCE_CONSUMER_SECRET) {
|
||||||
await createApp("salesforce", "salesforce", ["other"], "salesforce_other_calendar", {
|
await createApp("salesforce", "salesforce", ["crm"], "salesforce_other_calendar", {
|
||||||
consumer_key: process.env.SALESFORCE_CONSUMER_KEY,
|
consumer_key: process.env.SALESFORCE_CONSUMER_KEY,
|
||||||
consumer_secret: process.env.SALESFORCE_CONSUMER_SECRET,
|
consumer_secret: process.env.SALESFORCE_CONSUMER_SECRET,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (process.env.ZOHOCRM_CLIENT_ID && process.env.ZOHOCRM_CLIENT_SECRET) {
|
if (process.env.ZOHOCRM_CLIENT_ID && process.env.ZOHOCRM_CLIENT_SECRET) {
|
||||||
await createApp("zohocrm", "zohocrm", ["other"], "zohocrm_other_calendar", {
|
await createApp("zohocrm", "zohocrm", ["crm"], "zohocrm_other_calendar", {
|
||||||
client_id: process.env.ZOHOCRM_CLIENT_ID,
|
client_id: process.env.ZOHOCRM_CLIENT_ID,
|
||||||
client_secret: process.env.ZOHOCRM_CLIENT_SECRET,
|
client_secret: process.env.ZOHOCRM_CLIENT_SECRET,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await createApp("wipe-my-cal", "wipemycalother", ["other"], "wipemycal_other");
|
await createApp("wipe-my-cal", "wipemycalother", ["automation"], "wipemycal_other");
|
||||||
if (process.env.GIPHY_API_KEY) {
|
if (process.env.GIPHY_API_KEY) {
|
||||||
await createApp("giphy", "giphy", ["other"], "giphy_other", {
|
await createApp("giphy", "giphy", ["other"], "giphy_other", {
|
||||||
api_key: process.env.GIPHY_API_KEY,
|
api_key: process.env.GIPHY_API_KEY,
|
||||||
|
@ -312,7 +313,7 @@ export default async function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.VITAL_API_KEY && process.env.VITAL_WEBHOOK_SECRET) {
|
if (process.env.VITAL_API_KEY && process.env.VITAL_WEBHOOK_SECRET) {
|
||||||
await createApp("vital-automation", "vital", ["other"], "vital_other", {
|
await createApp("vital-automation", "vital", ["automation"], "vital_other", {
|
||||||
mode: process.env.VITAL_DEVELOPMENT_MODE || "sandbox",
|
mode: process.env.VITAL_DEVELOPMENT_MODE || "sandbox",
|
||||||
region: process.env.VITAL_REGION || "us",
|
region: process.env.VITAL_REGION || "us",
|
||||||
api_key: process.env.VITAL_API_KEY,
|
api_key: process.env.VITAL_API_KEY,
|
||||||
|
@ -326,8 +327,7 @@ export default async function main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Web3 apps
|
await createApp("huddle01", "huddle01video", ["conferencing"], "huddle01_video");
|
||||||
await createApp("huddle01", "huddle01video", ["web3", "video"], "huddle01_video");
|
|
||||||
|
|
||||||
// Payment apps
|
// Payment apps
|
||||||
if (
|
if (
|
||||||
|
@ -348,21 +348,22 @@ export default async function main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const generatedApps = JSON.parse(
|
for (const [, app] of Object.entries(appStoreMetadata)) {
|
||||||
fs.readFileSync(path.join(__dirname, "seed-app-store.config.json"), "utf8")
|
if (app.isTemplate && process.argv[2] !== "seed-templates") {
|
||||||
);
|
|
||||||
for (let i = 0; i < generatedApps.length; i++) {
|
|
||||||
const generatedApp = generatedApps[i];
|
|
||||||
if (generatedApp.isTemplate && process.argv[2] !== "seed-templates") {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validatedCategories = app.categories.filter(
|
||||||
|
(category): category is AppCategories => category in AppCategories
|
||||||
|
);
|
||||||
|
|
||||||
await createApp(
|
await createApp(
|
||||||
generatedApp.slug,
|
app.slug,
|
||||||
generatedApp.dirName,
|
app.dirName ?? app.slug,
|
||||||
generatedApp.categories,
|
validatedCategories,
|
||||||
generatedApp.type,
|
app.type,
|
||||||
undefined,
|
undefined,
|
||||||
generatedApp.isTemplate
|
app.isTemplate
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ type ListLocalOptions = {
|
||||||
|
|
||||||
export const listLocalHandler = async ({ ctx, input }: ListLocalOptions) => {
|
export const listLocalHandler = async ({ ctx, input }: ListLocalOptions) => {
|
||||||
const { prisma } = ctx;
|
const { prisma } = ctx;
|
||||||
const category = input.category === "conferencing" ? "video" : input.category;
|
const category = input.category;
|
||||||
const localApps = getLocalAppMetadata();
|
const localApps = getLocalAppMetadata();
|
||||||
|
|
||||||
const dbApps = await prisma.app.findMany({
|
const dbApps = await prisma.app.findMany({
|
||||||
|
|
|
@ -3,7 +3,7 @@ import type { PrismaClient } from "@prisma/client";
|
||||||
import { getLocalAppMetadata } from "@calcom/app-store/utils";
|
import { getLocalAppMetadata } from "@calcom/app-store/utils";
|
||||||
import { sendDisabledAppEmail } from "@calcom/emails";
|
import { sendDisabledAppEmail } from "@calcom/emails";
|
||||||
import { getTranslation } from "@calcom/lib/server";
|
import { getTranslation } from "@calcom/lib/server";
|
||||||
import type { AppCategories } from "@calcom/prisma/enums";
|
import { AppCategories } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
|
@ -53,7 +53,13 @@ export const toggleHandler = async ({ input, ctx }: ToggleOptions) => {
|
||||||
// If disabling an app then we need to alert users basesd on the app type
|
// If disabling an app then we need to alert users basesd on the app type
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
const translations = new Map();
|
const translations = new Map();
|
||||||
if (app.categories.some((category) => ["calendar", "video"].includes(category))) {
|
if (
|
||||||
|
app.categories.some((category) =>
|
||||||
|
(
|
||||||
|
[AppCategories.calendar, AppCategories.video, AppCategories.conferencing] as AppCategories[]
|
||||||
|
).includes(category)
|
||||||
|
)
|
||||||
|
) {
|
||||||
// Find all users with the app credentials
|
// Find all users with the app credentials
|
||||||
const appCredentials = await prisma.credential.findMany({
|
const appCredentials = await prisma.credential.findMany({
|
||||||
where: {
|
where: {
|
||||||
|
|
|
@ -21,6 +21,7 @@ declare namespace NodeJS {
|
||||||
readonly EMAIL_SERVER_USER: string | undefined;
|
readonly EMAIL_SERVER_USER: string | undefined;
|
||||||
readonly EMAIL_SERVER_PASSWORD: string | undefined;
|
readonly EMAIL_SERVER_PASSWORD: string | undefined;
|
||||||
readonly CRON_API_KEY: string | undefined;
|
readonly CRON_API_KEY: string | undefined;
|
||||||
|
readonly CRON_ENABLE_APP_SYNC: string | undefined;
|
||||||
readonly NEXT_PUBLIC_STRIPE_PUBLIC_KEY: string | undefined;
|
readonly NEXT_PUBLIC_STRIPE_PUBLIC_KEY: string | undefined;
|
||||||
readonly STRIPE_PRIVATE_KEY: string | undefined;
|
readonly STRIPE_PRIVATE_KEY: string | undefined;
|
||||||
readonly STRIPE_CLIENT_ID: string | undefined;
|
readonly STRIPE_CLIENT_ID: string | undefined;
|
||||||
|
|
|
@ -186,6 +186,7 @@
|
||||||
"CI",
|
"CI",
|
||||||
"CLOSECOM_API_KEY",
|
"CLOSECOM_API_KEY",
|
||||||
"CRON_API_KEY",
|
"CRON_API_KEY",
|
||||||
|
"CRON_ENABLE_APP_SYNC",
|
||||||
"DAILY_API_KEY",
|
"DAILY_API_KEY",
|
||||||
"DAILY_SCALE_PLAN",
|
"DAILY_SCALE_PLAN",
|
||||||
"DEBUG",
|
"DEBUG",
|
||||||
|
|
Loading…
Reference in New Issue