Compare commits

...
49 Commits
Author SHA1 Message Date
GitHub Actions 79a8ab79f8 chore: release v4.9.0
Post release / release (push) Canceled after 0s
2025-01-16 01:26:26 +00:00
Benny Joo ce87de4fc7 fix: Revert - chore: app router - all sub-pages in /apps #16976 (#18702)
* revert

* add incomplete-booking
2025-01-15 21:12:18 -03:00
Calcom BotandReplexica b128579729 feat: update translations via @replexica (#18699)
Co-authored-by: Replexica <support@replexica.com>
2025-01-15 20:38:44 +00:00
Benny Joo 3cc5d9c065 fix: revert app router error pages (#18696) 2025-01-15 16:47:30 -03:00
Benny Joo 7897e14273 fix: add back global-error.tsx (#18692) 2025-01-15 16:55:43 +00:00
GitHub Actions c581456586 chore: release v4.8.19
Post release / release (push) Canceled after 0s
2025-01-15 15:30:52 +00:00
36dd3e9e6c fix: disable custom templates for free plan (#18635)
* add upgrade badge

* adjust update handler

* code clean up

* clean up template code

* only handle email templates

* fixes

* code clean up

* fix workflow template zod type

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2025-01-15 15:25:14 +00:00
Alex van Andel f137aee436 Remove export keyword @ route - only default allowed (#18682) 2025-01-15 14:33:02 +00:00
Eunjae Lee 4f494d5a32 refactor: use DataTable for /bookings (#18589)
* refactor: use DataTable for /bookings

* remove unnecessary animation (for better scroll performance)

* add consistent search box
2025-01-15 14:29:50 +00:00
Benny Joo cce6e83578 chore: migrate /org team pages and team type pages and user type page (#18668) 2025-01-15 09:21:21 +00:00
Lauris Skraucis c21ba636d2 fix: Booker atom name booking field (#18653)
* feat: split name booking field in v2

* fix: booker atom handle split name

* fix: after and before start dates bookings filter

* chore: bump platform libraries
2025-01-15 14:08:06 +05:30
Benny Joo 5ccf5e6be8 chore: migrate /team booking pages (#18656)
* wip

* remove pages/team/[slug]

* fix tests

* fix tests 2
2025-01-14 23:48:41 +00:00
Joe Au-YeungandAlex van Andel 66b3e735d8 feat: Routing form submitted but no booking - Salesforce actions (#18616)
* Add booking incomplete actions table

* Add incomplete booking page tab

* Add `getincompleteBookingSettings` trpc endpoints

* Add incomplete booking page

* Abstract enabled apps array

* Handle no enabled credentials and no actions

* Add enabled field to incomplete booking action db record

* Add new write record entries

* UI add separation between switch and inputs

* Fix typo

* clean up

* Add saveIncompleteBookingSettings endpoint

* Save incomplete booking settings

* Fix language around when to write to field

* Add `credentialId` to action record

* Choose which credential to assign to action

* Save credential to action

* Revert "Save credential to action"

This reverts commit ba6a1c808baed54f6d3d4c6155cf192c813d0696.

* Revert "Choose which credential to assign to action"

This reverts commit 968f6e5295d098c64abe95268afd1fa139a175ba.

* Revert "Add `credentialId` to action record"

This reverts commit 579f9ff4167b65aa987659e4e8f8c26f9e773317.

* Add credentialId to action record - rewrite migration file

* Revert "Add credentialId to action record - rewrite migration file"

This reverts commit 2843a92c61820e7f7a1614a557d3f7ea96342107.

* Revert "Add booking incomplete actions table"

This reverts commit 7ec75bef4a0f0a9d07be0142da64c49d739439ea.

* Revert "Add enabled field to incomplete booking action db record"

This reverts commit d279a1da05819eafa8fc5d664e3be733e0687e8d.

* Write migration in single commit

* Rename table

* Rename table - remove underscores

* Remove credential relationship

* Type fix - changing table name

* Fix table name

* Change writeToRecordObject to object

* Salesforce add incomplete booking, write to record

* Add incomplete booking actions to `triggerFormSubmittedNoEventWebhooks`

* Remove console.log

* Type fixes

* Type fixes

* Iterate if incompleteBookingActions

* Choose which credential to assign to action

* Save credential to action

* Fix getServerSideProp changes

* Type fix

* Type fix

* Type fix

* Type fix

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2025-01-14 23:17:16 +00:00
Praash 7a8b9ededb fix: html render from calendar invite (#18577) 2025-01-14 20:40:31 +00:00
Benny Joo 501c49f369 perf: no need for two query fetches for d link (private booking page) (#18659)
* return eventData

* use eventData and do not do extra query fetch
2025-01-14 17:15:56 +00:00
Amit Sharma 536604455b fix: BUG: adding filters to settings/teams/[team-id]/members page throws error (#18631) 2025-01-14 16:30:54 +00:00
MorganandPeer Richelsen cfb76a9689 chore: platform libraries readme publish reasons (#18646)
* chore: platform libraries readme publish reasons

* fixup! chore: platform libraries readme publish reasons

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2025-01-14 16:28:34 +00:00
Udit TakkarandPeer Richelsen 30180edc0b fix: phone number in confirmation email (#18642)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2025-01-14 16:27:24 +00:00
MorganandCarina Wollendorfer 2aa3d1d4e0 feat: get org ooo entries and filters/sort (#18645)
* feat: get org ooo entries and filters/sort

* remove console.log

---------

Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2025-01-14 15:33:22 +00:00
Eunjae Lee 21694c039a perf: improve routing insights view (2) (#18566) 2025-01-14 12:28:39 -03:00
Benny Joo a8c29b5459 chore: log error to Sentry in app router error page (#18637) 2025-01-14 15:23:07 +00:00
Anik Dhabal Babu 6cd7454f43 fix: /booking page datefilter (#18654)
* fix: /booking page datefilter

* Update get.handler.ts

* Update get.handler.ts

* shit
2025-01-14 15:14:28 +00:00
Calcom BotandReplexica 99b2419dd7 feat: update translations via @replexica (#18598)
* chore: sync with main

* feat: update translations via @replexica

---------

Co-authored-by: Replexica <support@replexica.com>
2025-01-14 13:47:22 +00:00
Benny Joo f6d0a2ca09 fix: potential fix for flaky layout shift (#18651)
* potential fix: layout shift

* fix lint error
2025-01-14 13:36:20 +00:00
Alex van Andel f5ab9bd40b Allow lint to error but continue (unblock pipeline) 2025-01-14 13:12:24 +00:00
Benny JooandAlex van Andel 07085b9ffb chore: clean up config.matcher in middleware (#18638)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2025-01-14 10:24:36 +00:00
Benny Joo 14151b827f fix: make ESLint work and fix lint errors that were undetected before (#18639)
* fix eslint config

* fix

* add it to dev dep

* fix

* sync eslint version

* force ts-node to compile our ESLint plugin's TS files into CommonJS (which ESLint requires)

* fix some lint errors

* fix lint errors

* remove duplicate classname

* make @typescript-eslint/ban-types a warn for packages/trpc files

* fix lint errors in trpc

* fix lint errors in trpc - 2

* fix

* fix

* fix lint warnings
2025-01-14 08:58:38 +00:00
Kartik Saini 28b757f19c feat: do not show automation webhooks in webhook list (#18607) 2025-01-14 10:55:22 +05:30
Alex van Andel 68455db860 fix: main lint errors (#18634)
* fix: error-page.tsx related lint errors

* fix: lint no continue-on-error

* Adding more lint fixes

* Bring back annotate code linting results

* Bring back linting continue-on-error

* Slimline lint
2025-01-13 22:01:20 +00:00
Udit Takkar 96ff88d266 fix: disable sending sms when email is present (#18632)
* fix: disable sending sms when email is present

* fix: update test
2025-01-13 20:38:44 +00:00
GitHub Actions f88a91dad0 chore: release v4.8.18
Post release / release (push) Canceled after 0s
2025-01-13 18:12:32 +00:00
Keith WilliamsandAlex van Andel 35059d7727 revert: "feat: bulk shorten links with dub.links.createMany (#18539)" (#18587)
This reverts commit 4902c6a0f8.

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2025-01-13 18:07:27 +00:00
Carina WollendorferandCarinaWolli 68f76e2acc disable emails to all guests (#18628)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2025-01-13 15:58:43 +00:00
Benny Joo 80aac06ff3 chore: restore error pages for pages router (#18625) 2025-01-13 12:42:00 +00:00
sean-brydon 0f47528ba2 fix: parsing teamId (#18623) 2025-01-13 12:16:59 +00:00
Benny Joo 113dc5fca6 chore: try using custom 404 in pages/_error (#18622) 2025-01-13 11:35:10 +00:00
Benny Joo 0befdde5d7 chore: gracefully handle 404s from pages router's dynamic pages + tests (#18618)
* restore pages/_error

* set custom header in pages/_error

* handle it in middleware

* add test

* remove logs

* better test description
2025-01-13 15:46:56 +05:30
Benny Joo b3ed6e39e3 chore: redirect to /500 if pathname does not exist + better error handling (#18615)
* fix lint error

* fix booking page and better error handling
2025-01-13 00:04:36 +00:00
sean-brydon e07b1eda32 Async False (#18611) 2025-01-12 20:31:43 +00:00
Benny Joo 8262894bde fix: app/not-found cannot be a static page (#18610) 2025-01-12 20:21:03 +01:00
Benny Joo a3d7bcd22e feat: render custom error page for unexpected sever error + remove pages/_error (#18606)
* remove page/_error

* refactor app/error
2025-01-12 14:12:02 +01:00
Omar LópezandBenny Joo 62b753afba chore: app router 404 page (#18597)
* wip

* wip

* fix not found page

* render middleware for /settings pages

* fix

* remove global-error page

* add metadata to not-found page

* make not-found page static

* remove 404

* adding not-found to middleware is not necessary

* add every routes to config.matcher

* fix test

* fix style

* use i18n string

* fix tests

* fix

* fix

* revert unneeded changes

* fix

* fix

* fix

* fix style

* fix

* remove 404

* remove log

* fix

* fix

* fix

* fix

* better naming

* parallel testing

---------

Co-authored-by: Benny Joo <sldisek783@gmail.com>
2025-01-11 02:42:08 +00:00
bb40b2f6bf chore: app router - all sub-pages in /apps (#16976)
* chore: apps/[slug] remove pages router

* remove apps/[slug] pages from /future

* chore: apps/installed remove pages router

* chore: apps/installation remove pages router

* remove Head element

* fix metadata

* fix test

* fix another test

* chore: apps/categories remove pages router

* revert unneeded changes

* update middleware

* Remove <Head>

* remove unused import and code

* remove unused import and code again

* fix

* fix category page

* add split icon

* add /routing paths to middleware matcher

* wip

* remove HeadSeo from App.tsx

* clean up head-seo test

* add generateAppMetadata

* use generateAppMetadata in apps/[slug] page

* delete file

* remove log

* fix

* fix

* fix apps/installed pages

* fix cateogires pages

* fix

* fix imports

* wip

* fix

* fix

* fix metadata

* fix

* redirect /apps/routing-forms to /routing

* replace all usages of /apps/routing-forms to /routing

* better naming

* /routing -> /routing/forms

* fix

* fix

* fix

* fix

* remove backPath as it is irrelevant when withoutMain is true

* fix type checks

* fix type check in apps/[slug]

* refactors

* fix

* fix test

* fix

* fix

* fix

* Replace multiple leading slashes with a single slash

* migrate routing-forms too

* add re routing

* fix

* add redirection

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-01-10 20:16:11 -05:00
NizzyandOmar López f628dc83a8 detailed customer card (#18511)
Co-authored-by: Omar López <zomars@me.com>
2025-01-10 23:51:27 +00:00
sean-brydonandOmar López 2fd2578504 feat: Round Robin weights future members toggle (#17782)
Co-authored-by: Omar López <zomars@me.com>
2025-01-10 14:24:19 -07:00
Syed Ali ShahbazandCarinaWolli a29407349d docs: fix error introduced in #18578 (#18593)
* Fix error

* remove indent

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2025-01-10 19:48:12 +00:00
Joe Au-YeungandAnik Dhabal Babu e7df1ef493 fix: Create dub links if valid url (#18588)
* Create dub links if valid

* Map through filtered links

---------

Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-01-10 15:58:16 +00:00
Kartik Saini b652ae7714 fix: wrong Meeting Ended payload in zapier (#18202)
* fix: wrong Meeting Ended payload in zapier

* fix
2025-01-10 15:55:35 +01:00
Peer Richelsen cec1c9ee46 feat: granola.ai app (#18585)
* new granola app

* granola app
2025-01-10 14:47:18 +00:00
187 changed files with 3401 additions and 1210 deletions
+2 -12
View File
@@ -12,16 +12,6 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/dangerous-git-checkout
- uses: ./.github/actions/yarn-install
- name: Run Linting with Reports
run: yarn lint:report
- name: Run Lint
run: yarn lint
continue-on-error: true
- name: Merge lint reports
run: jq -s '[.[]]|flatten' lint-results/*.json &> lint-results/eslint_report.json
- name: Upload ESLint report
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: lint-results
path: lint-results
+6
View File
@@ -0,0 +1,6 @@
undecided:
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
+8
View File
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
+3
View File
@@ -0,0 +1,3 @@
undecided:
- calcom-monorepo
- "@calcom/prisma"
+1 -1
View File
@@ -29,7 +29,7 @@
"@axiomhq/winston": "^1.2.0",
"@calcom/platform-constants": "*",
"@calcom/platform-enums": "*",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.81",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.82",
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
@@ -3,6 +3,8 @@ import { ApiProperty, ApiPropertyOptional, PartialType } from "@nestjs/swagger";
import { Transform } from "class-transformer";
import { IsDate, IsInt, IsOptional, IsString, IsEnum, isDate } from "class-validator";
import { SkipTakePagination } from "@calcom/platform-types";
export enum OutOfOfficeReason {
UNSPECIFIED = "unspecified",
VACATION = "vacation",
@@ -80,3 +82,47 @@ export class CreateOutOfOfficeEntryDto {
}
export class UpdateOutOfOfficeEntryDto extends PartialType(CreateOutOfOfficeEntryDto) {}
export enum SortOrder {
asc = "asc",
desc = "desc",
}
type SortOrderType = keyof typeof SortOrder;
export class GetOutOfOfficeEntryFiltersDTO extends SkipTakePagination {
@IsOptional()
@IsEnum(SortOrder, {
message: 'SortStart must be either "asc" or "desc".',
})
@ApiProperty({
required: false,
description: "Sort results by their start time in ascending or descending order.",
example: "?sortStart=asc OR ?sortStart=desc",
enum: SortOrder,
})
sortStart?: SortOrderType;
@IsOptional()
@IsEnum(SortOrder, {
message: 'SortEnd must be either "asc" or "desc".',
})
@ApiProperty({
required: false,
description: "Sort results by their end time in ascending or descending order.",
example: "?sortEnd=asc OR ?sortEnd=desc",
enum: SortOrder,
})
sortEnd?: SortOrderType;
}
export class GetOrgUsersOutOfOfficeEntryFiltersDTO extends GetOutOfOfficeEntryFiltersDTO {
@IsString()
@IsOptional()
@ApiProperty({
type: String,
required: false,
description: "Filter ooo entries by the user email address. user must be within your organization.",
example: "example@domain.com",
})
email?: string;
}
@@ -46,12 +46,19 @@ export class UserOOORepository {
});
}
async getUserOOOPaginated(userId: number, skip: number, take: number) {
async getUserOOOPaginated(
userId: number,
skip: number,
take: number,
sort?: { sortStart?: "asc" | "desc"; sortEnd?: "asc" | "desc" }
) {
return this.dbRead.prisma.outOfOfficeEntry.findMany({
where: { userId },
skip,
take,
include: { reason: true },
...(sort?.sortStart && { orderBy: { start: sort.sortStart } }),
...(sort?.sortEnd && { orderBy: { end: sort.sortEnd } }),
});
}
@@ -2,6 +2,8 @@ import {
CreateOutOfOfficeEntryDto,
UpdateOutOfOfficeEntryDto,
OutOfOfficeReason,
GetOutOfOfficeEntryFiltersDTO,
SortOrder,
} from "@/modules/ooo/inputs/ooo.input";
import { UserOOORepository } from "@/modules/ooo/repositories/ooo.repository";
import { UsersRepository } from "@/modules/users/users.repository";
@@ -128,8 +130,13 @@ export class UserOOOService {
return this.formatOooReason(ooo);
}
async getUserOOOPaginated(userId: number, skip: number, take: number) {
const ooos = await this.oooRepository.getUserOOOPaginated(userId, skip, take);
async getUserOOOPaginated(
userId: number,
skip: number,
take: number,
sort?: { sortStart?: "asc" | "desc"; sortEnd?: "asc" | "desc" }
) {
const ooos = await this.oooRepository.getUserOOOPaginated(userId, skip, take, sort);
return ooos.map((ooo) => this.formatOooReason(ooo));
}
}
@@ -8,13 +8,19 @@ import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard";
import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard";
import { IsUserInOrg } from "@/modules/auth/guards/users/is-user-in-org.guard";
import { IsUserOOO } from "@/modules/ooo/guards/is-user-ooo";
import { CreateOutOfOfficeEntryDto, UpdateOutOfOfficeEntryDto } from "@/modules/ooo/inputs/ooo.input";
import {
CreateOutOfOfficeEntryDto,
UpdateOutOfOfficeEntryDto,
GetOutOfOfficeEntryFiltersDTO,
GetOrgUsersOutOfOfficeEntryFiltersDTO,
} from "@/modules/ooo/inputs/ooo.input";
import {
UserOooOutputDto,
UserOooOutputResponseDto,
UserOoosOutputResponseDto,
} from "@/modules/ooo/outputs/ooo.output";
import { UserOOOService } from "@/modules/ooo/services/ooo.service";
import { OrgUsersOOOService } from "@/modules/organizations/controllers/users/ooo/services/organization-users-ooo.service";
import {
Controller,
UseGuards,
@@ -33,10 +39,9 @@ import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToInstance } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { SkipTakePagination } from "@calcom/platform-types";
@Controller({
path: "/v2/organizations/:orgId/users/:userId/ooo",
path: "/v2/organizations/:orgId",
version: API_VERSIONS_VALUES,
})
@UseInterceptors(ClassSerializerInterceptor)
@@ -44,18 +49,22 @@ import { SkipTakePagination } from "@calcom/platform-types";
@UseGuards(IsOrgGuard)
@DocsTags("Orgs / Users / OOO")
export class OrganizationsUsersOOOController {
constructor(private readonly userOOOService: UserOOOService) {}
constructor(
private readonly userOOOService: UserOOOService,
private readonly orgUsersOOOService: OrgUsersOOOService
) {}
@Get()
@Get("/users/:userId/ooo")
@Roles("ORG_ADMIN")
@PlatformPlan("ESSENTIALS")
@UseGuards(IsUserInOrg)
@ApiOperation({ summary: "Get all ooo entries of a user" })
async getOrganizationUserOOO(
@Param("userId", ParseIntPipe) userId: number,
@Query() query: SkipTakePagination
@Query() query: GetOutOfOfficeEntryFiltersDTO
): Promise<UserOoosOutputResponseDto> {
const ooos = await this.userOOOService.getUserOOOPaginated(userId, query.skip ?? 0, query.take ?? 250);
const { skip, take, ...rest } = query ?? { skip: 0, take: 250 };
const ooos = await this.userOOOService.getUserOOOPaginated(userId, skip ?? 0, take ?? 250, rest);
return {
status: SUCCESS_STATUS,
@@ -63,7 +72,7 @@ export class OrganizationsUsersOOOController {
};
}
@Post()
@Post("/users/:userId/ooo")
@Roles("ORG_ADMIN")
@PlatformPlan("ESSENTIALS")
@UseGuards(IsUserInOrg)
@@ -79,7 +88,7 @@ export class OrganizationsUsersOOOController {
};
}
@Patch("/:oooId")
@Patch("/users/:userId/ooo/:oooId")
@Roles("ORG_ADMIN")
@PlatformPlan("ESSENTIALS")
@UseGuards(IsUserInOrg, IsUserOOO)
@@ -97,7 +106,7 @@ export class OrganizationsUsersOOOController {
};
}
@Delete("/:oooId")
@Delete("/users/:userId/ooo/:oooId")
@Roles("ORG_ADMIN")
@PlatformPlan("ESSENTIALS")
@UseGuards(IsUserInOrg, IsUserOOO)
@@ -111,4 +120,23 @@ export class OrganizationsUsersOOOController {
data: plainToInstance(UserOooOutputDto, ooo, { strategy: "excludeAll" }),
};
}
@Get("/ooo")
@Roles("ORG_ADMIN")
@PlatformPlan("ESSENTIALS")
@ApiOperation({ summary: "Get all OOO entries of org users" })
async getOrganizationUsersOOO(
@Param("orgId", ParseIntPipe) orgId: number,
@Query() query: GetOrgUsersOutOfOfficeEntryFiltersDTO
): Promise<UserOoosOutputResponseDto> {
const { skip, take, email, ...rest } = query ?? { skip: 0, take: 250 };
const ooos = await this.orgUsersOOOService.getOrgUsersOOOPaginated(orgId, skip ?? 0, take ?? 250, rest, {
email,
});
return {
status: SUCCESS_STATUS,
data: ooos.map((ooo) => plainToInstance(UserOooOutputDto, ooo, { strategy: "excludeAll" })),
};
}
}
@@ -366,6 +366,31 @@ describe("Organizations User OOO Endpoints", () => {
});
});
it("should get 2 ooo entries", async () => {
return request(app.getHttpServer())
.get(`/v2/organizations/${org.id}/ooo?sortEnd=desc&email=${teammate1Email}`)
.expect(200)
.then((response) => {
const responseBody = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
const data = responseBody.data as UserOooOutputDto[];
expect(data.length).toEqual(2);
const oooUno = data.find((ooo) => ooo.id === oooCreatedViaApiId);
expect(oooUno).toBeDefined();
if (oooUno) {
expect(oooUno.reason).toEqual("vacation");
expect(oooUno.toUserId).toEqual(teammate2.id);
expect(oooUno.userId).toEqual(teammate1.id);
expect(oooUno.start).toEqual("2025-06-01T00:00:00.000Z");
expect(oooUno.end).toEqual("2025-06-10T23:59:59.999Z");
}
// test sort
expect(data[1].id).toEqual(oooCreatedViaApiId);
});
});
it("should get 2 ooo entries", async () => {
return request(app.getHttpServer())
.get(`/v2/organizations/${org.id}/users/${teammate1.id}/ooo`)
@@ -0,0 +1,33 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { Injectable } from "@nestjs/common";
@Injectable()
export class OrgUsersOOORepository {
constructor(private readonly dbRead: PrismaReadService) {}
async getOrgUsersOOOPaginated(
orgId: number,
skip: number,
take: number,
sort?: { sortStart?: "asc" | "desc"; sortEnd?: "asc" | "desc" },
filters?: { email?: string }
) {
console.log({ sort, filters });
return this.dbRead.prisma.outOfOfficeEntry.findMany({
where: {
user: {
...(filters?.email && { email: filters.email }),
profiles: {
some: {
organizationId: orgId,
},
},
},
},
skip,
take,
include: { reason: true },
...(sort?.sortStart && { orderBy: { start: sort.sortStart } }),
...(sort?.sortEnd && { orderBy: { end: sort.sortEnd } }),
});
}
}
@@ -0,0 +1,26 @@
import { UserOOORepository } from "@/modules/ooo/repositories/ooo.repository";
import { UserOOOService } from "@/modules/ooo/services/ooo.service";
import { OrgUsersOOORepository } from "@/modules/organizations/controllers/users/ooo/repositories/organizations-users-ooo.repository";
import { UsersRepository } from "@/modules/users/users.repository";
import { Injectable } from "@nestjs/common";
@Injectable()
export class OrgUsersOOOService {
constructor(
private readonly oooRepository: UserOOORepository,
private readonly oooUserService: UserOOOService,
private readonly usersRepository: UsersRepository,
private readonly orgUsersOOORepository: OrgUsersOOORepository
) {}
async getOrgUsersOOOPaginated(
orgId: number,
skip: number,
take: number,
sort?: { sortStart?: "asc" | "desc"; sortEnd?: "asc" | "desc" },
filters?: { email?: string }
) {
const ooos = await this.orgUsersOOORepository.getOrgUsersOOOPaginated(orgId, skip, take, sort, filters);
return ooos.map((ooo) => this.oooUserService.formatOooReason(ooo));
}
}
@@ -15,6 +15,8 @@ import { OrganizationsTeamsMembershipsController } from "@/modules/organizations
import { OrganizationsTeamsController } from "@/modules/organizations/controllers/teams/organizations-teams.controller";
import { OrganizationsTeamsSchedulesController } from "@/modules/organizations/controllers/teams/schedules/organizations-teams-schedules.controller";
import { OrganizationsUsersOOOController } from "@/modules/organizations/controllers/users/ooo/organizations-users-ooo-controller";
import { OrgUsersOOORepository } from "@/modules/organizations/controllers/users/ooo/repositories/organizations-users-ooo.repository";
import { OrgUsersOOOService } from "@/modules/organizations/controllers/users/ooo/services/organization-users-ooo.service";
import { OrganizationsUsersController } from "@/modules/organizations/controllers/users/organizations-users.controller";
import { OrganizationsWebhooksController } from "@/modules/organizations/controllers/webhooks/organizations-webhooks.controller";
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
@@ -91,6 +93,8 @@ import { Module } from "@nestjs/common";
OutputTeamEventTypesResponsePipe,
UserOOOService,
UserOOORepository,
OrgUsersOOOService,
OrgUsersOOORepository,
],
exports: [
OrganizationsService,
@@ -1,12 +1,25 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Transform } from "class-transformer";
import { IsOptional, Validate } from "class-validator";
import { SkipTakePagination } from "@calcom/platform-types";
import { IsNumber, IsOptional, Max, Min, Validate } from "class-validator";
import { IsEmailStringOrArray } from "../validators/isEmailStringOrArray";
export class GetUsersInput extends SkipTakePagination {
export class GetUsersInput {
@ApiProperty({ required: false, description: "The number of items to return", example: 10 })
@Transform(({ value }: { value: string }) => value && parseInt(value))
@IsNumber()
@Min(1)
@Max(1000)
@IsOptional()
take?: number;
@ApiProperty({ required: false, description: "The number of items to skip", example: 0 })
@Transform(({ value }: { value: string }) => value && parseInt(value))
@IsNumber()
@Min(0)
@IsOptional()
skip?: number;
@IsOptional()
@Validate(IsEmailStringOrArray)
@Transform(({ value }: { value: string | string[] }) => {
+115 -2
View File
@@ -2516,6 +2516,8 @@
"description": "The number of items to return",
"example": 10,
"schema": {
"minimum": 1,
"maximum": 1000,
"type": "number"
}
},
@@ -2526,6 +2528,7 @@
"description": "The number of items to skip",
"example": 0,
"schema": {
"minimum": 0,
"type": "number"
}
},
@@ -2716,6 +2719,34 @@
"schema": {
"type": "number"
}
},
{
"name": "sortStart",
"required": false,
"in": "query",
"description": "Sort results by their start time in ascending or descending order.",
"example": "?sortStart=asc OR ?sortStart=desc",
"schema": {
"enum": [
"asc",
"desc"
],
"type": "string"
}
},
{
"name": "sortEnd",
"required": false,
"in": "query",
"description": "Sort results by their end time in ascending or descending order.",
"example": "?sortEnd=asc OR ?sortEnd=desc",
"schema": {
"enum": [
"asc",
"desc"
],
"type": "string"
}
}
],
"responses": {
@@ -2824,6 +2855,88 @@
]
}
},
"/v2/organizations/{orgId}/ooo": {
"get": {
"operationId": "OrganizationsUsersOOOController_getOrganizationUsersOOO",
"summary": "Get all OOO entries of org users",
"parameters": [
{
"name": "orgId",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
},
{
"name": "take",
"required": false,
"in": "query",
"description": "The number of items to return",
"example": 10,
"schema": {
"type": "number"
}
},
{
"name": "skip",
"required": false,
"in": "query",
"description": "The number of items to skip",
"example": 0,
"schema": {
"type": "number"
}
},
{
"name": "sortStart",
"required": false,
"in": "query",
"description": "Sort results by their start time in ascending or descending order.",
"example": "?sortStart=asc OR ?sortStart=desc",
"schema": {
"enum": [
"asc",
"desc"
],
"type": "string"
}
},
{
"name": "sortEnd",
"required": false,
"in": "query",
"description": "Sort results by their end time in ascending or descending order.",
"example": "?sortEnd=asc OR ?sortEnd=desc",
"schema": {
"enum": [
"asc",
"desc"
],
"type": "string"
}
},
{
"name": "email",
"required": false,
"in": "query",
"description": "Filter ooo entries by the user email address. user must be within your organization.",
"example": "example@domain.com",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Orgs / Users / OOO"
]
}
},
"/v2/organizations/{orgId}/webhooks": {
"get": {
"operationId": "OrganizationsWebhooksController_getAllOrganizationWebhooks",
@@ -7888,7 +8001,7 @@
"type": {
"type": "string",
"example": "name",
"description": "only allowed value for type is `name`"
"description": "only allowed value for type is `name`. Used for having 1 booking field for both first name and last name."
},
"label": {
"type": "string"
@@ -8682,7 +8795,7 @@
"boolean"
],
"example": "name",
"description": "only allowed value for type is `name`",
"description": "only allowed value for type is `name`. Used for having 1 booking field for both first name and last name.",
"default": "name"
},
"label": {
+21 -1
View File
@@ -3,7 +3,8 @@ import i18next from "i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { headers } from "next/headers";
import { constructGenericImage } from "@calcom/lib/OgImages";
import type { MeetingImageProps } from "@calcom/lib/OgImages";
import { constructGenericImage, constructMeetingImage } from "@calcom/lib/OgImages";
import { IS_CALCOM, WEBAPP_URL, APP_NAME, SEO_IMG_OGIMG, CAL_URL } from "@calcom/lib/constants";
import { buildCanonical } from "@calcom/lib/next-seo.config";
import { truncateOnWord } from "@calcom/lib/text";
@@ -103,3 +104,22 @@ export const _generateMetadata = async (
},
};
};
export const generateMeetingMetadata = async (
meeting: MeetingImageProps,
getTitle: (t: TFunction<string, undefined>) => string,
getDescription: (t: TFunction<string, undefined>) => string,
hideBranding?: boolean,
origin?: string
) => {
const metadata = await _generateMetadataWithoutImage(getTitle, getDescription, hideBranding, origin);
const image = SEO_IMG_OGIMG + constructMeetingImage(meeting);
return {
...metadata,
openGraph: {
...metadata.openGraph,
images: [image],
},
};
};
+22 -163
View File
@@ -10,12 +10,18 @@ import { userMetadata } from "@calcom/prisma/zod-utils";
const inputSchema = z.object({
customer: z.object({
name: z.string().optional(),
email: z.string().email(),
username: z.string().optional(),
timeZone: z.string().optional(),
emailVerified: z.boolean().optional(),
identityProvider: z.string().optional(),
twoFactorEnabled: z.boolean().optional(),
lastActiveAt: z.string().optional(),
teamName: z.string().optional(),
teamSlug: z.string().optional(),
isOrganization: z.boolean().optional(),
stripeCustomerId: z.string().optional(),
}),
cardKeys: z.array(z.string()),
});
@@ -53,168 +59,10 @@ async function handler(request: Request) {
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Email",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: customer.email || "Unknown",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Email Verified?",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentBadge: {
badgeLabel: customer.emailVerified ? "Yes" : "No",
badgeColor: customer.emailVerified ? "GREEN" : "RED",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Username",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: customer.username || "Unknown",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "User ID",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: "Unknown",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Time Zone",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: customer.timeZone || "Unknown",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Two Factor Enabled?",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentBadge: {
badgeLabel: customer.twoFactorEnabled ? "Yes" : "No",
badgeColor: customer.twoFactorEnabled ? "GREEN" : "RED",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Identity Provider",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: customer.identityProvider || "Unknown",
},
},
],
componentText: {
textSize: "L",
textColor: "MUTED",
text: "User does not exist!",
},
},
],
@@ -223,18 +71,29 @@ async function handler(request: Request) {
});
}
// Fetch team details including userId and team name
const teamMemberships = await UserRepository.findTeamsByUserId({ userId: user.id });
const firstTeam = teamMemberships.teams[0] ?? null;
// Parse user metadata
const parsedMetadata = userMetadata.parse(user.metadata);
const cards = await Promise.all(
cardExamples.map(async (cardFn) => {
return cardFn(
user.name || "Unknown",
user.email,
user.id.toString(),
user.username || "Unknown",
user.timeZone,
user.emailVerified,
user.twoFactorEnabled
user.twoFactorEnabled,
user.identityProvider,
user.lastActiveAt,
firstTeam?.name || "Unknown",
firstTeam?.slug || "Unknown",
firstTeam?.isOrganization || false,
(user.metadata as { stripeCustomerId?: string })?.stripeCustomerId || "Unknown"
);
})
);
+5 -17
View File
@@ -4,9 +4,6 @@ import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { EventRepository } from "@calcom/lib/server/repository/event";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/d/[link]/[slug]/getServerSideProps";
import { type PageProps } from "@lib/d/[link]/[slug]/getServerSideProps";
@@ -17,24 +14,15 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) =>
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
const pageProps = await getData(legacyCtx);
const { booking, user: username, slug: eventSlug, isTeamEvent } = pageProps;
const { booking, eventData, isBrandingHidden } = pageProps;
const rescheduleUid = booking?.uid;
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(legacyCtx.req);
const org = isValidOrgDomain ? currentOrgDomain : null;
const event = await EventRepository.getPublicEvent({
username,
eventSlug,
isTeamEvent,
org,
fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true",
});
const profileName = event?.profile?.name ?? "";
const title = event?.title ?? "";
const profileName = eventData?.profile?.name ?? "";
const title = eventData?.title ?? "";
return await _generateMetadata(
(t) => `${rescheduleUid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
(t) => `${rescheduleUid ? t("reschedule") : ""} ${title}`
(t) => `${rescheduleUid ? t("reschedule") : ""} ${title}`,
isBrandingHidden
);
};
@@ -1,10 +1,10 @@
import { type PageProps } from "@pages/org/[orgSlug]/[user]/[type]";
import Page from "@pages/org/[orgSlug]/[user]/[type]/embed";
import withEmbedSsrAppDir from "app/WithEmbedSSR";
import { WithLayout } from "app/layoutHOC";
import type { OrgTypePageProps } from "app/org/[orgSlug]/[user]/[type]/page";
import { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
const getEmbedData = withEmbedSsrAppDir<OrgTypePageProps>(getServerSideProps);
export default WithLayout({ getLayout: null, getData: getEmbedData, isBookingPage: true, Page });
@@ -1,9 +0,0 @@
import Page, { type PageProps } from "@pages/org/[orgSlug]/[user]/[type]";
import { withAppDirSsr } from "app/WithAppDirSsr";
import { WithLayout } from "app/layoutHOC";
import { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
const getData = withAppDirSsr<PageProps>(getServerSideProps);
export default WithLayout({ getLayout: null, getData, Page });
@@ -1,29 +0,0 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps as _PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
import type { PageProps } from "~/team/team-view";
import TeamPage from "~/team/team-view";
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams));
return await _generateMetadata(
(t) => props.team.name || t("nameless_team"),
(t) => props.team.name || t("nameless_team")
);
};
const getData = withAppDirSsr<PageProps>(getServerSideProps);
export default WithLayout({
Page: TeamPage,
getData,
getLayout: null,
isBookingPage: true,
})<"P">;
@@ -1,45 +0,0 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps as _PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { EventRepository } from "@calcom/lib/server/repository/event";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import type { PageProps } from "~/team/type-view";
import TypePage from "~/team/type-view";
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
const props = await getData(legacyCtx);
const { user: username, slug: eventSlug, booking } = props;
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(legacyCtx.req, legacyCtx.params?.orgSlug);
const event = await EventRepository.getPublicEvent({
username,
eventSlug,
isTeamEvent: true,
org: isValidOrgDomain ? currentOrgDomain : null,
fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true",
});
const profileName = event?.profile?.name ?? "";
const title = event?.title ?? "";
return await _generateMetadata(
(t) => `${booking?.uid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
(t) => `${booking?.uid ? t("reschedule") : ""} ${title}`
);
};
const getData = withAppDirSsr<PageProps>(getServerSideProps);
export default WithLayout({
Page: TypePage,
getData,
getLayout: null,
isBookingPage: true,
})<"P">;
@@ -1,29 +0,0 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps as _PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
import type { PageProps } from "~/team/team-view";
import TeamPage from "~/team/team-view";
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams));
return await _generateMetadata(
(t) => props.team.name || t("nameless_team"),
(t) => props.team.name || t("nameless_team")
);
};
const getData = withAppDirSsr<PageProps>(getServerSideProps);
export default WithLayout({
Page: TeamPage,
getData,
getLayout: null,
isBookingPage: true,
})<"P">;
@@ -1,11 +0,0 @@
import withEmbedSsrAppDir from "app/WithEmbedSSR";
import { WithLayout } from "app/layoutHOC";
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import type { PageProps } from "~/team/type-view";
import LegacyPage from "~/team/type-view";
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
export default WithLayout({ getLayout: null, getData: getEmbedData, Page: LegacyPage })<"P">;
@@ -1,44 +0,0 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { EventRepository } from "@calcom/lib/server/repository/event";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import LegacyPage, { type PageProps as LegacyPageProps } from "~/team/type-view";
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
const props = await getData(legacyCtx);
const { user: username, slug: eventSlug, booking } = props;
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(legacyCtx.req, legacyCtx.params?.orgSlug);
const event = await EventRepository.getPublicEvent({
username,
eventSlug,
isTeamEvent: true,
org: isValidOrgDomain ? currentOrgDomain : null,
fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true",
});
const profileName = event?.profile?.name ?? "";
const title = event?.title ?? "";
return await _generateMetadata(
(t) => `${booking?.uid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
(t) => `${booking?.uid ? t("reschedule") : ""} ${title}`
);
};
const getData = withAppDirSsr<LegacyPageProps>(getServerSideProps);
export default WithLayout({
Page: LegacyPage,
getData,
getLayout: null,
isBookingPage: true,
})<"P">;
@@ -1,11 +0,0 @@
import withEmbedSsrAppDir from "app/WithEmbedSSR";
import { WithLayout } from "app/layoutHOC";
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
import type { PageProps } from "~/team/team-view";
import LegacyPage from "~/team/team-view";
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
export default WithLayout({ getLayout: null, getData: getEmbedData, Page: LegacyPage })<"P">;
-28
View File
@@ -1,28 +0,0 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps as _PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
import type { PageProps } from "~/team/team-view";
import LegacyPage from "~/team/team-view";
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams));
return await _generateMetadata(
(t) => props.team.name || t("nameless_team"),
(t) => props.team.name || t("nameless_team")
);
};
const getData = withAppDirSsr<PageProps>(getServerSideProps);
export default WithLayout({
Page: LegacyPage,
getData,
getLayout: null,
})<"P">;
@@ -0,0 +1,64 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps } from "app/_types";
import { generateMeetingMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
import type { PageProps as TeamTypePageProps } from "~/team/type-view";
import TeamTypePage from "~/team/type-view";
import UserTypePage from "~/users/views/users-type-public-view";
import type { PageProps as UserTypePageProps } from "~/users/views/users-type-public-view";
export type OrgTypePageProps = UserTypePageProps | TeamTypePageProps;
const getData = withAppDirSsr<OrgTypePageProps>(getServerSideProps);
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
const props = await getData(legacyCtx);
const { booking, isSEOIndexable, eventData, isBrandingHidden } = props;
const rescheduleUid = booking?.uid;
const profileName = eventData?.profile?.name ?? "";
const profileImage = eventData?.profile.image;
const title = eventData?.title ?? "";
const meeting = {
title,
profile: { name: profileName, image: profileImage },
users: [
...(eventData?.users || []).map((user) => ({
name: `${user.name}`,
username: `${user.username}`,
})),
],
};
const metadata = await generateMeetingMetadata(
meeting,
(t) => `${rescheduleUid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
(t) => `${rescheduleUid ? t("reschedule") : ""} ${title}`,
isBrandingHidden,
getOrgFullOrigin(eventData?.entity.orgSlug ?? null)
);
return {
...metadata,
robots: {
follow: !(eventData?.hidden || !isSEOIndexable),
index: !(eventData?.hidden || !isSEOIndexable),
},
};
};
const Page = async (props: OrgTypePageProps) => {
if ((props as TeamTypePageProps)?.teamId) {
return <TeamTypePage {...(props as TeamTypePageProps)} />;
}
return <UserTypePage {...(props as UserTypePageProps)} />;
};
export default WithLayout({ getLayout: null, getData, ServerPage: Page, isBookingPage: true });
+1
View File
@@ -0,0 +1 @@
export { default, generateMetadata } from "app/team/[slug]/page";
@@ -0,0 +1 @@
export { default, generateMetadata } from "app/team/[slug]/[type]/page";
@@ -0,0 +1 @@
export { default, generateMetadata } from "app/team/[slug]/page";
+58
View File
@@ -0,0 +1,58 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps } from "app/_types";
import { generateMeetingMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import LegacyPage from "~/team/type-view";
import type { PageProps as LegacyPageProps } from "~/team/type-view";
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
const props = await getData(legacyCtx);
const { booking, isSEOIndexable, eventData, isBrandingHidden } = props;
const profileName = eventData?.profile?.name ?? "";
const profileImage = eventData?.profile.image;
const title = eventData?.title ?? "";
const meeting = {
title,
profile: { name: profileName, image: profileImage },
users: [
...(eventData?.users || []).map((user) => ({
name: `${user.name}`,
username: `${user.username}`,
})),
],
};
const metadata = await generateMeetingMetadata(
meeting,
(t) => `${booking?.uid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
(t) => `${booking?.uid ? t("reschedule") : ""} ${title}`,
isBrandingHidden,
getOrgFullOrigin(eventData.entity.orgSlug ?? null)
);
return {
...metadata,
robots: {
follow: !(eventData?.hidden || !isSEOIndexable),
index: !(eventData?.hidden || !isSEOIndexable),
},
};
};
const getData = withAppDirSsr<LegacyPageProps>(getServerSideProps);
export default WithLayout({
Page: LegacyPage,
getData,
getLayout: null,
isBookingPage: true,
})<"P">;
+52
View File
@@ -0,0 +1,52 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import type { PageProps as _PageProps } from "app/_types";
import { generateMeetingMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import { cookies, headers } from "next/headers";
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
import { getOrgOrTeamAvatar } from "@calcom/lib/defaultAvatarImage";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
import type { PageProps } from "~/team/team-view";
import LegacyPage from "~/team/team-view";
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
const { team, markdownStrippedBio, isSEOIndexable, currentOrgDomain } = await getData(
buildLegacyCtx(headers(), cookies(), params, searchParams)
);
const meeting = {
title: markdownStrippedBio ?? "",
profile: {
name: `${team.name}`,
image: getOrgOrTeamAvatar(team),
},
};
const metadata = await generateMeetingMetadata(
meeting,
(t) => team.name || t("nameless_team"),
(t) => team.name || t("nameless_team"),
false,
getOrgFullOrigin(currentOrgDomain ?? null)
);
return {
...metadata,
robots: {
follow: isSEOIndexable,
index: isSEOIndexable,
},
};
};
const getData = withAppDirSsr<PageProps>(getServerSideProps);
export default WithLayout({
Page: LegacyPage,
getData,
getLayout: null,
isBookingPage: true,
})<"P">;
+14 -14
View File
@@ -511,9 +511,9 @@ function BookingListItem(booking: BookingItemProps) {
</DialogFooter>
</DialogContent>
</Dialog>
<tr data-testid="booking-item" className="hover:bg-muted group transition ">
<div data-testid="booking-item" className="hover:bg-muted group w-full">
<div className="flex flex-col sm:flex-row">
<td className="hidden align-top ltr:pl-3 rtl:pr-6 sm:table-cell sm:min-w-[12rem]">
<div className="hidden align-top ltr:pl-3 rtl:pr-6 sm:table-cell sm:min-w-[12rem]">
<div className="flex h-full items-center">
{eventTypeColor && (
<div className="h-[70%] w-0.5" style={{ backgroundColor: eventTypeColor }} />
@@ -562,8 +562,10 @@ function BookingListItem(booking: BookingItemProps) {
</div>
</Link>
</div>
</td>
<td data-testid="title-and-attendees" className={`w-full px-4${isRejected ? " line-through" : ""}`}>
</div>
<div
data-testid="title-and-attendees"
className={`w-full px-4${isRejected ? " line-through" : ""}`}>
<Link href={bookingLink}>
{/* Time and Badges for mobile */}
<div className="w-full pb-2 pt-4 sm:hidden">
@@ -648,8 +650,8 @@ function BookingListItem(booking: BookingItemProps) {
)}
</div>
</Link>
</td>
<td className="flex w-full flex-col flex-wrap items-end justify-end space-x-2 space-y-2 py-4 pl-4 text-right text-sm font-medium ltr:pr-4 rtl:pl-4 sm:flex-row sm:flex-nowrap sm:items-start sm:space-y-0 sm:pl-0">
</div>
<div className="flex w-full flex-col flex-wrap items-end justify-end space-x-2 space-y-2 py-4 pl-4 text-right text-sm font-medium ltr:pr-4 rtl:pl-4 sm:flex-row sm:flex-nowrap sm:items-start sm:space-y-0 sm:pl-0">
{isUpcoming && !isCancelled ? (
<>
{isPending && <TableActions actions={pendingActions} />}
@@ -674,7 +676,7 @@ function BookingListItem(booking: BookingItemProps) {
<TableActions actions={chargeCardActions} />
</div>
)}
</td>
</div>
</div>
<BookingItemBadges
booking={booking}
@@ -683,7 +685,7 @@ function BookingListItem(booking: BookingItemProps) {
userTimeFormat={userTimeFormat}
userTimeZone={userTimeZone}
/>
</tr>
</div>
{isBookingReroutable(parsedBooking) && (
<RerouteDialog
@@ -712,7 +714,7 @@ const BookingItemBadges = ({
const { t } = useLocale();
return (
<div className="hidden h-9 flex-row pb-4 pl-6 sm:flex">
<div className="hidden h-9 flex-row items-center pb-4 pl-6 sm:flex">
{isPending && (
<Badge className="ltr:mr-2 rtl:ml-2" variant="orange">
{t("unconfirmed")}
@@ -1251,11 +1253,9 @@ const AssignmentReasonTooltip = ({ assignmentReason }: { assignmentReason: Assig
const badgeTitle = assignmentReasonBadgeTitleMap(assignmentReason.reasonEnum);
return (
<Tooltip content={<p>{assignmentReason.reasonString}</p>}>
<div className="-mt-1">
<Badge className="ltr:mr-2 rtl:ml-2" variant="gray">
{t(badgeTitle)}
</Badge>
</div>
<Badge className="ltr:mr-2 rtl:ml-2" variant="gray">
{t(badgeTitle)}
</Badge>
</Tooltip>
);
};
+31 -12
View File
@@ -1,6 +1,9 @@
"use client";
import React from "react";
import { HttpError } from "@calcom/lib/http-error";
import { Button } from "@calcom/ui";
type Props = {
statusCode?: number | null;
@@ -53,21 +56,37 @@ export const ErrorPage: React.FC<Props> = (props) => {
return (
<>
<div className="bg-default min-h-screen px-4">
<main className="mx-auto max-w-xl pb-6 pt-16 sm:pt-24">
<div className="text-center">
<p className="text-emphasis text-sm font-semibold uppercase tracking-wide">{statusCode}</p>
<h1 className="text-emphasis mt-2 text-4xl font-extrabold tracking-tight sm:text-5xl">
<div className="bg-subtle flex h-screen">
<div className="rtl: bg-default m-auto rounded-md p-10 text-right ltr:text-left">
<h1 className="font-cal text-emphasis text-6xl">{statusCode}</h1>
<h2 className="text-emphasis mt-6 max-w-2xl text-2xl font-medium">
It&apos;s not you, it&apos;s us.
</h2>
<p className="text-default mb-6 mt-4 max-w-2xl text-sm">
Something went wrong on our end. Get in touch with our support team, and we&apos;ll get it fixed
right away for you.
</p>
<div className="mb-8 flex flex-col">
<p className="text-default mb-4 max-w-2xl text-sm">
Please provide the following text when contacting support to better help you:
</p>
<pre className="bg-emphasis text-emphasis w-full max-w-2xl whitespace-normal break-words rounded-md p-4">
{message}
</h1>
</pre>
</div>
</main>
{displayDebug && (
<div className="flex-wrap">
<ErrorDebugPanel error={error} />
</div>
)}
<Button href="mailto:support@cal.com">Contact Support</Button>
<Button color="secondary" href="javascript:history.back()" className="ml-2">
Go back
</Button>
</div>
</div>
{displayDebug && (
<div className="flex-wrap">
<ErrorDebugPanel error={error} />
</div>
)}
</>
);
};
@@ -121,6 +121,7 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
return {
props: {
eventData,
entity: eventData.entity,
duration: getMultipleDurationValue(
eventData.metadata?.multipleDuration,
+10 -4
View File
@@ -22,7 +22,6 @@ const safeGet = async <T = any>(key: string): Promise<T | undefined> => {
const middleware = async (req: NextRequest): Promise<NextResponse<unknown>> => {
const url = req.nextUrl;
const requestHeaders = new Headers(req.headers);
requestHeaders.set("x-url", req.url);
if (!url.pathname.startsWith("/api")) {
@@ -155,6 +154,7 @@ export const config = {
matcher: [
"/403",
"/500",
"/icons",
"/d/:path*",
"/more/:path*",
"/maintenance/:path*",
@@ -187,14 +187,20 @@ export const config = {
"/workflows/:path*",
"/getting-started/:path*",
"/apps",
"/bookings/:status/",
"/bookings/:path*",
"/video/:path*",
"/teams",
"/future/teams/",
"/teams/:path*",
"/signup/:path*",
"/settings/:path*",
"/reschedule/:path*",
"/availability/:path*",
"/booking/:path*",
"/routing-forms/:path*",
"/team/:path*",
"/org/[orgSlug]/[user]/[type]",
"/org/[orgSlug]/team/[slug]/[type]",
"/org/[orgSlug]/team/[slug]",
"/org/[orgSlug]",
],
};
@@ -1,8 +1,15 @@
"use client";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { Fragment, ReactElement, useState } from "react";
import { z } from "zod";
import {
useReactTable,
getCoreRowModel,
getFilteredRowModel,
getSortedRowModel,
createColumnHelper,
} from "@tanstack/react-table";
import type { ReactElement } from "react";
import { Fragment, useMemo, useState } from "react";
import type { z } from "zod";
import { WipeMyCalActionButton } from "@calcom/app-store/wipemycalother/components";
import dayjs from "@calcom/dayjs";
@@ -10,20 +17,20 @@ import { FilterToggle } from "@calcom/features/bookings/components/FilterToggle"
import { FiltersContainer } from "@calcom/features/bookings/components/FiltersContainer";
import type { filterQuerySchema } from "@calcom/features/bookings/lib/useFilterQuery";
import { useFilterQuery } from "@calcom/features/bookings/lib/useFilterQuery";
import { DataTableProvider, DataTableWrapper } from "@calcom/features/data-table";
import Shell from "@calcom/features/shell/Shell";
import { useInViewObserver } from "@calcom/lib/hooks/useInViewObserver";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import type { HorizontalTabItemProps, VerticalTabItemProps } from "@calcom/ui";
import { Alert, Button, EmptyScreen, HorizontalTabs } from "@calcom/ui";
import { Alert, EmptyScreen, HorizontalTabs } from "@calcom/ui";
import useMeQuery from "@lib/hooks/useMeQuery";
import BookingListItem from "@components/booking/BookingListItem";
import SkeletonLoader from "@components/booking/SkeletonLoader";
import { validStatuses } from "~/bookings/lib/validStatuses";
import type { validStatuses } from "~/bookings/lib/validStatuses";
type BookingListingStatus = z.infer<NonNullable<typeof filterQuerySchema>>["status"];
type BookingOutput = RouterOutputs["viewer"]["bookings"]["get"]["bookings"][0];
@@ -66,7 +73,24 @@ const descriptionByStatus: Record<NonNullable<BookingListingStatus>, string> = {
unconfirmed: "unconfirmed_bookings",
};
export default function Bookings({ status }: { status: (typeof validStatuses)[number] }) {
type BookingsProps = {
status: (typeof validStatuses)[number];
};
export default function Bookings(props: BookingsProps) {
return (
<DataTableProvider>
<BookingsContent {...props} />
</DataTableProvider>
);
}
type RowData = {
booking: BookingOutput;
recurringInfo?: RecurringInfo;
};
function BookingsContent({ status }: BookingsProps) {
const { data: filterQuery } = useFilterQuery();
const { t } = useLocale();
@@ -87,13 +111,32 @@ export default function Bookings({ status }: { status: (typeof validStatuses)[nu
}
);
// Animate page (tab) transitions to look smoothing
const columns = useMemo(() => {
const columnHelper = createColumnHelper<RowData>();
const buttonInView = useInViewObserver(() => {
if (!query.isFetching && query.hasNextPage && query.status === "success") {
query.fetchNextPage();
}
});
return [
columnHelper.display({
id: "custom-view",
cell: (props) => {
const { booking, recurringInfo } = props.row.original;
return (
<BookingListItem
key={booking.id}
loggedInUser={{
userId: user?.id,
userTimeZone: user?.timeZone,
userTimeFormat: user?.timeFormat,
userEmail: user?.email,
}}
listingStatus={status}
recurringInfo={recurringInfo}
{...booking}
/>
);
},
}),
];
}, [user, status]);
const isEmpty = !query.data?.pages[0]?.bookings.length;
@@ -120,6 +163,27 @@ export default function Bookings({ status }: { status: (typeof validStatuses)[nu
return true;
};
const flatData = useMemo(() => {
return (
query.data?.pages.flatMap((page) =>
page.bookings.filter(filterBookings).map((booking) => ({
booking,
recurringInfo: page.recurringInfo.find(
(info) => info.recurringEventId === booking.recurringEventId
),
}))
) || []
);
}, [query.data]);
const table = useReactTable<RowData>({
data: flatData,
columns,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
});
let recurringInfoToday: RecurringInfo | undefined;
const bookingsToday =
@@ -136,8 +200,6 @@ export default function Bookings({ status }: { status: (typeof validStatuses)[nu
})
)[0] || [];
const [animationParentRef] = useAutoAnimate<HTMLDivElement>();
return (
<div className="flex flex-col">
<div className="flex flex-row flex-wrap justify-between">
@@ -146,7 +208,7 @@ export default function Bookings({ status }: { status: (typeof validStatuses)[nu
</div>
<FiltersContainer isFiltersVisible={isFiltersVisible} />
<main className="w-full">
<div className="flex w-full flex-col" ref={animationParentRef}>
<div className="flex w-full flex-col">
{query.status === "error" && (
<Alert severity="error" title={t("something_went_wrong")} message={query.error.message} />
)}
@@ -158,69 +220,41 @@ export default function Bookings({ status }: { status: (typeof validStatuses)[nu
<WipeMyCalActionButton bookingStatus={status} bookingsEmpty={isEmpty} />
<p className="text-subtle mb-2 text-xs font-medium uppercase leading-4">{t("today")}</p>
<div className="border-subtle overflow-hidden rounded-md border">
<table className="w-full max-w-full table-fixed">
<tbody className="bg-default divide-subtle divide-y" data-testid="today-bookings">
<Fragment>
{bookingsToday.map((booking: BookingOutput) => (
<BookingListItem
key={booking.id}
loggedInUser={{
userId: user?.id,
userTimeZone: user?.timeZone,
userTimeFormat: user?.timeFormat,
userEmail: user?.email,
}}
listingStatus={status}
recurringInfo={recurringInfoToday}
{...booking}
/>
))}
</Fragment>
</tbody>
</table>
<div
className="bg-default divide-subtle w-full max-w-full divide-y"
data-testid="today-bookings">
<Fragment>
{bookingsToday.map((booking: BookingOutput) => (
<BookingListItem
key={booking.id}
loggedInUser={{
userId: user?.id,
userTimeZone: user?.timeZone,
userTimeFormat: user?.timeFormat,
userEmail: user?.email,
}}
listingStatus={status}
recurringInfo={recurringInfoToday}
{...booking}
/>
))}
</Fragment>
</div>
</div>
</div>
)}
<div className="pt-2 xl:pt-0">
<div className="border-subtle overflow-hidden rounded-md border">
<table data-testid={`${status}-bookings`} className="w-full max-w-full table-fixed">
<tbody className="bg-default divide-subtle divide-y" data-testid="bookings">
{query.data.pages.map((page, index) => (
<Fragment key={index}>
{page.bookings.filter(filterBookings).map((booking: BookingOutput) => {
const recurringInfo = page.recurringInfo.find(
(info) => info.recurringEventId === booking.recurringEventId
);
return (
<BookingListItem
key={booking.id}
loggedInUser={{
userId: user?.id,
userTimeZone: user?.timeZone,
userTimeFormat: user?.timeFormat,
userEmail: user?.email,
}}
listingStatus={status}
recurringInfo={recurringInfo}
{...booking}
/>
);
})}
</Fragment>
))}
</tbody>
</table>
</div>
<div className="text-default p-4 text-center" ref={buttonInView.ref}>
<Button
color="minimal"
loading={query.isFetchingNextPage}
disabled={!query.hasNextPage}
onClick={() => query.fetchNextPage()}>
{query.hasNextPage ? t("load_more_results") : t("no_more_results")}
</Button>
</div>
</div>
<DataTableWrapper
table={table}
testId={`${status}-bookings`}
bodyTestId="bookings"
hideHeader={true}
isPending={query.isFetching && !flatData}
hasNextPage={query.hasNextPage}
fetchNextPage={query.fetchNextPage}
isFetching={query.isFetching}
variant="compact"
/>
</>
)}
{query.status === "success" && isEmpty && (
+1 -26
View File
@@ -5,7 +5,6 @@ import { useSearchParams } from "next/navigation";
import { Booker } from "@calcom/atoms/monorepo";
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
import type { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
@@ -26,7 +25,6 @@ function Type({
slug,
user,
booking,
isEmbed,
isBrandingHidden,
eventData,
isInstantMeeting,
@@ -34,33 +32,12 @@ function Type({
teamMemberEmail,
crmOwnerRecordType,
crmAppSlug,
isSEOIndexable,
isEmbed,
}: PageProps) {
const searchParams = useSearchParams();
const { profile, users, hidden, title } = eventData;
return (
<main className={getBookerWrapperClasses({ isEmbed: !!isEmbed })}>
<BookerSeo
username={user}
eventSlug={slug}
rescheduleUid={booking?.uid}
hideBranding={isBrandingHidden}
isTeamEvent
eventData={
profile && users && title && hidden !== undefined
? {
profile,
users,
title,
hidden,
}
: undefined
}
entity={eventData.entity}
bookingData={booking}
isSEOIndexable={isSEOIndexable}
/>
<Booker
username={user}
eventSlug={slug}
@@ -87,6 +64,4 @@ function Type({
);
}
Type.isBookingPage = true;
export default Type;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "4.8.16",
"version": "4.9.0",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
+269 -26
View File
@@ -1,18 +1,63 @@
/* eslint-disable import/no-anonymous-default-export */
import type { Card } from "@pages/api/plain";
import dayjs from "@calcom/dayjs";
export default (
name: string,
email: string,
id: string,
username: string,
timeZone: string,
emailVerified: Date | null,
twoFactorEnabled: boolean | null
twoFactorEnabled: boolean | null,
identityProvider: string | null,
lastActiveAt: Date | null,
teamName: string | null,
teamSlug: string | null,
isOrganization: boolean | null,
stripeCustomerId: string | null
): Card => {
return {
key: "customer-card",
timeToLiveSeconds: null,
components: [
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentText: {
text: "Basic Information",
textColor: "NORMAL",
textSize: "L",
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Name",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: name || "Unknown",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
@@ -42,31 +87,6 @@ export default (
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Email Verified?",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentBadge: {
badgeLabel: emailVerified === null ? "No" : emailVerified ? "Yes" : "No",
badgeColor: emailVerified === null ? "RED" : emailVerified ? "GREEN" : "RED",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
@@ -115,6 +135,24 @@ export default (
spacerSize: "M",
},
},
{
componentDivider: {
dividerSpacingSize: "M",
},
},
// Account Settings Section Title
{
componentText: {
text: "Account Settings",
textColor: "NORMAL",
textSize: "L",
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
@@ -139,6 +177,211 @@ export default (
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Identity Provider",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: identityProvider || "Unknown",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Last Active At",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: lastActiveAt ? dayjs(lastActiveAt).fromNow() : "Unknown",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentDivider: {
dividerSpacingSize: "M",
},
},
// Team Information Section Title
{
componentText: {
text: "Team Information",
textColor: "NORMAL",
textSize: "L",
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Team Name",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: teamName || "Unknown",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Team Slug",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: teamSlug || "Unknown",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Is Organization",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentBadge: {
badgeLabel: isOrganization ? "Yes" : "No",
badgeColor: isOrganization ? "GREEN" : "RED",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentDivider: {
dividerSpacingSize: "M",
},
},
{
componentText: {
text: "Security & Billing",
textColor: "NORMAL",
textSize: "M",
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Stripe Customer ID",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentText: {
text: stripeCustomerId || "Unknown",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
{
componentText: {
text: "Email Verified?",
textColor: "MUTED",
},
},
],
rowAsideContent: [
{
componentBadge: {
badgeLabel: emailVerified === null ? "No" : emailVerified ? "Yes" : "No",
badgeColor: emailVerified === null ? "RED" : emailVerified ? "GREEN" : "RED",
},
},
],
},
},
{
componentSpacer: {
spacerSize: "M",
},
},
{
componentRow: {
rowMainContent: [
@@ -2,10 +2,17 @@ import type { Card } from "@pages/api/plain";
import customerCardDisplay from "@pages/api/plain/customer-card-display";
export const cardExamples: ((
name: string,
email: string,
id: string,
username: string,
timeZone: string,
emailVerified: Date | null,
twoFactorEnabled: boolean | null
twoFactorEnabled: boolean | null,
identityProvider: string | null,
lastActiveAt: Date | null,
teamName: string | null,
teamSlug: string | null,
isOrganization: boolean | null,
stripeCustomerId: string | null
) => Card)[] = [customerCardDisplay];
@@ -1,7 +1,20 @@
import { getServerSideProps as _getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
import withEmbedSsr from "@lib/withEmbedSsr";
import { getServerSideProps as _getServerSideProps } from ".";
import PageWrapper from "@components/PageWrapper";
export { default, type PageProps } from ".";
import type { PageProps as TeamTypePageProps } from "~/team/type-view";
import TeamTypePage from "~/team/type-view";
import UserTypePage from "~/users/views/users-type-public-view";
import type { PageProps as UserTypePageProps } from "~/users/views/users-type-public-view";
export type PageProps = UserTypePageProps | TeamTypePageProps;
export default function Page(props: PageProps) {
if ((props as TeamTypePageProps)?.teamId) return <TeamTypePage {...(props as TeamTypePageProps)} />;
return <UserTypePage {...(props as UserTypePageProps)} />;
}
Page.PageWrapper = PageWrapper;
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
@@ -1,17 +0,0 @@
import PageWrapper from "@components/PageWrapper";
import type { PageProps as TeamTypePageProps } from "~/team/type-view";
import TeamTypePage from "~/team/type-view";
import UserTypePage from "~/users/views/users-type-public-view";
import type { PageProps as UserTypePageProps } from "~/users/views/users-type-public-view";
export { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
export type PageProps = UserTypePageProps | TeamTypePageProps;
export default function Page(props: PageProps) {
if ((props as TeamTypePageProps)?.teamId) return <TeamTypePage {...(props as TeamTypePageProps)} />;
return <UserTypePage {...(props as UserTypePageProps)} />;
}
Page.PageWrapper = PageWrapper;
+9 -2
View File
@@ -1,7 +1,14 @@
import { getServerSideProps as _getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
import withEmbedSsr from "@lib/withEmbedSsr";
import { getServerSideProps as _getServerSideProps } from "./index";
import PageWrapper from "@components/PageWrapper";
export { default } from "./index";
import TeamPage, { type PageProps } from "~/team/team-view";
const Page = (props: PageProps) => <TeamPage {...props} />;
Page.PageWrapper = PageWrapper;
export default Page;
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
-1
View File
@@ -1 +0,0 @@
export { getServerSideProps, default } from "@pages/team/[slug]";
@@ -1 +0,0 @@
export { getServerSideProps, default } from "@pages/team/[slug]/[type]";
@@ -1 +0,0 @@
export { getServerSideProps, default } from "@pages/team/[slug]";
-10
View File
@@ -1,10 +0,0 @@
import PageWrapper from "@components/PageWrapper";
import TeamPage, { type PageProps } from "~/team/team-view";
const Page = (props: PageProps) => <TeamPage {...props} />;
Page.PageWrapper = PageWrapper;
export default Page;
export { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
-10
View File
@@ -1,10 +0,0 @@
import PageWrapper from "@components/PageWrapper";
import TypePage, { type PageProps } from "~/team/type-view";
const Page = (props: PageProps) => <TypePage {...props} />;
Page.PageWrapper = PageWrapper;
export default Page;
export { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
@@ -0,0 +1,15 @@
import { expectPageToBeNotFound } from "playwright/lib/testUtils";
import { test } from "./lib/fixtures";
test.describe("App Router - error handling", () => {
test("404s must render App Router's not-found page regardless of whether pages router or app router is used", async ({
page,
}) => {
await expectPageToBeNotFound({ page, url: "/123491234" });
await expectPageToBeNotFound({ page, url: "/team/123491234" });
await expectPageToBeNotFound({ page, url: "/org/123491234" });
await expectPageToBeNotFound({ page, url: "/insights/123491234" });
await expectPageToBeNotFound({ page, url: "/login/123491234" });
});
});
@@ -31,6 +31,8 @@ function getOrgOrigin(orgSlug: string | null) {
return orgOrigin;
}
test.describe.configure({ mode: "parallel" });
test.describe("Bookings", () => {
test.afterEach(async ({ orgs, users, page }) => {
await users.deleteAll();
@@ -68,7 +68,18 @@ async function verifyRobotsMetaTag({ page, orgSlug, urls, expectedContent }: Ver
const { url } = await goToUrlWithErrorHandling(relativeUrl);
const metaTag = await page.locator('head > meta[name="robots"]');
const metaTagValue = await metaTag.getAttribute("content");
expect(metaTagValue).toEqual(expectedContent);
expect(metaTagValue).not.toBeNull();
expect(
metaTagValue
?.split(",")
.map((s) => s.trim())
.join(",")
).toEqual(
expectedContent
.split(",")
.map((s) => s.trim())
.join(",")
);
}
});
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "يسمح للحاجزين بحجز الاجتماعات عندما يكون المضيف متاحاً. استخدم هذا الخيار لزيادة عدد الاجتماعات المحتملة التي يمكن حجزها عندما يكون التوزيع المتساوي للاجتماعات بين المضيفين أقل أهمية.",
"rr_distribution_method_balanced_title": "توازن الأحمال",
"rr_distribution_method_balanced_description": "سنراقب عدد الحجوزات التي تمت مع كل مضيف ونقارنها مع الآخرين، مع تعطيل بعض المضيفين الذين تجاوزوا الحد بكثير لضمان توزيع الحجوزات بشكل متساوٍ.",
"edit_team_member_weights": "تعديل الأوزان",
"weights_updated_successfully": "تم تحديث الأوزان بنجاح",
"require_confirmation_for_free_email": "يتطلب التأكيد فقط لمزودي البريد الإلكتروني المجاني (مثل @gmail.com، @outlook.com)",
"exclude_emails_that_contain": "استبعاد رسائل البريد الإلكتروني التي تحتوي على ...",
"require_emails_that_contain": "يتطلب رسائل بريد إلكتروني تحتوي على ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "أنت تقوم بحجز تجريبي.",
"icon_showcase": "معرض الأيقونات",
"icons_showcase": "معرض الأيقونات",
"feature_currently_disabled": "الميزة معطلة حالياً",
"choose_an_option": "اختر خيارًا",
"enter_option_value": "أدخل قيمة الخيار",
"remove_option": "إزالة الخيار",
"managed_users": "المستخدمون المُدارون",
"managed_users_description": "عرض جميع المستخدمين المُدارين الذين تم إنشاؤهم بواسطة عميل OAuth الخاص بك",
"select_oAuth_client": "اختر عميل OAuth",
"on_every_instance": "في كل مثيل",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ أضف السلاسل الجديدة أعلاه هنا ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Görüş təşkilatçısı mövcud olduqda, görüşlərin bron edilməsinə imkan verir. Bu, potensial görüşlərin sayını maksimuma çatdırmaq və görüşlərin təşkilatçılar arasında bərabər paylanmasının daha az əhəmiyyətli olduğu hallarda istifadə olunur.",
"rr_distribution_method_balanced_title": "Yük balanslaşdırma",
"rr_distribution_method_balanced_description": "Hər bir təşkilatçı ilə neçə bron edildiyini izləyəcəyik və bunu digərləri ilə müqayisə edəcəyik, çox irəlidə olan bəzi təşkilatçıları deaktiv edərək bronların bərabər paylanmasını təmin edəcəyik.",
"edit_team_member_weights": "Çəkiləri redaktə et",
"weights_updated_successfully": "Çəkilər uğurla yeniləndi",
"require_confirmation_for_free_email": "Yalnız pulsuz e-poçt provayderləri üçün təsdiq tələb edin (Məs. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Tərkibində olan e-poçtları istisna et...",
"require_emails_that_contain": "E-poçt ünvanları ... tərkibli olmalıdır",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Siz test rezervasiyası edirsiniz.",
"icon_showcase": "İkon nümayişi",
"icons_showcase": "İkonların nümayişi",
"feature_currently_disabled": "Funksiya hazırda deaktivdir",
"choose_an_option": "Seçim edin",
"enter_option_value": "Seçim dəyərini daxil edin",
"remove_option": "Seçimi sil",
"managed_users": "İdarə edilən istifadəçilər",
"managed_users_description": "OAuth müştəriniz tərəfindən yaradılmış bütün idarə edilən istifadəçiləri görün",
"select_oAuth_client": "OAuth müştərisini seçin",
"on_every_instance": "Hər nümunədə",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni sətirləri bura əlavə edin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Позволява на клиентите да запазват срещи, когато има наличен домакин. Използвайте това, за да увеличите максимално броя на потенциалните срещи и когато равномерното разпределение на срещите между домакините е по-малко важно.",
"rr_distribution_method_balanced_title": "Балансиране на натоварването",
"rr_distribution_method_balanced_description": "Ще следим колко резервации са направени с всеки домакин и ще ги сравняваме с останалите, като деактивираме някои домакини, които са прекалено напред, за да бъдат резервациите равномерно разпределени.",
"edit_team_member_weights": "Редактиране на тежести",
"weights_updated_successfully": "Тежестите са актуализирани успешно",
"require_confirmation_for_free_email": "Изисквайте потвърждение само за безплатни имейл доставчици (напр. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Изключи имейли, които съдържат ...",
"require_emails_that_contain": "Изисквай имейли, които съдържат...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Правите тестова резервация.",
"icon_showcase": "Галерия с икони",
"icons_showcase": "Галерия с икони",
"feature_currently_disabled": "Функцията е деактивирана в момента",
"choose_an_option": "Изберете опция",
"enter_option_value": "Въведете стойност на опцията",
"remove_option": "Премахване на опцията",
"managed_users": "Управлявани потребители",
"managed_users_description": "Вижте всички управлявани потребители, създадени от вашия OAuth клиент",
"select_oAuth_client": "Изберете OAuth клиент",
"on_every_instance": "При всяко изпълнение",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавете новите си низове над този ред ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Permet als usuaris reservar reunions sempre que hi hagi un amfitrió disponible. Utilitzeu-ho per maximitzar el nombre potencial de reunions reservades i quan la distribució equitativa de reunions entre els amfitrions és menys important",
"rr_distribution_method_balanced_title": "Equilibri de càrrega",
"rr_distribution_method_balanced_description": "Monitoritzarem quantes reserves s'han fet amb cada amfitrió i ho compararem amb els altres, desactivant alguns amfitrions que estiguin massa avançats perquè les reserves es distribueixin uniformement",
"edit_team_member_weights": "Editar pesos",
"weights_updated_successfully": "Els pesos s'han actualitzat correctament",
"require_confirmation_for_free_email": "Requereix confirmació només per a proveïdors de correu gratuït (Ex. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Excloure correus electrònics que continguin...",
"require_emails_that_contain": "Requereix correus electrònics que continguin...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Estàs fent una reserva de prova.",
"icon_showcase": "Mostra d'icones",
"icons_showcase": "Mostra d'icones",
"feature_currently_disabled": "La funcionalitat està actualment desactivada",
"choose_an_option": "Tria una opció",
"enter_option_value": "Introdueix el valor de l'opció",
"remove_option": "Elimina l'opció",
"managed_users": "Usuaris gestionats",
"managed_users_description": "Veure tots els usuaris gestionats creats pel teu client OAuth",
"select_oAuth_client": "Selecciona el client OAuth",
"on_every_instance": "A cada instància",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Afegiu les vostres noves cadenes a dalt ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Umožňuje rezervujícím sjednat schůzky kdykoliv, když je hostitel k dispozici. Použijte tuto možnost pro maximalizaci počtu potenciálních schůzek a v případech, kdy rovnoměrné rozdělení schůzek mezi hostitele není tak důležité.",
"rr_distribution_method_balanced_title": "Vyvažování zátěže",
"rr_distribution_method_balanced_description": "Budeme sledovat, kolik rezervací bylo provedeno u jednotlivých hostitelů a porovnávat je s ostatními. Některé hostitele, kteří jsou příliš napřed, dočasně deaktivujeme, aby byly rezervace rovnoměrně distribuovány.",
"edit_team_member_weights": "Upravit váhy",
"weights_updated_successfully": "Váhy byly úspěšně aktualizovány",
"require_confirmation_for_free_email": "Vyžadovat potvrzení pouze pro poskytovatele bezplatných e-mailů (např. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Vyloučit e-maily obsahující...",
"require_emails_that_contain": "Vyžadovat e-maily obsahující ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Provádíte testovací rezervaci.",
"icon_showcase": "Ukázka ikon",
"icons_showcase": "Ukázka ikon",
"feature_currently_disabled": "Funkce je momentálně vypnutá",
"choose_an_option": "Vyberte možnost",
"enter_option_value": "Zadejte hodnotu možnosti",
"remove_option": "Odebrat možnost",
"managed_users": "Spravovaní uživatelé",
"managed_users_description": "Zobrazit všechny spravované uživatele vytvořené vaším OAuth klientem",
"select_oAuth_client": "Vybrat OAuth klienta",
"on_every_instance": "Na každé instanci",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Přidejte své nové řetězce nahoru ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Giver bookere mulighed for at booke møder, når en vært er tilgængelig. Brug dette for at maksimere antallet af potentielle møder, der kan bookes, når den jævne fordeling af møder på tværs af værter er mindre vigtig.",
"rr_distribution_method_balanced_title": "Load balancing",
"rr_distribution_method_balanced_description": "Vi vil overvåge, hvor mange bookinger der er foretaget med hver vært og sammenligne dette med andre, og deaktivere nogle værter, der er for langt foran, så bookinger fordeles jævnt.",
"edit_team_member_weights": "Rediger vægtninger",
"weights_updated_successfully": "Vægtninger blev opdateret",
"require_confirmation_for_free_email": "Kræv kun bekræftelse for gratis email-udbydere (F.eks. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Ekskluder e-mails, der indeholder ...",
"require_emails_that_contain": "Kræv e-mails, der indeholder ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Du foretager en testbooking.",
"icon_showcase": "Ikonoversigt",
"icons_showcase": "Ikonoversigt",
"feature_currently_disabled": "Funktionen er i øjeblikket deaktiveret",
"choose_an_option": "Vælg en mulighed",
"enter_option_value": "Indtast værdi for mulighed",
"remove_option": "Fjern mulighed",
"managed_users": "Administrerede brugere",
"managed_users_description": "Se alle administrerede brugere oprettet af din OAuth-klient",
"select_oAuth_client": "Vælg OAuth-klient",
"on_every_instance": "På hver forekomst",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Tilføj dine nye strenge ovenfor her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Ermöglicht Buchenden, Meetings zu buchen, wann immer ein Host verfügbar ist. Nutzen Sie dies, um die Anzahl potenzieller Meetings zu maximieren, wenn die gleichmäßige Verteilung der Meetings auf die Hosts weniger wichtig ist.",
"rr_distribution_method_balanced_title": "Lastverteilung",
"rr_distribution_method_balanced_description": "Wir überwachen, wie viele Buchungen bei jedem Host eingegangen sind und vergleichen dies mit anderen. Hosts, die zu weit voraus sind, werden temporär deaktiviert, damit die Buchungen gleichmäßig verteilt werden.",
"edit_team_member_weights": "Gewichtungen bearbeiten",
"weights_updated_successfully": "Gewichtungen erfolgreich aktualisiert",
"require_confirmation_for_free_email": "Bestätigung nur für kostenlose E-Mail-Anbieter erforderlich (z.B. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "E-Mails ausschließen, die ... enthalten",
"require_emails_that_contain": "E-Mails müssen enthalten ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Sie führen eine Testbuchung durch.",
"icon_showcase": "Icon-Übersicht",
"icons_showcase": "Icons-Übersicht",
"feature_currently_disabled": "Funktion ist derzeit deaktiviert",
"choose_an_option": "Option auswählen",
"enter_option_value": "Optionswert eingeben",
"remove_option": "Option entfernen",
"managed_users": "Verwaltete Benutzer",
"managed_users_description": "Sehen Sie alle verwalteten Benutzer, die von Ihrem OAuth-Client erstellt wurden",
"select_oAuth_client": "OAuth-Client auswählen",
"on_every_instance": "Bei jeder Instanz",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Fügen Sie Ihre neuen Code-Zeilen über dieser hinzu ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Επιτρέπει στους χρήστες να κάνουν κρατήσεις όποτε είναι διαθέσιμος ένας διοργανωτής. Χρησιμοποιήστε το για να μεγιστοποιήσετε τον αριθμό των πιθανών συναντήσεων και όταν η ομοιόμορφη κατανομή των συναντήσεων μεταξύ των διοργανωτών είναι λιγότερο σημαντική.",
"rr_distribution_method_balanced_title": "Εξισορρόπηση φόρτου",
"rr_distribution_method_balanced_description": "Θα παρακολουθούμε πόσες κρατήσεις έχουν γίνει με κάθε διοργανωτή και θα τις συγκρίνουμε με άλλους, απενεργοποιώντας κάποιους διοργανωτές που έχουν προχωρήσει πολύ μπροστά ώστε οι κρατήσεις να κατανέμονται ομοιόμορφα.",
"edit_team_member_weights": "Επεξεργασία βαρών",
"weights_updated_successfully": "Τα βάρη ενημερώθηκαν με επιτυχία",
"require_confirmation_for_free_email": "Απαίτηση επιβεβαίωσης μόνο για παρόχους δωρεάν email (π.χ. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Εξαίρεση email που περιέχουν ...",
"require_emails_that_contain": "Απαιτούνται emails που περιέχουν ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Κάνετε μια δοκιμαστική κράτηση.",
"icon_showcase": "Παρουσίαση εικονιδίων",
"icons_showcase": "Παρουσίαση εικονιδίων",
"feature_currently_disabled": "Η λειτουργία είναι απενεργοποιημένη",
"choose_an_option": "Επιλέξτε μια επιλογή",
"enter_option_value": "Εισαγάγετε την τιμή της επιλογής",
"remove_option": "Αφαίρεση επιλογής",
"managed_users": "Διαχειριζόμενοι χρήστες",
"managed_users_description": "Δείτε όλους τους διαχειριζόμενους χρήστες που δημιουργήθηκαν από το OAuth client σας",
"select_oAuth_client": "Επιλέξτε OAuth Client",
"on_every_instance": "Σε κάθε περίπτωση",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Προσθέστε τις νέες συμβολοσειρές σας πάνω από εδώ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Allows bookers to book meetings whenever a host is available. Use this to maximize the number of potential meetings booked and when the even distribution of meetings across hosts is less important.",
"rr_distribution_method_balanced_title": "Load balancing",
"rr_distribution_method_balanced_description": "We will monitor how many bookings have been made with each host and compare this with others, disabling some hosts that are too far ahead so bookings are evenly distributed.",
"edit_team_member_weights": "Edit Weights",
"weights_updated_successfully": "Weights updated successfully",
"require_confirmation_for_free_email": "Only require confirmation for free email providers (Ex. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Exclude emails that contain ...",
"require_emails_that_contain": "Require emails that contain ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "You are doing a test booking.",
"icon_showcase": "Icon showcase",
"icons_showcase": "Icons showcase",
"feature_currently_disabled": "Feature is currently disabled",
"choose_an_option": "Choose an option",
"enter_option_value": "Enter option value",
"remove_option": "Remove option",
"managed_users": "Managed Users",
"managed_users_description": "See all the managed users created by your OAuth client",
"select_oAuth_client": "Select Oauth Client",
"on_every_instance": "On every instance",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Permite a los usuarios agendar reuniones cuando un anfitrión esté disponible. Usa esta opción para maximizar el número de reuniones potenciales y cuando la distribución equitativa entre anfitriones es menos importante.",
"rr_distribution_method_balanced_title": "Equilibrio de carga",
"rr_distribution_method_balanced_description": "Monitoreamos cuántas reservas se han realizado con cada anfitrión y las comparamos con otros, deshabilitando algunos anfitriones que están muy adelantados para que las reservas se distribuyan de manera uniforme.",
"edit_team_member_weights": "Editar pesos",
"weights_updated_successfully": "Pesos actualizados exitosamente",
"require_confirmation_for_free_email": "Solo requerir confirmación para proveedores de correo gratuito (Ej. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Excluir correos electrónicos que contengan ...",
"require_emails_that_contain": "Requerir correos que contengan...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Estás haciendo una reserva de prueba.",
"icon_showcase": "Muestra de íconos",
"icons_showcase": "Muestra de íconos",
"feature_currently_disabled": "La función está desactivada actualmente",
"choose_an_option": "Elige una opción",
"enter_option_value": "Ingresa el valor de la opción",
"remove_option": "Eliminar opción",
"managed_users": "Usuarios administrados",
"managed_users_description": "Ver todos los usuarios administrados creados por tu cliente OAuth",
"select_oAuth_client": "Seleccionar cliente OAuth",
"on_every_instance": "En cada instancia",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agrega tus nuevas cadenas arriba de esta línea ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Permite a los usuarios reservar reuniones cuando un anfitrión esté disponible. Utiliza esta opción para maximizar el número de reuniones potenciales y cuando la distribución equitativa entre anfitriones sea menos importante.",
"rr_distribution_method_balanced_title": "Equilibrio de carga",
"rr_distribution_method_balanced_description": "Monitorizaremos cuántas reservas se han realizado con cada anfitrión y las compararemos con otros, deshabilitando algunos anfitriones que estén muy por delante para que las reservas se distribuyan uniformemente.",
"edit_team_member_weights": "Editar pesos",
"weights_updated_successfully": "Pesos actualizados correctamente",
"require_confirmation_for_free_email": "Solo requerir confirmación para proveedores de correo gratuito (Ej. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Excluir correos que contengan...",
"require_emails_that_contain": "Requerir correos que contengan...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Estás haciendo una reserva de prueba.",
"icon_showcase": "Muestra de iconos",
"icons_showcase": "Muestra de iconos",
"feature_currently_disabled": "La función está actualmente desactivada",
"choose_an_option": "Elige una opción",
"enter_option_value": "Ingresa el valor de la opción",
"remove_option": "Eliminar opción",
"managed_users": "Usuarios gestionados",
"managed_users_description": "Ver todos los usuarios gestionados creados por tu cliente OAuth",
"select_oAuth_client": "Seleccionar cliente OAuth",
"on_every_instance": "En cada instancia",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agregue sus nuevas cadenas arriba ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Võimaldab broneerijatel broneerida kohtumisi alati, kui võõrustaja on saadaval. Kasutage seda, et maksimeerida potentsiaalsete broneeritud kohtumiste arvu, kui kohtumiste ühtlane jaotamine võõrustajate vahel on vähem oluline.",
"rr_distribution_method_balanced_title": "Koormuse tasakaalustamine",
"rr_distribution_method_balanced_description": "Jälgime, kui palju broneeringuid on tehtud iga võõrustajaga ja võrdleme seda teistega, keelates mõned võõrustajad, kes on liiga kaugel ees, et broneeringud oleksid ühtlaselt jaotatud.",
"edit_team_member_weights": "Muuda kaale",
"weights_updated_successfully": "Kaalud edukalt uuendatud",
"require_confirmation_for_free_email": "Nõua kinnitust ainult tasuta e-posti teenusepakkujate puhul (nt @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Välista e-kirjad, mis sisaldavad ...",
"require_emails_that_contain": "Nõutud on e-mailid, mis sisaldavad ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Teed proovibroneeringut.",
"icon_showcase": "Ikoonide ülevaade",
"icons_showcase": "Ikoonide ülevaade",
"feature_currently_disabled": "Funktsioon on praegu välja lülitatud",
"choose_an_option": "Vali valik",
"enter_option_value": "Sisesta valiku väärtus",
"remove_option": "Eemalda valik",
"managed_users": "Hallatavad kasutajad",
"managed_users_description": "Vaata kõiki hallatavaid kasutajaid, mis on loodud sinu OAuth kliendi poolt",
"select_oAuth_client": "Vali OAuth klient",
"on_every_instance": "Igal korral",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Erreserbatzaileei bilerak erreserbatzeko aukera ematen die ostalari bat erabilgarri dagoenean. Erabili hau bilera potentzialen kopurua maximizatzeko eta ostalarien arteko banaketa orekatua hain garrantzitsua ez denean",
"rr_distribution_method_balanced_title": "Karga orekatzea",
"rr_distribution_method_balanced_description": "Ostalari bakoitzarekin egin diren erreserben kopurua monitorizatu eta besteekin konparatuko dugu, aurreregi doazen ostalari batzuk desgaituz erreserbak modu orekatuan banatzeko",
"edit_team_member_weights": "Pisuak editatu",
"weights_updated_successfully": "Pisuak behar bezala eguneratu dira",
"require_confirmation_for_free_email": "Baieztapena eskatu soilik doako posta elektronikoko hornitzaileentzat (Adib. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Baztertu hau duten emailak...",
"require_emails_that_contain": "Helbide elektronikoak hau eduki behar du ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Proba-erreserba bat egiten ari zara.",
"icon_showcase": "Ikonoen erakusleihoa",
"icons_showcase": "Ikonoen erakusleihoa",
"feature_currently_disabled": "Eginbidea desgaituta dago",
"choose_an_option": "Aukeratu aukera bat",
"enter_option_value": "Sartu aukeraren balioa",
"remove_option": "Kendu aukera",
"managed_users": "Kudeatutako erabiltzaileak",
"managed_users_description": "Ikusi zure OAuth bezeroak sortutako kudeatutako erabiltzaile guztiak",
"select_oAuth_client": "Hautatu OAuth bezeroa",
"on_every_instance": "Instantzia bakoitzean",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Gehitu zure kate berriak honen gainean ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Sallii varaajien tehdä varauksia aina kun isäntä on saatavilla. Käytä tätä maksimoidaksesi mahdollisten varausten määrän ja kun tapaamisten tasainen jakautuminen isäntien kesken ei ole niin tärkeää.",
"rr_distribution_method_balanced_title": "Kuormituksen tasaus",
"rr_distribution_method_balanced_description": "Seuraamme kunkin isännän varausmääriä ja vertaamme niitä muihin. Poistamme käytöstä isäntiä, joilla on liikaa varauksia, jotta varaukset jakautuvat tasaisesti.",
"edit_team_member_weights": "Muokkaa painotuksia",
"weights_updated_successfully": "Painotukset päivitetty onnistuneesti",
"require_confirmation_for_free_email": "Vaadi vahvistus vain ilmaisten sähköpostipalvelujen käyttäjiltä (esim. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Sulje pois sähköpostit, jotka sisältävät ...",
"require_emails_that_contain": "Vaadi sähköpostit, jotka sisältävät ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Teet testikäyntivarausta.",
"icon_showcase": "Kuvakegalleria",
"icons_showcase": "Kuvakegalleria",
"feature_currently_disabled": "Ominaisuus on tällä hetkellä pois käytöstä",
"choose_an_option": "Valitse vaihtoehto",
"enter_option_value": "Syötä vaihtoehdon arvo",
"remove_option": "Poista vaihtoehto",
"managed_users": "Hallinnoidut käyttäjät",
"managed_users_description": "Näe kaikki OAuth-asiakassovelluksesi luomat hallinnoidut käyttäjät",
"select_oAuth_client": "Valitse OAuth-asiakassovellus",
"on_every_instance": "Jokaisessa instanssissa",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Lisää uudet merkkijonot tämän yläpuolelle ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Permet aux participants de réserver des réunions dès qu'un hôte est disponible. Utilisez cette option pour maximiser le nombre potentiel de réunions réservées lorsque la distribution équitable des réunions entre les hôtes est moins importante.",
"rr_distribution_method_balanced_title": "Équilibrage de charge",
"rr_distribution_method_balanced_description": "Nous surveillerons le nombre de réservations effectuées avec chaque hôte et le comparerons avec les autres, en désactivant certains hôtes trop en avance pour que les réservations soient réparties équitablement.",
"edit_team_member_weights": "Modifier les pondérations",
"weights_updated_successfully": "Pondérations mises à jour avec succès",
"require_confirmation_for_free_email": "Exiger une confirmation uniquement pour les fournisseurs de messagerie gratuits (Ex. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Exclure les e-mails contenant ...",
"require_emails_that_contain": "Exiger des e-mails qui contiennent...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Vous effectuez une réservation test.",
"icon_showcase": "Présentation des icônes",
"icons_showcase": "Présentation des icônes",
"feature_currently_disabled": "La fonctionnalité est actuellement désactivée",
"choose_an_option": "Choisir une option",
"enter_option_value": "Saisir la valeur de l'option",
"remove_option": "Supprimer l'option",
"managed_users": "Utilisateurs gérés",
"managed_users_description": "Voir tous les utilisateurs gérés créés par votre client OAuth",
"select_oAuth_client": "Sélectionner le client OAuth",
"on_every_instance": "Sur chaque instance",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Ajoutez vos nouvelles chaînes ci-dessus ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "מאפשר לקובעי פגישות לקבוע פגישות בכל פעם שמארח זמין. השתמשו באפשרות זו כדי למקסם את מספר הפגישות הפוטנציאליות ובמקרים בהם חלוקה שווה של פגישות בין המארחים פחות חשובה",
"rr_distribution_method_balanced_title": "איזון עומסים",
"rr_distribution_method_balanced_description": "אנחנו נעקוב אחר מספר ההזמנות שנקבעו עם כל מארח ונשווה זאת לאחרים, תוך השבתה זמנית של מארחים שקיבלו יותר מדי הזמנות כדי להבטיח חלוקה שווה",
"edit_team_member_weights": "עריכת משקולות",
"weights_updated_successfully": "המשקולות עודכנו בהצלחה",
"require_confirmation_for_free_email": "דרוש אישור רק עבור ספקי אימייל חינמיים (לדוגמה @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "הוצא כתובות דוא\"ל המכילות...",
"require_emails_that_contain": "נדרשות כתובות דוא\"ל המכילות...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "אתה מבצע הזמנת ניסיון.",
"icon_showcase": "תצוגת אייקון",
"icons_showcase": "תצוגת אייקונים",
"feature_currently_disabled": "התכונה מושבתת כרגע",
"choose_an_option": "בחר אפשרות",
"enter_option_value": "הזן ערך אפשרות",
"remove_option": "הסר אפשרות",
"managed_users": "משתמשים מנוהלים",
"managed_users_description": "צפה בכל המשתמשים המנוהלים שנוצרו על ידי לקוח OAuth שלך",
"select_oAuth_client": "בחר לקוח OAuth",
"on_every_instance": "בכל מופע",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Lehetővé teszi a foglalóknak, hogy találkozókat foglaljanak, amikor a házigazda elérhető. Használja ezt a lehetőséget a potenciális találkozók számának maximalizálására, amikor a találkozók egyenletes elosztása a házigazdák között kevésbé fontos.",
"rr_distribution_method_balanced_title": "Terheléselosztás",
"rr_distribution_method_balanced_description": "Figyelemmel kísérjük, hogy hány foglalás történt az egyes házigazdákkal, és összehasonlítjuk másokkal, letiltva azokat a házigazdákat, akik túl sok foglalással rendelkeznek, így a foglalások egyenletesen oszlanak el.",
"edit_team_member_weights": "Súlyok szerkesztése",
"weights_updated_successfully": "A súlyok sikeresen frissítve",
"require_confirmation_for_free_email": "Csak ingyenes e-mail szolgáltatók esetén kérjen megerősítést (Pl. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Zárja ki azokat az e-maileket, amelyek tartalmazzák ...",
"require_emails_that_contain": "E-mail címek szükségesek, amelyek tartalmazzák ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Tesztfoglalást végzel.",
"icon_showcase": "Ikon bemutató",
"icons_showcase": "Ikonok bemutatója",
"feature_currently_disabled": "A funkció jelenleg ki van kapcsolva",
"choose_an_option": "Válassz egy opciót",
"enter_option_value": "Add meg az opció értékét",
"remove_option": "Opció eltávolítása",
"managed_users": "Kezelt felhasználók",
"managed_users_description": "Az OAuth kliense által létrehozott összes kezelt felhasználó megtekintése",
"select_oAuth_client": "OAuth kliens kiválasztása",
"on_every_instance": "Minden példányon",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adja hozzá az új karakterláncokat fent ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Permette ai prenotanti di fissare incontri ogni volta che un host è disponibile. Usa questa opzione per massimizzare il numero di potenziali incontri prenotabili e quando la distribuzione uniforme degli incontri tra gli host è meno importante.",
"rr_distribution_method_balanced_title": "Bilanciamento del carico",
"rr_distribution_method_balanced_description": "Monitoreremo quante prenotazioni sono state effettuate con ciascun host e le confronteremo con gli altri, disabilitando alcuni host che sono troppo avanti per garantire una distribuzione uniforme delle prenotazioni.",
"edit_team_member_weights": "Modifica pesi",
"weights_updated_successfully": "Pesi aggiornati con successo",
"require_confirmation_for_free_email": "Richiedi conferma solo per i provider di posta elettronica gratuiti (Es. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Escludi le email che contengono ...",
"require_emails_that_contain": "Richiedi email che contengono ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Stai effettuando una prenotazione di prova.",
"icon_showcase": "Galleria delle icone",
"icons_showcase": "Galleria delle icone",
"feature_currently_disabled": "La funzionalità è attualmente disattivata",
"choose_an_option": "Scegli un'opzione",
"enter_option_value": "Inserisci il valore dell'opzione",
"remove_option": "Rimuovi opzione",
"managed_users": "Utenti gestiti",
"managed_users_description": "Visualizza tutti gli utenti gestiti creati dal tuo client OAuth",
"select_oAuth_client": "Seleziona client OAuth",
"on_every_instance": "Su ogni istanza",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Aggiungi le tue nuove stringhe qui sopra ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "ホストが対応可能な時はいつでも予約を受け付けることができます。潜在的な予約数を最大化したい場合や、ホスト間での予約の均等な配分が重要でない場合に使用します。",
"rr_distribution_method_balanced_title": "負荷分散",
"rr_distribution_method_balanced_description": "各ホストの予約数を監視し、他のホストと比較して予約数が多すぎるホストを一時的に無効にすることで、予約を均等に配分します。",
"edit_team_member_weights": "ウェイトを編集",
"weights_updated_successfully": "ウェイトが正常に更新されました",
"require_confirmation_for_free_email": "フリーメールプロバイダー(@gmail.com、@outlook.comなど)のみ確認を必要とする",
"exclude_emails_that_contain": "...を含むメールアドレスを除外",
"require_emails_that_contain": "メールアドレスに含まれる必要がある文字列...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "テスト予約を実行中です。",
"icon_showcase": "アイコンショーケース",
"icons_showcase": "アイコンショーケース",
"feature_currently_disabled": "この機能は現在無効になっています",
"choose_an_option": "オプションを選択",
"enter_option_value": "オプション値を入力",
"remove_option": "オプションを削除",
"managed_users": "管理対象ユーザー",
"managed_users_description": "OAuthクライアントによって作成された管理対象ユーザーをすべて表示",
"select_oAuth_client": "OAuthクライアントを選択",
"on_every_instance": "すべてのインスタンスで",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ この上に新しい文字列を追加してください ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "អនុញ្ញាតឱ្យអ្នកកក់អាចកក់កិច្ចប្រជុំនៅពេលដែលម្ចាស់មានពេលទំនេរ។ ប្រើវាដើម្បីបង្កើនចំនួនកិច្ចប្រជុំដែលអាចកក់បាន និងនៅពេលដែលការចែកចាយស្មើគ្នានៃកិច្ចប្រជុំរវាងម្ចាស់មានសារៈសំខាន់តិចជាង។",
"rr_distribution_method_balanced_title": "ការរក្សាតុល្យភាពបន្ទុក",
"rr_distribution_method_balanced_description": "យើងនឹងតាមដានចំនួនការកក់ដែលបានធ្វើឡើងជាមួយម្ចាស់នីមួយៗ និងប្រៀបធៀបជាមួយអ្នកផ្សេងទៀត ដោយបិទម្ចាស់មួយចំនួនដែលមានការកក់ច្រើនពេកដើម្បីឱ្យការកក់ត្រូវបានចែកចាយស្មើៗគ្នា។",
"edit_team_member_weights": "កែសម្រួលទម្ងន់",
"weights_updated_successfully": "ការធ្វើបច្ចុប្បន្នភាពទម្ងន់បានជោគជ័យ",
"require_confirmation_for_free_email": "តម្រូវឱ្យមានការបញ្ជាក់សម្រាប់តែអ្នកផ្តល់សេវាអ៊ីមែលឥតគិតថ្លៃប៉ុណ្ណោះ (ឧទាហរណ៍៖ @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "ច្រានចោលអ៊ីមែលដែលមាន ...",
"require_emails_that_contain": "តម្រូវអ៊ីមែលដែលមាន...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "អ្នកកំពុងធ្វើការកក់សាកល្បង។",
"icon_showcase": "ការបង្ហាញរូបតំណាង",
"icons_showcase": "ការបង្ហាញរូបតំណាងនានា",
"feature_currently_disabled": "មុខងារត្រូវបានបិទដំណើរការនាពេលបច្ចុប្បន្ន",
"choose_an_option": "ជ្រើសរើសជម្រើសមួយ",
"enter_option_value": "បញ្ចូលតម្លៃជម្រើស",
"remove_option": "លុបជម្រើស",
"managed_users": "អ្នកប្រើប្រាស់ដែលគ្រប់គ្រង",
"managed_users_description": "មើលអ្នកប្រើប្រាស់ដែលគ្រប់គ្រងទាំងអស់ដែលបានបង្កើតដោយ OAuth client របស់អ្នក",
"select_oAuth_client": "ជ្រើសរើស OAuth Client",
"on_every_instance": "នៅលើគ្រប់ករណី",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ បន្ថែមខ្សែអក្សរថ្មីរបស់អ្នកនៅខាងលើនេះ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "호스트가 가능한 시간대에 예약자가 미팅을 예약할 수 있습니다. 호스트 간 미팅 분배의 균형보다 잠재적 미팅 예약 수를 최대화하고 싶을 때 사용하세요.",
"rr_distribution_method_balanced_title": "부하 분산",
"rr_distribution_method_balanced_description": "각 호스트의 예약 수를 모니터링하고 다른 호스트들과 비교하여, 예약이 균등하게 분배되도록 예약이 너무 많은 호스트를 일시적으로 비활성화합니다.",
"edit_team_member_weights": "가중치 수정",
"weights_updated_successfully": "가중치가 성공적으로 업데이트되었습니다",
"require_confirmation_for_free_email": "무료 이메일 제공업체(@gmail.com, @outlook.com 등)에 대해서만 확인 필요",
"exclude_emails_that_contain": "다음이 포함된 이메일 제외 ...",
"require_emails_that_contain": "허용된 이메일 도메인 ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "테스트 예약을 진행하고 있습니다.",
"icon_showcase": "아이콘 쇼케이스",
"icons_showcase": "아이콘 쇼케이스",
"feature_currently_disabled": "현재 기능이 비활성화되어 있습니다",
"choose_an_option": "옵션 선택",
"enter_option_value": "옵션 값 입력",
"remove_option": "옵션 삭제",
"managed_users": "관리 대상 사용자",
"managed_users_description": "OAuth 클라이언트가 생성한 모든 관리 대상 사용자 보기",
"select_oAuth_client": "OAuth 클라이언트 선택",
"on_every_instance": "모든 인스턴스에서",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 여기에 새 문자열을 추가하세요 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Stelt boekers in staat om afspraken te maken wanneer een host beschikbaar is. Gebruik dit om het aantal potentiële afspraken te maximaliseren en wanneer de gelijke verdeling van afspraken over hosts minder belangrijk is.",
"rr_distribution_method_balanced_title": "Evenredige verdeling",
"rr_distribution_method_balanced_description": "We monitoren hoeveel boekingen er bij elke host zijn gemaakt en vergelijken dit met anderen. Hosts die te ver voorlopen worden tijdelijk uitgeschakeld zodat boekingen gelijkmatig worden verdeeld.",
"edit_team_member_weights": "Gewichten bewerken",
"weights_updated_successfully": "Gewichten succesvol bijgewerkt",
"require_confirmation_for_free_email": "Vereist alleen bevestiging voor gratis e-mailproviders (bijv. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "E-mailadressen uitsluiten die ... bevatten",
"require_emails_that_contain": "E-mailadressen moeten bevatten ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Je doet een testboeking.",
"icon_showcase": "Icoon overzicht",
"icons_showcase": "Iconen overzicht",
"feature_currently_disabled": "Functie is momenteel uitgeschakeld",
"choose_an_option": "Kies een optie",
"enter_option_value": "Voer optiewaarde in",
"remove_option": "Optie verwijderen",
"managed_users": "Beheerde gebruikers",
"managed_users_description": "Bekijk alle beheerde gebruikers die zijn aangemaakt door je OAuth-client",
"select_oAuth_client": "Selecteer OAuth-client",
"on_every_instance": "Op elke instantie",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Voeg uw nieuwe strings hierboven toe ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Lar personer bestille møter når som helst en vert er tilgjengelig. Bruk dette for å maksimere antall potensielle møter som kan bestilles, og når jevn fordeling av møter mellom verter er mindre viktig.",
"rr_distribution_method_balanced_title": "Lastbalansering",
"rr_distribution_method_balanced_description": "Vi vil overvåke hvor mange bestillinger som er gjort med hver vert og sammenligne dette med andre, og deaktivere enkelte verter som ligger for langt foran slik at bestillinger blir jevnt fordelt.",
"edit_team_member_weights": "Rediger vekting",
"weights_updated_successfully": "Vekting ble oppdatert",
"require_confirmation_for_free_email": "Krev kun bekreftelse for gratis e-postleverandører (f.eks. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Ekskluder e-poster som inneholder ...",
"require_emails_that_contain": "Krev e-postadresser som inneholder ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Du gjør en testbestilling.",
"icon_showcase": "Ikonvisning",
"icons_showcase": "Ikonvisning",
"feature_currently_disabled": "Funksjonen er deaktivert",
"choose_an_option": "Velg et alternativ",
"enter_option_value": "Skriv inn alternativverdi",
"remove_option": "Fjern alternativ",
"managed_users": "Administrerte brukere",
"managed_users_description": "Se alle administrerte brukere opprettet av OAuth-klienten din",
"select_oAuth_client": "Velg OAuth-klient",
"on_every_instance": "På hver instans",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Legg til dine nye strenger over her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Umożliwia rezerwację spotkań, gdy którykolwiek z gospodarzy jest dostępny. Używaj tej opcji, aby zmaksymalizować liczbę potencjalnych spotkań, gdy równomierne rozłożenie spotkań między gospodarzami jest mniej istotne.",
"rr_distribution_method_balanced_title": "Równoważenie obciążenia",
"rr_distribution_method_balanced_description": "System będzie monitorować liczbę rezerwacji przypisanych do każdego gospodarza i porównywać ją z innymi, dezaktywując tymczasowo gospodarzy z większą liczbą spotkań, aby zapewnić równomierną dystrybucję.",
"edit_team_member_weights": "Edytuj wagi",
"weights_updated_successfully": "Wagi zostały zaktualizowane pomyślnie",
"require_confirmation_for_free_email": "Wymagaj potwierdzenia tylko dla dostawców darmowej poczty (np. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Wyklucz e-maile, które zawierają ...",
"require_emails_that_contain": "Wymagaj adresów e-mail zawierających...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Wykonujesz testową rezerwację.",
"icon_showcase": "Prezentacja ikon",
"icons_showcase": "Prezentacja ikon",
"feature_currently_disabled": "Funkcja jest obecnie wyłączona",
"choose_an_option": "Wybierz opcję",
"enter_option_value": "Wprowadź wartość opcji",
"remove_option": "Usuń opcję",
"managed_users": "Zarządzani użytkownicy",
"managed_users_description": "Zobacz wszystkich zarządzanych użytkowników utworzonych przez twojego klienta OAuth",
"select_oAuth_client": "Wybierz klienta OAuth",
"on_every_instance": "W każdej instancji",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodaj nowe ciągi powyżej ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Permite que os agendadores marquem reuniões sempre que um anfitrião estiver disponível. Use isso para maximizar o número de reuniões potenciais agendadas quando a distribuição uniforme de reuniões entre os anfitriões for menos importante.",
"rr_distribution_method_balanced_title": "Balanceamento de carga",
"rr_distribution_method_balanced_description": "Monitoraremos quantas reservas foram feitas com cada anfitrião e compararemos com os outros, desativando alguns anfitriões que estão muito à frente para que as reservas sejam distribuídas uniformemente.",
"edit_team_member_weights": "Editar pesos",
"weights_updated_successfully": "Pesos atualizados com sucesso",
"require_confirmation_for_free_email": "Exigir confirmação apenas para provedores de e-mail gratuitos (Ex. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Excluir e-mails que contêm ...",
"require_emails_that_contain": "Exigir emails que contenham ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Você está fazendo um agendamento de teste.",
"icon_showcase": "Mostruário de ícones",
"icons_showcase": "Mostruário de ícones",
"feature_currently_disabled": "O recurso está desativado no momento",
"choose_an_option": "Escolha uma opção",
"enter_option_value": "Digite o valor da opção",
"remove_option": "Remover opção",
"managed_users": "Usuários gerenciados",
"managed_users_description": "Veja todos os usuários gerenciados criados pelo seu cliente OAuth",
"select_oAuth_client": "Selecionar cliente OAuth",
"on_every_instance": "Em cada instância",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adicione suas novas strings aqui em cima ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Permite que os agendadores marquem reuniões sempre que um anfitrião estiver disponível. Use isso para maximizar o número de reuniões potenciais agendadas quando a distribuição uniforme de reuniões entre os anfitriões for menos importante.",
"rr_distribution_method_balanced_title": "Balanceamento de carga",
"rr_distribution_method_balanced_description": "Monitoraremos quantas reservas foram feitas com cada anfitrião e compararemos com os outros, desativando alguns anfitriões que estão muito à frente para que as reservas sejam distribuídas uniformemente.",
"edit_team_member_weights": "Editar pesos",
"weights_updated_successfully": "Pesos atualizados com sucesso",
"require_confirmation_for_free_email": "Exigir confirmação apenas para provedores de e-mail gratuitos (Ex. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Excluir emails que contêm ...",
"require_emails_that_contain": "Exigir emails que contenham ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Você está fazendo uma reserva de teste.",
"icon_showcase": "Mostruário de ícones",
"icons_showcase": "Mostruário de ícones",
"feature_currently_disabled": "Recurso está atualmente desativado",
"choose_an_option": "Escolha uma opção",
"enter_option_value": "Digite o valor da opção",
"remove_option": "Remover opção",
"managed_users": "Usuários gerenciados",
"managed_users_description": "Veja todos os usuários gerenciados criados pelo seu cliente OAuth",
"select_oAuth_client": "Selecionar cliente OAuth",
"on_every_instance": "Em cada instância",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Permite programatorilor să rezerve întâlniri oricând un host este disponibil. Folosește această opțiune pentru a maximiza numărul potențial de întâlniri programate și când distribuția uniformă a întâlnirilor între hosți este mai puțin importantă.",
"rr_distribution_method_balanced_title": "Echilibrarea încărcării",
"rr_distribution_method_balanced_description": "Vom monitoriza câte rezervări au fost făcute cu fiecare host și vom compara acest lucru cu ceilalți, dezactivând temporar hosții care au prea multe programări, astfel încât rezervările să fie distribuite uniform.",
"edit_team_member_weights": "Editare ponderi",
"weights_updated_successfully": "Ponderile au fost actualizate cu succes",
"require_confirmation_for_free_email": "Solicită confirmare doar pentru furnizorii de email gratuit (Ex. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Excludează emailurile care conțin ...",
"require_emails_that_contain": "Necesită adrese de email care conțin ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Efectuați o rezervare de test.",
"icon_showcase": "Prezentare pictograme",
"icons_showcase": "Prezentare pictograme",
"feature_currently_disabled": "Funcționalitatea este momentan dezactivată",
"choose_an_option": "Alege o opțiune",
"enter_option_value": "Introdu valoarea opțiunii",
"remove_option": "Elimină opțiunea",
"managed_users": "Utilizatori administrați",
"managed_users_description": "Vezi toți utilizatorii administrați creați de clientul tău OAuth",
"select_oAuth_client": "Selectează clientul OAuth",
"on_every_instance": "La fiecare instanță",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adăugați stringurile noi deasupra acestui rând ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Позволяет участникам бронировать встречи, когда организатор доступен. Используйте этот метод для максимизации количества потенциальных встреч, когда равномерное распределение встреч между организаторами менее важно.",
"rr_distribution_method_balanced_title": "Балансировка нагрузки",
"rr_distribution_method_balanced_description": "Мы будем отслеживать количество бронирований у каждого организатора и сравнивать их между собой, отключая тех организаторов, у которых слишком много встреч, чтобы обеспечить равномерное распределение.",
"edit_team_member_weights": "Изменить веса",
"weights_updated_successfully": "Веса успешно обновлены",
"require_confirmation_for_free_email": "Требовать подтверждение только для бесплатных почтовых сервисов (например, @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Исключить email-адреса, содержащие...",
"require_emails_that_contain": "Требуются email-адреса, содержащие...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Вы выполняете тестовое бронирование.",
"icon_showcase": "Витрина иконок",
"icons_showcase": "Витрина иконок",
"feature_currently_disabled": "Функция в данный момент отключена",
"choose_an_option": "Выберите вариант",
"enter_option_value": "Введите значение варианта",
"remove_option": "Удалить вариант",
"managed_users": "Управляемые пользователи",
"managed_users_description": "Просмотр всех управляемых пользователей, созданных вашим OAuth-клиентом",
"select_oAuth_client": "Выберите OAuth-клиент",
"on_every_instance": "На каждом экземпляре",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавьте строки выше ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Umožňuje rezervujúcim dohodnúť stretnutia kedykoľvek je hostiteľ k dispozícii. Použite túto možnosť na maximalizáciu počtu potenciálnych stretnutí a v prípadoch, keď rovnomerná distribúcia stretnutí medzi hostiteľmi nie je až taká dôležitá.",
"rr_distribution_method_balanced_title": "Vyvažovanie záťaže",
"rr_distribution_method_balanced_description": "Budeme sledovať počet rezervácií u každého hostiteľa a porovnávať ich s ostatnými. Hostiteľov, ktorí majú príliš veľa rezervácií, dočasne deaktivujeme, aby boli stretnutia distribuované rovnomerne.",
"edit_team_member_weights": "Upraviť váhy",
"weights_updated_successfully": "Váhy boli úspešne aktualizované",
"require_confirmation_for_free_email": "Vyžadovať potvrdenie len pre poskytovateľov bezplatných e-mailov (napr. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Vylúčiť e-maily, ktoré obsahujú...",
"require_emails_that_contain": "Vyžadovať e-maily, ktoré obsahujú ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Robíte testovaciu rezerváciu.",
"icon_showcase": "Ukážka ikony",
"icons_showcase": "Ukážka ikon",
"feature_currently_disabled": "Funkcia je momentálne deaktivovaná",
"choose_an_option": "Vyberte možnosť",
"enter_option_value": "Zadajte hodnotu možnosti",
"remove_option": "Odstrániť možnosť",
"managed_users": "Spravovaní používatelia",
"managed_users_description": "Zobraziť všetkých spravovaných používateľov vytvorených vaším OAuth klientom",
"select_oAuth_client": "Vyberte OAuth klienta",
"on_every_instance": "Pri každej inštancii",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Pridajte svoje nové reťazce nad túto líniu ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Omogućava zakazivanje sastanaka kad god je domaćin dostupan. Koristite ovo da maksimalno povećate broj potencijalnih sastanaka kada je ravnomerna distribucija sastanaka među domaćinima manje važna.",
"rr_distribution_method_balanced_title": "Balansiranje opterećenja",
"rr_distribution_method_balanced_description": "Pratićemo broj zakazanih sastanaka kod svakog domaćina i upoređivati ga sa ostalima, privremeno onemogućavajući domaćine koji imaju previše sastanaka kako bi distribucija bila ravnomerna.",
"edit_team_member_weights": "Izmeni težine",
"weights_updated_successfully": "Težine su uspešno ažurirane",
"require_confirmation_for_free_email": "Zahtevaj potvrdu samo za besplatne email provajdere (npr. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Isključi e-poruke koje sadrže ...",
"require_emails_that_contain": "Zahtevaj email adrese koje sadrže...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Pravite probnu rezervaciju.",
"icon_showcase": "Prikaz ikonica",
"icons_showcase": "Prikaz ikonica",
"feature_currently_disabled": "Funkcija je trenutno onemogućena",
"choose_an_option": "Izaberite opciju",
"enter_option_value": "Unesite vrednost opcije",
"remove_option": "Ukloni opciju",
"managed_users": "Upravljani korisnici",
"managed_users_description": "Pogledajte sve upravljane korisnike koje je kreirao vaš OAuth klijent",
"select_oAuth_client": "Izaberite OAuth klijenta",
"on_every_instance": "Na svakoj instanci",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodajte svoje nove stringove iznad ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Låter bokare boka möten när en värd är tillgänglig. Använd detta för att maximera antalet potentiella möten som kan bokas och när jämn fördelning av möten mellan värdar är mindre viktigt.",
"rr_distribution_method_balanced_title": "Lastbalansering",
"rr_distribution_method_balanced_description": "Vi övervakar hur många bokningar som har gjorts med varje värd och jämför detta med andra, och inaktiverar vissa värdar som ligger för långt före så att bokningarna fördelas jämnt.",
"edit_team_member_weights": "Redigera vikter",
"weights_updated_successfully": "Vikterna har uppdaterats",
"require_confirmation_for_free_email": "Kräv endast bekräftelse för leverantörer av gratise-post (t.ex. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Uteslut e-postmeddelanden som innehåller ...",
"require_emails_that_contain": "Kräv e-postadresser som innehåller ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Du gör en testbokning.",
"icon_showcase": "Ikonvisning",
"icons_showcase": "Ikonvisning",
"feature_currently_disabled": "Funktionen är för närvarande inaktiverad",
"choose_an_option": "Välj ett alternativ",
"enter_option_value": "Ange alternativets värde",
"remove_option": "Ta bort alternativ",
"managed_users": "Hanterade användare",
"managed_users_description": "Se alla hanterade användare som skapats av din OAuth-klient",
"select_oAuth_client": "Välj OAuth-klient",
"on_every_instance": "På varje instans",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Rezervasyon yapanların, bir ev sahibi müsait olduğunda toplantı ayarlamasına olanak tanır. Potansiyel toplantı rezervasyonlarının sayısını en üst düzeye çıkarmak ve ev sahipleri arasında toplantıların eşit dağılımının daha az önemli olduğu durumlarda bunu kullanın.",
"rr_distribution_method_balanced_title": "Yük dengeleme",
"rr_distribution_method_balanced_description": "Her ev sahibiyle yapılan rezervasyon sayısını takip edip diğerleriyle karşılaştıracağız ve rezervasyonların eşit dağılımını sağlamak için çok ileri giden bazı ev sahiplerini devre dışı bırakacağız.",
"edit_team_member_weights": "Ağırlıkları düzenle",
"weights_updated_successfully": "Ağırlıklar başarıyla güncellendi",
"require_confirmation_for_free_email": "Sadece ücretsiz e-posta sağlayıcıları için onay iste (Örn. @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "... içeren e-postaları hariç tut",
"require_emails_that_contain": "E-postaların içermesi gereken ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Test rezervasyonu yapıyorsunuz.",
"icon_showcase": "İkon vitrini",
"icons_showcase": "İkonlar vitrini",
"feature_currently_disabled": "Özellik şu anda devre dışı",
"choose_an_option": "Bir seçenek belirleyin",
"enter_option_value": "Seçenek değerini girin",
"remove_option": "Seçeneği kaldır",
"managed_users": "Yönetilen kullanıcılar",
"managed_users_description": "OAuth istemciniz tarafından oluşturulan tüm yönetilen kullanıcıları görün",
"select_oAuth_client": "OAuth istemcisi seçin",
"on_every_instance": "Her örnekte",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni dizelerinizi yukarıya ekleyin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Дозволяє учасникам бронювати зустрічі, коли хост доступний. Використовуйте це для максимізації кількості потенційних зустрічей та коли рівномірний розподіл зустрічей між хостами менш важливий.",
"rr_distribution_method_balanced_title": "Балансування навантаження",
"rr_distribution_method_balanced_description": "Ми будемо відстежувати кількість бронювань для кожного хоста та порівнювати їх з іншими, деактивуючи тих хостів, які значно випереджають інших, щоб бронювання розподілялися рівномірно.",
"edit_team_member_weights": "Редагувати ваги",
"weights_updated_successfully": "Ваги успішно оновлено",
"require_confirmation_for_free_email": "Вимагати підтвердження лише для безкоштовних поштових служб (наприклад, @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Виключити електронні адреси, які містять ...",
"require_emails_that_contain": "Вимагати електронні адреси, що містять...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Ви робите тестове бронювання.",
"icon_showcase": "Галерея іконок",
"icons_showcase": "Галерея іконок",
"feature_currently_disabled": "Функцію наразі вимкнено",
"choose_an_option": "Оберіть опцію",
"enter_option_value": "Введіть значення опції",
"remove_option": "Видалити опцію",
"managed_users": "Керовані користувачі",
"managed_users_description": "Переглянути всіх керованих користувачів, створених вашим OAuth-клієнтом",
"select_oAuth_client": "Оберіть OAuth-клієнта",
"on_every_instance": "На кожному екземплярі",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "Cho phép người đặt lịch đặt cuộc họp bất cứ khi nào người chủ trì có mặt. Sử dụng tính năng này để tối đa hóa số lượng cuộc họp tiềm năng được đặt và khi việc phân phối cuộc họp đồng đều giữa các chủ trì ít quan trọng hơn.",
"rr_distribution_method_balanced_title": "Cân bằng tải",
"rr_distribution_method_balanced_description": "Chúng tôi sẽ theo dõi số lượng đặt lịch đã được thực hiện với từng chủ trì và so sánh với những người khác, vô hiệu hóa một số chủ trì đã nhận quá nhiều lịch để đảm bảo việc đặt lịch được phân phối đồng đều.",
"edit_team_member_weights": "Chỉnh sửa trọng số",
"weights_updated_successfully": "Đã cập nhật trọng số thành công",
"require_confirmation_for_free_email": "Chỉ yêu cầu xác nhận đối với các nhà cung cấp email miễn phí (Ví dụ: @gmail.com, @outlook.com)",
"exclude_emails_that_contain": "Loại trừ các email chứa ...",
"require_emails_that_contain": "Yêu cầu email phải chứa ...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "Bạn đang thực hiện đặt lịch thử nghiệm.",
"icon_showcase": "Trưng bày biểu tượng",
"icons_showcase": "Trưng bày các biểu tượng",
"feature_currently_disabled": "Tính năng hiện đang bị vô hiệu hóa",
"choose_an_option": "Chọn một tùy chọn",
"enter_option_value": "Nhập giá trị tùy chọn",
"remove_option": "Xóa tùy chọn",
"managed_users": "Người dùng được quản lý",
"managed_users_description": "Xem tất cả người dùng được quản lý được tạo bởi OAuth client của bạn",
"select_oAuth_client": "Chọn OAuth Client",
"on_every_instance": "Trên mỗi phiên bản",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "允许预订者在任何主持人可用时预订会议。当会议在主持人之间的均衡分配不太重要时,可使用此选项来最大化潜在会议预订数量。",
"rr_distribution_method_balanced_title": "负载均衡",
"rr_distribution_method_balanced_description": "系统会监控每位主持人已接受的预订数量并与其他人进行比较,如果某些主持人的预订数量远超他人,系统会暂时停用这些主持人,以确保预订均匀分配。",
"edit_team_member_weights": "编辑权重",
"weights_updated_successfully": "权重更新成功",
"require_confirmation_for_free_email": "仅对免费邮箱提供商要求确认(例如 @gmail.com、@outlook.com)",
"exclude_emails_that_contain": "排除包含...的电子邮件",
"require_emails_that_contain": "要求电子邮件包含...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "您正在进行测试预订。",
"icon_showcase": "图标展示",
"icons_showcase": "图标展示",
"feature_currently_disabled": "该功能当前已禁用",
"choose_an_option": "选择一个选项",
"enter_option_value": "输入选项值",
"remove_option": "删除选项",
"managed_users": "托管用户",
"managed_users_description": "查看由您的 OAuth 客户端创建的所有托管用户",
"select_oAuth_client": "选择 OAuth 客户端",
"on_every_instance": "在每个实例上",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 在此上方添加您的新字符串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2874,6 +2874,8 @@
"rr_distribution_method_availability_description": "讓預約者可以在任何主持人有空的時候預約會議。當會議在主持人之間的平均分配不是那麼重要時,可使用此選項來最大化潛在會議預約數量。",
"rr_distribution_method_balanced_title": "負載平衡",
"rr_distribution_method_balanced_description": "系統會監控每位主持人的預約數量並與其他人進行比較,當某些主持人的預約量遠超過其他人時,系統會暫時停用這些主持人,以確保預約能夠均勻分配。",
"edit_team_member_weights": "編輯權重",
"weights_updated_successfully": "權重更新成功",
"require_confirmation_for_free_email": "僅對免費電子郵件供應商要求確認(例如 @gmail.com、@outlook.com)",
"exclude_emails_that_contain": "排除包含...的電子郵件",
"require_emails_that_contain": "要求電子郵件包含...",
@@ -2917,11 +2919,13 @@
"dry_run_mode_active": "您正在進行測試預約。",
"icon_showcase": "圖示展示",
"icons_showcase": "圖示展示",
"feature_currently_disabled": "此功能目前已停用",
"choose_an_option": "選擇一個選項",
"enter_option_value": "輸入選項值",
"remove_option": "移除選項",
"managed_users": "受管理使用者",
"managed_users_description": "查看由您的 OAuth 用戶端建立的所有受管理使用者",
"select_oAuth_client": "選擇 OAuth 用戶端",
"on_every_instance": "在每個執行個體上",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 請在此處新增您的字串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -1,4 +1,5 @@
import type { DehydratedState } from "@tanstack/react-query";
import type { EmbedProps } from "app/WithEmbedSSR";
import type { GetServerSideProps } from "next";
import { encode } from "querystring";
import type { z } from "zod";
@@ -18,7 +19,6 @@ import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import type { UserProfile } from "@calcom/types/UserProfile";
import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect";
import type { EmbedProps } from "app/WithEmbedSSR";
import { ssrInit } from "@server/lib/ssr";
@@ -23,7 +23,6 @@ describe("pagesAndRewritePaths", () => {
"video",
"workflows",
"403",
"404",
"500",
"bookings",
"event-types",
+101 -2
View File
@@ -2389,6 +2389,8 @@
"description": "The number of items to return",
"example": 10,
"schema": {
"minimum": 1,
"maximum": 1000,
"type": "number"
}
},
@@ -2399,6 +2401,7 @@
"description": "The number of items to skip",
"example": 0,
"schema": {
"minimum": 0,
"type": "number"
}
},
@@ -2581,6 +2584,28 @@
"schema": {
"type": "number"
}
},
{
"name": "sortStart",
"required": false,
"in": "query",
"description": "Sort results by their start time in ascending or descending order.",
"example": "?sortStart=asc OR ?sortStart=desc",
"schema": {
"enum": ["asc", "desc"],
"type": "string"
}
},
{
"name": "sortEnd",
"required": false,
"in": "query",
"description": "Sort results by their end time in ascending or descending order.",
"example": "?sortEnd=asc OR ?sortEnd=desc",
"schema": {
"enum": ["asc", "desc"],
"type": "string"
}
}
],
"responses": {
@@ -2681,6 +2706,80 @@
"tags": ["Orgs / Users / OOO"]
}
},
"/v2/organizations/{orgId}/ooo": {
"get": {
"operationId": "OrganizationsUsersOOOController_getOrganizationUsersOOO",
"summary": "Get all OOO entries of org users",
"parameters": [
{
"name": "orgId",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
},
{
"name": "take",
"required": false,
"in": "query",
"description": "The number of items to return",
"example": 10,
"schema": {
"type": "number"
}
},
{
"name": "skip",
"required": false,
"in": "query",
"description": "The number of items to skip",
"example": 0,
"schema": {
"type": "number"
}
},
{
"name": "sortStart",
"required": false,
"in": "query",
"description": "Sort results by their start time in ascending or descending order.",
"example": "?sortStart=asc OR ?sortStart=desc",
"schema": {
"enum": ["asc", "desc"],
"type": "string"
}
},
{
"name": "sortEnd",
"required": false,
"in": "query",
"description": "Sort results by their end time in ascending or descending order.",
"example": "?sortEnd=asc OR ?sortEnd=desc",
"schema": {
"enum": ["asc", "desc"],
"type": "string"
}
},
{
"name": "email",
"required": false,
"in": "query",
"description": "Filter ooo entries by the user email address. user must be within your organization.",
"example": "example@domain.com",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"tags": ["Orgs / Users / OOO"]
}
},
"/v2/organizations/{orgId}/webhooks": {
"get": {
"operationId": "OrganizationsWebhooksController_getAllOrganizationWebhooks",
@@ -7312,7 +7411,7 @@
"type": {
"type": "string",
"example": "name",
"description": "only allowed value for type is `name`"
"description": "only allowed value for type is `name`. Used for having 1 booking field for both first name and last name."
},
"label": {
"type": "string"
@@ -8038,7 +8137,7 @@
"boolean"
],
"example": "name",
"description": "only allowed value for type is `name`",
"description": "only allowed value for type is `name`. Used for having 1 booking field for both first name and last name.",
"default": "name"
},
"label": {
+1 -1
View File
@@ -15,7 +15,7 @@ Once your account is created, the next step is to create an OAuth client. This a
2. Add an OAuth client.
1. Name: anything is fine. You can use your company name or your website.
2. Redirect URIs: Used to validate origin of requests - your website's URLs from which atoms are allowed to make requests to our API. Supports wildcard syntax where "*" would support any origin, and "*app.com" would support "example.app.com", and "https//example.com*" where it would support any origin from that domain.
3. Booking, reschedule and cancel URLs: URLs of pages where users land after a successful booking or if they want to reschedule or cancel a booking. We will pass information in the URL when redirecting to your pages and you will use our hooks and components to implement the pages. See [this guide](/platform/booking-redirects).
3. Booking, reschedule and cancel URLs: URLs of pages where users land after a successful booking or if they want to reschedule or cancel a booking. We will pass information in the URL when redirecting to your pages and you will use our hooks and components to implement the pages. See [this guide](/platform/guides/booking-redirects).
4. Permissions - most likely you need all enabled:
1. Event type: event type is a user event that others can book. For example, Alice is a language teacher and she has an event type “30 minutes Italian lesson” that others can then book.
2. Booking: When Bob books the Italian lesson, a booking is created and shows up on the calendar that Alice connected using atoms.
+1 -1
View File
@@ -22,7 +22,7 @@
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"dotenv": "^16.3.1",
"eslint": "^8",
"eslint": "^8.34.0",
"eslint-config-next": "14.0.4",
"postcss": "^8",
"tailwindcss": "^3.3.0",
+130 -164
View File
@@ -44,169 +44,135 @@
"icon": "github",
"url": "https://github.com/calcom/cal.com"
},
"feedback": {
"thumbsRating": true,
"suggestEdit": true
},
"modeToggle": {
"default": "light",
"isHidden": true
},
"topbarCtaButton": {
"name": "Login",
"url": "https://app.cal.com/auth/login"
},
"anchors": [
{
"name": "Developer",
"icon": "code",
"url": "https://cal.com/docs"
},
{
"name": "GitHub",
"icon": "github",
"url": "https://github.com/calcom/cal.com"
},
{
"name": "Blog",
"icon": "newspaper",
"url": "https://cal.com/blog"
}
],
"navigation": [
{
"group": "Documentation",
"pages": [
"welcome",
{
"group": "Quick Start",
"icon": "play",
"iconType": "solid",
"pages": [
"quick-start/quickstart",
"quick-start/sign-up",
"quick-start/complete-onboarding",
"quick-start/customization",
"quick-start/calendar"
]
},
{
"group": "Event Types",
"icon": "calendar",
"iconType": "solid",
"pages": [
"event-types/event-types",
"event-types/booking-questions",
"event-types/add-events",
"event-types/managed-events",
"event-types/adding-zoom",
"event-types/booking-frequency",
"event-types/round-robin",
"event-types/dynamic",
"event-types/collective-events",
"event-types/create-first-event",
"event-types/create-recurring",
"event-types/create-secret",
"event-types/custom-name",
"event-types/custom-time-slots",
"event-types/disable-confirmation",
"event-types/disable-guests",
"event-types/display-issues",
"event-types/dynamic",
"event-types/event-buffer",
"event-types/hide-notes",
"event-types/how-to-add-location",
"event-types/how-to-receive-payments",
"event-types/how-to-requires",
"event-types/how-to-set-buffer",
"event-types/how-to-time-slot-intervals",
"event-types/limit-future-bookings",
"event-types/location-of-event",
"event-types/min-notice",
"event-types/multiple-durations",
"event-types/offer-seats",
"event-types/recurring-events",
"event-types/require-criteria",
"event-types/secret-events",
"event-types/setup-location",
"event-types/single-use",
"event-types/split-name",
"event-types/sponsor",
"event-types/symbolic-issues"
]
},
{
"group": "Availabilities",
"icon": "magnifying-glass",
"iconType": "solid",
"pages": [
"availabilities/date-overrides",
"availabilities/multiple-schedules",
"availabilities/multiple-slots"
]
},
{
"group": "Bookings",
"icon": "calendar-check",
"iconType": "solid",
"pages": [
"bookings/no-show",
"bookings/paid-bookings",
"bookings/prefill-fields"
]
},
{
"group": "Embedding",
"icon": "code",
"iconType": "solid",
"pages": [
"embedding/adding-embed",
"embedding/adding-slots",
"embedding/embed-events",
"embedding/embed-instructions",
"embedding/embed-snippet-generator",
"embedding/prefill-booking-form-embed"
]
},
{
"group": "Auth and Provisioning",
"icon": "user-lock",
"iconType": "solid",
"pages": [
"auth-and-provision/scim-okta",
"auth-and-provision/oidc-with-okta"
]
},
{
"group": "Apps and Integrations",
"icon": "code-branch",
"iconType": "solid",
"pages": [
"apps-and-integrations/apps",
"apps-and-integrations/app-store",
"apps-and-integrations/adding-first-app",
"apps-and-integrations/assign-people",
"apps-and-integrations/build-greeter-app",
"apps-and-integrations/how-to-build-an-app",
"apps-and-integrations/syncing-third-party"
]
},
{
"group": "Developers",
"icon": "terminal",
"iconType": "solid",
"pages": [
"apps-and-integrations/oauth",
"apps-and-integrations/open-source",
"webhooks"
]
}
]
}
],
"footerSocials": {
"x": "https://x.com/calcom",
"github": "https://github.com/calcom",
"linkedin": "https://www.linkedin.com/company/cal-com"
{
"name": "Blog",
"icon": "newspaper",
"url": "https://cal.com/blog"
}
],
"navigation": [
{
"group": "Documentation",
"pages": [
"welcome",
{
"group": "Quick Start",
"icon": "play",
"iconType": "solid",
"pages": [
"quick-start/quickstart",
"quick-start/sign-up",
"quick-start/complete-onboarding",
"quick-start/customization",
"quick-start/calendar"
]
},
{
"group": "Event Types",
"icon": "calendar",
"iconType": "solid",
"pages": [
"event-types/event-types",
"event-types/booking-questions",
"event-types/add-events",
"event-types/managed-events",
"event-types/adding-zoom",
"event-types/booking-frequency",
"event-types/round-robin",
"event-types/dynamic",
"event-types/collective-events",
"event-types/create-first-event",
"event-types/create-recurring",
"event-types/create-secret",
"event-types/custom-name",
"event-types/custom-time-slots",
"event-types/disable-confirmation",
"event-types/disable-guests",
"event-types/display-issues",
"event-types/dynamic",
"event-types/event-buffer",
"event-types/hide-notes",
"event-types/how-to-add-location",
"event-types/how-to-receive-payments",
"event-types/how-to-requires",
"event-types/how-to-set-buffer",
"event-types/how-to-time-slot-intervals",
"event-types/limit-future-bookings",
"event-types/location-of-event",
"event-types/min-notice",
"event-types/multiple-durations",
"event-types/offer-seats",
"event-types/recurring-events",
"event-types/require-criteria",
"event-types/secret-events",
"event-types/setup-location",
"event-types/single-use",
"event-types/split-name",
"event-types/sponsor",
"event-types/symbolic-issues"
]
},
{
"group": "Availabilities",
"icon": "magnifying-glass",
"iconType": "solid",
"pages": [
"availabilities/date-overrides",
"availabilities/multiple-schedules",
"availabilities/multiple-slots"
]
},
{
"group": "Bookings",
"icon": "calendar-check",
"iconType": "solid",
"pages": ["bookings/no-show", "bookings/paid-bookings", "bookings/prefill-fields"]
},
{
"group": "Embedding",
"icon": "code",
"iconType": "solid",
"pages": [
"embedding/adding-embed",
"embedding/adding-slots",
"embedding/embed-events",
"embedding/embed-instructions",
"embedding/embed-snippet-generator",
"embedding/prefill-booking-form-embed"
]
},
{
"group": "Auth and Provisioning",
"icon": "user-lock",
"iconType": "solid",
"pages": ["auth-and-provision/scim-okta", "auth-and-provision/oidc-with-okta"]
},
{
"group": "Apps and Integrations",
"icon": "code-branch",
"iconType": "solid",
"pages": [
"apps-and-integrations/apps",
"apps-and-integrations/app-store",
"apps-and-integrations/adding-first-app",
"apps-and-integrations/assign-people",
"apps-and-integrations/build-greeter-app",
"apps-and-integrations/how-to-build-an-app",
"apps-and-integrations/syncing-third-party"
]
},
{
"group": "Developers",
"icon": "terminal",
"iconType": "solid",
"pages": ["apps-and-integrations/oauth", "apps-and-integrations/open-source", "webhooks"]
}
]
}
],
"footerSocials": {
"x": "https://x.com/calcom",
"github": "https://github.com/calcom",
"linkedin": "https://www.linkedin.com/company/cal-com"
}
}
+4
View File
@@ -2876,6 +2876,8 @@ checksums:
rr_distribution_method_availability_description: 0fc05c527e1307e0c20165f0cefe2ed4
rr_distribution_method_balanced_title: f13fe5c1c5e35ad5b2e0139df5c3e62f
rr_distribution_method_balanced_description: 23fd5a95ac9d9f6283883180ba30db32
edit_team_member_weights: 66ba6fc048187689fc71b00be1d1ca0f
weights_updated_successfully: e5ad5ff71665572c8512f1cb4112359c
require_confirmation_for_free_email: 669a3c050cc065e85df79e0e7f12bbfd
exclude_emails_that_contain: d43255c873c75c8fe1adcb8f8b7af914
require_emails_that_contain: 54623674a8bf704fd44299c59eb31363
@@ -2919,10 +2921,12 @@ checksums:
dry_run_mode_active: 921ba1004a593504ba9719886864d90e
icon_showcase: 0879657c77461a19cafbdaa1e168b95e
icons_showcase: 8771aa0011d8ffefbe9370312152ef6a
feature_currently_disabled: 3d4991fb6097adb96e8e2afca1e44b7e
choose_an_option: 1c57b133afef47a60666d5d37b9b321e
enter_option_value: 03bdf1e9fd1bdba6f895bf6f85d2fd28
remove_option: 42e52c46cc51e56ebb5540bfb6f19b95
managed_users: c6db1c3bb5c9b451471ca9b8d7dc8366
managed_users_description: 45fea5321847b0c8ee72ce33684c1283
select_oAuth_client: 0ebdcf296c0f68eca626cdb153204607
on_every_instance: db0e9e92055a38572fffa40f44440035
ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS: 18323f2f3fabb169a7cb50ff62433850
@@ -87,8 +87,7 @@ export const AlbyPaymentComponent = (props: IAlbyPaymentComponentProps) => {
<p className="text-sm">Click or scan the invoice below to pay</p>
<Link
href={`lightning:${paymentRequest}`}
className="inline-flex items-center justify-center rounded-2xl rounded-md border border-transparent p-2 bg-white
font-medium text-black shadow-sm hover:brightness-95 focus:outline-none focus:ring-offset-2 p-2">
className="inline-flex items-center justify-center rounded-2xl rounded-md border border-transparent bg-white p-2 font-medium text-black shadow-sm hover:brightness-95 focus:outline-none focus:ring-offset-2">
<QRCode size={192} value={paymentRequest} />
</Link>
@@ -34,6 +34,7 @@ import ga4_config_json from "./ga4/config.json";
import { metadata as giphy__metadata_ts } from "./giphy/_metadata";
import { metadata as googlecalendar__metadata_ts } from "./googlecalendar/_metadata";
import { metadata as googlevideo__metadata_ts } from "./googlevideo/_metadata";
import granola_config_json from "./granola/config.json";
import gtm_config_json from "./gtm/config.json";
import horizon_workrooms_config_json from "./horizon-workrooms/config.json";
import { metadata as hubspot__metadata_ts } from "./hubspot/_metadata";
@@ -135,6 +136,7 @@ export const appStoreMetadata = {
giphy: giphy__metadata_ts,
googlecalendar: googlecalendar__metadata_ts,
googlevideo: googlevideo__metadata_ts,
granola: granola_config_json,
gtm: gtm_config_json,
"horizon-workrooms": horizon_workrooms_config_json,
hubspot: hubspot__metadata_ts,
@@ -34,6 +34,7 @@ export const apiHandlers = {
giphy: import("./giphy/api"),
googlecalendar: import("./googlecalendar/api"),
googlevideo: import("./googlevideo/api"),
granola: import("./granola/api"),
gtm: import("./gtm/api"),
"horizon-workrooms": import("./horizon-workrooms/api"),
hubspot: import("./hubspot/api"),

Some files were not shown because too many files have changed in this diff Show More