Merge branch 'feat/organizations' into feat/organizations-banner

This commit is contained in:
Leo Giovanetti 2023-06-01 12:41:22 -03:00
commit 68b7b7fa9d
37 changed files with 364 additions and 239 deletions

View File

@ -1,27 +1,75 @@
name: "Apply issue labels to PR"
on:
pull_request:
types: [opened]
pull_request_target:
types:
- opened
jobs:
label_on_pr:
runs-on: ubuntu-latest
permissions:
contents: none
issues: read
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 16
- name: Apply labels from linked issue to PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_DATA: ${{ toJson(github.event.pull_request) }}
run: node ./.github/workflows/scripts/apply-issue-labels-to-pr.ts
uses: actions/github-script@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
async function getLinkedIssues(owner, repo, prNumber) {
const query = `query GetLinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 10) {
nodes {
number
labels(first: 10) {
nodes {
name
}
}
}
}
}
}
}`;
const variables = {
owner: owner,
repo: repo,
prNumber: prNumber,
};
const result = await github.graphql(query, variables);
return result.repository.pullRequest.closingIssuesReferences.nodes;
}
const pr = context.payload.pull_request;
const linkedIssues = await getLinkedIssues(
context.repo.owner,
context.repo.repo,
pr.number
);
const labelsToAdd = new Set();
for (const issue of linkedIssues) {
if (issue.labels && issue.labels.nodes) {
for (const label of issue.labels.nodes) {
labelsToAdd.add(label.name);
}
}
}
if (labelsToAdd.size) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: Array.from(labelsToAdd),
});
}

View File

@ -1,139 +0,0 @@
const https = require("https");
async function applyLabelFromLinkedIssueToPR(pr, token) {
// Get the labels from issues linked to the PR
const query = `
query GetLinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 10) {
nodes {
number
labels(first: 10) {
nodes {
name
}
}
}
}
}
}
}
`;
const graphqlData = JSON.stringify({
query,
variables: {
owner: pr.base.repo.owner.login,
repo: pr.base.repo.name,
prNumber: pr.number,
},
});
const requestOptions = {
hostname: "api.github.com",
path: "/graphql",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": graphqlData.length,
Authorization: "Bearer " + token,
"User-Agent": "Node.js",
},
};
// Use the native Node.js request library to make the request
let linkedIssues;
linkedIssues = await new Promise((resolve, reject) => {
const request = https.request(requestOptions, (response) => {
let responseBody = "";
response.on("data", (chunk) => {
responseBody += chunk;
});
response.on("end", () => {
resolve(JSON.parse(responseBody)?.data?.repository?.pullRequest?.closingIssuesReferences?.nodes);
});
});
request.on("error", (error) => {
reject(error);
});
request.write(graphqlData);
request.end();
});
if (!linkedIssues || linkedIssues.length === 0) {
console.log("No issue linked.");
return;
}
// Iterate over linked issues and apply labels to PR
for (const issue of linkedIssues) {
const labels = issue?.labels?.nodes?.map((label) => label.name);
if (!labels || labels.length === 0) {
console.log(`No labels found on the linked issue #${issue.number}.`);
continue;
}
const labelsData = JSON.stringify({
labels: labels,
});
const requestOptions = {
hostname: "api.github.com",
path: `/repos/${pr.base.repo.owner.login}/${pr.base.repo.name}/issues/${pr.number}/labels`,
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": labelsData.length,
Authorization: "Bearer " + token,
"User-Agent": "Node.js",
},
};
let labelResult;
labelResult = await new Promise((resolve, reject) => {
const request = https.request(requestOptions, (response) => {
let responseBody = "";
response.on("data", (chunk) => {
responseBody += chunk;
});
response.on("data", () => {});
response.on("end", () => {
resolve(JSON.parse(responseBody));
});
});
request.on("error", (error) => {
reject(error);
});
request.write(labelsData);
request.end();
});
if (labelResult.message) {
console.log(`Error labelling PR: ${labelResult.message}`);
continue;
}
console.log(
`Applied labels: ${labels.join(", ")} to PR #${pr.number} from linked issue #${issue.number}`
);
}
}
// Pass the PR data and GitHub token to the script
(async () => {
if (!process.env.PR_DATA) {
console.log("No PR data found.");
return;
}
const prData = JSON.parse(process.env.PR_DATA);
const token = process.env.GITHUB_TOKEN;
await applyLabelFromLinkedIssueToPR(prData, token);
})();

View File

@ -133,7 +133,12 @@ async function handler(req: NextApiRequest) {
});
if (!team) throw new HttpError({ statusCode: 404, message: "teamId not found" });
if (!team.members) throw new HttpError({ statusCode: 404, message: "team has no members" });
const allMemberIds = team.members.map((membership) => membership.userId);
const allMemberIds = team.members.reduce((allMemberIds: number[], member) => {
if (member.accepted) {
allMemberIds.push(member.userId);
}
return allMemberIds;
}, []);
const members = await prisma.user.findMany({
where: { id: { in: allMemberIds } },
select: availabilityUserSelect,

View File

@ -0,0 +1,27 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server";
import { createContext } from "@calcom/trpc/server/createContext";
import { viewerRouter } from "@calcom/trpc/server/routers/viewer/_router";
import { TRPCError } from "@trpc/server";
import { getHTTPStatusCodeFromError } from "@trpc/server/http";
async function handler(req: NextApiRequest, res: NextApiResponse) {
/** @see https://trpc.io/docs/server-side-calls */
const ctx = await createContext({ req, res });
const caller = viewerRouter.createCaller(ctx);
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return await caller.slots.getSchedule(req.query as any /* Let tRPC handle this */);
} catch (cause) {
if (cause instanceof TRPCError) {
const statusCode = getHTTPStatusCodeFromError(cause);
throw new HttpError({ statusCode, message: cause.message });
}
throw cause;
}
}
export default defaultResponder(handler);

View File

@ -0,0 +1,9 @@
import { defaultHandler } from "@calcom/lib/server";
import { withMiddleware } from "~/lib/helpers/withMiddleware";
export default withMiddleware()(
defaultHandler({
GET: import("./_get"),
})
);

View File

@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "2.9.4",
"version": "2.9.5.1",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",

View File

@ -26,6 +26,8 @@
"rejection_confirmation": "رفض الحجز",
"manage_this_event": "قم بإدارة هذا الحدث",
"invite_team_member": "دعوة عضو في الفريق",
"invite_team_individual_segment": "دعوة فرد",
"invite_team_bulk_segment": "استيراد بحجم جماعي",
"invite_team_notifcation_badge": "دعوة",
"your_event_has_been_scheduled": "تم جدولة الحدث الخاص بك",
"your_event_has_been_scheduled_recurring": "تم جدولة الحدث المتكرر الخاص بك",
@ -222,6 +224,7 @@
"go_back_login": "العودة إلى صفحة تسجيل الدخول",
"error_during_login": "حدث خطأ ما أثناء تسجيل الدخول. ارجع إلى شاشة تسجيل الدخول وحاول مجددًا.",
"request_password_reset": "إرسال بريد إلكتروني لإعادة التعيين",
"send_invite": "إرسال دعوة",
"forgot_password": "هل نسيت كلمة المرور؟",
"forgot": "هل نسيت؟",
"done": "تم",
@ -237,6 +240,8 @@
"set_availability": "تحديد الوقت الذي تكون فيه متاحًا",
"continue_without_calendar": "المتابعة من دون تقويم",
"connect_your_calendar": "ربط التقويم لديك",
"connect_your_video_app": "اربط تطبيقاتك الفيديو الخاصة بك",
"connect_your_video_app_instructions": "قم بتوصيل تطبيقات الفيديو لديك لاستخدامها في أنواع الأحداث الخاصة بك.",
"connect_your_calendar_instructions": "اربط التقويم لديك للتحقق تلقائيًا من الأوقات المشغولة والأحداث الجديدة أثناء جدولتها.",
"set_up_later": "الإعداد لاحقًا",
"current_time": "الوقت الحالي",
@ -366,6 +371,7 @@
"create_webhook": "إنشاء الويب هوك",
"booking_cancelled": "تم إلغاء الحجز",
"booking_rescheduled": "تمت إعادة جدولة الحجز",
"recording_ready": "رابط تنزيل التسجيل جاهز",
"booking_created": "تم إنشاء الحجز",
"meeting_ended": "انتهى الاجتماع",
"form_submitted": "تم إرسال النموذج",
@ -464,6 +470,7 @@
"friday": "الجمعة",
"saturday": "السبت",
"sunday": "الأحد",
"all_booked_today": "تم حجز الكل.",
"slots_load_fail": "تعذر تحميل الفترات الزمنية المتاحة.",
"additional_guests": "إضافة ضيوف",
"your_name": "اسمك",
@ -751,6 +758,10 @@
"new_event_type_to_book_description": "قم بإنشاء نوع حدث جديد ليحجز الأشخاص من خلاله.",
"length": "الطول",
"minimum_booking_notice": "الحد الأدنى من الوقت للحجز",
"offset_toggle": "أوقات بدء الإزاحة",
"offset_toggle_description": "إزاحة الفتحات الزمنية المعروضة للحاجزين بعدد محدد من الدقائق",
"offset_start": "إزاحة بمقدار",
"offset_start_description": "على سبيل المثال، هذا سيُظهر فتحات زمنية لمن يقوم بالحجز لديك {{ adjustedTime }} بدلاً من {{ originalTime }}",
"slot_interval": "الفترات الزمنية بين عمليات الحجز",
"slot_interval_default": "استخدام طول الحدث (الوضع الافتراضي)",
"delete_event_type": "هل تريد حذف نوع الحدث؟",
@ -903,6 +914,7 @@
"duplicate": "تكرار",
"offer_seats": "عرض مقاعد",
"offer_seats_description": "عرض مقاعد للحجز. هذا يعطل تلقائياً حجز الضيوف وحجز الاشتراك.",
"seats_available_one": "المقاعد المتاحة",
"seats_available_other": "المقاعد المتاحة",
"number_of_seats": "عدد المقاعد لكل حجز",
"enter_number_of_seats": "أدخل عدد المقاعد",
@ -1037,6 +1049,7 @@
"event_cancelled_trigger": "متى تم إلغاء هذا الحدث",
"new_event_trigger": "متى تم حجز الحدث الجديد",
"email_host_action": "إرسال رسالة إلكترونية إلى المضيف",
"email_attendee_action": "إرسال رسالة إلكترونية إلى الحضور",
"sms_attendee_action": "إرسال رسالة نصية قصيرة إلى واحد من الحضور",
"sms_number_action": "إرسال رسالة نصية قصيرة إلى رقم محدد",
"workflows": "سير العمل",
@ -1189,6 +1202,7 @@
"create_workflow": "إنشاء سير عمل",
"do_this": "افعل هذا",
"turn_off": "إيقاف التشغيل",
"turn_on": "تشغيل",
"settings_updated_successfully": "تم تحديث الإعدادات بنجاح",
"error_updating_settings": "خطأ في تحديث الإعدادات",
"personal_cal_url": "عنوان {{appName}} URL الخاص بي",
@ -1245,6 +1259,7 @@
"calendars_description": "قم بتهيئة كيفية تفاعل أنواع الأحداث مع التقويمات الخاصة بك",
"appearance_description": "إدارة إعدادات مظهر الحجز الخاص بك",
"conferencing_description": "أضف تطبيقات مؤتمرات الفيديو المفضلة لديك لاجتماعاتك",
"add_conferencing_app": "إضافة تطبيق عقد اجتماعات",
"password_description": "إدارة إعدادات كلمات مرور حسابك",
"2fa_description": "إدارة إعدادات كلمات مرور حسابك",
"we_just_need_basic_info": "نحتاج إلى بعض المعلومات الأساسية لإعداد ملفك الشخصي.",
@ -1325,7 +1340,6 @@
"exchange_version_2013_SP1": "2013 SP1",
"exchange_version_2015": "2015",
"exchange_version_2016": "2016",
"routing_forms_description": "يمكنك رؤية جميع النماذج والتوجيهات التي أنشأتها هنا.",
"routing_forms_send_email_owner": "إرسال رسالة إلكترونية إلى المالك",
"routing_forms_send_email_owner_description": "يرسل رسالة بريد إلكتروني إلى المالك عند إرسال النموذج",
"add_new_form": "إضافة استمارة جديدة",
@ -1619,6 +1633,7 @@
"email_user_cta": "عرض الدعوة",
"email_no_user_invite_heading": "لقد تمت دعوتك للانضمام إلى فريق في {{appName}}",
"email_no_user_invite_subheading": "دعاك {{invitedBy}} للانضمام إلى فريقه على {{appName}}. إن {{appName}} هو برنامج جدولة الأحداث الذي يمكّنك أنت وفريقك من جدولة الاجتماعات دون الحاجة إلى المراسلة عبر البريد الإلكتروني.",
"email_user_invite_subheading": "دعاك {{invitedBy}} للانضمام إلى فريقه {{teamName}} على {{appName}}. إن {{appName}} هو برنامج جدولة الأحداث الذي يمكّنك أنت وفريقك من جدولة الاجتماعات دون الحاجة إلى المراسلة عبر البريد الإلكتروني.",
"email_no_user_invite_steps_intro": "سنرشدك خلال بضع خطوات قصيرة وستستمتع بجدولة خالية من الإجهاد مع فريقك وسريعة.",
"email_no_user_step_one": "اختر اسم المستخدم الخاص بك",
"email_no_user_step_two": "ربط حساب التقويم الخاص بك",
@ -1694,6 +1709,15 @@
"spot_popular_event_types_description": "تعرف أي أنواع من الأحداث لديك تتلقى أكبر عدد من النقرات والحجوزات",
"no_responses_yet": "لا توجد ردود بعد",
"this_will_be_the_placeholder": "سيكون هذا هو العنصر النائب",
"error_booking_event": "حدث خطأ عند حجز الحدث، الرجاء تحديث الصفحة والمحاولة ثانيةً",
"timeslot_missing_title": "لم يتم تحديد مساحة زمنية",
"timeslot_missing_description": "الرجاء تحديد فترة زمنية لحجز الحدث.",
"timeslot_missing_cta": "حدد الوقت",
"switch_monthly": "التبديل إلى العرض الشهري",
"switch_weekly": "التبديل إلى العرض الأسبوعي",
"switch_multiday": "التبديل إلى العرض اليومي",
"num_locations": "خيارات الموقع {{num}}",
"select_on_next_step": "حدد في الخطوة التالية",
"this_meeting_has_not_started_yet": "لم يبدأ هذا الاجتماع بعد",
"this_app_requires_connected_account": "{{appName}} يتطلب حساب {{dependencyName}} متصل",
"connect_app": "اتصال {{dependencyName}}",
@ -1723,6 +1747,7 @@
"locked_apps_description": "سيتمكن الأعضاء من رؤية التطبيقات النشطة، ولكن لن يتمكنوا من تعديل أي إعدادات للتطبيق",
"locked_webhooks_description": "سيكون الأعضاء قادرين على رؤية webhooks النشطة، ولكن من دون القدرة على تعديل أي إعدادات Webhook",
"locked_workflows_description": "سيكون الأعضاء قادرين على رؤية مسارات العمل النشطة، ولكن من دون القدرة على تعديل أي إعدادات لمسار العمل",
"locked_by_admin": "مقفل من قبل المشرف",
"app_not_connected": "أنت غير متصل بحساب {{appName}}.",
"connect_now": "اتصل الآن",
"managed_event_dialog_confirm_button_one": "الاستبدال وإخطار {{count}} عضو",
@ -1780,5 +1805,21 @@
"seats_and_no_show_fee_error": "لا يمكن حاليًا تمكين المقاعد وفرض رسوم عدم الحضور",
"complete_your_booking": "أكمل الحجز",
"complete_your_booking_subject": "أكمل الحجز: {{title}} في {{date}}",
"email_invite_team": "تم دعوة {{email}}"
"confirm_your_details": "تأكيد التفاصيل الخاصة بك",
"currency_string": "{{amount, currency}}",
"charge_card_dialog_body": "أنت على وشك استحصال مبلغ {{amount, currency}} من أحد الحضور. هل أنت متأكد من أنك تريد المتابعة؟",
"charge_attendee": "استحصال مبلغ {{amount, currency}} من أحد الحضور",
"payment_app_commission": "يتطلب الدفع ({{paymentFeePercentage}}% + {{fee, currency}} عمولة لكل معاملة)",
"email_invite_team": "تم دعوة {{email}}",
"email_invite_team_bulk": "{{userCount}} مستخدم تم دعوتهم",
"error_collecting_card": "خطأ في تحصيل البطاقة",
"image_size_limit_exceed": "لا ينبغي أن تتجاوز الصورة التي تم تحميلها الحد الأقصى لحجم 5 ميغابايت",
"inline_embed": "Inline Embed",
"load_inline_content": "يحمّل نوع الحدث الخاص بك مباشرة مع محتوى موقعك الأخير.",
"floating_pop_up_button": "زر منبثق عائم",
"floating_button_trigger_modal": "يضع زرًا عائمًا على موقعك يقوم بتشغيل توجيه مع نوع الحدث الخاص بك.",
"pop_up_element_click": "ينبثق عبر النقر على العنصر",
"open_dialog_with_element_click": "افتح مربع الحوار الخاص بك عندما ينقر شخص على عنصر ما.",
"need_help_embedding": "بحاجة إلى مساعدة؟ راجع أدلتنا لإدراج Cal على Wix، Squarespace، أو WordPress، تحقق من أسئلتنا المشتركة، أو استكشف خيارات متقدمة متضمنة.",
"book_my_cal": "ثبّت الحجز في رزنامتي"
}

View File

@ -26,6 +26,8 @@
"rejection_confirmation": "Odmítnout rezervaci",
"manage_this_event": "Spravovat tuto událost",
"invite_team_member": "Pozvat člena týmu",
"invite_team_individual_segment": "Pozvat jednotlivce",
"invite_team_bulk_segment": "Hromadný import",
"invite_team_notifcation_badge": "Pozv.",
"your_event_has_been_scheduled": "Vaše událost byla naplánována",
"your_event_has_been_scheduled_recurring": "Opakovaná událost byla naplánována",
@ -222,6 +224,7 @@
"go_back_login": "Zpět na přihlašovací stránku",
"error_during_login": "Při přihlašování došlo k chybě. Přejděte zpět na přihlašovací obrazovku a zkuste to znovu.",
"request_password_reset": "Žádost o obnovení hesla",
"send_invite": "Odeslat pozvánku",
"forgot_password": "Zapomenuté heslo",
"forgot": "Zapomněli jste?",
"done": "Hotovo",
@ -237,6 +240,8 @@
"set_availability": "Nastavte, jak jste dostupní",
"continue_without_calendar": "Pokračovat bez kalendáře",
"connect_your_calendar": "Připojit svůj kalendář",
"connect_your_video_app": "Propojte své video aplikace",
"connect_your_video_app_instructions": "Propojte své video aplikace a používejte je v typech událostí.",
"connect_your_calendar_instructions": "Připojit kalendář pro automatickou kontrolu časů, kdy nejste dostupní, a nově naplánovaných událostí.",
"set_up_later": "Zprovoznit později",
"current_time": "Aktuální čas",
@ -366,6 +371,7 @@
"create_webhook": "Vytvořit webhook",
"booking_cancelled": "Rezervace zrušena",
"booking_rescheduled": "Rezervace přesunuta",
"recording_ready": "Odkaz ke stažení záznamu je připraven",
"booking_created": "Rezervace vytvořena",
"meeting_ended": "Schůzka skončila",
"form_submitted": "Formulář byl odeslán",
@ -464,6 +470,7 @@
"friday": "pátek",
"saturday": "sobota",
"sunday": "neděle",
"all_booked_today": "Vše zarezervováno.",
"slots_load_fail": "Nepodařilo se načíst dostupné časové sloty.",
"additional_guests": "Přidat hosty",
"your_name": "Vaše jméno",
@ -751,6 +758,10 @@
"new_event_type_to_book_description": "Vytvořit nový typ události, ve které si lidé mohou rezervovat časy.",
"length": "Doba trvání",
"minimum_booking_notice": "Minimální potřebná doba",
"offset_toggle": "Posunutí časů začátku",
"offset_toggle_description": "Posunutí časových slotů zobrazených rezervujícím osobám o zadaný počet minut",
"offset_start": "Posunutí o",
"offset_start_description": "například v tomto případě se vašim rezervujícím zobrazí časové sloty v {{ adjustedTime }} namísto {{ originalTime }}",
"slot_interval": "Časové intervaly",
"slot_interval_default": "Použít délku události (výchozí)",
"delete_event_type": "Odstranit typ události?",
@ -903,6 +914,7 @@
"duplicate": "Duplikovat",
"offer_seats": "Nabídka míst",
"offer_seats_description": "Nabídnout místa k rezervaci. Tím se vypnou rezervace hostů a volitelné rezervace.",
"seats_available_one": "Dostupné místo",
"seats_available_other": "Dostupná místa",
"number_of_seats": "Počet míst na rezervaci",
"enter_number_of_seats": "Zadejte počet míst",
@ -1037,6 +1049,7 @@
"event_cancelled_trigger": "při zrušení události",
"new_event_trigger": "při rezervaci nové události",
"email_host_action": "odeslání e-mailu hostiteli",
"email_attendee_action": "odeslat e-mail účastníkům",
"sms_attendee_action": "odeslání SMS účastníkovi",
"sms_number_action": "odeslání SMS na konkrétní číslo",
"workflows": "Pracovní postupy",
@ -1189,6 +1202,7 @@
"create_workflow": "Vytvoření pracovního postupu",
"do_this": "Provést",
"turn_off": "Vypnout",
"turn_on": "Zapnout",
"settings_updated_successfully": "Nastavení aktualizováno",
"error_updating_settings": "Chyba při aktualizaci nastavení",
"personal_cal_url": "Moje osobní adresa URL {{appName}}",
@ -1245,6 +1259,7 @@
"calendars_description": "Konfigurace způsobu interakce typů událostí s kalendáři",
"appearance_description": "Správa nastavení vzhledu rezervace",
"conferencing_description": "Přidejte si oblíbené videokonferenční pro vaše schůzky",
"add_conferencing_app": "Přidat videokonferenční aplikaci",
"password_description": "Správa nastavení hesel k vašemu účtu",
"2fa_description": "Správa nastavení hesel k vašemu účtu",
"we_just_need_basic_info": "K nastavení vašeho profilu potřebujeme jen několik základních informací.",
@ -1619,6 +1634,7 @@
"email_user_cta": "Zobrazit pozvánku",
"email_no_user_invite_heading": "Byli jste pozváni do týmu v aplikaci {{appName}}",
"email_no_user_invite_subheading": "Uživatel {{invitedBy}} vás pozval, abyste se připojili k jeho týmu v aplikaci {{appName}}. {{appName}} je plánovač událostí, který vám a vašemu týmu umožňuje plánovat schůzky bez e-mailového ping-pongu.",
"email_user_invite_subheading": "Uživatel {{invitedBy}} vás pozval, abyste se připojili k jeho týmu „{{teamName}}“ v aplikaci {{appName}}. {{appName}} je plánovač událostí, který vám a vašemu týmu umožňuje plánovat schůzky bez e-mailového pingpongu.",
"email_no_user_invite_steps_intro": "Provedeme vás několika krátkými kroky a za malou chvíli si budete se svým týmem užívat plánování bez stresu.",
"email_no_user_step_one": "Vyberte si uživatelské jméno",
"email_no_user_step_two": "Připojte svůj účet kalendáře",
@ -1694,6 +1710,15 @@
"spot_popular_event_types_description": "Zjistěte, které typy vašich událostí získávají nejvíce kliknutí a rezervací",
"no_responses_yet": "Zatím žádné odpovědi",
"this_will_be_the_placeholder": "Toto bude zástupný znak",
"error_booking_event": "Při rezervaci události došlo k chybě, obnovte prosím stránku a zkuste to znovu",
"timeslot_missing_title": "Nebyl vybrán žádný časový slot",
"timeslot_missing_description": "Vyberte časový slot, aby mohla být událost zaregistrována.",
"timeslot_missing_cta": "Vybrat časový slot",
"switch_monthly": "Přepnout na měsíční zobrazení",
"switch_weekly": "Přepnout na týdenní zobrazení",
"switch_multiday": "Přepnout na denní zobrazení",
"num_locations": "Možná místa konání: {{num}}",
"select_on_next_step": "Vyberte v dalším kroku",
"this_meeting_has_not_started_yet": "Tato schůzka ještě nezačala",
"this_app_requires_connected_account": "{{appName}} vyžaduje připojený účet {{dependencyName}}",
"connect_app": "Připojit aplikaci {{dependencyName}}",
@ -1723,6 +1748,7 @@
"locked_apps_description": "Členové uvidí aktivní aplikace, ale nastavení aplikací upravovat nemohou",
"locked_webhooks_description": "Členové uvidí aktivní webhooky, ale nastavení webhooků upravovat nemohou",
"locked_workflows_description": "Členové uvidí aktivní pracovní postupy, ale nastavení pracovních postupů upravovat nemohou",
"locked_by_admin": "Uzamčeno správcem týmu",
"app_not_connected": "Nemáte připojený účet {{appName}}.",
"connect_now": "Připojte se",
"managed_event_dialog_confirm_button_one": "Nahradit a upozornit {{count}} člena",
@ -1780,5 +1806,34 @@
"seats_and_no_show_fee_error": "V současné době nelze povolit místa a strhnout poplatek za nedostavení se",
"complete_your_booking": "Dokončete svou rezervaci",
"complete_your_booking_subject": "Dokončete svou rezervaci: {{title}} dne {{date}}",
"email_invite_team": "Pozvánka pro uživatele {{email}} odeslána"
"confirm_your_details": "Potvrďte své údaje",
"currency_string": "{{amount, currency}}",
"charge_card_dialog_body": "Chystáte se účtovat účastníkovi {{amount, currency}}. Opravdu chcete pokračovat?",
"charge_attendee": "Naúčtovat účastníkovi {{amount, currency}}",
"payment_app_commission": "Vyžadujte platbu ({{paymentFeePercentage}} % + provizi {{fee, currency}} za transakci)",
"email_invite_team": "Pozvánka pro uživatele {{email}} odeslána",
"email_invite_team_bulk": "Počet pozvaných uživatelů: {{userCount}}",
"error_collecting_card": "Chyba při výběru karty",
"image_size_limit_exceed": "Velikost nahraného obrázku by neměla překročit limit 5 MB",
"inline_embed": "Embed inline",
"load_inline_content": "Načte váš typ události přímo „inline“ s ostatním obsahem webu.",
"floating_pop_up_button": "Plovoucí překryvné tlačítko",
"floating_button_trigger_modal": "Umístí na váš web plovoucí tlačítko, které spustí modální okno s vaším typem události.",
"pop_up_element_click": "Překryvné okno při kliknutí na prvek",
"open_dialog_with_element_click": "Otevře dialogové okno Cal, pokud někdo klikne na prvek.",
"need_help_embedding": "Potřebujete pomoc? Prohlédněte si naše průvodce pro vložení Cal do Wixu, Squarespace nebo WordPressu, podívejte se na naše nejčastější dotazy nebo prozkoumejte pokročilé možnosti vložení.",
"book_my_cal": "Book my Cal",
"invite_as": "Pozvat jako",
"form_updated_successfully": "Formulář byl aktualizován.",
"email_not_cal_member_cta": "Připojte se ke svému týmu",
"disable_attendees_confirmation_emails": "Vypnutí výchozích potvrzovacích e-mailů pro účastníky",
"disable_attendees_confirmation_emails_description": "U tohoto typu události je aktivní alespoň jeden pracovní postup, který po rezervaci události odešle účastníkům e-mail.",
"disable_host_confirmation_emails": "Vypnutí výchozích potvrzovacích e-mailů pro hostitele",
"disable_host_confirmation_emails_description": "U tohoto typu události je aktivní alespoň jeden pracovní postup, který po rezervaci události odešle hostiteli e-mail.",
"add_an_override": "Přidejte změnu",
"import_from_google_workspace": "Importovat uživatele z Google Workspace",
"connect_google_workspace": "Propojit s Google Workspace",
"google_workspace_admin_tooltip": "Používání této funkce vyžaduje, abyste byli administrátory Google Workspace",
"first_event_type_webhook_description": "Vytvořte svůj první webhook pro tento typ události",
"create_for": "Vytvořit pro"
}

View File

@ -1281,7 +1281,6 @@
"exchange_authentication_standard": "Grundlæggende autentificering",
"exchange_authentication_ntlm": "NTLM autentificering",
"exchange_compression": "GZip komprimering",
"routing_forms_description": "Du kan se alle former og ruter du har oprettet her.",
"routing_forms_send_email_owner": "Send e-mail til Ejer",
"routing_forms_send_email_owner_description": "Sender en e-mail til ejeren når formularen er indsendt",
"add_new_form": "Tilføj ny formular",

View File

@ -222,6 +222,7 @@
"go_back_login": "Zurück zur Login-Seite",
"error_during_login": "Beim Anmelden ist ein Fehler aufgetreten. Gehen Sie zurück zum Anmeldebildschirm und versuchen Sie es erneut.",
"request_password_reset": "Passwort zurücksetzen",
"send_invite": "Einladung senden",
"forgot_password": "Passwort vergessen",
"forgot": "Vergessen?",
"done": "Erledigt",
@ -237,6 +238,7 @@
"set_availability": "Verfügbarkeit festlegen",
"continue_without_calendar": "Ohne Kalender fortfahren",
"connect_your_calendar": "Kalender verbinden",
"connect_your_video_app": "Verbinden Sie Ihre Video-Apps",
"connect_your_calendar_instructions": "Verbinden Sie Ihren Kalender, um automatisch zu prüfen, ob Termine verfügbar sind.",
"set_up_later": "Später einrichten",
"current_time": "Aktuelle Zeit",
@ -464,6 +466,7 @@
"friday": "Freitag",
"saturday": "Samstag",
"sunday": "Sonntag",
"all_booked_today": "Alle gebucht.",
"slots_load_fail": "Die verfügbaren Zeitfenster konnten nicht geladen werden.",
"additional_guests": "+ Weitere Gäste",
"your_name": "Ihr Name",
@ -1694,6 +1697,7 @@
"spot_popular_event_types_description": "Sehen Sie, welche Ihrer Ereignistypen die meisten Klicks und Buchungen erhalten",
"no_responses_yet": "Noch keine Antworten",
"this_will_be_the_placeholder": "Dies wird der Platzhalter sein",
"switch_monthly": "Zur monatlichen Ansicht wechseln",
"this_meeting_has_not_started_yet": "Dieses Meeting hat noch nicht begonnen",
"this_app_requires_connected_account": "{{appName}} benötigt ein verbundenes {{dependencyName}}-Konto",
"connect_app": "{{dependencyName}} verbinden",
@ -1734,7 +1738,7 @@
"managed_event_dialog_clarification": "Wenn Sie die URL ersetzen, werden wir sie benachrichtigen. Gehen Sie zurück und entfernen Sie sie, wenn Sie sie nicht überschreiben möchten.",
"review_event_type": "Ereignistyp überprüfen",
"looking_for_more_analytics": "Suchen Sie nach weiteren Analysedaten?",
"looking_for_more_insights": "Suchen Sie nach weiteren Einsichten?",
"looking_for_more_insights": "Möchten Sie mehr Insights?",
"add_filter": "Filter hinzufügen",
"select_user": "Benutzer auswählen",
"select_event_type": "Ereignistyp auswählen",

View File

@ -1843,5 +1843,6 @@
"create_for": "Create for",
"setup_organisation": "Setup an Organization",
"organisation_banner_description": "Create an environments where your teams can create shared apps, workflows and event types with round-robin and collective scheduling.",
"organisation_banner_title": "Manage organizations with multiple teams"
"organisation_banner_title": "Manage organizations with multiple teams",
"additional_url_parameters":"Additional URL parameters"
}

View File

@ -1634,7 +1634,7 @@
"email_user_cta": "Visualizza invito",
"email_no_user_invite_heading": "Sei stato invitato a unirti a un team su {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} ti ha invitato a unirti al suo team su {{appName}}. {{appName}} è uno strumento di pianificazione di eventi che permette al tuo team di pianificare le riunioni senza scambiare decine di e-mail.",
"email_user_invite_subheading": "{{invitedBy}} ti ha invitato a unirti al suo team `{{teamName}}` su {{appName}}. {{appName}} è uno strumento di pianificazione di eventi che permette al tuo team di pianificare le riunioni senza scambiare decine di e-mail.",
"email_user_invite_subheading": "{{invitedBy}} ti ha invitato a unirti al suo team `{{teamName}}` su {{appName}}. {{appName}} è uno strumento di pianificazione di eventi che permette a te e al tuo team di pianificare le riunioni senza scambiare decine di e-mail.",
"email_no_user_invite_steps_intro": "Ti assisteremo nei passaggi iniziali in modo che tu possa pianificare eventi per il tuo team senza stress e in pochissimo tempo.",
"email_no_user_step_one": "Scegli il tuo nome utente",
"email_no_user_step_two": "Collega il tuo account di calendario",
@ -1760,7 +1760,7 @@
"managed_event_dialog_clarification": "Se decidi di sostituirlo, i membri ne saranno avvisati. Torna indietro e rimuovili se non desideri sovrascriverlo.",
"review_event_type": "Rivedi tipo di evento",
"looking_for_more_analytics": "Hai bisogno di più dati analitici?",
"looking_for_more_insights": "Hai bisogno di maggiori approfondimenti?",
"looking_for_more_insights": "Ti servono più Insights?",
"add_filter": "Aggiungi filtro",
"select_user": "Seleziona utente",
"select_event_type": "Seleziona tipo di evento",
@ -1821,7 +1821,7 @@
"floating_button_trigger_modal": "Inserisce nel sito un pulsante mobile che attiva una finestra modale con il tuo tipo di evento.",
"pop_up_element_click": "Finestra popup tramite clic su un elemento",
"open_dialog_with_element_click": "Apre la finestra di dialogo di Cal quando un utente clicca su un elemento.",
"need_help_embedding": "Hai bisogno di aiuto? Consulta le nostre guide per integrare Cal su Wix, Squarespace o WordPress, legge le risposte alle domande più frequenti o esplora le opzioni di icorporamento avanzate.",
"need_help_embedding": "Hai bisogno di aiuto? Consulta le nostre guide per integrare Cal su Wix, Squarespace o WordPress, leggi le risposte alle domande più frequenti o esplora le opzioni di incorporamento avanzate.",
"book_my_cal": "Prenota il mio Cal",
"invite_as": "Invita come",
"form_updated_successfully": "Modulo aggiornato correttamente.",

View File

@ -1258,7 +1258,6 @@
"exchange_authentication_standard": "Enkel autentisering",
"exchange_authentication_ntlm": "NTLM autentisering",
"exchange_compression": "GZip komprimering",
"routing_forms_description": "Du kan se alle skjemaer og viderekoblinger du har laget her.",
"routing_forms_send_email_owner": "Send E-post til Eier",
"routing_forms_send_email_owner_description": "Sender en e-post til eieren når skjemaet er sendt inn",
"add_new_form": "Legg til nytt skjema",

View File

@ -761,7 +761,7 @@
"offset_toggle": "Deslocar horas de início",
"offset_toggle_description": "Desloca na quantidade de minutos especificada as alocações de tempo mostradas aos reservantes",
"offset_start": "Deslocar em",
"offset_start_description": "ex.: serão mostrados espaços de tempo aos seus reservantes à(s) {{ adjustedTime }} em vez de {{ originalTime }}",
"offset_start_description": "ex.: serão mostradas alocações de tempo aos seus reservantes à(s) {{ adjustedTime }} em vez de {{ originalTime }}",
"slot_interval": "Intervalos de tempo",
"slot_interval_default": "Usar a duração do evento (padrão)",
"delete_event_type": "Excluir tipo de evento?",
@ -1340,7 +1340,7 @@
"exchange_version_2013_SP1": "2013 SP1",
"exchange_version_2015": "2015",
"exchange_version_2016": "2016",
"routing_forms_description": "Você pode ver todos os formulários e rotas que criou aqui",
"routing_forms_description": "Criar formulários para direcionar os participantes para os destinos corretos",
"routing_forms_send_email_owner": "Enviar e-mail ao proprietário",
"routing_forms_send_email_owner_description": "Envia um e-mail ao proprietário após o envio do formulário",
"add_new_form": "Adicionar novo formulário",

View File

@ -1340,7 +1340,6 @@
"exchange_version_2013_SP1": "2013 SP1",
"exchange_version_2015": "2015",
"exchange_version_2016": "2016",
"routing_forms_description": "Aqui pode ver todos os formulários e rotas que criou.",
"routing_forms_send_email_owner": "Enviar e-mail ao proprietário",
"routing_forms_send_email_owner_description": "Envia um e-mail ao proprietário quando o formulário for submetido",
"add_new_form": "Adicionar novo formulário",

View File

@ -195,6 +195,7 @@
"page_doesnt_exist": "Ova stranica ne postoji.",
"check_spelling_mistakes_or_go_back": "Proverite greške u pisanju ili idite nazad na prethodnu stranicu.",
"404_page_not_found": "404: Ovu stranicu ne možemo da nadjemo.",
"booker_event_not_found": "Nismo mogli da pronađemo događaj koji pokušavate da zakažete.",
"getting_started": "Započnite",
"15min_meeting": "Sastanak od 15 Min",
"30min_meeting": "Sastanak od 30 Min",
@ -363,6 +364,7 @@
"user_dynamic_booking_disabled": "Neki od korisnika u ovoj grupi trenutno su onemogućili dinamičke grupne rezervacije",
"allow_dynamic_booking_tooltip": "Moguće je dinamičko kreiranje linkova za grupne rezervacije navođenjem više korisničkih imena sa znakom „+”. Na primer: „{{appName}}/bailey+peer”",
"allow_dynamic_booking": "Dozvolite učesnicima da rezervišu termin sa vama putem dinamičkih grupnih rezervacija",
"dynamic_booking": "Dinamični linkovi grupa",
"email": "E-pošta",
"email_placeholder": "ppetrovic@example.com",
"full_name": "Ime i prezime",
@ -373,6 +375,8 @@
"booking_rescheduled": "Rezervacija Odložena",
"recording_ready": "Link za preuzimanje snimka je spreman",
"booking_created": "Rezervacija Napravljena",
"booking_rejected": "Rezevacija je odbijena",
"booking_requested": "Rezervacija je zahtevana",
"meeting_ended": "Sastanak se završio",
"form_submitted": "Formular poslat",
"event_triggers": "Okidači Dogadjaja",
@ -1815,6 +1819,7 @@
"email_invite_team_bulk": "Pozvano je {{userCount}} korisnika",
"error_collecting_card": "Greška u sakupljanju kartice",
"image_size_limit_exceed": "Otpremljena slika ne sme da prevazilazi ograničenje od 5mb",
"unauthorized_workflow_error_message": "{{errorCode}}: Niste ovlašćeni da omogućite ili onemogućite ovaj radni tok",
"inline_embed": "Ugradi neposredno",
"load_inline_content": "Direktno učitava vaš tip događaja neposredno sa vašim ostalim sadržajem na veb sajtu.",
"floating_pop_up_button": "Plutajuće iskačuće dugme",

View File

@ -760,6 +760,7 @@
"minimum_booking_notice": "Minimum Bildirim",
"offset_toggle": "Ofset başlangıç saatleri",
"offset_toggle_description": "Belirli bir dakika sayısına göre rezervasyon yapanlara gösterilen ofset zaman aralıkları",
"offset_start": "Ofset",
"offset_start_description": "örneğin; bu işlem rezervasyon yapan kişinin zaman aralıklarını {{ originalTime }} yerine {{ adjustedTime }} olarak görmesine neden olur",
"slot_interval": "Zaman dilimi aralıkları",
"slot_interval_default": "Etkinlik uzunluğunu kullan (varsayılan)",
@ -1759,7 +1760,7 @@
"managed_event_dialog_clarification": "Değiştirmeyi seçmeniz durumunda kullanıcıları bilgilendireceğiz. Üzerine yazmak istemiyorsanız geri dönerek onları kaldırın.",
"review_event_type": "Etkinlik Türünü inceleyin",
"looking_for_more_analytics": "Daha fazla analiz mi arıyorsunuz?",
"looking_for_more_insights": "Daha fazla Öngörü mü arıyorsunuz?",
"looking_for_more_insights": "Daha fazla Insights mı arıyorsunuz?",
"add_filter": "Filtre ekle",
"select_user": "Kullanıcı seç",
"select_event_type": "Etkinlik Türü seç",
@ -1812,15 +1813,27 @@
"payment_app_commission": "Ödeme talep edin (işlem başına %{{paymentFeePercentage}} + {{fee, currency}} komisyon)",
"email_invite_team": "{{email}} davet edildi",
"email_invite_team_bulk": "{{userCount}} kullanıcı davet edildi",
"error_collecting_card": "Kart bilgileri alınırken hata oluştu",
"image_size_limit_exceed": "Yüklenen resim boyutu 5 MB boyut sınırını aşmamalıdır",
"inline_embed": "Satır İçine Yerleştir",
"load_inline_content": "Etkinlik türünüzü doğrudan diğer web sitesi içeriğinizle birlikte yükler.",
"floating_pop_up_button": "Kayan açılır düğme",
"floating_button_trigger_modal": "Sitenize, bir olay türüne sahip bir modu tetikleyen kayan bir düğme ekler.",
"pop_up_element_click": "Ögenin tıklanmasıyla açılır",
"open_dialog_with_element_click": "Birisi bir öğeyi tıkladığında Cal iletişim kutunuzu açın.",
"need_help_embedding": "Yardıma mı ihtiyacınız var? Cal'ı Wix, Squarespace veya WordPress'e yerleştirmek için kılavuzlarımıza göz atın. Ayrıca Sıkça Sorulan Sorular bölümümüze göz atın veya gelişmiş yerleştirme seçeneklerini keşfedin.",
"book_my_cal": "Cal'ımı rezerve et",
"invite_as": "Davet edin",
"form_updated_successfully": "Form başarıyla güncellendi.",
"email_not_cal_member_cta": "Ekibinize ekleyin",
"disable_attendees_confirmation_emails": "Katılımcılar için varsayılan onay e-postalarını devre dışı bırakın",
"disable_attendees_confirmation_emails_description": "Bu etkinlik türünde etkinlik rezerve edildiğinde katılımcılara bir e-posta gönderen en az bir iş akışı etkin.",
"disable_host_confirmation_emails": "Organizatörler için varsayılan onay e-postalarını devre dışı bırakın",
"disable_host_confirmation_emails_description": "Bu etkinlik türündeki bir etkinlik rezerve edildiğinde organizatörlere e-posta gönderen en az bir iş akışı etkin.",
"add_an_override": "Üzerine yazma ekle",
"import_from_google_workspace": "Kullanıcıları, Google Workspace'ten içe aktarın",
"connect_google_workspace": "Google Workspace'i bağlayın",
"google_workspace_admin_tooltip": "Bu özelliği kullanmak için Çalışma Alanı Yöneticisi olmalısınız",
"first_event_type_webhook_description": "Bu etkinlik türü için ilk web kancanızı oluşturun"
"first_event_type_webhook_description": "Bu etkinlik türü için ilk web kancanızı oluşturun",
"create_for": "Oluşturun"
}

View File

@ -1760,7 +1760,7 @@
"managed_event_dialog_clarification": "Якщо ви захочете замінити її, ми повідомимо учасників. Поверніться і вилучіть їх, замість того щоб перезаписувати.",
"review_event_type": "Переглянути тип заходу",
"looking_for_more_analytics": "Потрібно більше аналітичних даних?",
"looking_for_more_insights": "Потрібно більше аналітики?",
"looking_for_more_insights": "Потрібно більше висновків Insights?",
"add_filter": "Додати фільтр",
"select_user": "Вибрати користувача",
"select_event_type": "Вибрати тип заходу",

View File

@ -1340,7 +1340,7 @@
"exchange_version_2013_SP1": "2013 SP1",
"exchange_version_2015": "2015",
"exchange_version_2016": "2016",
"routing_forms_description": "创建表格以将参与者引导到正确的目的地",
"routing_forms_description": "创建表格以将参与者引导到正确的目",
"routing_forms_send_email_owner": "向所有者发送电子邮件",
"routing_forms_send_email_owner_description": "提交表单后向所有者发送电子邮件",
"add_new_form": "添加新表格",

View File

@ -1,5 +1,6 @@
import { v4 as uuidv4 } from "uuid";
import type { CalendarEvent } from "@calcom/types/Calendar";
import type { PartialReference } from "@calcom/types/EventManager";
import type { VideoApiAdapter, VideoCallData } from "@calcom/types/VideoApiAdapter";
@ -11,19 +12,28 @@ const JitsiVideoApiAdapter = (): VideoApiAdapter => {
getAvailability: () => {
return Promise.resolve([]);
},
createMeeting: async (): Promise<VideoCallData> => {
createMeeting: async (eventData: CalendarEvent): Promise<VideoCallData> => {
const appKeys = await getAppKeysFromSlug(metadata.slug);
const meetingID = uuidv4();
const meetingPattern = (appKeys.jitsiPathPattern as string) || "{uuid}";
const hostUrl = (appKeys.jitsiHost as string) || "https://meet.jit.si/cal";
// Default Value
const hostUrl = appKeys.jitsiHost || "https://meet.jit.si/cal";
//Allows "/{Type}-with-{Attendees}" slug
const meetingID = meetingPattern
.replaceAll("{uuid}", uuidv4())
.replaceAll("{Title}", eventData.title)
.replaceAll("{Event Type Title}", eventData.type)
.replaceAll("{Scheduler}", eventData.attendees.map((a) => a.name).join("-"))
.replaceAll("{Organizer}", eventData.organizer.name)
.replaceAll("{Location}", eventData.location || "")
.replaceAll("{Team}", eventData.team?.name || "")
.replaceAll(" ", "-"); //Last Rule! - Replace all blanks (%20) with dashes;
return Promise.resolve({
type: metadata.type,
id: meetingID,
password: "",
url: hostUrl + "/" + meetingID,
url: hostUrl + "/" + encodeURIComponent(meetingID),
});
},
deleteMeeting: async (): Promise<void> => {

View File

@ -2,6 +2,7 @@ import { z } from "zod";
export const appKeysSchema = z.object({
jitsiHost: z.string().optional(),
jitsiPathPattern: z.string().optional(),
});
export const appDataSchema = z.object({});

View File

@ -3,15 +3,19 @@ import { useState } from "react";
import { useAppContextWithSchema } from "@calcom/app-store/EventTypeAppContext";
import AppCard from "@calcom/app-store/_components/AppCard";
import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import { Tooltip } from "@calcom/ui";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Tooltip, TextField } from "@calcom/ui";
import type { appDataSchema } from "../zod";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ eventType, app }) {
const { t } = useLocale();
const [getAppData, setAppData] = useAppContextWithSchema<typeof appDataSchema>();
const [enabled, setEnabled] = useState(getAppData("enabled"));
const [additionalParameters, setAdditionalParameters] = useState("");
const eventTypeURL = eventType.URL;
const query = additionalParameters !== "" ? `?${additionalParameters}` : "";
const eventTypeURL = eventType.URL + query;
function QRCode({ size, data }: { size: number; data: string }) {
const QR_URL = "https://api.qrserver.com/v1/create-qr-code/?size=" + size + "&data=" + data;
@ -19,7 +23,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
<Tooltip content={eventTypeURL}>
<a download href={QR_URL} target="_blank" rel="noreferrer">
<img
className="hover:bg-muted border hover:shadow-sm"
className="hover:bg-muted border-default border hover:shadow-sm"
style={{ padding: size / 16, borderRadius: size / 20 }}
width={size}
src={QR_URL}
@ -42,10 +46,22 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
}
}}
switchChecked={enabled}>
<div className="max-w-60 flex items-baseline justify-between gap-2 text-sm ">
<QRCode size={256} data={eventTypeURL} />
<QRCode size={128} data={eventTypeURL} />
<QRCode size={64} data={eventTypeURL} />
<div className="flex w-full flex-col gap-2 text-sm">
<div className="flex w-full">
<TextField
name="hello"
value={additionalParameters}
onChange={(e) => setAdditionalParameters(e.target.value)}
label={t("additional_url_parameters")}
containerClassName="w-full"
/>
</div>
<div className="max-w-60 flex items-baseline gap-2">
<QRCode size={256} data={eventTypeURL} />
<QRCode size={128} data={eventTypeURL} />
<QRCode size={64} data={eventTypeURL} />
</div>
</div>
</AppCard>
);

View File

@ -103,7 +103,7 @@ const BookerComponent = ({
since that's not a valid option, so it would set the layout to null.
*/}
{!isMobile && (
<div className="[&>div]:bg-default fixed top-2 right-3 z-10">
<div className="[&>div]:bg-default dark:[&>div]:bg-muted fixed top-2 right-3 z-10">
<ToggleGroup
onValueChange={onLayoutToggle}
defaultValue={layout}
@ -133,7 +133,7 @@ const BookerComponent = ({
className={classNames(
// Sets booker size css variables for the size of all the columns.
...getBookerSizeClassNames(layout, bookerState),
"bg-default grid max-w-full auto-rows-fr items-start overflow-clip dark:[color-scheme:dark] sm:transition-[width] sm:duration-300 sm:motion-reduce:transition-none md:flex-row",
"bg-default dark:bg-muted grid max-w-full auto-rows-fr items-start overflow-clip dark:[color-scheme:dark] sm:transition-[width] sm:duration-300 sm:motion-reduce:transition-none md:flex-row",
layout === "small_calendar" && "border-subtle rounded-md border"
)}>
<AnimatePresence>

View File

@ -20,7 +20,7 @@ export function BookFormAsModal({ visible, onCancel }: { visible: boolean; onCan
<DialogContent
type={undefined}
enableOverflow
className="[&_.modalsticky]:border-t-subtle [&_.modalsticky]:bg-default max-h-[80vh] pt-6 pb-0 [&_.modalsticky]:sticky [&_.modalsticky]:bottom-0 [&_.modalsticky]:left-0 [&_.modalsticky]:right-0 [&_.modalsticky]:-mx-8 [&_.modalsticky]:border-t [&_.modalsticky]:px-6 [&_.modalsticky]:py-4">
className="[&_.modalsticky]:border-t-subtle [&_.modalsticky]:bg-default dark:[&_.modalsticky]:bg-muted max-h-[80vh] pt-6 pb-0 [&_.modalsticky]:sticky [&_.modalsticky]:bottom-0 [&_.modalsticky]:left-0 [&_.modalsticky]:right-0 [&_.modalsticky]:-mx-8 [&_.modalsticky]:border-t [&_.modalsticky]:px-6 [&_.modalsticky]:py-4">
<h1 className="font-cal text-emphasis text-xl leading-5">{t("confirm_your_details")} </h1>
<div className="mt-4 flex space-x-2 rounded-md leading-none">
<Badge variant="grayWithoutHover" startIcon={Calendar} size="lg">

View File

@ -11,7 +11,7 @@ export const LargeCalendar = () => {
);
return (
<div className="bg-default flex h-full w-full flex-col items-center justify-center">
<div className="bg-default dark:bg-muted flex h-full w-full flex-col items-center justify-center">
Something big is coming...
<br />
<button

View File

@ -2,7 +2,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
const UnAvailableMessage = ({ children, title }: { children: React.ReactNode; title: string }) => (
<div className="mx-auto w-full max-w-2xl">
<div className="border-subtle bg-default overflow-hidden rounded-lg border p-10">
<div className="border-subtle bg-default dark:bg-muted overflow-hidden rounded-lg border p-10">
<h2 className="font-cal mb-4 text-3xl">{title}</h2>
{children}
</div>

View File

@ -39,7 +39,7 @@ export const AvailableTimes = ({
return (
<div className={classNames("text-default", className)}>
<header className="bg-muted before:bg-muted mb-5 flex w-full flex-row items-center font-medium">
<header className="bg-default before:bg-default dark:bg-muted dark:before:bg-muted mb-5 flex w-full flex-row items-center font-medium">
<span className={classNames(isLargeTimeslots && "w-full text-center")}>
<span className="text-emphasis font-semibold">
{nameOfDay(i18n.language, Number(date.format("d")), "short")}

View File

@ -489,7 +489,6 @@ function getBookingData({
}
}
});
debugger;
const reqBody = bookingDataSchema.parse(req.body);
if ("customInputs" in reqBody) {
if (reqBody.customInputs) {
@ -585,7 +584,6 @@ async function handler(
...eventType,
bookingFields: getBookingFieldsWithSystemFields(eventType),
};
debugger;
const {
recurringCount,
allRecurringDates,

View File

@ -1,17 +1,27 @@
import { Suspense } from "react";
import NoSSR from "@calcom/core/components/NoSSR";
import { Meta } from "@calcom/ui";
import { Loader } from "@calcom/ui/components/icon";
import { Meta, SkeletonText, SkeletonContainer } from "@calcom/ui";
import { FlagAdminList } from "../components/FlagAdminList";
const SkeletonLoader = () => {
return (
<SkeletonContainer>
<div className="divide-subtle mt-6 mb-8 space-y-6">
<SkeletonText className="h-8 w-full" />
<SkeletonText className="h-8 w-full" />
</div>
</SkeletonContainer>
);
};
export const FlagListingView = () => {
return (
<>
<Meta title="Feature Flags" description="Here you can toggle your Cal.com instance features." />
<NoSSR>
<Suspense fallback={<Loader />}>
<Suspense fallback={<SkeletonLoader />}>
<FlagAdminList />
</Suspense>
</NoSSR>

View File

@ -29,7 +29,7 @@ export const DateSelect = () => {
range &&
(range === "tdy" || range === "w" || range === "t" || range === "m" || range === "y")
) {
setDateRange([dayjs(start), dayjs(end), range]);
setDateRange([dayjs(start).startOf("d"), dayjs(end).endOf("d"), range]);
return;
} else if (start && !end) {
// If only start time has value that means selected date should push to dateRange with last value null

View File

@ -5,7 +5,7 @@ import Link from "next/link";
import type { NextRouter } from "next/router";
import { useRouter } from "next/router";
import type { Dispatch, ReactNode, SetStateAction } from "react";
import React, { Fragment, useEffect, useState, useRef, useLayoutEffect } from "react";
import React, { Fragment, useEffect, useState, useRef } from "react";
import { Toaster } from "react-hot-toast";
import dayjs from "@calcom/dayjs";
@ -21,6 +21,7 @@ import AdminPasswordBanner from "@calcom/features/users/components/AdminPassword
import classNames from "@calcom/lib/classNames";
import { APP_NAME, DESKTOP_APP_LINK, JOIN_SLACK, ROADMAP, WEBAPP_URL } from "@calcom/lib/constants";
import getBrandColours from "@calcom/lib/getBrandColours";
import { useIsomorphicLayoutEffect } from "@calcom/lib/hooks/useIsomorphicLayoutEffect";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { isKeyInObject } from "@calcom/lib/isKeyInObject";
import { trpc } from "@calcom/trpc/react";
@ -143,7 +144,7 @@ const Layout = (props: LayoutProps) => {
const bannerRef = useRef<HTMLDivElement | null>(null);
const [bannersHeight, setBannersHeight] = useState<number>(0);
useLayoutEffect(() => {
useIsomorphicLayoutEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
const { offsetHeight } = entries[0].target as HTMLElement;
setBannersHeight(offsetHeight);
@ -641,34 +642,34 @@ const NavigationItem: React.FC<{
return (
<Fragment>
<Tooltip side="right" content={t(item.name)} className="lg:hidden">
<Link
href={item.href}
aria-label={t(item.name)}
className={classNames(
"hover:bg-emphasis [&[aria-current='page']]:bg-emphasis hover:text-emphasis text-default group flex items-center rounded-md py-2 px-3 text-sm font-medium",
isChild
? `[&[aria-current='page']]:text-emphasis hidden h-8 pl-16 lg:flex lg:pl-11 [&[aria-current='page']]:bg-transparent ${
props.index === 0 ? "mt-0" : "mt-px"
}`
: "[&[aria-current='page']]:text-emphasis mt-0.5 text-sm"
)}
aria-current={current ? "page" : undefined}>
{item.icon && (
<item.icon
className="h-4 w-4 flex-shrink-0 ltr:mr-2 rtl:ml-2 [&[aria-current='page']]:text-inherit"
aria-hidden="true"
aria-current={current ? "page" : undefined}
/>
)}
{isLocaleReady ? (
<span className="hidden w-full justify-between lg:flex">
<div className="flex">{t(item.name)}</div>
{item.badge && item.badge}
</span>
) : (
<SkeletonText className="h-3 w-32" />
)}
</Link>
<Link
href={item.href}
aria-label={t(item.name)}
className={classNames(
"hover:bg-emphasis [&[aria-current='page']]:bg-emphasis hover:text-emphasis text-default group flex items-center rounded-md py-2 px-3 text-sm font-medium",
isChild
? `[&[aria-current='page']]:text-emphasis hidden h-8 pl-16 lg:flex lg:pl-11 [&[aria-current='page']]:bg-transparent ${
props.index === 0 ? "mt-0" : "mt-px"
}`
: "[&[aria-current='page']]:text-emphasis mt-0.5 text-sm"
)}
aria-current={current ? "page" : undefined}>
{item.icon && (
<item.icon
className="h-4 w-4 flex-shrink-0 ltr:mr-2 rtl:ml-2 [&[aria-current='page']]:text-inherit"
aria-hidden="true"
aria-current={current ? "page" : undefined}
/>
)}
{isLocaleReady ? (
<span className="hidden w-full justify-between lg:flex">
<div className="flex">{t(item.name)}</div>
{item.badge && item.badge}
</span>
) : (
<SkeletonText className="h-3 w-32" />
)}
</Link>
</Tooltip>
{item.child &&
isCurrent({ router, isChild, item }) &&

View File

@ -0,0 +1,3 @@
import { useEffect, useLayoutEffect } from "react";
export const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;

View File

@ -5,6 +5,7 @@ import type { NextApiResponse, GetServerSidePropsContext } from "next";
import { stripeDataSchema } from "@calcom/app-store/stripepayment/lib/server";
import updateChildrenEventTypes from "@calcom/features/ee/managed-event-types/lib/handleChildrenEventTypes";
import { validateIntervalLimitOrder } from "@calcom/lib";
import logger from "@calcom/lib/logger";
import { WorkflowActions, WorkflowTriggerEvents } from "@calcom/prisma/client";
import { SchedulingType } from "@calcom/prisma/enums";
@ -287,7 +288,12 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
});
const res = ctx.res as NextApiResponse;
if (typeof res?.revalidate !== "undefined") {
await res?.revalidate(`/${ctx.user.username}/${eventType.slug}`);
try {
await res?.revalidate(`/${ctx.user.username}/${eventType.slug}`);
} catch (e) {
// if reach this it is because the event type page has not been created, so it is not possible to revalidate it
logger.debug((e as Error)?.message);
}
}
return { eventType };
};

View File

@ -7,13 +7,13 @@ export const getScheduleSchema = z
// endTime ISOString
endTime: z.string(),
// Event type ID
eventTypeId: z.number().int().optional(),
eventTypeId: z.coerce.number().int().optional(),
// Event type slug
eventTypeSlug: z.string(),
eventTypeSlug: z.string().optional(),
// invitee timezone
timeZone: z.string().optional(),
// or list of users (for dynamic events)
usernameList: z.array(z.string()).optional(),
usernameList: z.union([z.string(), z.array(z.string())]).optional(),
debug: z.boolean().optional(),
// to handle event types with multiple duration options
duration: z
@ -21,9 +21,16 @@ export const getScheduleSchema = z
.optional()
.transform((val) => val && parseInt(val)),
})
.transform((val) => {
// Need this so we can pass a single username in the query string form public API
if (val.usernameList) {
val.usernameList = Array.isArray(val.usernameList) ? val.usernameList : [val.usernameList];
}
return val;
})
.refine(
(data) => !!data.eventTypeId || !!data.usernameList,
"Either usernameList or eventTypeId should be filled in."
(data) => !!data.eventTypeId || (!!data.usernameList && !!data.eventTypeSlug),
"You need to either pass an eventTypeId OR an usernameList/eventTypeSlug combination"
);
export const reserveSlotSchema = z

View File

@ -11,8 +11,7 @@ import isTimeOutOfBounds from "@calcom/lib/isOutOfBounds";
import logger from "@calcom/lib/logger";
import { performance } from "@calcom/lib/server/perfObserver";
import getTimeSlots from "@calcom/lib/slots";
import { availabilityUserSelect } from "@calcom/prisma";
import prisma from "@calcom/prisma";
import prisma, { availabilityUserSelect } from "@calcom/prisma";
import { SchedulingType } from "@calcom/prisma/enums";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import type { EventBusyDate } from "@calcom/types/Calendar";
@ -140,6 +139,12 @@ export async function getEventType(input: TGetScheduleInputSchema) {
export async function getDynamicEventType(input: TGetScheduleInputSchema) {
// For dynamic booking, we need to get and update user credentials, schedule and availability in the eventTypeObject as they're required in the new availability logic
if (!input.eventTypeSlug) {
throw new TRPCError({
message: "eventTypeSlug is required for dynamic booking",
code: "BAD_REQUEST",
});
}
const dynamicEventType = getDefaultEvent(input.eventTypeSlug);
const users = await prisma.user.findMany({
where: {

View File

@ -30,7 +30,7 @@ const ColorPicker = (props: ColorPickerProps) => {
return (
<div className="mt-1 flex h-[38px] items-center justify-center">
<Popover.Root>
<div className="border-default flex h-full w-10 items-center items-center justify-center border ltr:rounded-l-md ltr:border-r-0 rtl:rounded-r-md rtl:border-l-0">
<div className="border-default min-w-9 flex h-full items-center justify-center border ltr:rounded-l-md ltr:border-r-0 rtl:rounded-r-md rtl:border-l-0">
<Popover.Trigger asChild>
<button
className="h-5 w-5 rounded-sm"

View File

@ -30,7 +30,9 @@ function BasePhoneInput({ name, className = "", onChange, ...rest }: PhoneInputP
required: rest.required,
placeholder: rest.placeholder,
}}
onChange={(value) => onChange(value)}
onChange={(value) => {
onChange("+" + value);
}}
containerClass={classNames(
"hover:border-emphasis dark:focus:border-emphasis border-default !bg-default rounded-md border focus-within:outline-none focus-within:ring-2 focus-within:ring-brand-default disabled:cursor-not-allowed",
className
@ -63,7 +65,7 @@ const useDefaultCountry = () => {
retry: false,
onSuccess: (data) => {
if (isSupportedCountry(data?.countryCode)) {
setDefaultCountry(data.countryCode);
setDefaultCountry(data.countryCode.toLowerCase());
}
},
});