Compare commits

...
93 Commits
Author SHA1 Message Date
Alex van Andel 56a2b66190 v2.6.7
release.yml / release (push) Canceled after 0s
2023-03-06 23:53:04 +00:00
Carina WollendorferandCarinaWolli a0f55707e2 make "text" the default value (#7543)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-03-06 23:27:29 +00:00
Alex van Andel b247075a19 Revert "Update CODEOWNERS"
This reverts commit 25ec5313fb.
2023-03-06 23:18:45 +00:00
Alex van Andel 25ec5313fb Update CODEOWNERS
Add kodiakhq as reviewer for translations.
2023-03-06 23:16:17 +00:00
Efraín Rochín 33f35e77fd Fix/incorrect timezone on booking success (#7544)
* set the selected timezone to formatToLocalizedTimezone function

* remove console.logs
2023-03-06 23:12:43 +00:00
Benjamin BabicandPeer Richelsen 69808bb9a4 1172 booking uid design update (#7537)
* booking/[uid] additional fields design update

* add host badge to the who field

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-03-06 22:45:40 +00:00
GitStart-Cal.comandgitstart-calcom 18b6a93476 Fix: Filter Timezones by cities (#7540)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-03-06 17:52:20 +00:00
Peer Richelsen e627cc51fd max-height for description + scroll bar, removing description icon (#7535)
* max-height for description, scroll

* added nicer scrollbar

* removing info icon for more description space
2023-03-06 16:44:31 +00:00
Peer Richelsen b349def58b Update README.md 2023-03-06 15:49:22 +01:00
github-actions[bot]andCrowdin Bot 6a6faf30ff New Crowdin translations by Github Action (#7534)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-03-06 14:50:58 +01:00
Alex van AndelandPeer Richelsen 5616dba4db Minor typo fixups :pray (#7530)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-03-06 13:46:21 +00:00
bca1a1b86c New Crowdin translations by Github Action (#7522)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-03-06 14:20:14 +01:00
Julius MarmingeandAlex van Andel ddf7a234ab use inner context type (#7449)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-03-06 13:17:16 +00:00
Nafees NazikandAlex van Andel fa7260d72a feat: use next transpile packages feature (#7527)
* feat: use next transpile packages feature

* Also remove next-transpile-modules from storybook (+ remove experimental)

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-03-06 13:10:39 +00:00
Joe Au-YeungandAlex van Andel 5f9f6ef025 Only show Google Meet dependency when GCal is not set as destination calendar (#7442)
* Query for destination calendar

* Only display Meet warning when destination calendar is not GCal

* Add conditional

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-03-06 12:37:18 +00:00
3d41c64d81 feat: error message for invalid variables in custom event name (#7426)
* feat: add custom validate name util

* refactor: separate custom event type modal into a
different component

* feat: add validation to zod

* chore: add i18n key

* feat: add dynamic imports

* fix: padding

* Omit cache-hit exit 1, assuming it'll fail regardless

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-03-06 12:14:54 +00:00
sean-brydonandAlex van Andel 8a9b985760 Bulk edit locations when default conferencing app is set (#7520)
* Bulk edit default locations

* Update [category]

* Open modal on none link events

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-03-06 10:53:35 +00:00
Udit Takkar c8956680ad fix: use slug in options (#7523)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-03-06 10:47:57 +00:00
Udit Takkar c2668acd33 fix: use recordingId to check loading (#7524)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-03-06 10:39:54 +00:00
Peer Richelsen 89bf8321ba Update CONTRIBUTING.md 2023-03-05 23:00:57 +01:00
github-actions[bot]andCrowdin Bot da625f1527 New Crowdin translations by Github Action (#7516)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-03-05 17:57:16 +01:00
Udit TakkarandAlex van Andel 2dddd4ce77 feat: create trpc route to fetch download link (#7495)
* feat: create trpc route to fetch download link

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: removing file, adding isdownloadable logix

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* chore

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: type error

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* Some performance enhancements and better error logging

* Add error message here too

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-03-05 12:59:07 +00:00
Leo Giovanetti cc1d606ba8 Implementing CAL-1173 (#7509)
* Implementation

* Added check when no pass is provided

* Refactoring login url to function
2023-03-05 02:09:45 +00:00
Alex van Andel 262c8cf37c Removed @next/font (#7514) 2023-03-04 20:07:50 +00:00
Alex van Andel 4366405b21 Updates to yarn.lock / removed outdated deps from downstream 2023-03-04 16:23:15 +00:00
Hariom BalharaandAlex van Andel 07399974ce Small Embed Fixes (#7513)
* Fix bg beyond rounded border in embed

* Consider margin as well for mainElement height

* Fix loader positioning

* Give better name to embed artifacts

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-03-04 16:11:04 +00:00
Alex van Andel 9d7cd73c7f Fixed lockfile, bumped nextjs to v13.2.3 2023-03-04 15:10:13 +00:00
1551a293c5 Made Skeleton Loader for Availibilty Page look more like actual content of the page (#7415)
Co-authored-by: Raunak Singh Jolly <raunak.singh@oriserve.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
2023-03-03 18:08:10 -07:00
47e948fbbc Fixes formatting issues with lists in event type description and bio (#7505)
* fix ul and ol in editor

* fix imports

* disable list for team about section

* fix event type description in list

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-03-04 00:20:13 +01:00
Omar López c8b01f6992 Revert "fix: use only slug (#7504)" (#7506)
This reverts commit f3143d99ee.
2023-03-03 15:44:14 -07:00
Udit Takkar f3143d99ee fix: use only slug (#7504)
* fix: use only slug

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* chore

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: create slug key

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-03-03 15:43:26 -07:00
github-actions[bot] 61a5c3a3b5 New Crowdin translations by Github Action (#7467) 2023-03-03 17:41:00 +01:00
Carina WollendorferandCarinaWolli 056edd8ec2 fix event type options (#7473)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-03-03 09:36:23 -07:00
Hariom Balhara d8387835a8 Well, you can have 0 attendees in a booking (#7494) 2023-03-03 09:35:58 -07:00
Nafees Nazik 26ea249481 Fix: email encoding bug (#7490)
* fix: existing decode Uri

* feat: unescape characters in base email

* fix: encoding in booking page
2023-03-03 09:35:11 -07:00
2d26d73eb3 Fix/DST issues (#7462)
* use the organizer timezone and invitee timezone to show slots

* type fixes

* set start date on selected TZ on booking payload

* add considerations when only the invitee is on DTS

* Apply suggestions from code review

Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Omar López <zomars@me.com>

* fixes typo

* Apply suggestions from code review

Co-authored-by: alannnc <alannnc@gmail.com>

* cleaning

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2023-03-03 09:33:16 -07:00
Hariom Balhara b8b24c04dc We dont support button html (#7500) 2023-03-03 09:30:44 -07:00
Alex van Andel 62dcd6612c Removed some of the UTC logic which shouldn't be there (#7425)
* Removed some of the UTC logic which shouldn't be there

* Upgrade rhf, remove SSR on [schedule] page

* Fix potential bug with date

* Amend schedule trpc call & don't show edit availability if id is undefined

* type fix

* Order date overrides in list by date

* Changed logic to check for unavailable

* Fix bug report spotted by @CarinaWolli

I can save date overrides for march 1st and 2nd (my date/time when saving was march 1st at 11PM GMT-5). It is saved successfully in the database however the date overrides don't show up in the list.

That's why:

- new Date() is utc so it is already march 2nd
- override.date has always timestamp 00:00 so it is also smaller than the utc time which was in my case 2023-03-02T04:00

* Actual fix, gotta remember it's the schedule tz
2023-03-03 14:02:02 +01:00
zomars 295748adca Update yarn.lock 2023-03-02 14:00:21 -07:00
Omar López 6e93224c9b Tree shake icons (#7051)
* Attempt to tree shake icons

* Tree shaking icons
2023-03-02 13:56:10 -07:00
4d8198d113 Filter Timezones by cities (#7118)
* Filter Timezones by cities

* Update yarn.lock

* Removes large endpoint from batching

* Adds caching for large response

* Updates test snapshots

---------

Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2023-03-02 13:42:41 -07:00
Nafees Nazik 5fe0ca7913 fix: escape markdown in users OG image (#7474)
* fix: escape markdown in bio

* chore: add remark and strip-markdown package

* feat: strip markdown

* fix: remove first commit
2023-03-02 12:55:57 -07:00
cfb625e934 Editor for event type description (#7450)
* add editor for event type description

* enable same things for markdownIt

* show links in blue

* fix placeholder design

* format description for event list

* limit event descript ot 4 lines in list

* shorten event-type description whenever needed

* add editor to create event type dialog

* fix link title in event type list

* Fix overwriting users column when saving event types (#7445)

* Only overwrite user column when present

* Clean up

* Merge branch 'main' into feat/editor-event-type-description

* Merge branch 'main' into feat/editor-event-type-description

# Conflicts:
#	apps/web/pages/settings/my-account/profile.tsx

* Linting

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2023-03-02 12:55:25 -07:00
Hariom Balhara e12b21a73c Check app-store types on CI (#7190)
* Check app-store types on CI

* Fix ESLINt error
2023-03-02 11:58:31 -07:00
Warrenandzomars 841346c5f2 Install DeploySentinel Playwright reporter (#7405)
* install reporter

* Update e2e-app-store.yml

---------

Co-authored-by: zomars <zomars@me.com>
2023-03-02 11:25:30 -07:00
zomars 1270742078 Linting 2023-03-02 11:23:41 -07:00
517cfde5b8 Feature/ Manage Booking Questions (#6560)
* WIP

* Create Booking Questions builder

* Renaming things

* wip

* wip

* Implement Add Guests and other fixes

* Fixes after testing

* Fix wrong status code 404

* Fixes

* Lint fixes

* Self review comments addressed

* More self review comments addressed

* Feedback from zomars

* BugFixes after testing

* More fixes discovered during review

* Update packages/lib/hooks/useHasPaidPlan.ts

Co-authored-by: Omar López <zomars@me.com>

* More fixes discovered during review

* Update packages/ui/components/form/inputs/Input.tsx

Co-authored-by: Omar López <zomars@me.com>

* More fixes discovered during review

* Update packages/features/bookings/lib/getBookingFields.ts

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* More PR review fixes

* Hide label using labelSrOnly

* Fix Carinas feedback and implement 2 workflows thingy

* Misc fixes

* Fixes from Loom comments and PR

* Fix a lint errr

* Fix cancellation reason

* Fix regression in edit due to name conflict check

* Update packages/features/form-builder/FormBuilder.tsx

Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>

* Fix options not set when default value is used

* Restoring reqBody to avoid uneeded conflicts with main

* Type fix

* Update apps/web/components/booking/pages/BookingPage.tsx

Co-authored-by: Omar López <zomars@me.com>

* Update packages/features/form-builder/FormBuilder.tsx

Co-authored-by: Omar López <zomars@me.com>

* Update apps/web/components/booking/pages/BookingPage.tsx

Co-authored-by: Omar López <zomars@me.com>

* Apply suggestions from code review

Co-authored-by: Omar López <zomars@me.com>

* Show fields but mark them disabled

* Apply suggestions from code review

Co-authored-by: Omar López <zomars@me.com>

* More comments

* Fix booking success page crash when a booking doesnt have newly added required fields response

* Dark theme asterisk not visible

* Make location required in zodSchema as was there in production

* Linting

* Remove _metadata.ts files for apps that have config.json

* Revert "Remove _metadata.ts files for apps that have config.json"

This reverts commit d79bdd336cf312a30a8943af94c059947bd91ccd.

* Fix lint error

* Fix missing condition for samlSPConfig

* Delete unexpectedly added file

* yarn.lock change not required

* fix types

* Make checkboxes rounded

* Fix defaultLabel being stored as label due to SSR rendering

* Shaved 16kb from booking page

* Explicit types for profile

* Show payment value only if price is greater than 0

* Fix type error

* Add back inferred types as they are failing

* Fix duplicate label on number

---------

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
2023-03-02 11:15:28 -07:00
Hariom BalharaandAlex van Andel 51bf613621 Add hariom as codeowner for Routing Forms (#7469)
* Add hariom as codeowner for Routing Forms

* Update CODEOWNERS

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-03-02 16:22:09 +00:00
sean-brydonandAlex van Andel f4c3ab6431 Availability uiux improvements (#7466)
* Delete modal + button on [schedule]

* Update to V2 component

* Default Icon

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-03-02 16:20:02 +00:00
Alex van Andel e8acc3e500 Apparently we must use paths-ignore instead of paths 2023-03-02 14:51:24 +00:00
Alex van Andel a6ba0f5b57 Exclude **.md/CODEOWNERS files from triggering pr.yml wf 2023-03-02 14:36:33 +00:00
Alex van Andel b82d924f03 Update CODEOWNERS
Add @calcom/core to all single person claims
2023-03-02 14:16:25 +00:00
Hariom Balhara 1cdb51f424 Fix tests flakiness in embed (#7468) 2023-03-02 13:58:46 +00:00
Joe Au-YeungandHariom Balhara aca0dc1b01 Fix webhook e2e test (#7455)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-03-02 12:13:55 +00:00
zomars e5e8c33c34 Adds missing cleanups to e2e tests 2023-03-01 16:35:28 -07:00
github-actions[bot]andCrowdin Bot c76847a8f1 New Crowdin translations by Github Action (#7453)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-03-01 22:58:06 +01:00
Joe Au-Yeung 7b1ced914b Fix overwriting users column when saving event types (#7445)
* Only overwrite user column when present

* Clean up
2023-03-01 10:16:04 -07:00
zomars a6cd173910 Update playwright.config.ts 2023-02-28 16:43:44 -07:00
zomars dcd39c3f04 Update pr.yml 2023-02-28 16:42:13 -07:00
Efraín Rochínandzomars aa0a1fcc95 upgrade nextjs to 13.2 version and replace @next/font for in built ne… (#7440)
* upgrade nextjs to 13.2 version and replace @next/font for in built next/font

* Sync packages

* Update yarn.lock

* Updates edge configs

---------

Co-authored-by: zomars <zomars@me.com>
2023-02-28 16:38:46 -07:00
Udit Takkar 76cf352bce fix: set organiser email to external id (#7406)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-02-28 14:53:20 -07:00
github-actions[bot]andCrowdin Bot 86ab19be30 New Crowdin translations by Github Action (#7409)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-02-28 14:52:08 -07:00
Julius Marmingeandzomars e4b766b9d6 refactor: cleanup use of rawInput and pipe mdwr in trpc router (#7422)
* cleanup use of rawInput and pipe mdwr

* Update ssg.ts

* Update ssg.ts

* Update viewer.tsx

---------

Co-authored-by: zomars <zomars@me.com>
2023-02-28 14:40:19 -07:00
Efraín Rochínandzomars 2529f71770 Upgrade prisma to 4.11.0 version (#7438)
* Upgrade prisma to 4.11.0 version

* Update yarn.lock

* Syncs packages

* Update yarn.lock

* Removes deprecations

---------

Co-authored-by: zomars <zomars@me.com>
2023-02-28 14:15:50 -07:00
zomars 9ea0f02c93 Update production-build.yml 2023-02-28 14:14:24 -07:00
zomars fb1828c8f9 GH actions improvements
commit 3ea36a4b9626c99bf343caf8bacb756a09699990
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 13:26:58 2023 -0700

    Revert "One more time"

    This reverts commit fd8b559a135820567f40dbb47a4b4dfc2ef44755.

commit fd8b559a135820567f40dbb47a4b4dfc2ef44755
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 13:00:49 2023 -0700

    One more time

commit e8fe71d795a74c00eb59139389df454a2ff8d807
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 12:40:28 2023 -0700

    Fix?

commit 84517e6ddbca2248b72d6cd912c2c32f9efb743c
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 12:38:20 2023 -0700

    Revert "Consolitades setup step"

    This reverts commit 5e8d1983cc5ac309fa6875a2fee77aa6287b7151.

commit 5e8d1983cc5ac309fa6875a2fee77aa6287b7151
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 12:34:52 2023 -0700

    Consolitades setup step

commit 8e732c4cf9662daf84ab8c3acf702292fb6dd4e5
Merge: 00485a9d3 12f2c1f43
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 12:25:18 2023 -0700

    Merge remote-tracking branch 'upstream/main'

commit 00485a9d3313d415906141493febdbf56dff649d
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 12:08:29 2023 -0700

    Update action.yml

commit 95c30af12f91b4cc310f0a61b5d584591bebc3ee
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 12:05:42 2023 -0700

    Update action.yml

commit e56d8a73e25c664afb0066aedc792250abc3b86a
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 11:09:21 2023 -0700

    Removed setup

commit ff83d88ac6cd7338204ce93a84c285189bd6d352
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 10:54:30 2023 -0700

    Is this redundant?

commit b40b65e8da1238abe097913a3422f24be342c27e
Merge: 774ba24ae 29e9bd0ee
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 10:53:25 2023 -0700

    Merge remote-tracking branch 'upstream/main'

commit 774ba24aeb1d0fde55963fe6aecdf3fddddfa497
Merge: 450812044 9c794bac1
Author: zomars <zomars@me.com>
Date:   Tue Feb 28 10:18:53 2023 -0700

    Merge remote-tracking branch 'upstream/main'

commit 450812044137b665eb90422daf58e8e042cf1492
Author: Omar López <zomars@me.com>
Date:   Tue Feb 28 07:35:02 2023 -0700

    Update action.yml

commit c1d6937f4c22039138284985a4da723ac4ea7803
Author: Omar López <zomars@me.com>
Date:   Tue Feb 28 07:19:52 2023 -0700

    Update action.yml

commit de3ec8b5d8a94ae3cf9aa1e7f3accdf5a8101159
Author: Omar López <zomars@me.com>
Date:   Tue Feb 28 07:05:39 2023 -0700

    Update action.yml

commit b6f6382f1554394ea743acc92824fd7e222ee97d
Author: Omar López <zomars@me.com>
Date:   Tue Feb 28 06:30:55 2023 -0700

    Update action.yml

commit e79c03c7a31930dd99ccd9f5cdc156b808c869a5
Author: Omar López <zomars@me.com>
Date:   Tue Feb 28 06:06:51 2023 -0700

    Update action.yml

commit 55cc76f526aefa83a8275b06ad3f55b6d201e8a6
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 21:09:05 2023 -0700

    Update pr.yml

commit ffc5d245613f1364e302a4c91e32490d8621b987
Merge: 2c41c16af 4eecd4220
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 21:03:22 2023 -0700

    Merge branch 'up-main'

commit 2c41c16afc2f1c4c0ad77f1064b7e1369146ac48
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 20:23:13 2023 -0700

    Updates webhook response

commit cc5ef15b431226d97d3128e8460e9b6f505cba44
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 20:11:35 2023 -0700

    Typefix

commit 50be9255cb8f839c2e8914e03a963c63b7dd5948
Author: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Date:   Mon Feb 27 15:45:40 2023 -0500

    Fix - add team members to emails (#7207)

    * On booking add team members & translation

    * Add team members to round robin create

    * Only update calendars on reschedule if there is a calendar reference

    * Send email on reschedules

    * Send team email on cancelled event

    * Add team members to calendar event description

    * Clean up

    * Convert other emails to organizer & teams

    * Type check fixes

    * More type fixes

    * Change organizer scheduled input to an object

    * early return updateCalendarEvent

    * Introduce team member type

    * Fix type errors

    * Put team members before attendees

    * Remove lodash cloneDeep

    * Update packages/core/EventManager.ts

    Co-authored-by: Omar López <zomars@me.com>

    * Remove booking select object

    * Revert "Remove booking select object"

    This reverts commit 9f121ff4ebbaec9a0643111f96c5337b930ddd5b.

    * Refactor email manager (#7270)

    Co-authored-by: zomars <zomars@me.com>

    * Type change

    * Remove conditional check for updateAllCalendarEvents

    ---------

    Co-authored-by: zomars <zomars@me.com>

commit c7d22a851c584600952e62f8a383a52bee1ad87e
Author: Omar López <zomars@me.com>
Date:   Mon Feb 27 12:39:26 2023 -0700

    Update viewer.tsx (#7428)

commit db5b802a157e4e4abdf5cb55149eb9a4d4e19a12
Author: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Date:   Mon Feb 27 13:47:21 2023 -0500

    Add destination calendar name to DestinationCalendarSelector (#6701)

    * Add destination calendar name

    * Type fix

    * Search through calendars only for destination calendar credential

    * Refactor get connected calendars

    * Clean up

    ---------

    Co-authored-by: zomars <zomars@me.com>

commit 27512b16d2e189826a7719cbf7cec1fd55ad419a
Author: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Date:   Mon Feb 27 02:24:43 2023 -0500

    Team Workflows (#7038)

    Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
    Co-authored-by: CarinaWolli <wollencarina@gmail.com>
    Co-authored-by: zomars <zomars@me.com>
    Co-authored-by: Peer Richelsen <peeroke@gmail.com>

commit 7b47956a34b9e474c9667ce0f1dbc9a45991a991
Author: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Date:   Sun Feb 26 00:45:05 2023 +0530

    feat: get country code from ip geolocation (#6880)

    * feat: get coutnry code from ip geolocation

    Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

    * fix: create new api route for fetching code

    Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

    * chore: replace city with country

    Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

    * refactor: create hook for country

    Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

    ---------

    Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

commit 658ccc651743c81ab08b4a8a5fecdfd3149ef518
Author: Esaú Morais <55207584+esau-morais@users.noreply.github.com>
Date:   Sat Feb 25 16:07:41 2023 -0300

    Update settings back button redirect link (#7403)

    * fix(schedule): close on click #7143

    * fix(EventSetupTab): validLocations length will always match validLocations length #7138

    * fix(SettingsLayout): go back to right route #7379

commit b5849ca30fde97a986cb697220b47712c24834e3
Author: Omar López <zomars@me.com>
Date:   Fri Feb 24 20:57:49 2023 -0700

    Zomars/cal 884 paid events not sending the link (#7318)

    * WIP

    * Sends correct emails for paid bookings

    * Update PaymentService.ts

    * Update webhook.ts

    * Update webhook.ts

commit a57369d521b3e0c278e72a4002842a2267a72636
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 17:56:55 2023 -0700

    Whitespace!!

commit 4d74fa89bf3967696ffd72191b387ad48cb29240
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 16:36:08 2023 -0700

    fix

commit fe1724accc81a46b12c1f6a3cdf1669908a4df42
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 16:23:24 2023 -0700

    Build fix

commit 0ce2c654cefb505bc0772cbd424c2d1887e009c1
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 16:17:48 2023 -0700

    LFG

commit d8be3316799ed7b032f9539e2c98d938872e7483
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 15:59:52 2023 -0700

    Please

commit d27f36f8896269f1b4083d4e5fb5443bfb86e49a
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 15:41:33 2023 -0700

    Pls

commit eb4d01eff2210a087bd69b0756784c2ad9b0647b
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 15:28:01 2023 -0700

    Test

commit c3fd449cfda8ba3c8a43718591f44a36fa95e4ae
Author: zomars <zomars@me.com>
Date:   Mon Feb 27 14:07:52 2023 -0700

    a

commit 21cc8834157c9cca6c6c49ea54f1b227a29264db
Author: zomars <zomars@me.com>
Date:   Sat Feb 25 09:35:42 2023 -0700

    Fingers crossed

commit f674d7f99b752b9a5cf6c167d10b744d2b7401af
Author: zomars <zomars@me.com>
Date:   Sat Feb 25 09:31:44 2023 -0700

    cache-build not skipping

commit 3cbbead2f2707488c53ac9f982aa8e704ff93e33
Author: zomars <zomars@me.com>
Date:   Sat Feb 25 09:16:16 2023 -0700

    One more time

commit 20672bfbc8f039ae5e699465bf7cab27dde8ba1b
Author: zomars <zomars@me.com>
Date:   Sat Feb 25 09:00:51 2023 -0700

    Caching is hard

commit a6ff8974a4571da10fac48f2555ed1f92f9b0b9d
Author: zomars <zomars@me.com>
Date:   Sat Feb 25 08:45:09 2023 -0700

    Running out of ideas

commit d7577926add336d806509231c35f41f5bf826f22
Author: zomars <zomars@me.com>
Date:   Sat Feb 25 08:31:54 2023 -0700

    Possible caching conflicts

commit 1cc8e6e8977f255f0095c0f03b5ad11195220a35
Author: zomars <zomars@me.com>
Date:   Sat Feb 25 08:05:35 2023 -0700

    e2e

commit 4636609be2efe6a028d1faae180f992376021862
Author: zomars <zomars@me.com>
Date:   Sat Feb 25 08:03:08 2023 -0700

    Cache fixes

commit 6db3b1afbe3d2b1ccecce6d83f5410f1ffa4453f
Author: zomars <zomars@me.com>
Date:   Sat Feb 25 07:49:50 2023 -0700

    Test

commit 8d2587bc3a72daaba4e74b2ee7878199451b3577
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 21:20:27 2023 -0700

    db-cache fixes

commit 0991c54f36a47d05b0baab036ab3ed1039c59154
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 21:07:13 2023 -0700

    Adds skipping steps

commit e8e437ba0178c5cab54613e1c1f1cac1cd0d73ad
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 21:04:38 2023 -0700

    Fixes

commit d5912767bd3a5b57c9150e0b851f51b2d9f6750b
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 20:48:15 2023 -0700

    Cache fixes

commit 88aa6d560ecb9e8f4603344454fabd68bc3fa990
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 20:17:23 2023 -0700

    Fix

commit 594d8b5efe0f320de13862dba552e8fffb9d2cde
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 20:17:23 2023 -0700

    Fix

commit 0b29558a6c248dce961cb462a46f5047ef917545
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 20:12:45 2023 -0700

    Fix

commit f80259232c6b6f58556d2f39212329d87c3468ca
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 20:09:53 2023 -0700

    Fix

commit 73b02c2c920e2f0f9996d1cf6580fb4195ec69e4
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 20:04:07 2023 -0700

    Fix

commit 4c375c89523651779b47f0fc53e0effb78300a08
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 19:59:15 2023 -0700

    FFS

commit fbe8eb969f4a22840b8a518ee87892b488964638
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 19:56:59 2023 -0700

    Fixes

commit 619b26de0205c4094966fe36b40ca37def743309
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 19:53:39 2023 -0700

    Fix

commit 737a29b80a53b0fdda953fa9c9068da60b018fcb
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 19:51:38 2023 -0700

    Fix

commit a9cf1fd3830306c246283debfb8654f011053688
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 19:49:20 2023 -0700

    Another fix

commit 0770c5b8141dc819bdfb065ba41793edfb9660d2
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 19:45:41 2023 -0700

    Fix

commit efb9d0ccd48446ddf66eb84bd45dedb36132e025
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 19:43:42 2023 -0700

     Fix env setter

commit 5cda52f1f95cbfcaf34f8b196101834e6adcda1a
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 19:23:11 2023 -0700

    Add env-cache

commit ad8e36891e3adb9621202a054e188727f9a2beac
Author: zomars <zomars@me.com>
Date:   Fri Feb 24 18:56:27 2023 -0700

    Create env file workflow
2023-02-28 13:52:50 -07:00
Alex van Andel 12f2c1f431 Negate node_modules to decrease the file count 2023-02-28 19:06:12 +00:00
zomars 29e9bd0ee7 Run cached installs
Should still be faster. Ensures prisma client is up to date.
2023-02-28 10:33:01 -07:00
zomars 9c794bac1a Only match exact key 2023-02-28 10:12:55 -07:00
zomars feca7c23fb Removes partial matching for DB 2023-02-28 10:03:13 -07:00
Omar López 31599cdd26 Fixes prod build hashing
https://github.com/actions/cache/issues/1125#issuecomment-1447688501
2023-02-28 07:38:28 -07:00
Alex van Andel e018eb9ebe Omit cache-hit exit 1, assuming it'll fail regardless 2023-02-28 11:40:12 +00:00
Alex van Andel a3f3f1f263 Match env file exactly without restore-keys 2023-02-28 11:30:54 +00:00
d19eaae5b6 Solves Github actions caching issues (#7431)
* Create env file workflow

* Add env-cache

* Fix env setter

* Fix

* Another fix

* Fix

* Fix

* Fixes

* FFS

* Fix

* Fix

* Fix

* Fix

* Fix

* Cache fixes

* Fixes

* Adds skipping steps

* db-cache fixes

* Test

* Cache fixes

* e2e

* Possible caching conflicts

* Running out of ideas

* Caching is hard

* One more time

* cache-build not skipping

* Fingers crossed

* a

* Test

* Pls

* Please

* LFG

* Build fix

* fix

* Whitespace!!

* Zomars/cal 884 paid events not sending the link (#7318)

* WIP

* Sends correct emails for paid bookings

* Update PaymentService.ts

* Update webhook.ts

* Update webhook.ts

* Update settings back button redirect link (#7403)

* fix(schedule): close on click #7143

* fix(EventSetupTab): validLocations length will always match validLocations length #7138

* fix(SettingsLayout): go back to right route #7379

* feat: get country code from ip geolocation (#6880)

* feat: get coutnry code from ip geolocation

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: create new api route for fetching code

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* chore: replace city with country

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* refactor: create hook for country

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* Team Workflows (#7038)

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>

* Add destination calendar name to DestinationCalendarSelector (#6701)

* Add destination calendar name

* Type fix

* Search through calendars only for destination calendar credential

* Refactor get connected calendars

* Clean up

---------

Co-authored-by: zomars <zomars@me.com>

* Update viewer.tsx (#7428)

* Fix - add team members to emails (#7207)

* On booking add team members & translation

* Add team members to round robin create

* Only update calendars on reschedule if there is a calendar reference

* Send email on reschedules

* Send team email on cancelled event

* Add team members to calendar event description

* Clean up

* Convert other emails to organizer & teams

* Type check fixes

* More type fixes

* Change organizer scheduled input to an object

* early return updateCalendarEvent

* Introduce team member type

* Fix type errors

* Put team members before attendees

* Remove lodash cloneDeep

* Update packages/core/EventManager.ts

Co-authored-by: Omar López <zomars@me.com>

* Remove booking select object

* Revert "Remove booking select object"

This reverts commit 9f121ff4ebbaec9a0643111f96c5337b930ddd5b.

* Refactor email manager (#7270)

Co-authored-by: zomars <zomars@me.com>

* Type change

* Remove conditional check for updateAllCalendarEvents

---------

Co-authored-by: zomars <zomars@me.com>

* Typefix

* Updates webhook response

* Update pr.yml

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Esaú Morais <55207584+esau-morais@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-02-28 10:41:55 +00:00
zomars 4eecd42204 Updates webhook response 2023-02-27 20:23:13 -07:00
zomars b9f90144c5 Typefix 2023-02-27 20:12:02 -07:00
Joe Au-YeungandOmar López 8b07b839e9 Fix - add team members to emails (#7207)
* On booking add team members & translation

* Add team members to round robin create

* Only update calendars on reschedule if there is a calendar reference

* Send email on reschedules

* Send team email on cancelled event

* Add team members to calendar event description

* Clean up

* Convert other emails to organizer & teams

* Type check fixes

* More type fixes

* Change organizer scheduled input to an object

* early return updateCalendarEvent

* Introduce team member type

* Fix type errors

* Put team members before attendees

* Remove lodash cloneDeep

* Update packages/core/EventManager.ts

Co-authored-by: Omar López <zomars@me.com>

* Remove booking select object

* Revert "Remove booking select object"

This reverts commit 9f121ff4ebbaec9a0643111f96c5337b930ddd5b.

* Refactor email manager (#7270)

Co-authored-by: zomars <zomars@me.com>

* Type change

* Remove conditional check for updateAllCalendarEvents

---------

Co-authored-by: zomars <zomars@me.com>
2023-02-27 13:45:40 -07:00
Omar López 2a98ca4b91 Update viewer.tsx (#7428) 2023-02-27 12:39:26 -07:00
Joe Au-Yeungandzomars b9064b1f72 Add destination calendar name to DestinationCalendarSelector (#6701)
* Add destination calendar name

* Type fix

* Search through calendars only for destination calendar credential

* Refactor get connected calendars

* Clean up

---------

Co-authored-by: zomars <zomars@me.com>
2023-02-27 11:47:21 -07:00
0ec71e52ef Team Workflows (#7038)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-02-27 12:54:43 +05:30
Udit Takkar c20835a4c8 feat: get country code from ip geolocation (#6880)
* feat: get coutnry code from ip geolocation

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: create new api route for fetching code

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* chore: replace city with country

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* refactor: create hook for country

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-02-25 12:15:05 -07:00
Esaú Morais 8c63b42cec Update settings back button redirect link (#7403)
* fix(schedule): close on click #7143

* fix(EventSetupTab): validLocations length will always match validLocations length #7138

* fix(SettingsLayout): go back to right route #7379
2023-02-25 12:07:41 -07:00
Omar López ddb752102f Zomars/cal 884 paid events not sending the link (#7318)
* WIP

* Sends correct emails for paid bookings

* Update PaymentService.ts

* Update webhook.ts

* Update webhook.ts
2023-02-24 20:57:49 -07:00
zomars ef0a7950cf Update action.yml 2023-02-24 17:10:39 -07:00
Ben HybertandOmar López 641301afab fix text-align on team (#7372)
* fix text-align on team

* more fixes to the length

* use truncate for long text

* Update packages/features/settings/layouts/SettingsLayout.tsx

---------

Co-authored-by: Omar López <zomars@me.com>
2023-02-24 16:59:24 -07:00
Peer Richelsenandzomars 7d1bc5f0c5 removed todo to issue workflow (#7401)
* removed todo to issue workflow

* Revert "removed todo to issue workflow"

This reverts commit 915c18fe2b94d981433a10f26f912d2c4e8b28d3.

* Make todo only when code reaches main

---------

Co-authored-by: zomars <zomars@me.com>
2023-02-24 15:51:14 -07:00
github-actions[bot]andCrowdin Bot aaccfa49ce New Crowdin translations by Github Action (#7365)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-02-24 15:32:23 -07:00
zomars 1b1f3ddfd9 Update action.yml 2023-02-24 15:31:37 -07:00
zomars f7eda5aa3a Moved cache-db to yarn-build 2023-02-24 13:16:26 -07:00
Omar López b2b846647d Update [type].tsx (#7387) 2023-02-24 13:06:57 -07:00
alannnc 4edc973876 fix for showAttendees default value (#7389) 2023-02-24 12:52:25 -07:00
zomars 3b0126ff35 Typo 🤦‍♂️ 2023-02-24 12:51:34 -07:00
zomars cc4192817b Caching is hard 2023-02-24 12:45:49 -07:00
263 changed files with 8804 additions and 4283 deletions
+12 -11
View File
@@ -17,16 +17,17 @@ scripts/ @calcom/core
apps/docs @calcom/docs
apps/swagger @calcom/docs
packages/app-store-cli @hariombalhara
packages/dayjs @zomars
packages/emails @zomars
packages/embeds @hariombalhara
packages/eslint-plugin @zomars
packages/features @zomars
packages/features/kbar @alishaz-polymath
packages/features/ee/impersonation @sean-brydon
packages/features/ee/payments @alannnc
packages/features/ee/workflows @CarinaWolli
packages/features/tips @PeerRich
packages/app-store-cli @hariombalhara @calcom/core
packages/dayjs @zomars @calcom/core
packages/emails @zomars @calcom/core
packages/embeds @hariombalhara @calcom/core
packages/eslint-plugin @zomars @calcom/core
packages/features @zomars @calcom/core
packages/features/kbar @alishaz-polymath @calcom/core
packages/features/ee/impersonation @sean-brydon @calcom/core
packages/features/ee/payments @alannnc @calcom/core
packages/features/ee/workflows @CarinaWolli @calcom/core
packages/features/tips @PeerRich @calcom/core
packages/ui @calcom/ui
packages/config @calcom/core
packages/app-store/ee/routing-forms @hariombalhara @calcom/core
+15 -10
View File
@@ -1,23 +1,28 @@
name: Cache production build binaries
description: "Cache or restore if necessary"
env:
node_version: v16.x
inputs:
node_version:
required: false
default: v16.x
runs:
using: "composite"
steps:
- name: Cache production build
uses: actions/cache@v3
id: cache-build
env:
cache-name: prod-build
key-1: ${{ env.node_version }}-${{ hashFiles('yarn.lock') }}
key-2: ${{ hashFiles('apps/web/next.config.js') }}
key-1: ${{ inputs.node_version }}-${{ hashFiles('yarn.lock') }}
key-2: ${{ hashFiles('apps/**/**.[jt]s', 'apps/**/**.[jt]sx', 'packages/**/**.[jt]s', 'packages/**/**.[jt]sx', '!**/node_modules') }}
key-3: ${{ github.event.pull_request.number || github.ref }}
# Ensures production-build.yml will always be fresh
key-4: ${{ github.sha }}
with:
path: |
apps/web/.next
node_modules/.cache/turbo
${{ github.workspace }}/apps/web/.next
**/.turbo/**
**/dist/**
key: ${{ runner.os }}-${{ env.cache-name }}-${{ env.key-1 }}-${{ env.key-2 }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-${{ env.key-1 }}-${{ env.key-2 }}-
${{ runner.os }}-${{ env.cache-name }}-${{ env.key-1 }}-
key: ${{ runner.os }}-${{ env.cache-name }}-${{ env.key-1 }}-${{ env.key-2 }}-${{ env.key-3 }}-${{ env.key-4 }}
- run: yarn build
if: steps.cache-build.outputs.cache-hit != 'true'
shell: bash
+8 -4
View File
@@ -2,7 +2,8 @@ name: Cache database between jobs
description: "Cache or restore if necessary"
inputs:
DATABASE_URL:
required: true
required: false
default: "postgresql://postgres:@localhost:5432/calendso"
path:
required: false
default: "backups/backup.sql"
@@ -14,11 +15,14 @@ runs:
uses: actions/cache@v3
env:
cache-name: cache-db
key-1: ${{ hashFiles('packages/prisma/schema.prisma', 'packages/prisma/migrations/**/**.sql', 'packages/prisma/*.ts') }}
key-2: ${{ github.event.pull_request.number || github.ref }}
with:
path: ${{ inputs.path }}
key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.path }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-${{ inputs.path }}-${{ github.sha }}
key: ${{ runner.os }}-${{ env.cache-name }}-${{ inputs.path }}-${{ env.key-1 }}-${{ env.key-2 }}
- run: yarn db-seed
if: steps.cache-db.outputs.cache-hit != 'true'
shell: bash
- name: Postgres Dump Backup
if: steps.cache-db.outputs.cache-hit != 'true'
uses: tj-actions/pg-dump@v2.3
+16
View File
@@ -0,0 +1,16 @@
name: "Set environment variables"
description: "Configures environment variables for a workflow"
runs:
using: "composite"
steps:
- name: Create env file
uses: actions/cache@v3
id: env-cache
with:
path: gh.env
key: env-cache-${{ hashFiles('gh.env') }}
restore-keys: env-cache-
- name: Set Environment Variables
uses: tw3lveparsecs/github-actions-setvars@latest
with:
envFilePath: gh.env
-104
View File
@@ -1,104 +0,0 @@
name: Production Build
description: "Creates a production build, caches it and restores if necessary"
inputs:
DATABASE_URL:
required: true
NEXT_PUBLIC_WEBAPP_URL:
required: true
NEXT_PUBLIC_WEBSITE_URL:
required: true
NEXTAUTH_SECRET:
required: true
GOOGLE_API_CREDENTIALS:
required: true
GOOGLE_LOGIN_ENABLED:
required: true
CRON_API_KEY:
required: false
CALENDSO_ENCRYPTION_KEY:
required: true
DAILY_API_KEY:
required: true
NEXT_PUBLIC_STRIPE_PUBLIC_KEY:
required: true
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE:
required: true
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE:
required: true
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE:
required: true
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE:
required: true
NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN:
required: true
STRIPE_PRIVATE_KEY:
required: true
STRIPE_CLIENT_ID:
required: true
STRIPE_WEBHOOK_SECRET:
required: true
STRIPE_PRO_PLAN_PRODUCT_ID:
required: true
STRIPE_PREMIUM_PLAN_PRODUCT_ID:
required: true
STRIPE_FREE_PLAN_PRODUCT_ID:
required: true
PAYMENT_FEE_PERCENTAGE:
required: true
PAYMENT_FEE_FIXED:
required: true
SAML_DATABASE_URL:
required: true
SAML_ADMINS:
required: true
NEXTAUTH_URL:
required: true
NEXT_PUBLIC_IS_E2E:
required: true
TURBO_TOKEN:
required: true
TURBO_TEAM:
required: true
runs:
using: "composite"
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/cache-build
id: prod-build-cache
- name: Next.js production build
if: steps.prod-build-cache.outputs.cache-hit != 'true'
shell: bash
env:
DATABASE_URL: ${{ inputs.DATABASE_URL }}
NEXT_PUBLIC_WEBAPP_URL: ${{ inputs.NEXT_PUBLIC_WEBAPP_URL }}
NEXT_PUBLIC_WEBSITE_URL: ${{ inputs.NEXT_PUBLIC_WEBSITE_URL }}
NEXTAUTH_SECRET: ${{ inputs.NEXTAUTH_SECRET }}
GOOGLE_API_CREDENTIALS: ${{ inputs.GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: ${{ inputs.GOOGLE_LOGIN_ENABLED }}
CALENDSO_ENCRYPTION_KEY: ${{ inputs.CALENDSO_ENCRYPTION_KEY }}
DAILY_API_KEY: ${{ inputs.DAILY_API_KEY }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ inputs.NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE: ${{ inputs.NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE: ${{ inputs.NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE: ${{ inputs.NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE: ${{ inputs.NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE }}
NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN: ${{ inputs.NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN }}
STRIPE_PRIVATE_KEY: ${{ inputs.STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ inputs.STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ inputs.STRIPE_WEBHOOK_SECRET }}
STRIPE_PRO_PLAN_PRODUCT_ID: ${{ inputs.STRIPE_PRO_PLAN_PRODUCT_ID }}
STRIPE_PREMIUM_PLAN_PRODUCT_ID: ${{ inputs.STRIPE_PREMIUM_PLAN_PRODUCT_ID }}
STRIPE_FREE_PLAN_PRODUCT_ID: ${{ inputs.STRIPE_FREE_PLAN_PRODUCT_ID }}
PAYMENT_FEE_PERCENTAGE: ${{ inputs.PAYMENT_FEE_PERCENTAGE }}
PAYMENT_FEE_FIXED: ${{ inputs.PAYMENT_FEE_FIXED }}
SAML_DATABASE_URL: ${{ inputs.SAML_DATABASE_URL }}
SAML_ADMINS: ${{ inputs.SAML_ADMINS }}
NEXTAUTH_URL: ${{ inputs.NEXTAUTH_URL }}
NEXT_PUBLIC_IS_E2E: ${{ inputs.NEXT_PUBLIC_IS_E2E }}
TURBO_TOKEN: ${{ inputs.TURBO_TOKEN }}
TURBO_TEAM: ${{ inputs.TURBO_TEAM }}
run: |
yarn db-seed
yarn build
+6 -17
View File
@@ -1,30 +1,19 @@
name: Yarn install
description: "Install all NPM dependencies, caches them and restores if necessary"
env:
node_version: v16.x
inputs:
node_version:
required: false
default: v16.x
runs:
using: "composite"
steps:
- name: Use Node ${{ env.node_version }}
- name: Use Node ${{ inputs.node_version }}
uses: actions/setup-node@v3
with:
node-version: ${{ env.node_version }}
node-version: ${{ inputs.node_version }}
cache: "yarn"
- name: node_modules cache
id: node-modules-cache
uses: actions/cache@v3
env:
cache-name: node-modules-yarn
cache-key: ${{ env.node_version }}-${{ hashFiles('yarn.lock', 'packages/prisma/schema.prisma') }}
with:
path: |
**/node_modules/**
packages/prisma/zod
key: ${{ runner.os }}-${{ env.cache-name }}-${{ env.cache-key }}
restore-keys: ${{ runner.os }}-${{ env.cache-name }}-
- name: Yarn install
shell: bash
if: steps.node-modules-cache.outputs.cache-hit != 'true'
run: |
yarn install --prefer-offline --frozen-lockfile
yarn prisma generate
+3 -34
View File
@@ -7,37 +7,6 @@ jobs:
timeout-minutes: 20
name: E2E App-Store Apps
runs-on: ubuntu-latest
env:
DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
NEXT_PUBLIC_WEBAPP_URL: http://localhost:3000
NEXT_PUBLIC_WEBSITE_URL: http://localhost:3000
NEXTAUTH_SECRET: secret
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: true
# CRON_API_KEY: xxx
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
PAYMENT_FEE_PERCENTAGE: 0.005
PAYMENT_FEE_FIXED: 10
SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
SAML_ADMINS: pro@example.com
NEXTAUTH_URL: http://localhost:3000/api/auth
NEXT_PUBLIC_IS_E2E: 1
# EMAIL_FROM: e2e@cal.com
# EMAIL_SERVER_HOST: ${{ secrets.CI_EMAIL_SERVER_HOST }}
# EMAIL_SERVER_PORT: ${{ secrets.CI_EMAIL_SERVER_PORT }}
# EMAIL_SERVER_USER: ${{ secrets.CI_EMAIL_SERVER_USER }}
# EMAIL_SERVER_PASSWORD: ${{ secrets.CI_EMAIL_SERVER_PASSWORD }}
# MS_GRAPH_CLIENT_ID: xxx
# MS_GRAPH_CLIENT_SECRET: xxx
# ZOOM_CLIENT_ID: xxx
# ZOOM_CLIENT_SECRET: xxx
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
services:
postgres:
image: postgres:12.1
@@ -53,13 +22,13 @@ jobs:
- run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/yarn-playwright-install
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-db
with:
DATABASE_URL: ${{ env.DATABASE_URL }}
- uses: ./.github/actions/cache-build
- name: Run Tests
run: yarn test-e2e:app-store
env:
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
- name: Upload Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v2
+5 -39
View File
@@ -6,41 +6,6 @@ jobs:
e2e-embed:
timeout-minutes: 20
runs-on: ubuntu-latest
env:
DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
NEXT_PUBLIC_WEBAPP_URL: http://localhost:3000
NEXT_PUBLIC_WEBSITE_URL: http://localhost:3000
NEXTAUTH_SECRET: secret
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: true
# CRON_API_KEY: xxx
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE: ${{ secrets.NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE }}
NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN: 1
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
PAYMENT_FEE_PERCENTAGE: 0.005
PAYMENT_FEE_FIXED: 10
SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
SAML_ADMINS: pro@example.com
NEXTAUTH_URL: http://localhost:3000/api/auth
NEXT_PUBLIC_IS_E2E: 1
# EMAIL_FROM: e2e@cal.com
# EMAIL_SERVER_HOST: ${{ secrets.CI_EMAIL_SERVER_HOST }}
# EMAIL_SERVER_PORT: ${{ secrets.CI_EMAIL_SERVER_PORT }}
# EMAIL_SERVER_USER: ${{ secrets.CI_EMAIL_SERVER_USER }}
# EMAIL_SERVER_PASSWORD: ${{ secrets.CI_EMAIL_SERVER_PASSWORD }}
# MS_GRAPH_CLIENT_ID: xxx
# MS_GRAPH_CLIENT_SECRET: xxx
# ZOOM_CLIENT_ID: xxx
# ZOOM_CLIENT_SECRET: xxx
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
services:
postgres:
image: postgres:12.1
@@ -56,22 +21,23 @@ jobs:
- run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/yarn-playwright-install
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-db
with:
DATABASE_URL: ${{ env.DATABASE_URL }}
- uses: ./.github/actions/cache-build
- name: Run Tests
run: yarn turbo run embed-tests-update-snapshots:ci --scope=@calcom/embed-react --concurrency=1
env:
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
- name: Upload embed-core results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: test-results-core
name: test-results-embed-core
path: packages/embeds/embed-core/playwright/results
- name: Upload embed-react results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: test-results-react
name: test-results-embed-react
path: packages/embeds/embed-react/playwright/results
+8 -45
View File
@@ -4,46 +4,8 @@ on:
jobs:
e2e:
timeout-minutes: 20
runs-on: buildjet-4vcpu-ubuntu-2204
env:
DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
NEXT_PUBLIC_WEBAPP_URL: http://localhost:3000
NEXT_PUBLIC_WEBSITE_URL: http://localhost:3000
NEXTAUTH_SECRET: secret
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: true
# CRON_API_KEY: xxx
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE: ${{ secrets.NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE }}
NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN: 1
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
STRIPE_PRO_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_PRO_PLAN_PRODUCT_ID }}
STRIPE_PREMIUM_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_PREMIUM_PLAN_PRODUCT_ID }}
STRIPE_FREE_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_FREE_PLAN_PRODUCT_ID }}
PAYMENT_FEE_PERCENTAGE: 0.005
PAYMENT_FEE_FIXED: 10
SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
SAML_ADMINS: pro@example.com
NEXTAUTH_URL: http://localhost:3000/api/auth
NEXT_PUBLIC_IS_E2E: 1
# EMAIL_FROM: e2e@cal.com
# EMAIL_SERVER_HOST: ${{ secrets.CI_EMAIL_SERVER_HOST }}
# EMAIL_SERVER_PORT: ${{ secrets.CI_EMAIL_SERVER_PORT }}
# EMAIL_SERVER_USER: ${{ secrets.CI_EMAIL_SERVER_USER }}
# EMAIL_SERVER_PASSWORD: ${{ secrets.CI_EMAIL_SERVER_PASSWORD }}
# MS_GRAPH_CLIENT_ID: xxx
# MS_GRAPH_CLIENT_SECRET: xxx
# ZOOM_CLIENT_ID: xxx
# ZOOM_CLIENT_SECRET: xxx
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
name: E2E tests
runs-on: ubuntu-latest
services:
postgres:
image: postgres:12.1
@@ -52,19 +14,20 @@ jobs:
POSTGRES_DB: calendso
ports:
- 5432:5432
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/yarn-playwright-install
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-db
with:
DATABASE_URL: ${{ env.DATABASE_URL }}
- uses: ./.github/actions/cache-build
- run: yarn test-e2e
- name: Upload test results
- name: Run Tests
run: yarn test-e2e
env:
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
- name: Upload Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
+49
View File
@@ -0,0 +1,49 @@
name: Create .env file
on:
workflow_call:
env:
INPUT_ENV_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
INPUT_ENV_NEXT_PUBLIC_WEBAPP_URL: http://localhost:3000
INPUT_ENV_NEXT_PUBLIC_WEBSITE_URL: http://localhost:3000
INPUT_ENV_NEXTAUTH_SECRET: secret
INPUT_ENV_GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
INPUT_ENV_GOOGLE_LOGIN_ENABLED: true
# INPUT_ENV_CRON_API_KEY: xxx
INPUT_ENV_CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
INPUT_ENV_NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
INPUT_ENV_STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
INPUT_ENV_STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
INPUT_ENV_STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
INPUT_ENV_PAYMENT_FEE_PERCENTAGE: 0.005
INPUT_ENV_PAYMENT_FEE_FIXED: 10
INPUT_ENV_SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
INPUT_ENV_SAML_ADMINS: pro@example.com
INPUT_ENV_NEXTAUTH_URL: http://localhost:3000/api/auth
INPUT_ENV_NEXT_PUBLIC_IS_E2E: 1
# INPUT_ENV_EMAIL_FROM: e2e@cal.com
# INPUT_ENV_EMAIL_SERVER_HOST: ${{ secrets.CI_EMAIL_SERVER_HOST }}
# INPUT_ENV_EMAIL_SERVER_PORT: ${{ secrets.CI_EMAIL_SERVER_PORT }}
# INPUT_ENV_EMAIL_SERVER_USER: ${{ secrets.CI_EMAIL_SERVER_USER }}
# INPUT_ENV_EMAIL_SERVER_PASSWORD: ${{ secrets.CI_EMAIL_SERVER_PASSWORD }}
# INPUT_ENV_MS_GRAPH_CLIENT_ID: xxx
# INPUT_ENV_MS_GRAPH_CLIENT_SECRET: xxx
# INPUT_ENV_ZOOM_CLIENT_ID: xxx
# INPUT_ENV_ZOOM_CLIENT_SECRET: xxx
INPUT_ENV_TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
INPUT_ENV_TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
jobs:
create_env_file:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ozaytsev86/create-env-file@v1
with:
file-name: ${{ github.workspace }}/gh.env
- uses: actions/cache@v3
id: env-cache
with:
path: gh.env
key: env-cache-${{ hashFiles('gh.env') }}
+9 -9
View File
@@ -4,6 +4,9 @@ on:
pull_request_target:
branches:
- main
paths-ignore:
- "**.md"
- ".github/CODEOWNERS"
workflow_dispatch:
concurrency:
@@ -32,38 +35,35 @@ jobs:
- 'apps/web/**'
- 'packages/embeds/**'
setup:
name: Yarn install & cache
uses: ./.github/workflows/yarn-install.yml
env:
name: Create env file
uses: ./.github/workflows/env-create-file.yml
secrets: inherit
type-check:
name: Type check
needs: setup
uses: ./.github/workflows/check-types.yml
secrets: inherit
test:
name: Unit tests
needs: setup
uses: ./.github/workflows/test.yml
secrets: inherit
lint:
name: Linters
needs: setup
uses: ./.github/workflows/lint.yml
secrets: inherit
build:
name: Production build
needs: setup
needs: env
uses: ./.github/workflows/production-build.yml
secrets: inherit
e2e:
name: E2E tests
needs: [lint, build]
needs: [changes, lint, build]
uses: ./.github/workflows/e2e.yml
secrets: inherit
@@ -78,7 +78,7 @@ jobs:
name: E2E embeds tests
if: ${{ needs.changes.outputs.embed == 'true' }}
needs: [changes, lint, build]
uses: ./.github/workflows/e2e.yml
uses: ./.github/workflows/e2e-embed.yml
secrets: inherit
analyze:
+3 -63
View File
@@ -3,36 +3,6 @@ name: Production Build
on:
workflow_call:
env:
DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
NEXT_PUBLIC_WEBAPP_URL: http://localhost:3000
NEXT_PUBLIC_WEBSITE_URL: http://localhost:3000
NEXTAUTH_SECRET: secret
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: true
# CRON_API_KEY: xxx
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE: ${{ secrets.NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE }}
NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN: 1
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
STRIPE_PRO_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_PRO_PLAN_PRODUCT_ID }}
STRIPE_PREMIUM_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_PREMIUM_PLAN_PRODUCT_ID }}
STRIPE_FREE_PLAN_PRODUCT_ID: ${{ secrets.CI_STRIPE_FREE_PLAN_PRODUCT_ID }}
PAYMENT_FEE_PERCENTAGE: 0.005
PAYMENT_FEE_FIXED: 10
SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
SAML_ADMINS: pro@example.com
NEXTAUTH_URL: http://localhost:3000/api/auth
NEXT_PUBLIC_IS_E2E: 1
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
jobs:
build:
name: Build
@@ -48,38 +18,8 @@ jobs:
- 5432:5432
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- uses: ./.github/actions/yarn-install
- uses: ./.github/actions/yarn-build
with:
# GitHub Composite actions cannot read secrets, we have to pass them in here
DATABASE_URL: ${{ env.DATABASE_URL }}
NEXT_PUBLIC_WEBAPP_URL: ${{ env.NEXT_PUBLIC_WEBAPP_URL }}
NEXT_PUBLIC_WEBSITE_URL: ${{ env.NEXT_PUBLIC_WEBSITE_URL }}
NEXTAUTH_SECRET: ${{ env.NEXTAUTH_SECRET }}
GOOGLE_API_CREDENTIALS: ${{ env.GOOGLE_API_CREDENTIALS }}
GOOGLE_LOGIN_ENABLED: ${{ env.GOOGLE_LOGIN_ENABLED }}
CALENDSO_ENCRYPTION_KEY: ${{ env.CALENDSO_ENCRYPTION_KEY }}
DAILY_API_KEY: ${{ env.DAILY_API_KEY }}
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE: ${{ env.NEXT_PUBLIC_STRIPE_FREE_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE: ${{ env.NEXT_PUBLIC_STRIPE_PRO_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE: ${{ env.NEXT_PUBLIC_STRIPE_PREMIUM_PLAN_PRICE }}
NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE: ${{ env.NEXT_PUBLIC_STRIPE_PREMIUM_NEW_PLAN_PRICE }}
NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN: ${{ env.NEXT_PUBLIC_IS_PREMIUM_NEW_PLAN }}
STRIPE_PRIVATE_KEY: ${{ env.STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ env.STRIPE_CLIENT_ID }}
STRIPE_WEBHOOK_SECRET: ${{ env.STRIPE_WEBHOOK_SECRET }}
STRIPE_PRO_PLAN_PRODUCT_ID: ${{ env.STRIPE_PRO_PLAN_PRODUCT_ID }}
STRIPE_PREMIUM_PLAN_PRODUCT_ID: ${{ env.STRIPE_PREMIUM_PLAN_PRODUCT_ID }}
STRIPE_FREE_PLAN_PRODUCT_ID: ${{ env.STRIPE_FREE_PLAN_PRODUCT_ID }}
PAYMENT_FEE_PERCENTAGE: ${{ env.PAYMENT_FEE_PERCENTAGE }}
PAYMENT_FEE_FIXED: ${{ env.PAYMENT_FEE_FIXED }}
SAML_DATABASE_URL: ${{ env.SAML_DATABASE_URL }}
SAML_ADMINS: ${{ env.SAML_ADMINS }}
NEXTAUTH_URL: ${{ env.NEXTAUTH_URL }}
NEXT_PUBLIC_IS_E2E: ${{ env.NEXT_PUBLIC_IS_E2E }}
TURBO_TOKEN: ${{ env.TURBO_TOKEN }}
TURBO_TEAM: ${{ env.TURBO_TEAM }}
- uses: ./.github/actions/env-read-file
- uses: ./.github/actions/cache-db
with:
DATABASE_URL: ${{ env.DATABASE_URL }}
- uses: ./.github/actions/cache-build
+3 -7
View File
@@ -1,6 +1,6 @@
name: TODO to Issue
on:
pull_request_target: # So we can test on forks
push:
branches:
- main
workflow_dispatch:
@@ -12,17 +12,13 @@ on:
description: "By default, the commit entered above is compared to the one directly before it; to go back further, enter an earlier SHA here"
required: false
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
todos:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 2
- uses: actions/checkout@v3
- name: "Add TODOs to issues"
uses: "alstr/todo-to-issue-action@v4"
env:
+12 -30
View File
@@ -2,38 +2,20 @@
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: Server",
"type": "node-terminal",
"type": "node",
"request": "launch",
"command": "npm run dev",
"skipFiles": ["<node_internals>/**"],
"outFiles": [
"${workspaceFolder}/**/*.js",
"!**/node_modules/**"
],
"sourceMaps": true,
"resolveSourceMapLocations": [
"${workspaceFolder}/**",
"!**/node_modules/**"
]
},
{
"name": "Next.js: Client",
"type": "pwa-chrome",
"request": "launch",
"url": "http://localhost:3000"
},
{
"name": "Next.js: Full Stack",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev",
"name": "Next.js Node Debug",
"runtimeExecutable": "${workspaceFolder}/node_modules/next/dist/bin/next",
"env": {
"NODE_OPTIONS": "--inspect"
},
"cwd": "${workspaceFolder}/apps/web",
"console": "integratedTerminal",
"serverReadyAction": {
"pattern": "started server on .+, url: (https?://.+)",
"uriFormat": "%s",
"action": "debugWithChrome"
"sourceMapPathOverrides": {
"meteor://💻app/*": "${workspaceFolder}/*",
"webpack:///./~/*": "${workspaceFolder}/node_modules/*",
"webpack://?:*/*": "${workspaceFolder}/*"
}
}
]
}
}
+1 -1
View File
@@ -128,6 +128,6 @@ If you get errors, be sure to fix them before committing.
## Making a Pull Request
- Be sure to [check the "Allow edits from maintainers" option](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) while creating you PR.
- If your PR refers to or fixes an issue, be sure to add `refs #XXX` or `fixes #XXX` to the PR description. Replacing `XXX` with the respective issue number. Se more about [Linking a pull request to an issue
- If your PR refers to or fixes an issue, be sure to add `refs #XXX` or `fixes #XXX` to the PR description. Replacing `XXX` with the respective issue number. See more about [Linking a pull request to an issue
](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
- Be sure to fill the PR Template accordingly.
+9
View File
@@ -350,8 +350,17 @@ We have a list of [help wanted](https://github.com/calcom/cal.com/issues?q=is:is
<img alt="Bounties of cal" src="https://console.algora.io/api/og/cal/bounties.png?p=0&status=open&theme=light">
</picture>
</a>
<!-- CONTRIBUTORS -->
### Contributors
<a href="https://github.com/calcom/cal.com/graphs/contributors">
<img src="https://contrib.rocks/image?repo=calcom/cal.com" />
</a>
<!-- TRANSLATIONS -->
### Translations
+1 -1
View File
@@ -17,7 +17,7 @@
"license": "MIT",
"dependencies": {
"iframe-resizer-react": "^1.1.0",
"next": "^13.1.1",
"next": "^13.2.1",
"nextra": "^1.1.0",
"nextra-theme-docs": "^1.2.2",
"react": "^18.2.0",
+13 -16
View File
@@ -1,21 +1,7 @@
const withBundleAnalyzer = require("@next/bundle-analyzer");
const withTM = require("next-transpile-modules")([
"@calcom/app-store",
"@calcom/dayjs",
"@calcom/emails",
"@calcom/trpc",
"@calcom/embed-core",
"@calcom/embed-react",
"@calcom/features",
"@calcom/lib",
"@calcom/prisma",
"@calcom/ui",
]);
const glob = require("glob");
const plugins = [];
plugins.push(withTM, withBundleAnalyzer({ enabled: process.env.ANALYZE === "true" }));
plugins.push(withBundleAnalyzer({ enabled: process.env.ANALYZE === "true" }));
/** @type {import("next").NextConfig} */
const nextConfig = {
@@ -27,7 +13,18 @@ const nextConfig = {
typescript: {
ignoreBuildErrors: true,
},
experimental: { images: { allowFutureImage: true } },
transpilePackages: [
"@calcom/app-store",
"@calcom/dayjs",
"@calcom/emails",
"@calcom/trpc",
"@calcom/embed-core",
"@calcom/embed-react",
"@calcom/features",
"@calcom/lib",
"@calcom/prisma",
"@calcom/ui",
],
eslint: {
ignoreDuringBuilds: true,
},
+1 -1
View File
@@ -20,7 +20,7 @@
"@radix-ui/react-slider": "^1.0.0",
"@radix-ui/react-switch": "^1.0.0",
"@radix-ui/react-tooltip": "^1.0.0",
"next": "^13.1.1",
"next": "^13.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"storybook-addon-rtl-direction": "^0.0.19"
+1 -1
View File
@@ -14,7 +14,7 @@
"dependencies": {
"highlight.js": "^11.6.0",
"isarray": "2.0.5",
"next": "^13.1.1",
"next": "^13.2.1",
"openapi-snippet": "^0.13.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -1,6 +1,7 @@
import React from "react";
import { SkeletonText } from "@calcom/ui";
import { Button, SkeletonText } from "@calcom/ui";
import { FiMoreHorizontal } from "@calcom/ui/components/icon";
import classNames from "@lib/classNames";
@@ -18,17 +19,21 @@ export default SkeletonLoader;
function SkeletonItem() {
return (
<li className="group flex w-full items-center justify-between px-2 py-[23px] sm:px-6">
<div className="flex-grow truncate text-sm">
<div className="flex flex-col space-y-2">
<SkeletonText className="h-4 w-32" />
<SkeletonText className="h-2 w-32" />
</div>
</div>
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
<div className="flex justify-between space-x-2 rtl:space-x-reverse">
<SkeletonText className="h-6 w-12" />
<li>
<div className="flex items-center justify-between py-5 hover:bg-neutral-50 ltr:pl-4 rtl:pr-4 sm:ltr:pl-0 sm:rtl:pr-0">
<div className="items-between flex w-full flex-col justify-center hover:bg-neutral-50 sm:px-6">
<SkeletonText className="my-1 h-4 w-32" />
<SkeletonText className="my-1 h-2 w-24" />
<SkeletonText className="my-1 h-2 w-40" />
</div>
<Button
className="mx-5"
type="button"
variant="icon"
color="secondary"
StartIcon={FiMoreHorizontal}
disabled
/>
</div>
</li>
);
@@ -6,7 +6,7 @@ import dayjs from "@calcom/dayjs";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Badge } from "@calcom/ui";
import { FiCheckSquare, FiClock, FiInfo } from "@calcom/ui/components/icon";
import { FiCheckSquare, FiClock } from "@calcom/ui/components/icon";
import useRouterQuery from "@lib/hooks/useRouterQuery";
@@ -94,15 +94,7 @@ const BookingDescription: FC<Props> = (props) => {
"flex",
isBookingPage && "dark:text-darkgray-600 text-sm font-medium text-gray-600"
)}>
<div>
<FiInfo
className={classNames(
"ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]",
isBookingPage && "dark:text-darkgray-600 -mt-1 text-gray-500"
)}
/>
</div>
<div className="max-w-[calc(100%_-_2rem)] flex-shrink break-words">
<div className="scroll-bar scrollbar-track-w-20 max-h-[200px] max-w-full flex-shrink break-words [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600">
<EventTypeDescriptionSafeHTML eventType={eventType} />
</div>
</div>
@@ -0,0 +1,24 @@
import { FormattedNumber, IntlProvider } from "react-intl";
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
import { FiCreditCard } from "@calcom/ui/components/icon";
const BookingDescriptionPayment = (props: { eventType: Parameters<typeof getPaymentAppData>[0] }) => {
const paymentAppData = getPaymentAppData(props.eventType);
if (!paymentAppData || paymentAppData.price <= 0) return null;
return (
<p className="text-bookinglight -ml-2 px-2 text-sm ">
<FiCreditCard className="ml-[2px] -mt-1 inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
<IntlProvider locale="en">
<FormattedNumber
value={paymentAppData.price / 100.0}
style="currency"
currency={paymentAppData.currency?.toUpperCase()}
/>
</IntlProvider>
</p>
);
};
export default BookingDescriptionPayment;
@@ -223,6 +223,8 @@ function BookingListItem(booking: BookingItemProps) {
};
const showRecordingsButtons =
(booking.location === "integrations:daily" || booking?.location?.trim() === "") && isPast && isConfirmed;
const title = decodeURIComponent(booking.title);
return (
<>
<RescheduleDialog
@@ -355,12 +357,12 @@ function BookingListItem(booking: BookingItemProps) {
<div className="cursor-pointer py-4">
<div
title={booking.title}
title={title}
className={classNames(
"max-w-10/12 sm:max-w-56 text-sm font-medium leading-6 text-gray-900 md:max-w-full",
isCancelled ? "line-through" : ""
)}>
{booking.title}
{title}
<span> </span>
{!!booking?.eventType?.price && !booking.paid && (
@@ -17,7 +17,7 @@ export function TimezoneDropdown({
return (
<>
<div className="dark:focus-within:bg-darkgray-200 dark:bg-darkgray-100 dark:hover:bg-darkgray-200 -mx-[2px] !mt-3 flex w-fit items-center rounded-[4px] px-1 py-[2px] text-sm font-medium focus-within:bg-gray-200 hover:bg-gray-100 [&_svg]:focus-within:text-gray-900 dark:[&_svg]:focus-within:text-white [&_p]:focus-within:text-gray-900 dark:[&_p]:focus-within:text-white">
<div className="dark:focus-within:bg-darkgray-200 dark:bg-darkgray-100 dark:hover:bg-darkgray-200 -mx-[2px] !mt-3 flex w-fit max-w-[20rem] items-center rounded-[4px] px-1 py-[2px] text-sm font-medium focus-within:bg-gray-200 hover:bg-gray-100 lg:max-w-[12rem] [&_svg]:focus-within:text-gray-900 dark:[&_svg]:focus-within:text-white [&_p]:focus-within:text-gray-900 dark:[&_p]:focus-within:text-white">
<FiGlobe className="dark:text-darkgray-600 flex h-4 w-4 text-gray-600 ltr:mr-[2px] rtl:ml-[2px]" />
<TimeOptions onSelectTimeZone={handleSelectTimeZone} />
</div>
File diff suppressed because it is too large Load Diff
@@ -4,7 +4,7 @@ import { isValidPhoneNumber } from "libphonenumber-js";
import { Trans } from "next-i18next";
import Link from "next/link";
import { useEffect } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
import { Controller, useForm, useWatch, useFormContext } from "react-hook-form";
import { z } from "zod";
import type { EventLocationType, LocationObject } from "@calcom/app-store/locations";
@@ -49,16 +49,19 @@ const LocationInput = (props: {
defaultValue?: string;
}): JSX.Element | null => {
const { eventLocationType, locationFormMethods, ...remainingProps } = props;
const { control } = useFormContext() as typeof locationFormMethods;
if (eventLocationType?.organizerInputType === "text") {
return (
<input {...locationFormMethods.register(eventLocationType.variable)} type="text" {...remainingProps} />
);
} else if (eventLocationType?.organizerInputType === "phone") {
return (
<PhoneInput
<Controller
name={eventLocationType.variable}
control={locationFormMethods.control}
{...remainingProps}
control={control}
render={({ field: { onChange, value } }) => {
return <PhoneInput onChange={onChange} value={value} {...remainingProps} />;
}}
/>
);
}
@@ -113,7 +113,7 @@ const EventTypeScheduleDetails = () => {
);
const filterDays = (dayNum: number) =>
schedule?.schedule.availability.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
schedule?.schedule.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
return (
<div className="space-y-4 rounded border px-6 pb-4">
@@ -156,15 +156,17 @@ const EventTypeScheduleDetails = () => {
<FiGlobe className="ltr:mr-2 rtl:ml-2" />
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
</span>
<Button
href={`/availability/${schedule?.schedule.id}`}
disabled={isLoading}
color="minimal"
EndIcon={FiExternalLink}
target="_blank"
rel="noopener noreferrer">
{t("edit_availability")}
</Button>
{!!schedule?.id && (
<Button
href={`/availability/${schedule.id}`}
disabled={isLoading}
color="minimal"
EndIcon={FiExternalLink}
target="_blank"
rel="noopener noreferrer">
{t("edit_availability")}
</Button>
)}
</div>
</div>
);
@@ -0,0 +1,144 @@
import type { FC } from "react";
import type { SubmitHandler } from "react-hook-form";
import { FormProvider } from "react-hook-form";
import { useForm, useFormContext } from "react-hook-form";
import type { EventNameObjectType } from "@calcom/core/event";
import { getEventName } from "@calcom/core/event";
import { validateCustomEventName } from "@calcom/core/event";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button, Dialog, DialogClose, DialogFooter, DialogContent, TextField } from "@calcom/ui";
interface FormValues {
customEventName: string;
}
interface CustomEventTypeModalFormProps {
placeHolder: string;
close: () => void;
setValue: (value: string) => void;
event: EventNameObjectType;
defaultValue: string;
}
const CustomEventTypeModalForm: FC<CustomEventTypeModalFormProps> = (props) => {
const { t } = useLocale();
const { placeHolder, close, setValue, event } = props;
const { register, handleSubmit, watch, getValues } = useFormContext<FormValues>();
const onSubmit: SubmitHandler<FormValues> = (data) => {
setValue(data.customEventName);
close();
};
// const customEventName = watch("customEventName");
const previewText = getEventName({ ...event, eventName: watch("customEventName") });
const placeHolder_ = watch("customEventName") === "" ? previewText : placeHolder;
return (
<form
id="custom-event-name"
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
const isEmpty = getValues("customEventName") === "";
if (isEmpty) {
setValue("");
}
handleSubmit(onSubmit)(e);
}}>
<TextField
label={t("event_name_in_calendar")}
type="text"
placeholder={placeHolder_}
{...register("customEventName", {
validate: (value) => validateCustomEventName(value, t("invalid_event_name_variables")),
})}
className="mb-0"
/>
<div className="pt-6 text-sm">
<div className="mb-6 rounded-md bg-gray-100 p-2">
<h1 className="mb-2 ml-1 font-medium text-gray-900">{t("available_variables")}</h1>
<div className="mb-2.5 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Event type title}`}</p>
<p className="text-gray-900">{t("event_name_info")}</p>
</div>
<div className="mb-2.5 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Organiser}`}</p>
<p className="text-gray-900">{t("your_full_name")}</p>
</div>
<div className="mb-2.5 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Scheduler}`}</p>
<p className="text-gray-900">{t("scheduler_full_name")}</p>
</div>
<div className="mb-1 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Location}`}</p>
<p className="text-gray-900">{t("location_info")}</p>
</div>
</div>
<h1 className="mb-2 text-[14px] font-medium leading-4">{t("preview")}</h1>
<div
className="flex h-[212px] w-full rounded-md border-y bg-cover bg-center"
style={{
backgroundImage: "url(/calendar-preview.svg)",
}}>
<div className="m-auto flex items-center justify-center self-stretch">
<div className="mt-3 ml-11 box-border h-[110px] w-[120px] flex-col items-start gap-1 rounded-md border border-solid border-black bg-gray-100 text-[12px] leading-3">
<p className="overflow-hidden text-ellipsis p-1.5 font-medium text-gray-900">{previewText}</p>
<p className="ml-1.5 text-[10px] font-normal text-gray-600">8 - 10 AM</p>
</div>
</div>
</div>
</div>
</form>
);
};
interface CustomEventTypeModalProps {
placeHolder: string;
defaultValue: string;
close: () => void;
setValue: (value: string) => void;
event: EventNameObjectType;
}
const CustomEventTypeModal: FC<CustomEventTypeModalProps> = (props) => {
const { t } = useLocale();
const { defaultValue, placeHolder, close, setValue, event } = props;
const methods = useForm<FormValues>({
defaultValues: {
customEventName: defaultValue,
},
});
return (
<Dialog open={true} onOpenChange={close}>
<DialogContent
title={t("custom_event_name")}
description={t("custom_event_name_description")}
type="creation"
enableOverflow>
<FormProvider {...methods}>
<CustomEventTypeModalForm
event={event}
close={close}
setValue={setValue}
placeHolder={placeHolder}
defaultValue={defaultValue}
/>
</FormProvider>
<DialogFooter>
<DialogClose>{t("cancel")}</DialogClose>
<Button form="custom-event-name" type="submit" color="primary">
{t("create")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
export default CustomEventTypeModal;
@@ -1,214 +0,0 @@
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { EventTypeCustomInputType } from "@prisma/client";
import type { CustomInputParsed } from "pages/event-types/[type]";
import type { FC } from "react";
import type { Control, UseFormRegister } from "react-hook-form";
import { Controller, useFieldArray, useForm, useWatch } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button, Label, Select, TextField } from "@calcom/ui";
import { FiPlus, FiX } from "@calcom/ui/components/icon";
interface OptionTypeBase {
label: string;
value: EventTypeCustomInputType;
options?: { label: string; type: string }[];
}
interface Props {
onSubmit: (output: CustomInputParsed) => void;
onCancel: () => void;
selectedCustomInput?: CustomInputParsed;
}
type IFormInput = CustomInputParsed;
/**
* Getting a random ID gives us the option to know WHICH field is changed
* when the user edits a custom field.
* This UUID is only used to check for changes in the UI and not the ID we use in the DB
* There is very very very slim chance that this will cause a collision
* */
const randomId = () => Math.floor(Math.random() * 1000000 + new Date().getTime());
const CustomInputTypeForm: FC<Props> = (props) => {
const { t } = useLocale();
const inputOptions: OptionTypeBase[] = [
{ value: EventTypeCustomInputType.TEXT, label: t("text") },
{ value: EventTypeCustomInputType.TEXTLONG, label: t("multiline_text") },
{ value: EventTypeCustomInputType.NUMBER, label: t("number") },
{ value: EventTypeCustomInputType.BOOL, label: t("checkbox") },
{
value: EventTypeCustomInputType.RADIO,
label: t("radio"),
},
{ value: EventTypeCustomInputType.PHONE, label: t("phone_number") },
];
const { selectedCustomInput } = props;
const defaultValues = selectedCustomInput
? { ...selectedCustomInput, id: selectedCustomInput?.id || randomId() }
: {
id: randomId(),
type: EventTypeCustomInputType.TEXT,
};
const { register, control, getValues } = useForm<IFormInput>({
defaultValues,
});
const selectedInputType = useWatch({ name: "type", control });
const selectedInputOption = inputOptions.find((e) => selectedInputType === e.value);
const onCancel = () => {
props.onCancel();
};
return (
<div className="flex flex-col space-y-4">
<div>
<label htmlFor="type" className="block text-sm font-medium text-gray-700">
{t("input_type")}
</label>
<Controller
name="type"
control={control}
render={({ field }) => (
<Select
id="type"
defaultValue={selectedInputOption}
options={inputOptions}
isSearchable={false}
className="mt-1 mb-2 block w-full min-w-0 flex-1 text-sm"
onChange={(option) => option && field.onChange(option.value)}
value={selectedInputOption}
onBlur={field.onBlur}
name={field.name}
/>
)}
/>
</div>
<TextField
label={t("label")}
type="text"
id="label"
required
className="block w-full rounded-sm border-gray-300 text-sm"
defaultValue={selectedCustomInput?.label}
{...register("label", { required: true })}
/>
{(selectedInputType === EventTypeCustomInputType.TEXT ||
selectedInputType === EventTypeCustomInputType.TEXTLONG) && (
<TextField
label={t("placeholder")}
type="text"
id="placeholder"
className="block w-full rounded-sm border-gray-300 text-sm"
defaultValue={selectedCustomInput?.placeholder}
{...register("placeholder")}
/>
)}
{selectedInputType === EventTypeCustomInputType.RADIO && (
<RadioInputHandler control={control} register={register} />
)}
<div className="flex h-5 items-center">
<input
id="required"
type="checkbox"
className="text-primary-600 focus:ring-primary-500 h-4 w-4 rounded border-gray-300 ltr:mr-2 rtl:ml-2"
defaultChecked={selectedCustomInput?.required ?? true}
{...register("required")}
/>
<label htmlFor="required" className="block text-sm font-medium text-gray-700">
{t("is_required")}
</label>
</div>
<input
type="hidden"
id="eventTypeId"
value={selectedCustomInput?.eventTypeId || -1}
{...register("eventTypeId", { valueAsNumber: true })}
/>
<input
type="hidden"
id="id"
value={selectedCustomInput?.id || -1}
{...register("id", { valueAsNumber: true })}
/>
<div className="mt-5 flex justify-end space-x-2 rtl:space-x-reverse sm:mt-4">
<Button onClick={onCancel} type="button" color="secondary" className="ltr:mr-2 rtl:ml-2">
{t("cancel")}
</Button>
<Button
type="button"
onClick={() => {
props.onSubmit(getValues());
}}>
{t("save")}
</Button>
</div>
</div>
);
};
function RadioInputHandler({
register,
control,
}: {
register: UseFormRegister<IFormInput>;
control: Control<IFormInput>;
}) {
const { t } = useLocale();
const { fields, append, remove } = useFieldArray<IFormInput>({
control,
name: "options",
shouldUnregister: true,
});
const [animateRef] = useAutoAnimate<HTMLUListElement>();
return (
<div className="flex flex-col ">
<Label htmlFor="radio_options">{t("options")}</Label>
<ul
className="flex max-h-80 w-full flex-col space-y-1 overflow-y-scroll rounded-md bg-gray-50 p-4"
ref={animateRef}>
<>
{fields.map((option, index) => (
<li key={`${option.id}`}>
<TextField
id={option.id}
placeholder={t("enter_option", { index: index + 1 })}
addOnFilled={false}
label={t("option", { index: index + 1 })}
labelSrOnly
{...register(`options.${index}.label` as const, { required: true })}
addOnSuffix={
<Button
variant="icon"
color="minimal"
StartIcon={FiX}
onClick={() => {
remove(index);
}}
/>
}
/>
</li>
))}
<Button
color="minimal"
StartIcon={FiPlus}
className="!text-sm !font-medium"
onClick={() => {
append({ label: "", type: "text" });
}}>
{t("add_an_option")}
</Button>
</>
</ul>
</div>
);
}
export default CustomInputTypeForm;
@@ -1,5 +1,6 @@
import dynamic from "next/dynamic";
import Link from "next/link";
import type { CustomInputParsed, EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
import { useEffect, useState } from "react";
import { Controller, useFormContext } from "react-hook-form";
import short from "short-uuid";
@@ -8,30 +9,17 @@ import { v5 as uuidv5 } from "uuid";
import type { EventNameObjectType } from "@calcom/core/event";
import { getEventName } from "@calcom/core/event";
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
import CustomInputItem from "@calcom/features/eventtypes/components/CustomInputItem";
import { FormBuilder } from "@calcom/features/form-builder/FormBuilder";
import { APP_NAME, CAL_URL, IS_SELF_HOSTED } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import {
Badge,
Button,
Checkbox,
Dialog,
DialogClose,
DialogContent,
DialogFooter,
Label,
SettingsToggle,
showToast,
TextField,
Tooltip,
} from "@calcom/ui";
import { FiEdit, FiCopy, FiPlus } from "@calcom/ui/components/icon";
import CustomInputTypeForm from "@components/eventtype/CustomInputTypeForm";
import { Badge, Button, Checkbox, Label, SettingsToggle, showToast, TextField, Tooltip } from "@calcom/ui";
import { FiEdit, FiCopy } from "@calcom/ui/components/icon";
import RequiresConfirmationController from "./RequiresConfirmationController";
const CustomEventTypeModal = dynamic(() => import("@components/eventtype/CustomEventTypeModal"));
const generateHashedLink = (id: number) => {
const translator = short();
const seed = `${id}:${new Date().getTime()}`;
@@ -39,22 +27,11 @@ const generateHashedLink = (id: number) => {
return uid;
};
const getRandomId = (length = 8) => {
return (
-1 *
parseInt(
Math.ceil(Math.random() * Date.now())
.toPrecision(length)
.toString()
.replace(".", "")
)
);
};
export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps, "eventType" | "team">) => {
const connectedCalendarsQuery = trpc.viewer.connectedCalendars.useQuery();
const formMethods = useFormContext<FormValues>();
const { t } = useLocale();
const [showEventNameTip, setShowEventNameTip] = useState(false);
const [hashedLinkVisible, setHashedLinkVisible] = useState(!!eventType.hashedLink);
const [redirectUrlVisible, setRedirectUrlVisible] = useState(!!eventType.successRedirectUrl);
@@ -66,44 +43,39 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
host: eventType.users[0]?.name || "Nameless",
t,
};
const [previewText, setPreviewText] = useState(getEventName(eventNameObject));
const [customInputs, setCustomInputs] = useState<CustomInputParsed[]>(
eventType.customInputs.sort((a, b) => a.id - b.id) || []
);
const [selectedCustomInput, setSelectedCustomInput] = useState<CustomInputParsed | undefined>(undefined);
const [selectedCustomInputModalOpen, setSelectedCustomInputModalOpen] = useState(false);
const [requiresConfirmation, setRequiresConfirmation] = useState(eventType.requiresConfirmation);
const placeholderHashedLink = `${CAL_URL}/d/${hashedUrl}/${eventType.slug}`;
const seatsEnabled = formMethods.watch("seatsPerTimeSlotEnabled");
const removeCustom = (index: number) => {
formMethods.getValues("customInputs").splice(index, 1);
customInputs.splice(index, 1);
setCustomInputs([...customInputs]);
};
const replaceEventNamePlaceholder = (eventNameObject: EventNameObjectType, previewEventName: string) =>
previewEventName
.replace("{Event type title}", eventNameObject.eventType)
.replace("{Scheduler}", eventNameObject.attendeeName)
.replace("{Organiser}", eventNameObject.host);
const changePreviewText = (eventNameObject: EventNameObjectType, previewEventName: string) => {
setPreviewText(replaceEventNamePlaceholder(eventNameObject, previewEventName));
};
useEffect(() => {
!hashedUrl && setHashedUrl(generateHashedLink(eventType.users[0]?.id ?? team?.id));
}, [eventType.users, hashedUrl, team?.id]);
useEffect(() => {
if (eventType.customInputs) {
setCustomInputs(eventType.customInputs.sort((a, b) => a.id - b.id));
}
}, [eventType.customInputs]);
const toggleGuests = (enabled: boolean) => {
const bookingFields = formMethods.getValues("bookingFields");
formMethods.setValue(
"bookingFields",
bookingFields.map((field) => {
if (field.name === "guests") {
return {
...field,
hidden: !enabled,
};
}
return field;
})
);
};
const eventNamePlaceholder = replaceEventNamePlaceholder(eventNameObject, t("meeting_with_user"));
const eventNamePlaceholder = getEventName({
...eventNameObject,
eventName: formMethods.watch("eventName"),
});
const closeEventNameTip = () => setShowEventNameTip(false);
const setEventName = (value: string) => formMethods.setValue("eventName", value);
return (
<div className="flex flex-col space-y-8">
{/**
@@ -146,14 +118,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
type="text"
placeholder={eventNamePlaceholder}
defaultValue={eventType.eventName || ""}
{...formMethods.register("eventName", {
onChange: (e) => {
if (!e.target.value || !formMethods.getValues("eventName")) {
return setPreviewText(getEventName(eventNameObject));
}
changePreviewText(eventNameObject, e.target.value);
},
})}
{...formMethods.register("eventName")}
addOnSuffix={
<Button
type="button"
@@ -162,53 +127,18 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
color="minimal"
className="hover:stroke-3 min-w-fit px-0 hover:bg-transparent hover:text-black"
onClick={() => setShowEventNameTip((old) => !old)}
aria-label="edit custom name"
/>
}
/>
</div>
<hr />
<div className="">
<SettingsToggle
title={t("additional_inputs")}
description={t("additional_input_description")}
checked={customInputs.length > 0}
onCheckedChange={(e) => {
if (e && customInputs.length === 0) {
// Push a placeholders
setSelectedCustomInputModalOpen(true);
} else if (!e) {
formMethods.setValue("customInputs", []);
}
}}>
<ul className="my-4 rounded-md border">
{customInputs.map((customInput, idx) => (
<CustomInputItem
key={idx}
question={customInput.label}
type={customInput.type}
required={customInput.required}
editOnClick={() => {
setSelectedCustomInput(customInput);
setSelectedCustomInputModalOpen(true);
}}
deleteOnClick={() => removeCustom(idx)}
/>
))}
</ul>
{customInputs.length > 0 && (
<Button
StartIcon={FiPlus}
color="minimal"
type="button"
onClick={() => {
setSelectedCustomInput(undefined);
setSelectedCustomInputModalOpen(true);
}}>
{t("add_input")}
</Button>
)}
</SettingsToggle>
</div>
<FormBuilder
title={t("booking_questions_title")}
description={t("booking_questions_description")}
addFieldLabel={t("add_a_booking_question")}
formProp="bookingFields"
/>
<hr />
<RequiresConfirmationController
seatsEnabled={seatsEnabled}
@@ -216,22 +146,6 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
requiresConfirmation={requiresConfirmation}
onRequiresConfirmation={setRequiresConfirmation}
/>
<hr />
<Controller
name="disableGuests"
control={formMethods.control}
defaultValue={eventType.disableGuests}
render={({ field: { value, onChange } }) => (
<SettingsToggle
title={t("disable_guests")}
description={t("disable_guests_description")}
checked={value}
onCheckedChange={(e) => onChange(e)}
disabled={seatsEnabled}
/>
)}
/>
<hr />
<Controller
name="hideCalendarNotes"
@@ -247,22 +161,6 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
)}
/>
<hr />
<Controller
name="metadata.additionalNotesRequired"
control={formMethods.control}
defaultValue={!!eventType.metadata.additionalNotesRequired}
render={({ field: { value, onChange } }) => (
<div className="flex space-x-3 ">
<SettingsToggle
title={t("require_additional_notes")}
description={t("require_additional_notes_description")}
checked={!!value}
onCheckedChange={(e) => onChange(e)}
/>
</div>
)}
/>
<hr />
<Controller
name="successRedirectUrl"
control={formMethods.control}
@@ -363,13 +261,13 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
onCheckedChange={(e) => {
// Enabling seats will disable guests and requiring confirmation until fully supported
if (e) {
formMethods.setValue("disableGuests", true);
toggleGuests(false);
formMethods.setValue("requiresConfirmation", false);
setRequiresConfirmation(false);
formMethods.setValue("seatsPerTimeSlot", 2);
} else {
formMethods.setValue("seatsPerTimeSlot", null);
formMethods.setValue("disableGuests", false);
toggleGuests(true);
}
onChange(e);
}}>
@@ -407,130 +305,14 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
/>
{showEventNameTip && (
<Dialog open={showEventNameTip} onOpenChange={setShowEventNameTip}>
<DialogContent
title={t("custom_event_name")}
description={t("custom_event_name_description")}
type="creation"
enableOverflow>
<TextField
label={t("event_name_in_calendar")}
type="text"
placeholder={eventNamePlaceholder}
defaultValue={eventType.eventName || ""}
{...formMethods.register("eventName", {
onChange: (e) => {
if (!e.target.value || !formMethods.getValues("eventName")) {
return setPreviewText(getEventName(eventNameObject));
}
changePreviewText(eventNameObject, e.target.value);
},
})}
className="mb-0"
/>
<div className="text-sm">
<div className="mb-6 rounded-md bg-gray-100 p-2">
<h1 className="mb-2 ml-1 font-medium text-gray-900">{t("available_variables")}</h1>
<div className="mb-2.5 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Event type title}`}</p>
<p className="text-gray-900">{t("event_name_info")}</p>
</div>
<div className="mb-2.5 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Organiser}`}</p>
<p className="text-gray-900">{t("your_full_name")}</p>
</div>
<div className="mb-2.5 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Scheduler}`}</p>
<p className="text-gray-900">{t("scheduler_full_name")}</p>
</div>
<div className="mb-1 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Location}`}</p>
<p className="text-gray-900">{t("location_info")}</p>
</div>
</div>
<h1 className="mb-2 text-[14px] font-medium leading-4">{t("preview")}</h1>
<div
className="flex h-[212px] w-full rounded-md border-y bg-cover bg-center"
style={{
backgroundImage: "url(/calendar-preview.svg)",
}}>
<div className="m-auto flex items-center justify-center self-stretch">
<div className="mt-3 ml-11 box-border h-[110px] w-[120px] flex-col items-start gap-1 rounded-md border border-solid border-black bg-gray-100 text-[12px] leading-3">
<p className="overflow-hidden text-ellipsis p-1.5 font-medium text-gray-900">
{previewText}
</p>
<p className="ml-1.5 text-[10px] font-normal text-gray-600">8 - 10 AM</p>
</div>
</div>
</div>
</div>
<DialogFooter>
<DialogClose onClick={() => formMethods.setValue("eventName", eventType.eventName ?? "")}>
{t("cancel")}
</DialogClose>
<Button color="primary" onClick={() => setShowEventNameTip(false)}>
{t("create")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<CustomEventTypeModal
close={closeEventNameTip}
setValue={setEventName}
defaultValue={eventType.eventName || ""}
placeHolder={eventNamePlaceholder}
event={eventNameObject}
/>
)}
<Controller
name="customInputs"
control={formMethods.control}
defaultValue={customInputs}
render={() => (
<Dialog open={selectedCustomInputModalOpen} onOpenChange={setSelectedCustomInputModalOpen}>
<DialogContent
type="creation"
Icon={FiPlus}
title={t("add_new_custom_input_field")}
description={t("this_input_will_shown_booking_this_event")}>
<CustomInputTypeForm
selectedCustomInput={selectedCustomInput}
onSubmit={(values) => {
const customInput: CustomInputParsed = {
id: getRandomId(),
eventTypeId: -1,
label: values.label,
placeholder: values.placeholder,
required: values.required,
type: values.type,
options: values.options,
hasToBeCreated: true,
};
if (selectedCustomInput) {
selectedCustomInput.label = customInput.label;
selectedCustomInput.placeholder = customInput.placeholder;
selectedCustomInput.required = customInput.required;
selectedCustomInput.type = customInput.type;
selectedCustomInput.options = customInput.options || undefined;
selectedCustomInput.hasToBeCreated = false;
// Update by id
const inputIndex = customInputs.findIndex((input) => input.id === values.id);
customInputs[inputIndex] = selectedCustomInput;
setCustomInputs(customInputs);
formMethods.setValue("customInputs", customInputs);
} else {
const concatted = customInputs.concat({
...customInput,
options: customInput.options,
});
console.log(concatted);
setCustomInputs(concatted);
formMethods.setValue("customInputs", concatted);
}
setSelectedCustomInputModalOpen(false);
}}
onCancel={() => {
setSelectedCustomInputModalOpen(false);
}}
/>
</DialogContent>
</Dialog>
)}
/>
</div>
);
};
+21 -11
View File
@@ -13,8 +13,10 @@ import type { EventLocationType } from "@calcom/app-store/locations";
import { getEventLocationType, MeetLocationType, LocationType } from "@calcom/app-store/locations";
import { CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { md } from "@calcom/lib/markdownIt";
import { slugify } from "@calcom/lib/slugify";
import { Button, Label, Select, SettingsToggle, Skeleton, TextField } from "@calcom/ui";
import turndown from "@calcom/lib/turndownService";
import { Button, Editor, Label, Select, SettingsToggle, Skeleton, TextField } from "@calcom/ui";
import { FiEdit2, FiCheck, FiX, FiPlus } from "@calcom/ui/components/icon";
import { EditLocationDialog } from "@components/dialog/EditLocationDialog";
@@ -44,11 +46,14 @@ const getDefaultLocationValue = (options: EventTypeSetupProps["locationOptions"]
};
export const EventSetupTab = (
props: Pick<EventTypeSetupProps, "eventType" | "locationOptions" | "team" | "teamMembers">
props: Pick<
EventTypeSetupProps,
"eventType" | "locationOptions" | "team" | "teamMembers" | "destinationCalendar"
>
) => {
const { t } = useLocale();
const formMethods = useFormContext<FormValues>();
const { eventType, locationOptions, team } = props;
const { eventType, locationOptions, team, destinationCalendar } = props;
const [showLocationModal, setShowLocationModal] = useState(false);
const [editingLocationType, setEditingLocationType] = useState<string>("");
const [selectedLocation, setSelectedLocation] = useState<LocationOption | undefined>(undefined);
@@ -229,7 +234,10 @@ export const EventSetupTab = (
</li>
);
})}
{validLocations.some((location) => location.type === MeetLocationType) && (
{validLocations.some(
(location) =>
location.type === MeetLocationType && destinationCalendar?.integration !== "google_calendar"
) && (
<div className="flex text-sm text-gray-600">
<FiCheck className="mt-0.5 mr-1.5 h-2 w-2.5" />
<Trans i18nKey="event_type_requres_google_cal">
@@ -241,7 +249,6 @@ export const EventSetupTab = (
className="underline">
here.
</Link>{" "}
We will fall back to Cal video if you do not change it.
</p>
</Trans>
</div>
@@ -272,12 +279,15 @@ export const EventSetupTab = (
defaultValue={eventType.title}
{...formMethods.register("title")}
/>
<TextField
label={t("description")}
placeholder={t("quick_video_meeting")}
defaultValue={eventType.description ?? ""}
{...formMethods.register("description")}
/>
<div>
<Label>{t("description")}</Label>
<Editor
getText={() => md.render(formMethods.getValues("description") || eventType.description || "")}
setText={(value: string) => formMethods.setValue("description", turndown(value))}
excludedToolbarItems={["blockType"]}
placeholder={t("quick_video_meeting")}
/>
</div>
<TextField
required
label={t("URL")}
@@ -1,11 +1,11 @@
import { ArrowRightIcon } from "@heroicons/react/solid";
import MarkdownIt from "markdown-it";
import { useRouter } from "next/router";
import type { FormEvent } from "react";
import { useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { md } from "@calcom/lib/markdownIt";
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import turndown from "@calcom/lib/turndownService";
import { trpc } from "@calcom/trpc/react";
@@ -14,8 +14,6 @@ import { Avatar } from "@calcom/ui";
import type { IOnboardingPageProps } from "../../../pages/getting-started/[[...step]]";
const md = new MarkdownIt("default", { html: true, breaks: true });
type FormData = {
bio: string;
};
@@ -1,10 +1,10 @@
import { ShieldCheckIcon } from "@calcom/ui/components/icon";
import { FiShield } from "@calcom/ui/components/icon";
const TwoFactorModalHeader = ({ title, description }: { title: string; description: string }) => {
return (
<div className="mb-4 sm:flex sm:items-start">
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
<ShieldCheckIcon className="h-6 w-6 text-white" />
<FiShield className="h-6 w-6 text-white" />
</div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 className="font-cal text-lg font-medium leading-6 text-gray-900" id="modal-title">
+2 -4
View File
@@ -1,13 +1,11 @@
import MarkdownIt from "markdown-it";
import Link from "next/link";
import type { TeamPageProps } from "pages/team/[slug]";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { md } from "@calcom/lib/markdownIt";
import { Avatar } from "@calcom/ui";
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
type TeamType = TeamPageProps["team"];
type MembersType = TeamType["members"];
type MemberType = MembersType[number];
@@ -19,7 +17,7 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n
return (
<Link key={member.id} href={`/${member.username}`}>
<div className="sm:min-w-80 sm:max-w-80 dark:bg-darkgray-200 dark:hover:bg-darkgray-300 group flex min-h-full flex-col space-y-2 rounded-md bg-white p-4 hover:cursor-pointer hover:bg-gray-50 ">
<div className="sm:min-w-80 sm:max-w-80 dark:bg-darkgray-200 dark:hover:bg-darkgray-300 group flex min-h-full flex-col space-y-2 rounded-md bg-white p-4 hover:cursor-pointer hover:bg-gray-50 ">
<Avatar
size="md"
alt={member.name || ""}
@@ -1,3 +1,4 @@
import { StarIcon as StarIconSolid } from "@heroicons/react/solid";
import classNames from "classnames";
import { debounce, noop } from "lodash";
import { useRouter } from "next/router";
@@ -14,7 +15,7 @@ import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import { Button, Dialog, DialogClose, DialogContent, DialogHeader, Input, Label } from "@calcom/ui";
import { FiCheck, FiEdit2, FiExternalLink, StarIconSolid } from "@calcom/ui/components/icon";
import { FiCheck, FiEdit2, FiExternalLink } from "@calcom/ui/components/icon";
export enum UsernameChangeStatusEnum {
UPGRADE = "UPGRADE",
@@ -6,7 +6,7 @@ import type {
User,
VerificationToken,
} from "@prisma/client";
import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
import { identityProviderNameMap } from "@lib/auth";
+79 -2
View File
@@ -1,7 +1,56 @@
import type { Prisma, PrismaClient } from "@prisma/client";
import type { z } from "zod";
async function getBooking(prisma: PrismaClient, uid: string) {
const booking = await prisma.booking.findFirst({
import { getBookingResponsesPartialSchema } from "@calcom/features/bookings/lib/getBookingResponsesSchema";
import slugify from "@calcom/lib/slugify";
import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
type BookingSelect = {
description: true;
customInputs: true;
attendees: {
select: {
email: true;
name: true;
};
};
location: true;
smsReminderNumber: true;
};
// Backward Compatibility for booking created before we had managed booking questions
function getResponsesFromOldBooking(
rawBooking: Prisma.BookingGetPayload<{
select: BookingSelect;
}>
) {
const customInputs = rawBooking.customInputs || {};
const responses = Object.keys(customInputs).reduce((acc, label) => {
acc[slugify(label) as keyof typeof acc] = customInputs[label as keyof typeof customInputs];
return acc;
}, {});
return {
// It is possible to have no attendees in a booking when the booking is cancelled.
name: rawBooking.attendees[0]?.name || "Nameless",
email: rawBooking.attendees[0]?.email || "",
guests: rawBooking.attendees.slice(1).map((attendee) => {
return attendee.email;
}),
notes: rawBooking.description || "",
location: {
value: rawBooking.location || "",
optionValue: rawBooking.location || "",
},
...responses,
};
}
async function getBooking(
prisma: PrismaClient,
uid: string,
bookingFields: z.infer<typeof eventTypeBookingFields> & z.BRAND<"HAS_SYSTEM_FIELDS">
) {
const rawBooking = await prisma.booking.findFirst({
where: {
uid,
},
@@ -9,6 +58,7 @@ async function getBooking(prisma: PrismaClient, uid: string) {
startTime: true,
description: true,
customInputs: true,
responses: true,
smsReminderNumber: true,
location: true,
attendees: {
@@ -20,6 +70,14 @@ async function getBooking(prisma: PrismaClient, uid: string) {
},
});
if (!rawBooking) {
return rawBooking;
}
const booking = getBookingWithResponses(rawBooking, {
bookingFields,
});
if (booking) {
// @NOTE: had to do this because Server side cant return [Object objects]
// probably fixable with json.stringify -> json.parse
@@ -31,4 +89,23 @@ async function getBooking(prisma: PrismaClient, uid: string) {
export type GetBookingType = Prisma.PromiseReturnType<typeof getBooking>;
export const getBookingWithResponses = <
T extends Prisma.BookingGetPayload<{
select: BookingSelect & {
responses: true;
};
}>
>(
booking: T,
eventType: {
bookingFields: z.infer<typeof eventTypeBookingFields> & z.BRAND<"HAS_SYSTEM_FIELDS">;
}
) => {
return {
...booking,
responses: getBookingResponsesPartialSchema({
bookingFields: eventType.bookingFields,
}).parse(booking.responses || getResponsesFromOldBooking(booking)),
};
};
export default getBooking;
@@ -1,8 +1,11 @@
import * as fetch from "@lib/core/http/fetch-wrapper";
import type { BookingCreateBody, BookingResponse } from "@lib/types/booking";
import type { BookingCreateBody } from "@calcom/prisma/zod-utils";
const createBooking = async (data: BookingCreateBody) => {
const response = await fetch.post<BookingCreateBody, BookingResponse>("/api/book/event", data);
import * as fetch from "@lib/core/http/fetch-wrapper";
import type { BookingResponse } from "@lib/types/booking";
type BookingCreateBodyForMutation = Omit<BookingCreateBody, "location">;
const createBooking = async (data: BookingCreateBodyForMutation) => {
const response = await fetch.post<BookingCreateBodyForMutation, BookingResponse>("/api/book/event", data);
return response;
};
@@ -1,7 +1,8 @@
import type { BookingCreateBody } from "@calcom/prisma/zod-utils";
import type { AppsStatus } from "@calcom/types/Calendar";
import * as fetch from "@lib/core/http/fetch-wrapper";
import type { BookingCreateBody, BookingResponse } from "@lib/types/booking";
import type { BookingResponse } from "@lib/types/booking";
type ExtendedBookingCreateBody = BookingCreateBody & {
noEmail?: boolean;
-30
View File
@@ -2,36 +2,6 @@ import type { Attendee, Booking } from "@prisma/client";
import type { AppsStatus } from "@calcom/types/Calendar";
export type BookingCreateBody = {
email: string;
end: string;
web3Details?: {
userWallet: string;
userSignature: unknown;
};
eventTypeId: number;
eventTypeSlug: string;
guests?: string[];
location: string;
name: string;
notes?: string;
rescheduleUid?: string;
recurringEventId?: string;
start: string;
timeZone: string;
user?: string | string[];
language: string;
bookingUid?: string;
customInputs: { label: string; value: string | boolean }[];
metadata: {
[key: string]: string;
};
hasHashedBookingLink: boolean;
hashedLink?: string | null;
smsReminderNumber?: string;
ethSignature?: string;
};
export type BookingResponse = Booking & {
paymentUid?: string;
attendees: Attendee[];
+25 -21
View File
@@ -2,20 +2,6 @@ require("dotenv").config({ path: "../../.env" });
const CopyWebpackPlugin = require("copy-webpack-plugin");
const { withSentryConfig } = require("@sentry/nextjs");
const os = require("os");
const withTM = require("next-transpile-modules")([
"@calcom/app-store",
"@calcom/core",
"@calcom/dayjs",
"@calcom/emails",
"@calcom/embed-core",
"@calcom/embed-react",
"@calcom/embed-snippet",
"@calcom/features",
"@calcom/lib",
"@calcom/prisma",
"@calcom/trpc",
"@calcom/ui",
]);
const { withAxiom } = require("next-axiom");
const { i18n } = require("./next-i18next.config");
@@ -80,7 +66,6 @@ if (process.env.ANALYZE === "true") {
plugins.push(withBundleAnalyzer);
}
plugins.push(withTM);
plugins.push(withAxiom);
/** @type {import("next").NextConfig} */
const nextConfig = {
@@ -94,12 +79,31 @@ const nextConfig = {
eslint: {
ignoreDuringBuilds: !!process.env.CI,
},
// TODO: We need to have all components in `@calcom/ui/components` in order to use this
// modularizeImports: {
// "@calcom/ui": {
// transform: "@calcom/ui/components/{{member}}",
// },
// },
transpilePackages: [
"@calcom/app-store",
"@calcom/core",
"@calcom/dayjs",
"@calcom/emails",
"@calcom/embed-core",
"@calcom/embed-react",
"@calcom/embed-snippet",
"@calcom/features",
"@calcom/lib",
"@calcom/prisma",
"@calcom/trpc",
"@calcom/ui",
],
modularizeImports: {
"@calcom/ui/components/icon": {
transform: "@react-icons/all-files/fi/{{member}}",
skipDefaultConversion: true,
preventFullImport: true,
},
// TODO: We need to have all components in `@calcom/ui/components` in order to use this
// "@calcom/ui": {
// transform: "@calcom/ui/components/{{member}}",
// },
},
images: {
unoptimized: true,
},
+7 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "2.6.6",
"version": "2.6.7",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
@@ -45,7 +45,6 @@
"@hookform/resolvers": "^2.9.7",
"@next-auth/prisma-adapter": "^1.0.4",
"@next/bundle-analyzer": "^13.1.6",
"@next/font": "^13.1.1",
"@radix-ui/react-avatar": "^1.0.0",
"@radix-ui/react-collapsible": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.0",
@@ -70,12 +69,13 @@
"bcryptjs": "^2.4.3",
"classnames": "^2.3.1",
"dotenv-cli": "^6.0.0",
"eslint-config-next": "^13.1.6",
"eslint-config-next": "^13.2.1",
"googleapis": "^84.0.0",
"gray-matter": "^4.0.3",
"handlebars": "^4.7.7",
"ical.js": "^1.4.0",
"ics": "^2.37.0",
"jose": "^4.13.1",
"kbar": "^0.1.0-beta.36",
"libphonenumber-js": "^1.10.12",
"lodash": "^4.17.21",
@@ -85,14 +85,13 @@
"memory-cache": "^0.2.0",
"micro": "^10.0.1",
"mime-types": "^2.1.35",
"next": "^13.1.1",
"next": "^13.2.1",
"next-auth": "^4.18.8",
"next-axiom": "^0.16.0",
"next-collect": "^0.2.1",
"next-i18next": "^11.3.0",
"next-seo": "^4.26.0",
"next-themes": "^0.2.0",
"next-transpile-modules": "^10.0.0",
"nodemailer": "^6.7.8",
"otplib": "^12.0.1",
"qrcode": "^1.5.1",
@@ -104,7 +103,7 @@
"react-dom": "^18.2.0",
"react-easy-crop": "^3.5.2",
"react-feather": "^2.0.10",
"react-hook-form": "^7.34.2",
"react-hook-form": "^7.43.3",
"react-hot-toast": "^2.3.0",
"react-intl": "^5.25.1",
"react-live-chat-loader": "^2.7.3",
@@ -116,9 +115,11 @@
"react-use-intercom": "1.5.1",
"react-virtualized-auto-sizer": "^1.0.6",
"react-window": "^1.8.7",
"remark": "^14.0.2",
"rrule": "^2.7.1",
"schema-dts": "^1.1.0",
"short-uuid": "^4.2.0",
"strip-markdown": "^5.0.0",
"stripe": "^9.16.0",
"superjson": "1.9.1",
"tailwindcss-radix": "^2.6.0",
+5 -6
View File
@@ -1,5 +1,5 @@
import { BadgeCheckIcon } from "@heroicons/react/solid";
import classNames from "classnames";
import MarkdownIt from "markdown-it";
import type { GetServerSidePropsContext } from "next";
import Link from "next/link";
import { useRouter } from "next/router";
@@ -23,20 +23,19 @@ import defaultEvents, {
} from "@calcom/lib/defaultEvents";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme";
import { md } from "@calcom/lib/markdownIt";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import prisma from "@calcom/prisma";
import { baseEventTypeSelect } from "@calcom/prisma/selects";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { HeadSeo, AvatarGroup, Avatar } from "@calcom/ui";
import { BadgeCheckIcon, FiArrowRight } from "@calcom/ui/components/icon";
import { Avatar, AvatarGroup, HeadSeo } from "@calcom/ui";
import { FiArrowRight } from "@calcom/ui/components/icon";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
import type { EmbedProps } from "@lib/withEmbedSsr";
import { ssrInit } from "@server/lib/ssr";
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
export default function User(props: inferSSRProps<typeof getServerSideProps> & EmbedProps) {
const { users, profile, eventTypes, isDynamicGroup, dynamicNames, dynamicUsernames, isSingleUser } = props;
const [user] = users; //To be used when we only have a single user, not dynamic group
@@ -146,7 +145,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
{!isBioEmpty && (
<>
<div
className="dark:text-darkgray-600 text-sm text-gray-500 [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"
className=" dark:text-darkgray-600 text-sm text-gray-500 [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"
dangerouslySetInnerHTML={{ __html: md.render(user.bio || "") }}
/>
</>
+5 -9
View File
@@ -5,6 +5,7 @@ import type { LocationObject } from "@calcom/app-store/locations";
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { addListFormatting } from "@calcom/lib/markdownIt";
import type { User } from "@calcom/prisma/client";
import { isBrandingHidden } from "@lib/isBrandingHidden";
@@ -70,7 +71,8 @@ async function getUserPageProps(context: GetStaticPropsContext) {
const user = await prisma.user.findUnique({
where: {
username,
/** TODO: We should standarize this */
username: username.toLowerCase().replace(/( |%20)/g, "+"),
},
select: {
id: true,
@@ -123,13 +125,7 @@ async function getUserPageProps(context: GetStaticPropsContext) {
},
});
const md = new MarkdownIt("zero").enable([
//
"emphasis",
"list",
"newline",
"strikethrough",
]);
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
if (!user || !user.eventTypes.length) return { notFound: true };
@@ -157,7 +153,7 @@ async function getUserPageProps(context: GetStaticPropsContext) {
metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}),
recurringEvent: parseRecurringEvent(eventType.recurringEvent),
locations: privacyFilteredLocations(locations),
descriptionAsSafeHTML: eventType.description ? md.render(eventType.description) : null,
descriptionAsSafeHTML: eventType.description ? addListFormatting(md.render(eventType.description)) : null,
});
// Check if the user you are logging into has any active teams or premium user name
const hasActiveTeam =
+4 -2
View File
@@ -3,6 +3,7 @@ import type { GetServerSidePropsContext } from "next";
import type { LocationObject } from "@calcom/app-store/locations";
import { privacyFilteredLocations } from "@calcom/app-store/locations";
import { getAppFromSlug } from "@calcom/app-store/utils";
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
import { parseRecurringEvent } from "@calcom/lib";
import {
getDefaultEvent,
@@ -118,10 +119,10 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
});
if (!eventTypeRaw) return { notFound: true };
const eventType = {
...eventTypeRaw,
metadata: EventTypeMetaDataSchema.parse(eventTypeRaw.metadata || {}),
bookingFields: getBookingFieldsWithSystemFields(eventTypeRaw),
recurringEvent: parseRecurringEvent(eventTypeRaw.recurringEvent),
};
@@ -183,7 +184,8 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
prisma,
context.query.rescheduleUid
? (context.query.rescheduleUid as string)
: (context.query.bookingUid as string)
: (context.query.bookingUid as string),
eventTypeObject.bookingFields
);
}
+2 -2
View File
@@ -1,6 +1,6 @@
import { Inter } from "@next/font/google";
import localFont from "@next/font/local";
import { DefaultSeo } from "next-seo";
import { Inter } from "next/font/google";
import localFont from "next/font/local";
import Head from "next/head";
import Script from "next/script";
+55 -13
View File
@@ -2,6 +2,7 @@ import type { UserPermissionRole } from "@prisma/client";
import { IdentityProvider } from "@prisma/client";
import { readFileSync } from "fs";
import Handlebars from "handlebars";
import { SignJWT } from "jose";
import type { Session } from "next-auth";
import NextAuth from "next-auth";
import { encode } from "next-auth/jwt";
@@ -18,7 +19,7 @@ import checkLicense from "@calcom/features/ee/common/server/checkLicense";
import ImpersonationProvider from "@calcom/features/ee/impersonation/lib/ImpersonationProvider";
import { hostedCal, isSAMLLoginEnabled } from "@calcom/features/ee/sso/lib/saml";
import { ErrorCode, isPasswordValid, verifyPassword } from "@calcom/lib/auth";
import { APP_NAME, IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
import { APP_NAME, IS_TEAM_BILLING_ENABLED, WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants";
import { symmetricDecrypt } from "@calcom/lib/crypto";
import { defaultCookies } from "@calcom/lib/default-cookies";
import { randomString } from "@calcom/lib/random";
@@ -38,6 +39,21 @@ const transporter = nodemailer.createTransport<TransportOptions>({
const usernameSlug = (username: string) => slugify(username) + "-" + randomString(6).toLowerCase();
const signJwt = async (payload: { email: string }) => {
const secret = new TextEncoder().encode(process.env.CALENDSO_ENCRYPTION_KEY);
return new SignJWT(payload)
.setProtectedHeader({ alg: "HS256" })
.setSubject(payload.email)
.setIssuedAt()
.setIssuer(WEBSITE_URL)
.setAudience(`${WEBSITE_URL}/auth/login`)
.setExpirationTime("2m")
.sign(secret);
};
const loginWithTotp = async (user: { email: string }) =>
`/auth/login?totp=${await signJwt({ email: user.email })}`;
const providers: Provider[] = [
CredentialsProvider({
id: "credentials",
@@ -82,17 +98,19 @@ const providers: Provider[] = [
throw new Error(ErrorCode.IncorrectUsernamePassword);
}
if (user.identityProvider !== IdentityProvider.CAL) {
if (user.identityProvider !== IdentityProvider.CAL && !credentials.totpCode) {
throw new Error(ErrorCode.ThirdPartyIdentityProviderEnabled);
}
if (!user.password) {
if (!user.password && user.identityProvider !== IdentityProvider.CAL && !credentials.totpCode) {
throw new Error(ErrorCode.IncorrectUsernamePassword);
}
const isCorrectPassword = await verifyPassword(credentials.password, user.password);
if (!isCorrectPassword) {
throw new Error(ErrorCode.IncorrectUsernamePassword);
if (user.password) {
const isCorrectPassword = await verifyPassword(credentials.password, user.password);
if (!isCorrectPassword) {
throw new Error(ErrorCode.IncorrectUsernamePassword);
}
}
if (user.twoFactorEnabled) {
@@ -130,7 +148,7 @@ const providers: Provider[] = [
await limiter.check(10, user.email); // 10 requests per minute
// Check if the user you are logging into has any active teams
const hasActiveTeams =
user.teams.filter((m) => {
user.teams.filter((m: { team: { metadata: unknown } }) => {
if (!IS_TEAM_BILLING_ENABLED) return true;
const metadata = teamMetadataSchema.safeParse(m.team.metadata);
if (metadata.success && metadata.data?.subscriptionId) return true;
@@ -449,7 +467,11 @@ export default NextAuth({
console.error("Error while linking account of already existing user");
}
}
return true;
if (existingUser.twoFactorEnabled) {
return loginWithTotp(existingUser);
} else {
return true;
}
}
// If the email address doesn't match, check if an account already exists
@@ -461,7 +483,11 @@ export default NextAuth({
if (!userWithNewEmail) {
await prisma.user.update({ where: { id: existingUser.id }, data: { email: user.email } });
return true;
if (existingUser.twoFactorEnabled) {
return loginWithTotp(existingUser);
} else {
return true;
}
} else {
return "/auth/error?error=new-email-conflict";
}
@@ -477,7 +503,11 @@ export default NextAuth({
if (existingUserWithEmail) {
// if self-hosted then we can allow auto-merge of identity providers if email is verified
if (!hostedCal && existingUserWithEmail.emailVerified) {
return true;
if (existingUserWithEmail.twoFactorEnabled) {
return loginWithTotp(existingUserWithEmail);
} else {
return true;
}
}
// check if user was invited
@@ -499,7 +529,11 @@ export default NextAuth({
},
});
return true;
if (existingUserWithEmail.twoFactorEnabled) {
return loginWithTotp(existingUserWithEmail);
} else {
return true;
}
}
// User signs up with email/password and then tries to login with Google/SAML using the same email
@@ -511,7 +545,11 @@ export default NextAuth({
where: { email: existingUserWithEmail.email },
data: { password: null },
});
return true;
if (existingUserWithEmail.twoFactorEnabled) {
return loginWithTotp(existingUserWithEmail);
} else {
return true;
}
} else if (existingUserWithEmail.identityProvider === IdentityProvider.CAL) {
return "/auth/error?error=use-password-login";
}
@@ -534,7 +572,11 @@ export default NextAuth({
const linkAccountNewUserData = { ...account, userId: newUser.id };
await calcomAdapter.linkAccount(linkAccountNewUserData);
return true;
if (account.twoFactorEnabled) {
return loginWithTotp(newUser);
} else {
return true;
}
}
return false;
+1 -1
View File
@@ -83,7 +83,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
// get user's credentials + their connected integrations
const calendarCredentials = getCalendarCredentials(user.credentials);
// get all the connected integrations' calendars (from third party)
const connectedCalendars = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
const { connectedCalendars } = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
const calendars = connectedCalendars.flatMap((c) => c.calendars).filter(notEmpty);
const selectableCalendars = calendars.map((cal) => {
return { selected: selectedCalendarIds.findIndex((s) => s.externalId === cal.externalId) > -1, ...cal };
+3 -1
View File
@@ -8,7 +8,9 @@ async function handler(req: NextApiRequest & { userId?: number }) {
const session = await getSession({ req });
/* To mimic API behavior and comply with types */
req.userId = session?.user?.id || -1;
const booking = await handleNewBooking(req);
const booking = await handleNewBooking(req, {
isNotAnApiCall: true,
});
return booking;
}
+20
View File
@@ -0,0 +1,20 @@
import type { NextRequest } from "next/server";
export const config = {
runtime: "edge",
};
export default async function handler(req: NextRequest) {
const countryCode = req.headers.get("x-vercel-ip-country") ?? "";
return new Response(
JSON.stringify({
countryCode,
}),
{
status: 200,
headers: {
"content-type": "application/json",
},
}
);
}
@@ -1,39 +0,0 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { fetcher } from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
import { getSession } from "@calcom/lib/auth";
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
const getAccessLinkSchema = z.object({
download_link: z.string().url(),
// expires (timestamp), s3_bucket, s3_region, s3_key
});
const requestQuery = z.object({
recordingId: z.string(),
});
const isDownloadAllowed = async (req: NextApiRequest) => {
if (IS_SELF_HOSTED) return true;
const session = await getSession({ req });
// Check if user belong to active team
return !session?.user?.belongsToActiveTeam;
};
async function handler(
req: NextApiRequest,
res: NextApiResponse<z.infer<typeof getAccessLinkSchema> | void>
) {
const { recordingId } = requestQuery.parse(req.query);
if (!(await isDownloadAllowed(req))) return res.status(403);
const response = await fetcher(`/recordings/${recordingId}/access-link`).then(getAccessLinkSchema.parse);
return res.status(200).json(response);
}
export default defaultHandler({
GET: Promise.resolve({ default: defaultResponder(handler) }),
});
+5 -3
View File
@@ -1,6 +1,8 @@
import { ImageResponse } from "@vercel/og";
import type { NextApiRequest } from "next";
import { remark } from "remark";
import type { SatoriOptions } from "satori";
import strip from "strip-markdown";
import { z } from "zod";
import { Meeting, App, Generic } from "@calcom/lib/OgImages";
@@ -18,7 +20,7 @@ const interFontMedium = fetch(new URL("../../../../public/fonts/Inter-Medium.ttf
);
export const config = {
runtime: "experimental-edge",
runtime: "edge",
};
const meetingSchema = z.object({
@@ -106,8 +108,8 @@ export default async function handler(req: NextApiRequest) {
description: searchParams.get("description"),
imageType,
});
const img = new ImageResponse(<Generic title={title} description={description} />, ogConfig) as {
const description_ = await (await remark().use(strip).process(description)).toString();
const img = new ImageResponse(<Generic title={title} description={description_} />, ogConfig) as {
body: Buffer;
};
+1
View File
@@ -65,6 +65,7 @@ export default trpcNext.createNextApiHandler({
"viewer.public.i18n": `no-cache`,
// Revalidation time here should be 1 second, per https://github.com/calcom/cal.com/pull/6823#issuecomment-1423215321
"viewer.public.slots.getSchedule": `no-cache`, // FIXME
"viewer.public.cityTimezones": `max-age=${ONE_DAY_IN_SECONDS}, stale-while-revalidate`,
} as const;
// Find which element above is an exact match for this group of paths
+16 -4
View File
@@ -1,5 +1,5 @@
import { useRouter } from "next/router";
import { useReducer, useState } from "react";
import { useCallback, useReducer, useState } from "react";
import z from "zod";
import { AppSettings } from "@calcom/app-store/_components/AppSettings";
@@ -7,8 +7,9 @@ import { InstallAppButton } from "@calcom/app-store/components";
import type { EventLocationType } from "@calcom/app-store/locations";
import { getEventLocationTypeFromApp } from "@calcom/app-store/locations";
import { InstalledAppVariants } from "@calcom/app-store/utils";
import { AppSetDefaultLinkDailog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
import { AppSetDefaultLinkDialog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
import DisconnectIntegrationModal from "@calcom/features/apps/components/DisconnectIntegrationModal";
import { BulkEditDefaultConferencingModal } from "@calcom/features/eventtypes/components/BulkEditDefaultConferencingModal";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
@@ -113,11 +114,16 @@ interface IntegrationsListProps {
const IntegrationsList = ({ data, handleDisconnect, variant }: IntegrationsListProps) => {
const { data: defaultConferencingApp } = trpc.viewer.getUsersDefaultConferencingApp.useQuery();
const utils = trpc.useContext();
const [bulkUpdateModal, setBulkUpdateModal] = useState(false);
const [locationType, setLocationType] = useState<(EventLocationType & { slug: string }) | undefined>(
undefined
);
const onSuccessCallback = useCallback(() => {
setBulkUpdateModal(true);
showToast("Default app updated successfully", "success");
}, []);
const updateDefaultAppMutation = trpc.viewer.updateUserDefaultConferencingApp.useMutation({
onSuccess: () => {
showToast("Default app updated successfully", "success");
@@ -172,6 +178,7 @@ const IntegrationsList = ({ data, handleDisconnect, variant }: IntegrationsListP
updateDefaultAppMutation.mutate({
appSlug,
});
setBulkUpdateModal(true);
}
}}>
{t("change_default_conferencing_app")}
@@ -196,11 +203,16 @@ const IntegrationsList = ({ data, handleDisconnect, variant }: IntegrationsListP
})}
</List>
{locationType && (
<AppSetDefaultLinkDailog
<AppSetDefaultLinkDialog
locationType={locationType}
setLocationType={() => setLocationType(undefined)}
onSuccess={onSuccessCallback}
/>
)}
{bulkUpdateModal && (
<BulkEditDefaultConferencingModal open={bulkUpdateModal} setOpen={setBulkUpdateModal} />
)}
</>
);
};
+53 -5
View File
@@ -1,4 +1,5 @@
import classNames from "classnames";
import { jwtVerify } from "jose";
import type { GetServerSidePropsContext } from "next";
import { getCsrfToken, signIn } from "next-auth/react";
import Link from "next/link";
@@ -42,14 +43,14 @@ export default function Login({
isSAMLLoginEnabled,
samlTenantID,
samlProductID,
totpEmail,
}: inferSSRProps<typeof _getServerSideProps> & WithNonceProps) {
const { t } = useLocale();
const router = useRouter();
const methods = useForm<LoginValues>();
const { register, formState } = methods;
const [twoFactorRequired, setTwoFactorRequired] = useState(false);
const [twoFactorRequired, setTwoFactorRequired] = useState(!!totpEmail || false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const errorMessages: { [key: string]: string } = {
@@ -94,6 +95,16 @@ export default function Login({
</Button>
);
const ExternalTotpFooter = (
<Button
onClick={() => {
window.location.replace("/");
}}
color="minimal">
{t("cancel")}
</Button>
);
const onSubmit = async (values: LoginValues) => {
setErrorMessage(null);
telemetry.event(telemetryEventTypes.login, collectPageParameters());
@@ -120,7 +131,9 @@ export default function Login({
heading={twoFactorRequired ? t("2fa_code") : t("welcome_back")}
footerText={
twoFactorRequired
? TwoFactorFooter
? !totpEmail
? TwoFactorFooter
: ExternalTotpFooter
: process.env.NEXT_PUBLIC_DISABLE_SIGNUP !== "true"
? LoginFooter
: null
@@ -135,7 +148,7 @@ export default function Login({
<EmailField
id="email"
label={t("email_address")}
defaultValue={router.query.email as string}
defaultValue={totpEmail || (router.query.email as string)}
placeholder="john.doe@example.com"
required
{...register("email")}
@@ -152,7 +165,7 @@ export default function Login({
<PasswordField
id="password"
autoComplete="off"
required
required={!totpEmail}
className="mb-0"
{...register("password")}
/>
@@ -211,6 +224,40 @@ const _getServerSideProps = async function getServerSideProps(context: GetServer
const session = await getSession({ req });
const ssr = await ssrInit(context);
const verifyJwt = (jwt: string) => {
const secret = new TextEncoder().encode(process.env.CALENDSO_ENCRYPTION_KEY);
return jwtVerify(jwt, secret, {
issuer: WEBSITE_URL,
audience: `${WEBSITE_URL}/auth/login`,
algorithms: ["HS256"],
});
};
let totpEmail = null;
if (context.query.totp) {
try {
const decryptedJwt = await verifyJwt(context.query.totp as string);
if (decryptedJwt.payload) {
totpEmail = decryptedJwt.payload.email as string;
} else {
return {
redirect: {
destination: "/auth/error?error=JWT%20Invalid%20Payload",
permanent: false,
},
};
}
} catch (e) {
return {
redirect: {
destination: "/auth/error?error=Invalid%20JWT%3A%20Please%20try%20again",
permanent: false,
},
};
}
}
if (session) {
return {
redirect: {
@@ -238,6 +285,7 @@ const _getServerSideProps = async function getServerSideProps(context: GetServer
isSAMLLoginEnabled,
samlTenantID,
samlProductID,
totpEmail,
},
};
};
+72 -39
View File
@@ -1,4 +1,4 @@
import type { GetServerSidePropsContext } from "next";
import { useRouter } from "next/router";
import { Controller, useFieldArray, useForm } from "react-hook-form";
import { z } from "zod";
@@ -8,7 +8,7 @@ import Shell from "@calcom/features/shell/Shell";
import { availabilityAsString } from "@calcom/lib/availability";
import { yyyymmdd } from "@calcom/lib/date-fns";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { stringOrNumber } from "@calcom/prisma/zod-utils";
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
import { trpc } from "@calcom/trpc/react";
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
import type { Schedule as ScheduleType, TimeRange, WorkingHours } from "@calcom/types/schedule";
@@ -22,19 +22,20 @@ import {
Switch,
TimezoneSelect,
Tooltip,
Dialog,
DialogTrigger,
ConfirmationDialogContent,
VerticalDivider,
} from "@calcom/ui";
import { FiInfo, FiPlus } from "@calcom/ui/components/icon";
import { FiInfo, FiPlus, FiTrash } from "@calcom/ui/components/icon";
import { HttpError } from "@lib/core/http/error";
import { SelectSkeletonLoader } from "@components/availability/SkeletonLoader";
import EditableHeading from "@components/ui/EditableHeading";
import { ssrInit } from "@server/lib/ssr";
const querySchema = z.object({
schedule: stringOrNumber,
schedule: z.coerce.number().positive().optional(),
});
type AvailabilityFormValues = {
@@ -84,15 +85,29 @@ const DateOverride = ({ workingHours }: { workingHours: WorkingHours[] }) => {
);
};
export default function Availability({ schedule }: { schedule: number }) {
export default function Availability() {
const { t, i18n } = useLocale();
const router = useRouter();
const utils = trpc.useContext();
const me = useMeQuery();
const {
data: { schedule: scheduleId },
} = useTypedQuery(querySchema);
const { timeFormat } = me.data || { timeFormat: null };
const { data, isLoading } = trpc.viewer.availability.schedule.get.useQuery({ scheduleId: schedule });
const { data: defaultValues } = trpc.viewer.availability.defaultValues.useQuery({ scheduleId: schedule });
const form = useForm<AvailabilityFormValues>({ defaultValues });
const { control } = form;
const { data: schedule, isLoading } = trpc.viewer.availability.schedule.get.useQuery(
{ scheduleId },
{
enabled: !!scheduleId,
}
);
const form = useForm<AvailabilityFormValues>({
values: schedule && {
...schedule,
schedule: schedule?.availability || [],
},
});
const updateMutation = trpc.viewer.availability.schedule.update.useMutation({
onSuccess: async ({ prevDefaultId, currentDefaultId, ...data }) => {
if (prevDefaultId && currentDefaultId) {
@@ -120,10 +135,26 @@ export default function Availability({ schedule }: { schedule: number }) {
},
});
const deleteMutation = trpc.viewer.availability.schedule.delete.useMutation({
onError: (err) => {
if (err instanceof HttpError) {
const message = `${err.statusCode}: ${err.message}`;
showToast(message, "error");
}
},
onSettled: () => {
utils.viewer.availability.list.invalidate();
},
onSuccess: () => {
showToast(t("schedule_deleted_successfully"), "success");
router.push("/availability");
},
});
return (
<Shell
backPath="/availability"
title={data?.schedule.name ? data.schedule.name + " | " + t("availability") : t("availability")}
title={schedule?.name ? schedule.name + " | " + t("availability") : t("availability")}
heading={
<Controller
control={form.control}
@@ -134,8 +165,8 @@ export default function Availability({ schedule }: { schedule: number }) {
/>
}
subtitle={
data ? (
data.schedule.availability
schedule ? (
schedule.schedule
.filter((availability) => !!availability.days.length)
.map((availability) => (
<span key={availability.id}>
@@ -158,7 +189,7 @@ export default function Availability({ schedule }: { schedule: number }) {
</Skeleton>
<Switch
id="hiddenSwitch"
disabled={isLoading || data?.isDefault}
disabled={isLoading || schedule?.isDefault}
checked={form.watch("isDefault")}
onCheckedChange={(e) => {
form.setValue("isDefault", e);
@@ -166,6 +197,24 @@ export default function Availability({ schedule }: { schedule: number }) {
/>
</div>
<VerticalDivider />
<Dialog>
<DialogTrigger asChild>
<Button StartIcon={FiTrash} variant="icon" color="destructive" aria-label={t("delete")} />
</DialogTrigger>
<ConfirmationDialogContent
isLoading={deleteMutation.isLoading}
variety="danger"
title={t("delete_schedule")}
confirmBtnText={t("delete")}
loadingText={t("delete")}
onConfirm={() => {
scheduleId && deleteMutation.mutate({ scheduleId });
}}>
{t("delete_schedule_description")}
</ConfirmationDialogContent>
</Dialog>
<VerticalDivider />
<div className="border-l-2 border-gray-300" />
@@ -179,11 +228,12 @@ export default function Availability({ schedule }: { schedule: number }) {
form={form}
id="availability-form"
handleSubmit={async ({ dateOverrides, ...values }) => {
updateMutation.mutate({
scheduleId: schedule,
dateOverrides: dateOverrides.flatMap((override) => override.ranges),
...values,
});
scheduleId &&
updateMutation.mutate({
scheduleId,
dateOverrides: dateOverrides.flatMap((override) => override.ranges),
...values,
});
}}
className="flex flex-col sm:mx-0 xl:flex-row xl:space-x-6">
<div className="flex-1 flex-row xl:mr-0">
@@ -191,7 +241,7 @@ export default function Availability({ schedule }: { schedule: number }) {
<div>
{typeof me.data?.weekStart === "string" && (
<Schedule
control={control}
control={form.control}
name="schedule"
weekStart={
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].indexOf(
@@ -203,7 +253,7 @@ export default function Availability({ schedule }: { schedule: number }) {
</div>
</div>
<div className="my-6 rounded-md border">
{data?.workingHours && <DateOverride workingHours={data.workingHours} />}
{schedule?.workingHours && <DateOverride workingHours={schedule.workingHours} />}
</div>
</div>
<div className="min-w-40 col-span-3 space-y-2 lg:col-span-1">
@@ -243,20 +293,3 @@ export default function Availability({ schedule }: { schedule: number }) {
</Shell>
);
}
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const params = querySchema.safeParse(ctx.params);
const ssr = await ssrInit(ctx);
if (!params.success) return { notFound: true };
const scheduleId = params.data.schedule;
await ssr.viewer.availability.schedule.get.fetch({ scheduleId });
await ssr.viewer.availability.defaultValues.fetch({ scheduleId });
return {
props: {
schedule: scheduleId,
trpcState: ssr.dehydrate(),
},
};
};
+56 -83
View File
@@ -1,4 +1,4 @@
import { BookingStatus, WorkflowActions } from "@prisma/client";
import { BookingStatus } from "@prisma/client";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
import classNames from "classnames";
import { createEvent } from "ics";
@@ -23,6 +23,10 @@ import {
useIsBackgroundTransparent,
useIsEmbed,
} from "@calcom/embed-core/embed-iframe";
import {
SystemField,
getBookingFieldsWithSystemFields,
} from "@calcom/features/bookings/lib/getBookingFields";
import { parseRecurringEvent } from "@calcom/lib";
import CustomBranding from "@calcom/lib/CustomBranding";
import { APP_NAME } from "@calcom/lib/constants";
@@ -42,10 +46,11 @@ import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { Button, EmailInput, HeadSeo } from "@calcom/ui";
import { Badge, Button, EmailInput, HeadSeo } from "@calcom/ui";
import { FiX, FiExternalLink, FiChevronLeft, FiCheck, FiCalendar } from "@calcom/ui/components/icon";
import { timeZone } from "@lib/clock";
import { getBookingWithResponses } from "@lib/getBooking";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
import CancelBooking from "@components/booking/CancelBooking";
@@ -195,10 +200,7 @@ export default function Success(props: SuccessProps) {
window.scrollTo(0, document.body.scrollHeight);
}
const location: ReturnType<typeof getEventLocationValue> = props.bookingInfo.location
? props.bookingInfo.location
: // If there is no location set then we default to Cal Video
"integrations:daily";
const location = props.bookingInfo.location as ReturnType<typeof getEventLocationValue>;
const locationVideoCallUrl: string | undefined = bookingMetadataSchema.parse(
props?.bookingInfo?.metadata || {}
@@ -335,12 +337,12 @@ export default function Success(props: SuccessProps) {
}
return t("emailed_you_and_attendees" + titleSuffix);
}
const userIsOwner = !!(session?.user?.id && eventType.owner?.id === session.user.id);
useTheme(isSuccessBookingPage ? props.profile.theme : "light");
const title = t(
`booking_${needsConfirmation ? "submitted" : "confirmed"}${props.recurringBookings ? "_recurring" : ""}`
);
const customInputs = bookingInfo?.customInputs;
const locationToDisplay = getSuccessPageLocationMessage(
locationVideoCallUrl ? locationVideoCallUrl : location,
@@ -348,10 +350,6 @@ export default function Success(props: SuccessProps) {
bookingInfo.status
);
const hasSMSAttendeeAction =
eventType.workflows.find((workflowEventType) =>
workflowEventType.workflow.steps.find((step) => step.action === WorkflowActions.SMS_ATTENDEE)
) !== undefined;
const providerName = guessEventLocationType(location)?.label;
return (
@@ -499,7 +497,12 @@ export default function Success(props: SuccessProps) {
<>
{bookingInfo?.user && (
<div className="mb-3">
<p>{bookingInfo.user.name}</p>
<p>
<span className="mr-2">{bookingInfo.user.name}</span>
<Badge variant="blue" bold>
{t("Host")}
</Badge>
</p>
<p className="text-bookinglight">{bookingInfo.user.email}</p>
</div>
)}
@@ -541,63 +544,28 @@ export default function Success(props: SuccessProps) {
</div>
</>
)}
{customInputs &&
Object.keys(customInputs).map((key) => {
// This breaks if you have two label that are the same.
// TODO: Fix this in another PR
const customInput = customInputs[key as keyof typeof customInputs];
const eventTypeCustomFound = eventType.customInputs?.find((ci) => ci.label === key);
return (
<>
{eventTypeCustomFound?.type === "RADIO" && (
<>
<div className="border-bookinglightest dark:border-darkgray-300 col-span-3 mt-8 border-t pt-8 pr-3 font-medium">
{eventTypeCustomFound.label}
</div>
<div className="col-span-3 mt-1 mb-2">
{eventTypeCustomFound.options &&
eventTypeCustomFound.options.map((option) => {
const selected = option.label == customInput;
return (
<div
key={option.label}
className={classNames(
"flex space-x-1",
!selected && "text-gray-500"
)}>
<p>{option.label}</p>
<span>{option.label === customInput && "✅"}</span>
</div>
);
})}
</div>
</>
)}
{eventTypeCustomFound?.type !== "RADIO" && customInput !== "" && (
<>
<div className="border-bookinglightest dark:border-darkgray-300 col-span-3 mt-8 border-t pt-8 pr-3 font-medium">
{key}
</div>
<div className="col-span-3 mt-2 mb-2">
{typeof customInput === "boolean" ? (
<p>{customInput ? "true" : "false"}</p>
) : (
<p>{customInput}</p>
)}
</div>
</>
)}
</>
);
})}
{bookingInfo?.smsReminderNumber && hasSMSAttendeeAction && (
<>
<div className="mt-9 font-medium">{t("number_sms_notifications")}</div>
<div className="col-span-2 mb-2 mt-9">
<p>{bookingInfo.smsReminderNumber}</p>
</div>
</>
)}
{Object.entries(bookingInfo.responses).map(([name, response]) => {
const field = eventType.bookingFields.find((field) => field.name === name);
// We show location in the "where" section
// We show Booker Name, Emails and guests in Who section
// We show notes in additional notes section
// We show rescheduleReason at the top
if (!field) return null;
const isSystemField = SystemField.safeParse(field.name);
if (isSystemField.success) return null;
const label = field.label || t(field.defaultLabel || "");
return (
<>
<div className="mt-9 font-medium">{label}</div>
<div className="col-span-2 mb-2 mt-9">
<p className="break-words">{response.toString()}</p>
</div>
</>
);
})}
</div>
</div>
{(!needsConfirmation || !userIsOwner) &&
@@ -828,7 +796,6 @@ export function RecurringBookings({
t,
i18n: { language },
} = useLocale();
const recurringBookingsSorted = recurringBookings
? recurringBookings.sort((a: ConfigType, b: ConfigType) => (dayjs(a).isAfter(dayjs(b)) ? 1 : -1))
: null;
@@ -855,7 +822,7 @@ export function RecurringBookings({
{formatToLocalizedTime(dayjs(dateStr), language, undefined, !is24h)} -{" "}
{formatToLocalizedTime(dayjs(dateStr).add(duration, "m"), language, undefined, !is24h)}{" "}
<span className="text-bookinglight">
({formatToLocalizedTimezone(dayjs(dateStr), language)})
({formatToLocalizedTimezone(dayjs(dateStr), language, timeZone())})
</span>
</div>
))}
@@ -875,7 +842,7 @@ export function RecurringBookings({
{formatToLocalizedTime(date, language, undefined, !is24h)} -{" "}
{formatToLocalizedTime(dayjs(date).add(duration, "m"), language, undefined, !is24h)}{" "}
<span className="text-bookinglight">
({formatToLocalizedTimezone(dayjs(dateStr), language)})
({formatToLocalizedTimezone(dayjs(dateStr), language, timeZone())})
</span>
</div>
))}
@@ -892,7 +859,7 @@ export function RecurringBookings({
<br />
{formatToLocalizedTime(date, language, undefined, !is24h)} -{" "}
{formatToLocalizedTime(dayjs(date).add(duration, "m"), language, undefined, !is24h)}{" "}
<span className="text-bookinglight">({formatToLocalizedTimezone(date, language)})</span>
<span className="text-bookinglight">({formatToLocalizedTimezone(date, language, timeZone())})</span>
</div>
);
}
@@ -927,6 +894,8 @@ const getEventTypesFromDB = async (id: number) => {
locations: true,
price: true,
currency: true,
bookingFields: true,
disableGuests: true,
owner: {
select: userSelect,
},
@@ -951,6 +920,7 @@ const getEventTypesFromDB = async (id: number) => {
select: {
workflow: {
select: {
id: true,
steps: true,
},
},
@@ -973,6 +943,7 @@ const getEventTypesFromDB = async (id: number) => {
return {
isDynamic: false,
...eventType,
bookingFields: getBookingFieldsWithSystemFields(eventType),
metadata,
};
};
@@ -1017,7 +988,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
if (!parsedQuery.success) return { notFound: true };
const { uid, email, eventTypeSlug, cancel } = parsedQuery.data;
const bookingInfo = await prisma.booking.findFirst({
const bookingInfoRaw = await prisma.booking.findFirst({
where: {
uid,
},
@@ -1035,6 +1006,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
status: true,
metadata: true,
cancellationReason: true,
responses: true,
rejectionReason: true,
user: {
select: {
@@ -1059,27 +1031,28 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
},
},
});
if (!bookingInfo) {
if (!bookingInfoRaw) {
return {
notFound: true,
};
}
// @NOTE: had to do this because Server side cant return [Object objects]
// probably fixable with json.stringify -> json.parse
bookingInfo["startTime"] = (bookingInfo?.startTime as Date)?.toISOString() as unknown as Date;
bookingInfo["endTime"] = (bookingInfo?.endTime as Date)?.toISOString() as unknown as Date;
const eventTypeRaw = !bookingInfo.eventTypeId
const eventTypeRaw = !bookingInfoRaw.eventTypeId
? getDefaultEvent(eventTypeSlug || "")
: await getEventTypesFromDB(bookingInfo.eventTypeId);
: await getEventTypesFromDB(bookingInfoRaw.eventTypeId);
if (!eventTypeRaw) {
return {
notFound: true,
};
}
const bookingInfo = getBookingWithResponses(bookingInfoRaw, eventTypeRaw);
// @NOTE: had to do this because Server side cant return [Object objects]
// probably fixable with json.stringify -> json.parse
bookingInfo["startTime"] = (bookingInfo?.startTime as Date)?.toISOString() as unknown as Date;
bookingInfo["endTime"] = (bookingInfo?.endTime as Date)?.toISOString() as unknown as Date;
eventTypeRaw.users = !!eventTypeRaw.hosts?.length
? eventTypeRaw.hosts.map((host) => host.user)
: eventTypeRaw.users;
+2 -1
View File
@@ -3,7 +3,7 @@ import type { GetServerSidePropsContext } from "next";
import { parseRecurringEvent } from "@calcom/lib";
import prisma from "@calcom/prisma";
import { bookEventTypeSelect } from "@calcom/prisma/selects";
import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { customInputSchema, eventTypeBookingFields, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { asStringOrNull, asStringOrThrow } from "@lib/asStringOrNull";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
@@ -71,6 +71,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
...eventTypeRaw,
metadata: EventTypeMetaDataSchema.parse(eventTypeRaw.metadata || {}),
recurringEvent: parseRecurringEvent(eventTypeRaw.recurringEvent),
bookingFields: eventTypeBookingFields.parse(eventTypeRaw.bookingFields || []),
};
const eventTypeObject = [eventType].map((e) => {
+54 -41
View File
@@ -4,10 +4,11 @@ import { zodResolver } from "@hookform/resolvers/zod";
import type { PeriodType } from "@prisma/client";
import { SchedulingType } from "@prisma/client";
import type { GetServerSidePropsContext } from "next";
import { useState } from "react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { validateCustomEventName } from "@calcom/core/event";
import type { EventLocationType } from "@calcom/core/location";
import { validateBookingLimitOrder } from "@calcom/lib";
import { CAL_URL } from "@calcom/lib/constants";
@@ -16,6 +17,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
import { HttpError } from "@calcom/lib/http-error";
import prisma from "@calcom/prisma";
import { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
import type { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
@@ -86,6 +88,7 @@ export type FormValues = {
bookingLimits?: BookingLimit;
hosts: { userId: number }[];
hostsFixed: { userId: number }[];
bookingFields: z.infer<typeof eventTypeBookingFields>;
};
export type CustomInputParsed = typeof customInputSchema._output;
@@ -120,7 +123,7 @@ const EventTypePage = (props: EventTypeSetupProps) => {
extendsFeature: "EventType",
});
const { eventType, locationOptions, team, teamMembers, currentUserMembership } = props;
const { eventType, locationOptions, team, teamMembers, currentUserMembership, destinationCalendar } = props;
const [animationParentRef] = useAutoAnimate<HTMLDivElement>();
const updateMutation = trpc.viewer.eventTypes.update.useMutation({
@@ -178,48 +181,65 @@ const EventTypePage = (props: EventTypeSetupProps) => {
delete metadata.config?.useHostSchedulesForTeamEvent;
}
const formMethods = useForm<FormValues>({
defaultValues: {
title: eventType.title,
locations: eventType.locations || [],
recurringEvent: eventType.recurringEvent || null,
description: eventType.description ?? undefined,
schedule: eventType.schedule || undefined,
bookingLimits: eventType.bookingLimits || undefined,
length: eventType.length,
hidden: eventType.hidden,
periodDates: {
startDate: periodDates.startDate,
endDate: periodDates.endDate,
},
periodType: eventType.periodType,
periodCountCalendarDays: eventType.periodCountCalendarDays ? "1" : "0",
schedulingType: eventType.schedulingType,
minimumBookingNotice: eventType.minimumBookingNotice,
metadata,
hosts: !!eventType.hosts?.length
? eventType.hosts.filter((host) => !host.isFixed)
: eventType.users
.filter(() => eventType.schedulingType === SchedulingType.ROUND_ROBIN)
.map((user) => ({ userId: user.id })),
hostsFixed: !!eventType.hosts?.length
? eventType.hosts.filter((host) => host.isFixed)
: eventType.users
.filter(() => eventType.schedulingType === SchedulingType.COLLECTIVE)
.map((user) => ({ userId: user.id })),
const defaultValues = {
title: eventType.title,
locations: eventType.locations || [],
recurringEvent: eventType.recurringEvent || null,
description: eventType.description ?? undefined,
schedule: eventType.schedule || undefined,
bookingLimits: eventType.bookingLimits || undefined,
length: eventType.length,
hidden: eventType.hidden,
periodDates: {
startDate: periodDates.startDate,
endDate: periodDates.endDate,
},
bookingFields: eventType.bookingFields,
periodType: eventType.periodType,
periodCountCalendarDays: eventType.periodCountCalendarDays ? "1" : "0",
schedulingType: eventType.schedulingType,
minimumBookingNotice: eventType.minimumBookingNotice,
metadata,
hosts: !!eventType.hosts?.length
? eventType.hosts.filter((host) => !host.isFixed)
: eventType.users
.filter(() => eventType.schedulingType === SchedulingType.ROUND_ROBIN)
.map((user) => ({ userId: user.id })),
hostsFixed: !!eventType.hosts?.length
? eventType.hosts.filter((host) => host.isFixed)
: eventType.users
.filter(() => eventType.schedulingType === SchedulingType.COLLECTIVE)
.map((user) => ({ userId: user.id })),
} as const;
const formMethods = useForm<FormValues>({
defaultValues,
resolver: zodResolver(
z
.object({
// Length if string, is converted to a number or it can be a number
// Make it optional because it's not submitted from all tabs of the page
eventName: z
.string()
.refine((val) => validateCustomEventName(val, t("invalid_event_name_variables")) === true, {
message: t("invalid_event_name_variables"),
})
.optional(),
length: z.union([z.string().transform((val) => +val), z.number()]).optional(),
bookingFields: eventTypeBookingFields,
})
// TODO: Add schema for other fields later.
.passthrough()
),
});
useEffect(() => {
if (!formMethods.formState.isDirty) {
//TODO: What's the best way to sync the form with backend
formMethods.setValue("bookingFields", defaultValues.bookingFields);
}
}, [defaultValues]);
const appsMetadata = formMethods.getValues("metadata")?.apps;
const numberOfInstalledApps = eventTypeApps?.filter((app) => app.isInstalled).length || 0;
let numberOfActiveApps = 0;
@@ -241,6 +261,7 @@ const EventTypePage = (props: EventTypeSetupProps) => {
locationOptions={locationOptions}
team={team}
teamMembers={teamMembers}
destinationCalendar={destinationCalendar}
/>
),
availability: <AvailabilityTab isTeamEvent={!!team} />,
@@ -342,13 +363,7 @@ const EventTypePage = (props: EventTypeSetupProps) => {
};
const EventTypePageWrapper = (props: inferSSRProps<typeof getServerSideProps>) => {
const { data, isLoading } = trpc.viewer.eventTypes.get.useQuery(
{ id: props.type },
{
initialData: props.initialData,
}
);
const { data, isLoading } = trpc.viewer.eventTypes.get.useQuery({ id: props.type });
if (isLoading || !data) return null;
return <EventTypePage {...data} />;
};
@@ -391,9 +406,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
},
};
} catch (err) {
return {
notFound: true,
};
throw err;
}
};
+31 -9
View File
@@ -5,10 +5,9 @@ import type { FC } from "react";
import { useEffect, useState, memo } from "react";
import { z } from "zod";
import {
CreateEventTypeButton,
EventTypeDescriptionLazy as EventTypeDescription,
} from "@calcom/features/eventtypes/components";
import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components";
import CreateEventTypeDialog from "@calcom/features/eventtypes/components/CreateEventTypeDialog";
import { DuplicateDialog } from "@calcom/features/eventtypes/components/DuplicateDialog";
import Shell from "@calcom/features/shell/Shell";
import { APP_NAME, CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -35,6 +34,7 @@ import {
showToast,
Switch,
Tooltip,
CreateButton,
HorizontalTabs,
} from "@calcom/ui";
import {
@@ -119,7 +119,7 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
<Link
href={`/event-types/${type.id}?tabName=setup`}
className="flex-1 overflow-hidden pr-4 text-sm"
title={`${type.title} ${type.description ? `– ${type.description}` : ""}`}>
title={type.title}>
<div>
<span
className="font-semibold text-gray-700 ltr:mr-1 rtl:ml-1"
@@ -134,14 +134,15 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
</small>
) : null}
{readOnly && (
<span className="items-center rounded-sm bg-gray-100 px-1.5 py-0.5 text-xs font-medium text-gray-800 ltr:ml-2 ltr:mr-2 rtl:ml-2">
<Badge variant="gray" className="ml-2">
{t("readonly")}
</span>
</Badge>
)}
</div>
<EventTypeDescription
// @ts-expect-error FIXME: We have a type mismatch here @hariombalhara @sean-brydon
eventType={type}
shortenDescription
/>
</Link>
);
@@ -240,7 +241,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
const openDuplicateModal = (eventType: EventType, group: EventTypeGroup) => {
const query = {
...router.query,
dialog: "duplicate-event-type",
dialog: "duplicate",
title: eventType.title,
description: eventType.description,
slug: eventType.slug,
@@ -627,17 +628,37 @@ const CreateFirstEventTypeView = () => {
};
const CTA = () => {
const { t } = useLocale();
const query = trpc.viewer.eventTypes.getByViewer.useQuery();
if (!query.data) return null;
return <CreateEventTypeButton canAddEvents={true} options={query.data.profiles} />;
const profileOptions = query.data.profiles
.filter((profile) => !profile.readOnly)
.map((profile) => {
return {
teamId: profile.teamId,
label: profile.name || profile.slug,
image: profile.image,
slug: profile.slug,
};
});
return (
<CreateButton
subtitle={t("create_event_on").toUpperCase()}
options={profileOptions}
createDialog={CreateEventTypeDialog}
/>
);
};
const WithQuery = withQuery(trpc.viewer.eventTypes.getByViewer);
const EventTypesPage = () => {
const { t } = useLocale();
const router = useRouter();
const isMobile = useMediaQuery("(max-width: 768px)");
@@ -683,6 +704,7 @@ const EventTypesPage = () => {
)}
<EmbedDialog />
{router.query.dialog === "duplicate" && <DuplicateDialog />}
</>
)}
/>
@@ -1,8 +1,9 @@
import { useState } from "react";
import { useCallback, useState } from "react";
import type { EventLocationType } from "@calcom/app-store/locations";
import { getEventLocationTypeFromApp } from "@calcom/app-store/locations";
import { AppSetDefaultLinkDailog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
import { AppSetDefaultLinkDialog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
import { BulkEditDefaultConferencingModal } from "@calcom/features/eventtypes/components/BulkEditDefaultConferencingModal";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
@@ -62,17 +63,20 @@ const ConferencingLayout = () => {
},
});
const onSuccessCallback = useCallback(() => {
setBulkUpdateModal(true);
showToast("Default app updated successfully", "success");
}, []);
const updateDefaultAppMutation = trpc.viewer.updateUserDefaultConferencingApp.useMutation({
onSuccess: () => {
showToast("Default app updated successfully", "success");
utils.viewer.getUsersDefaultConferencingApp.invalidate();
},
onSuccess: onSuccessCallback,
onError: (error) => {
showToast(`Error: ${error.message}`, "error");
},
});
const [deleteAppModal, setDeleteAppModal] = useState(false);
const [bulkUpdateModal, setBulkUpdateModal] = useState(false);
const [locationType, setLocationType] = useState<(EventLocationType & { slug: string }) | undefined>(
undefined
);
@@ -167,7 +171,14 @@ const ConferencingLayout = () => {
</Dialog>
{locationType && (
<AppSetDefaultLinkDailog locationType={locationType} setLocationType={setLocationType} />
<AppSetDefaultLinkDialog
locationType={locationType}
setLocationType={setLocationType}
onSuccess={onSuccessCallback}
/>
)}
{bulkUpdateModal && (
<BulkEditDefaultConferencingModal open={bulkUpdateModal} setOpen={setBulkUpdateModal} />
)}
</div>
);
@@ -1,6 +1,5 @@
import { IdentityProvider } from "@prisma/client";
import crypto from "crypto";
import MarkdownIt from "markdown-it";
import { signOut } from "next-auth/react";
import type { BaseSyntheticEvent } from "react";
import { useRef, useState } from "react";
@@ -10,6 +9,7 @@ import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import { ErrorCode } from "@calcom/lib/auth";
import { APP_NAME } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { md } from "@calcom/lib/markdownIt";
import turndown from "@calcom/lib/turndownService";
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
import { trpc } from "@calcom/trpc/react";
@@ -41,8 +41,6 @@ import { FiAlertTriangle, FiTrash2 } from "@calcom/ui/components/icon";
import TwoFactor from "@components/auth/TwoFactor";
import { UsernameAvailabilityField } from "@components/ui/UsernameAvailability";
const md = new MarkdownIt("default", { html: true, breaks: true });
const SkeletonLoader = ({ title, description }: { title: string; description: string }) => {
return (
<SkeletonContainer>
@@ -371,6 +369,7 @@ const ProfileForm = ({
formMethods.setValue("bio", turndown(value), { shouldDirty: true });
}}
excludedToolbarItems={["blockType"]}
disableLists
/>
</div>
<Button disabled={isDisabled} color="primary" className="mt-8" type="submit">
+1 -3
View File
@@ -1,5 +1,4 @@
import classNames from "classnames";
import MarkdownIt from "markdown-it";
import type { GetServerSidePropsContext } from "next";
import Link from "next/link";
import { useRouter } from "next/router";
@@ -11,6 +10,7 @@ import { CAL_URL } from "@calcom/lib/constants";
import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme";
import { md } from "@calcom/lib/markdownIt";
import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { Avatar, Button, HeadSeo, AvatarGroup } from "@calcom/ui";
@@ -23,8 +23,6 @@ import Team from "@components/team/screens/Team";
import { ssrInit } from "@server/lib/ssr";
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
export type TeamPageProps = inferSSRProps<typeof getServerSideProps>;
function TeamPage({ team }: TeamPageProps) {
useTheme(team.theme);
+15 -1
View File
@@ -2,6 +2,7 @@ import type { GetServerSidePropsContext } from "next";
import type { LocationObject } from "@calcom/core/location";
import { privacyFilteredLocations } from "@calcom/core/location";
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
import { parseRecurringEvent } from "@calcom/lib";
import { getWorkingHours } from "@calcom/lib/availability";
import prisma from "@calcom/prisma";
@@ -78,6 +79,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
availability: true,
description: true,
length: true,
disableGuests: true,
schedulingType: true,
periodType: true,
periodStartDate: true,
@@ -96,12 +98,24 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
slotInterval: true,
metadata: true,
seatsPerTimeSlot: true,
bookingFields: true,
customInputs: true,
schedule: {
select: {
timeZone: true,
availability: true,
},
},
workflows: {
select: {
workflow: {
select: {
id: true,
steps: true,
},
},
},
},
team: {
select: {
members: {
@@ -163,7 +177,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
let booking: GetBookingType | null = null;
if (rescheduleUid) {
booking = await getBooking(prisma, rescheduleUid);
booking = await getBooking(prisma, rescheduleUid, getBookingFieldsWithSystemFields(eventTypeObject));
}
const weekStart = eventType.team?.members?.[0]?.user?.weekStart;
+11 -4
View File
@@ -2,9 +2,10 @@ import type { GetServerSidePropsContext } from "next";
import type { LocationObject } from "@calcom/app-store/locations";
import { privacyFilteredLocations } from "@calcom/app-store/locations";
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
import { parseRecurringEvent } from "@calcom/lib";
import prisma from "@calcom/prisma";
import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { customInputSchema, eventTypeBookingFields, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { asStringOrNull, asStringOrThrow } from "@lib/asStringOrNull";
import type { GetBookingType } from "@lib/getBooking";
@@ -13,6 +14,8 @@ import type { inferSSRProps } from "@lib/types/inferSSRProps";
import BookingPage from "@components/booking/pages/BookingPage";
import { ssrInit } from "@server/lib/ssr";
export type TeamBookingPageProps = inferSSRProps<typeof getServerSideProps>;
export default function TeamBookingPage(props: TeamBookingPageProps) {
@@ -22,6 +25,7 @@ export default function TeamBookingPage(props: TeamBookingPageProps) {
TeamBookingPage.isThemeSupported = true;
export async function getServerSideProps(context: GetServerSidePropsContext) {
const ssr = await ssrInit(context);
const eventTypeId = parseInt(asStringOrThrow(context.query.type));
const recurringEventCountQuery = asStringOrNull(context.query.count);
if (typeof eventTypeId !== "number" || eventTypeId % 1 !== 0) {
@@ -55,6 +59,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
metadata: true,
seatsPerTimeSlot: true,
schedulingType: true,
bookingFields: true,
workflows: {
include: {
workflow: {
@@ -92,12 +97,13 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
//TODO: Use zodSchema to verify it instead of using Type Assertion
locations: privacyFilteredLocations((eventTypeRaw.locations || []) as LocationObject[]),
recurringEvent: parseRecurringEvent(eventTypeRaw.recurringEvent),
bookingFields: eventTypeBookingFields.parse(eventTypeRaw.bookingFields || []),
};
const eventTypeObject = [eventType].map((e) => {
return {
...e,
metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}),
metadata: EventTypeMetaDataSchema.parse(e.metadata || {}),
bookingFields: getBookingFieldsWithSystemFields(eventType),
periodStartDate: e.periodStartDate?.toString() ?? null,
periodEndDate: e.periodEndDate?.toString() ?? null,
customInputs: customInputSchema.array().parse(e.customInputs || []),
@@ -114,7 +120,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
let booking: GetBookingType | null = null;
if (context.query.rescheduleUid) {
booking = await getBooking(prisma, context.query.rescheduleUid as string);
booking = await getBooking(prisma, context.query.rescheduleUid as string, eventTypeObject.bookingFields);
}
// Checking if number of recurring event ocurrances is valid against event type configuration
@@ -128,6 +134,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
return {
props: {
trpcState: ssr.dehydrate(),
profile: {
...eventTypeObject.team,
// FIXME: This slug is used as username on success page which is wrong. This is correctly set as username for user booking.
+2 -1
View File
@@ -4,6 +4,8 @@ import { test } from "./lib/fixtures";
test.describe.configure({ mode: "parallel" });
test.afterEach(({ users }) => users.deleteAll());
test.describe("App Store - Authed", () => {
test("Browse apple-calendar and try to install", async ({ page, users }) => {
const pro = await users.create();
@@ -12,7 +14,6 @@ test.describe("App Store - Authed", () => {
await page.click('[data-testid="app-store-app-card-apple-calendar"]');
await page.click('[data-testid="install-app-button"]');
await expect(page.locator(`text=Connect to Apple Server`)).toBeVisible();
await pro.delete();
});
test("Installed Apps - Navigation", async ({ page, users }) => {
@@ -2,6 +2,8 @@ import { expect } from "@playwright/test";
import { test } from "../lib/fixtures";
test.afterEach(({ users }) => users.deleteAll());
test("Can delete user account", async ({ page, users }) => {
const user = await users.create({
username: "delete-me",
@@ -2,6 +2,8 @@ import { expect } from "@playwright/test";
import { test } from "../lib/fixtures";
test.afterEach(({ users }) => users.deleteAll());
test("Can reset forgotten password", async ({ page, users }) => {
test.skip(process.env.NEXT_PUBLIC_IS_E2E !== "1", "It shouldn't if we can't skip email");
const user = await users.create();
@@ -47,6 +49,4 @@ test("Can reset forgotten password", async ({ page, users }) => {
await page.waitForSelector("[data-testid=dashboard-shell]");
await expect(page.locator("[data-testid=dashboard-shell]")).toBeVisible();
await users.deleteAll();
});
+1 -6
View File
@@ -10,15 +10,13 @@ import {
} from "./lib/testUtils";
test.describe.configure({ mode: "parallel" });
test.afterEach(async ({ users }) => users.deleteAll());
test.describe("free user", () => {
test.beforeEach(async ({ page, users }) => {
const free = await users.create();
await page.goto(`/${free.username}`);
});
test.afterEach(async ({ users }) => {
await users.deleteAll();
});
test("cannot book same slot multiple times", async ({ page }) => {
// Click first event type
@@ -58,9 +56,6 @@ test.describe("pro user", () => {
const pro = await users.create();
await page.goto(`/${pro.username}`);
});
test.afterEach(async ({ users }) => {
await users.deleteAll();
});
test("pro user's page has at least 2 visible events", async ({ page }) => {
// await page.pause();
+1 -1
View File
@@ -24,6 +24,6 @@ test.describe("Change Password Test", () => {
const toast = await page.waitForSelector("div[class*='data-testid-toast-success']");
await expect(toast).toBeTruthy();
expect(toast).toBeTruthy();
});
});
@@ -7,9 +7,14 @@ import {
selectSecondAvailableTimeSlotNextMonth,
} from "./lib/testUtils";
test("dynamic booking", async ({ page, users }) => {
test.afterEach(({ users }) => users.deleteAll());
// Due to some reason for Dynamic booking cancellation, daily video api_key is not set which causes cancellation to fail.
// This test is skipped until the issue is resolved in GH actions.
test.skip("dynamic booking", async ({ page, users }) => {
const pro = await users.create();
await pro.login();
const free = await users.create({ username: "free" });
await page.goto(`/${pro.username}+${free.username}`);
@@ -55,8 +60,6 @@ test("dynamic booking", async ({ page, users }) => {
const cancelledHeadline = await page.locator('[data-testid="cancelled-headline"]').innerText();
await expect(cancelledHeadline).toBe("This event is cancelled");
expect(cancelledHeadline).toBe("This event is cancelled");
});
await users.deleteAll();
});
@@ -88,6 +88,8 @@ async function expectToContainValidPreviewIframe(
test.describe.configure({ mode: "parallel" });
test.afterEach(({ users }) => users.deleteAll());
test.describe("Embed Code Generator Tests", () => {
test.beforeEach(async ({ users }) => {
const pro = await users.create();
+2
View File
@@ -139,6 +139,8 @@ test.describe("Event Types tests", () => {
const fillLocation = async (inputText: string) => {
await page.locator("#location-select").click();
await page.locator("text=In Person (Organizer Address)").click();
// eslint-disable-next-line playwright/no-wait-for-timeout
await page.waitForTimeout(250);
await page.locator('input[name="locationAddress"]').fill(inputText);
await page.locator("[data-testid=display-location]").check();
await page.locator("[data-testid=update-location]").click();
+32 -15
View File
@@ -1,7 +1,7 @@
import type { Page, WorkerInfo } from "@playwright/test";
import type Prisma from "@prisma/client";
import { MembershipRole, Prisma as PrismaType } from "@prisma/client";
import { hash } from "bcryptjs";
import { hashSync as hash } from "bcryptjs";
import dayjs from "@calcom/dayjs";
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
@@ -10,8 +10,8 @@ import { prisma } from "@calcom/prisma";
import type { TimeZoneEnum } from "./types";
// Don't import hashPassword from app as that ends up importing next-auth and initializing it before NEXTAUTH_URL can be updated during tests.
export async function hashPassword(password: string) {
const hashedPassword = await hash(password, 12);
export function hashPassword(password: string) {
const hashedPassword = hash(password, 12);
return hashedPassword;
}
@@ -74,11 +74,33 @@ export const createUsersFixture = (page: Page, workerInfo: WorkerInfo) => {
});
await prisma.eventType.create({
data: {
owner: {
connect: {
id: _user.id,
},
},
users: {
connect: {
id: _user.id,
},
},
title: "30 min",
slug: "30-min",
length: 30,
},
});
await prisma.eventType.create({
data: {
users: {
connect: {
id: _user.id,
},
},
owner: {
connect: {
id: _user.id,
},
},
title: "Paid",
slug: "paid",
length: 30,
@@ -92,6 +114,11 @@ export const createUsersFixture = (page: Page, workerInfo: WorkerInfo) => {
id: _user.id,
},
},
owner: {
connect: {
id: _user.id,
},
},
title: "Opt in",
slug: "opt-in",
requiresConfirmation: true,
@@ -270,17 +297,14 @@ type CustomUserOptsKeys = "username" | "password" | "completedOnboarding" | "loc
type CustomUserOpts = Partial<Pick<Prisma.User, CustomUserOptsKeys>> & { timeZone?: TimeZoneEnum };
// creates the actual user in the db.
const createUser = async (
workerInfo: WorkerInfo,
opts?: CustomUserOpts | null
): Promise<PrismaType.UserCreateInput> => {
const createUser = (workerInfo: WorkerInfo, opts?: CustomUserOpts | null): PrismaType.UserCreateInput => {
// build a unique name for our user
const uname = `${opts?.username || "user"}-${workerInfo.workerIndex}-${Date.now()}`;
return {
username: uname,
name: opts?.name,
email: `${uname}@example.com`,
password: await hashPassword(uname),
password: hashPassword(uname),
emailVerified: new Date(),
completedOnboarding: opts?.completedOnboarding ?? true,
timeZone: opts?.timeZone ?? dayjs.tz.guess(),
@@ -298,13 +322,6 @@ const createUser = async (
},
}
: undefined,
eventTypes: {
create: {
title: "30 min",
slug: "30-min",
length: 30,
},
},
};
};
@@ -4,6 +4,7 @@ import { test } from "./lib/fixtures";
import { selectFirstAvailableTimeSlotNextMonth, todo } from "./lib/testUtils";
test.describe.configure({ mode: "parallel" });
test.afterEach(({ users }) => users.deleteAll());
const IS_STRIPE_ENABLED = !!(
process.env.STRIPE_CLIENT_ID &&
@@ -32,9 +33,6 @@ test.describe("Stripe integration", () => {
await expect(
page.locator(`li:has-text("Stripe") >> [data-testid="stripe_payment-integration-disconnect-button"]`)
).toContainText("");
// Cleanup
await users.deleteAll();
});
});
@@ -70,9 +68,6 @@ test.describe("Stripe integration", () => {
// Make sure we're navigated to the success page
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
// Cleanup
await users.deleteAll();
});
todo("Pending payment booking should not be confirmed by default");
+1 -11
View File
@@ -159,6 +159,7 @@ test.afterEach(() => requestInterceptor.resetHandlers());
// Disable API mocking after the tests are done.
test.afterAll(() => requestInterceptor.close());
test.afterEach(({ users }) => users.deleteAll());
// TODO: Fix MSW mocking
test.fixme("Integrations", () => {
@@ -210,17 +211,6 @@ test.fixme("Integrations", () => {
});
};
test.describe("Zoom App", () => {
test.afterEach(async () => {
await prisma?.credential.deleteMany({
where: {
user: {
email: "pro@example.com",
},
type: "zoom_video",
},
});
});
test("Can add integration", async ({ page, users }) => {
const user = await users.create();
await user.login();
@@ -1 +1 @@
{"triggerEvent":"BOOKING_CREATED","createdAt":"[redacted/dynamic]","payload":{"type":"30 min","title":"30 min between PRO and Test Testson","description":"","additionalNotes":"","customInputs":{},"startTime":"[redacted/dynamic]","endTime":"[redacted/dynamic]","organizer":{"name":"PRO","email":"[redacted/dynamic]","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"},"attendees":[{"email":"test@example.com","name":"Test Testson","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"}],"location":"[redacted/dynamic]","destinationCalendar":null,"hideCalendarNotes":false,"requiresConfirmation":"[redacted/dynamic]","eventTypeId":"[redacted/dynamic]","seatsShowAttendees":false,"uid":"[redacted/dynamic]","bookingId":"[redacted/dynamic]","metadata":{},"additionalInformation":"[redacted/dynamic]"}}
{"triggerEvent":"BOOKING_CREATED","createdAt":"[redacted/dynamic]","payload":{"type":"30 min","title":"30 min between PRO and Test Testson","description":"","additionalNotes":"","customInputs":{},"startTime":"[redacted/dynamic]","endTime":"[redacted/dynamic]","organizer":{"name":"PRO","email":"[redacted/dynamic]","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"},"attendees":[{"email":"test@example.com","name":"Test Testson","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"}],"location":"[redacted/dynamic]","destinationCalendar":null,"hideCalendarNotes":false,"requiresConfirmation":"[redacted/dynamic]","eventTypeId":"[redacted/dynamic]","seatsShowAttendees":true,"uid":"[redacted/dynamic]","videoCallData":"[redacted/dynamic]","appsStatus":"[redacted/dynamic]","bookingId":"[redacted/dynamic]","metadata":{},"additionalInformation":"[redacted/dynamic]"}}
+3 -1
View File
@@ -1,9 +1,11 @@
import type { Page } from "@playwright/test";
import { expect, test } from "@playwright/test";
import { expect } from "@playwright/test";
import type { IncomingMessage, ServerResponse } from "http";
import { createServer } from "http";
import noop from "lodash/noop";
import { test } from "./fixtures";
export function todo(title: string) {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(title, noop);
+2
View File
@@ -5,6 +5,8 @@ import { test } from "./lib/fixtures";
test.describe.configure({ mode: "serial" });
test.afterEach(({ users }) => users.deleteAll());
test.describe("Onboarding", () => {
test.describe("Onboarding v2", () => {
test("Onboarding Flow", async ({ page, users }) => {
+2 -3
View File
@@ -14,10 +14,9 @@ const IS_STRIPE_ENABLED = !!(
test.describe.configure({ mode: "parallel" });
test.afterEach(({ users }) => users.deleteAll());
test.describe("Reschedule Tests", async () => {
test.afterEach(async ({ users }) => {
await users.deleteAll();
});
test("Should do a booking request reschedule from /bookings", async ({ page, users, bookings }) => {
const user = await users.create();
+6 -4
View File
@@ -1,14 +1,14 @@
import { expect } from "@playwright/test";
import { prisma } from "@calcom/prisma";
import { test } from "./lib/fixtures";
test.describe.configure({ mode: "parallel" });
test.describe("Teams", () => {
test.afterEach(async ({ users }) => {
await users.deleteAll();
});
test.afterEach(({ users }) => users.deleteAll());
test.describe("Teams", () => {
test("Can create teams via Wizard", async ({ page, users }) => {
const user = await users.create();
const inviteeEmail = `${user.username}+invitee@example.com`;
@@ -44,6 +44,8 @@ test.describe("Teams", () => {
await removeMemberButton.click();
await removeMemberButton.waitFor({ state: "hidden" });
expect(await page.locator('[data-testid="pending-member-item"]').count()).toBe(1);
// Cleanup here since this user is created without our fixtures.
await prisma.user.delete({ where: { email: inviteeEmail } });
});
await test.step("Can publish team", async () => {
+49 -5
View File
@@ -3,6 +3,8 @@ import { expect } from "@playwright/test";
import { test } from "./lib/fixtures";
import { createHttpServer, selectFirstAvailableTimeSlotNextMonth, waitFor } from "./lib/testUtils";
test.afterEach(({ users }) => users.deleteAll());
test("add webhook & test that creating an event triggers a webhook call", async ({
page,
users,
@@ -70,11 +72,53 @@ test("add webhook & test that creating an event triggers a webhook call", async
body.payload.appsStatus = dynamic;
body.payload.metadata.videoCallUrl = dynamic;
// if we change the shape of our webhooks, we can simply update this by clicking `u`
// console.log("BODY", body);
// Text files shouldn't have platform specific suffixes
testInfo.snapshotSuffix = "";
expect(JSON.stringify(body)).toMatchSnapshot(`webhookResponse.txt`);
expect(body).toMatchObject({
triggerEvent: "BOOKING_CREATED",
createdAt: "[redacted/dynamic]",
payload: {
type: "30 min",
title: "30 min between Nameless and Test Testson",
description: "",
additionalNotes: "",
customInputs: {},
startTime: "[redacted/dynamic]",
endTime: "[redacted/dynamic]",
organizer: {
id: "[redacted/dynamic]",
name: "Nameless",
email: "[redacted/dynamic]",
timeZone: "[redacted/dynamic]",
language: "[redacted/dynamic]",
},
responses: { email: "test@example.com", name: "Test Testson" },
userFieldsResponses: {},
attendees: [
{
email: "test@example.com",
name: "Test Testson",
timeZone: "[redacted/dynamic]",
language: "[redacted/dynamic]",
},
],
location: "[redacted/dynamic]",
destinationCalendar: null,
hideCalendarNotes: false,
requiresConfirmation: "[redacted/dynamic]",
eventTypeId: "[redacted/dynamic]",
seatsShowAttendees: true,
seatsPerTimeSlot: null,
uid: "[redacted/dynamic]",
eventTitle: "30 min",
eventDescription: null,
price: 0,
currency: "usd",
length: 30,
bookingId: "[redacted/dynamic]",
metadata: { videoCallUrl: "[redacted/dynamic]" },
status: "ACCEPTED",
additionalInformation: "[redacted/dynamic]",
},
});
webhookReceiver.close();
});
@@ -1 +0,0 @@
{"triggerEvent":"BOOKING_CREATED","createdAt":"[redacted/dynamic]","payload":{"type":"30 min","title":"30 min between Nameless and Test Testson","description":"","additionalNotes":"","customInputs":{},"startTime":"[redacted/dynamic]","endTime":"[redacted/dynamic]","organizer":{"id":"[redacted/dynamic]","name":"Nameless","email":"[redacted/dynamic]","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"},"attendees":[{"email":"test@example.com","name":"Test Testson","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"}],"location":"[redacted/dynamic]","destinationCalendar":null,"hideCalendarNotes":false,"requiresConfirmation":"[redacted/dynamic]","eventTypeId":"[redacted/dynamic]","seatsShowAttendees":false,"seatsPerTimeSlot":null,"uid":"[redacted/dynamic]","videoCallData":"[redacted/dynamic]","appsStatus":"[redacted/dynamic]","eventTitle":"30 min","eventDescription":null,"price":0,"currency":"usd","length":30,"bookingId":"[redacted/dynamic]","metadata":{"videoCallUrl":"[redacted/dynamic]"},"status":"ACCEPTED","additionalInformation":"[redacted/dynamic]"}}
+2 -2
View File
@@ -7,6 +7,8 @@ import { test } from "./lib/fixtures";
test.describe.configure({ mode: "parallel" });
test.afterEach(({ users }) => users.deleteAll());
test.describe("Wipe my Cal App Test", () => {
test("Browse upcoming bookings and validate button shows and triggering wipe my cal button", async ({
page,
@@ -47,7 +49,5 @@ test.describe("Wipe my Cal App Test", () => {
page.locator("data-testid=send_request").click();
// There will not be any today-bookings
await expect(page.locator('[data-testid="today-bookings"]')).toBeHidden();
await users.deleteAll();
});
});
+121 -2
View File
@@ -7,6 +7,7 @@
"upgrade_now": "تحديث الآن",
"accept_invitation": "اقبل الدعوة",
"calcom_explained": "يوفر {{appName}} بنية أساسية للجدولة للجميع.",
"calcom_explained_new_user": "إنهاء إعداد حسابك في {{appName}}! تبقت لك فقط بضع خطوات فقط لحل جميع مشاكل الجدولة لديك.",
"have_any_questions": "هل لديك أسئلة؟ نحن هنا للمساعدة.",
"reset_password_subject": "{{appName}}: إرشادات إعادة تعيين كلمة المرور",
"event_declined_subject": "تم الرفض: {{title}} في {{date}}",
@@ -23,6 +24,8 @@
"rejection_reason_description": "هل أنت متأكد من رغبتك في رفض الحجز؟ سنقوم بإبلاغ الشخص الذي حاول الحجز. يمكنك تقديم سبب أدناه.",
"rejection_confirmation": "رفض الحجز",
"manage_this_event": "قم بإدارة هذا الحدث",
"invite_team_member": "دعوة عضو في الفريق",
"invite_team_notifcation_badge": "استثمر.",
"your_event_has_been_scheduled": "تم جدولة الحدث الخاص بك",
"your_event_has_been_scheduled_recurring": "تم جدولة الحدث المتكرر الخاص بك",
"accept_our_license": "اقبل ترخيصنا من خلال تغيير متغير ‎.env المسمى <1>NEXT_PUBLIC_LICENSE_CONSENT</1> إلى '{{agree}}'.",
@@ -410,6 +413,8 @@
"password_updated_successfully": "تم تحديث كلمة المرور بنجاح",
"password_has_been_changed": "تم تغيير كلمة المرور بنجاح.",
"error_changing_password": "خطأ في تغيير كلمة المرور",
"session_timeout_changed": "تم تحديث ملفك الشخصي بنجاح.",
"session_timeout_change_error": "خطأ في تحديث تكوين الجلسة",
"something_went_wrong": "حدث خطأ ما.",
"something_doesnt_look_right": "هل يوجد شيء ما لا يبدو صحيحًا؟",
"please_try_again": "يُرجى المحاولة مجددًا.",
@@ -428,6 +433,7 @@
"password_hint_num": "يحتوي على رقم واحد على الأقل",
"invalid_password_hint": "يجب أن تتألف كلمة المرور على الأقل من {{passwordLength}} أحرف، ورقم واحد على الأقل، ومزيج من الحروف الكبيرة والصغيرة",
"incorrect_password": "كلمة المرور غير صحيحة.",
"incorrect_username_password": "اسم المستخدم أو كلمة المرور غير صحيحة.",
"24_h": "24 ساعة",
"use_setting": "استخدام الإعداد",
"am_pm": "صباحًا/مساءً",
@@ -455,6 +461,7 @@
"slots_load_fail": "تعذر تحميل الفترات الزمنية المتاحة.",
"additional_guests": "إضافة ضيوف",
"your_name": "اسمك",
"your_full_name": "اسمك الكامل",
"email_address": "عنوان البريد الإلكتروني",
"enter_valid_email": "يرجى إدخال بريد إلكتروني صالح",
"location": "الموقع",
@@ -508,6 +515,8 @@
"admin": "المشرف",
"administrator_user": "المستخدم المسؤول",
"lets_create_first_administrator_user": "لنقم بإنشاء المستخدم المسؤول الأول.",
"admin_user_created": "إعداد المستخدم المسؤول",
"admin_user_created_description": "لقد أنشأت بالفعل مستخدم مسؤول. يمكنك الآن تسجيل الدخول إلى حسابك.",
"new_member": "العضو الجديد",
"invite": "دعوة",
"add_team_members": "إضافة أعضاء الفريق",
@@ -589,7 +598,6 @@
"new_event_type_heading": "إنشاء نوع الحدث الأول لديك",
"new_event_type_description": "تتيح لك أنواع الأحداث مشاركة الروابط التي تعرض الأوقات المتاحة في تقويمك وتسمح للأشخاص بالحجز معك.",
"new_event_title": "إضافة نوع حدث جديد",
"new_event_subtitle": "قم بإنشاء نوع حدث يندرج تحت اسمك أو أحد الفرق.",
"new_team_event": "إضافة نوع حدث جديد لفريق",
"new_event_description": "قم بإنشاء نوع حدث جديد ليحجز الأشخاص من خلاله.",
"event_type_created_successfully": "تم إنشاء نوع الحدث {{eventTypeTitle}} بنجاح",
@@ -658,6 +666,7 @@
"edit_availability": "تعديل الإتاحة",
"configure_availability": "اضبط الأوقات التي تكون متاحًا فيها للحجز.",
"copy_times_to": "نسخ الأوقات إلى",
"copy_times_to_tooltip": "نسخ الأوقات إلى…",
"change_weekly_schedule": "تغيير جدولك الأسبوعي",
"logo": "الشعار",
"error": "خطأ",
@@ -669,6 +678,7 @@
"add_attendees": "إضافة الحضور",
"show_advanced_settings": "عرض الإعدادات المتقدمة",
"event_name": "اسم الحدث",
"event_name_in_calendar": "اسم الحدث في الرزنامة",
"event_name_tooltip": "الاسم الذي سيظهر في التقاويم",
"meeting_with_user": "الاجتماع مع {{attendeeName}}",
"additional_inputs": "مدخلات إضافية",
@@ -787,6 +797,7 @@
"number_apps_one": "{{count}} تطبيق",
"number_apps_other": "{{count}} من التطبيقات",
"trending_apps": "التطبيقات الرائجة",
"most_popular": "الأكثر شعبية",
"explore_apps": "تطبيقات {{category}}",
"installed_apps": "التطبيقات المثبتة",
"free_to_use_apps": "متاح",
@@ -797,12 +808,14 @@
"no_category_apps_description_analytics": "إضافة تطبيق تحليلات لصفحات الحجز الخاصة بك",
"no_category_apps_description_automation": "إضافة تطبيق أتمتة لاستخدامه",
"no_category_apps_description_other": "أضف أي نوع آخر من التطبيقات للقيام بأي شيء",
"no_category_apps_description_web3": "إضافة تطبيق web3 لصفحات الحجز لديك",
"installed_app_calendar_description": "قم بتعيين التقويمات للتحقق من وجود تعارضات لمنع الحجوزات المزدوجة.",
"installed_app_conferencing_description": "أضف تطبيقات مؤتمرات الفيديو المفضلة لديك لاجتماعاتك",
"installed_app_payment_description": "قم بإعداد أي خدمات معالجة دفع تود استخدامها عند التحصيل من العملاء.",
"installed_app_analytics_description": "تهيئة أي تطبيقات تستخدم لصفحات الحجز الخاصة بك",
"installed_app_other_description": "جميع تطبيقاتك المثبتة من الفئات الأخرى.",
"installed_app_automation_description": "تهيئة تطبيقات الأتمتة التي سيتم استخدامها",
"installed_app_web3_description": "تهيئة تطبيقات web3 التي يمكن استخدامها لصفحات الحجز لديك",
"analytics": "التحليلات",
"empty_installed_apps_headline": "لم يتم تثبيت أي تطبيق",
"empty_installed_apps_description": "تعطيك التطبيقات القدرة على تعزيز سير عملك وتحسين الجدولة في حياتك بشدة.",
@@ -930,6 +943,11 @@
"current_location": "الموقع الحالي",
"user_phone": "رقم هاتفك",
"new_location": "الموقع الجديد",
"session": "الجلسة",
"session_description": "التحكم في جلسة حسابك",
"session_timeout_after": "مهلة الجلسة بعد",
"session_timeout": "مهلة الجلسة",
"session_timeout_description": "أبطل جلسة عملك بعد مدة معينة من الوقت.",
"no_location": "لم يتم تحديد موقع",
"set_location": "تعيين الموقع",
"update_location": "تحديث الموقع",
@@ -1073,6 +1091,7 @@
"broken_video_action": "لم نتمكن من إضافة رابط <1>{{location}}</1> الاجتماع إلى الحدث المجدول. يرجى الاتصال بالمدعوين أو تحديث حدث التقويم الخاص بك لإضافة التفاصيل. يمكنك إما <3> تغيير موقعك في نوع الحدث </3> أو محاولة <5>إزالة التطبيق وتثبيته مرة أخرى.</5>",
"broken_calendar_action": "لم نتمكن من تحديث <1>{{calendar}}</1>. <2> يرجى التحقق من إعدادات التقويم أو إزالة التقويم وإضافته مرة أخرى</2>",
"attendee_name": "اسم الحاضر",
"scheduler_full_name": "الاسم الكامل للشخص الذي يحجز",
"broken_integration": "تكامل تالف",
"problem_adding_video_link": "حدثت مشكلة أثناء إضافة رابط فيديو",
"problem_updating_calendar": "حدثت مشكلة أثناء تحديث التقويم الخاص بك",
@@ -1215,6 +1234,7 @@
"connect_automation_apps": "توصيل تطبيقات الأتمتة",
"connect_analytics_apps": "توصيل تطبيقات التحليلات",
"connect_other_apps": "ربط تطبيقات أخرى",
"connect_web3_apps": "ربط تطبيقات web3",
"current_step_of_total": "الخطوة {{currentStep}} من {{maxSteps}}",
"add_variable": "إضافة متغير",
"custom_phone_number": "رقم هاتف مخصص",
@@ -1231,6 +1251,7 @@
"to": "إلى",
"workflow_turned_on_successfully": "تم تشغيل {{offOn}} سير العمل {{workflowName}} بنجاح",
"download_responses": "تنزيل الردود",
"download_responses_description": "تحميل جميع الردود على نموذجك بتنسيق CSV.",
"download": "التنزيل",
"create_your_first_form": "إنشاء أول استمارة",
"create_your_first_form_description": "باستخدام نماذج المسارات، يمكنك طرح أسئلة التأهيل والتوجيه إلى الشخص/نوع الحدث المناسب.",
@@ -1272,6 +1293,8 @@
"routing_forms_send_email_owner": "إرسال رسالة إلكترونية إلى المالك",
"routing_forms_send_email_owner_description": "يرسل رسالة بريد إلكتروني إلى المالك عند إرسال النموذج",
"add_new_form": "إضافة استمارة جديدة",
"create_your_first_route": "إنشاء مسارك الأول",
"route_to_the_right_person": "التوجه إلى الشخص المناسب بناء على الإجابات على النموذج لديك",
"form_description": "قم بإنشاء النموذج الخاص بك لتوجيه صاحب حجز",
"copy_link_to_form": "نسخ الرابط إلى النموذج",
"theme": "السمة",
@@ -1302,6 +1325,7 @@
"password_reset_leading": "إذا لم تستلم رسالة بريد إلكتروني قريبًا، تحقق من أن عنوان البريد الإلكتروني الذي أدخلته صحيح، وتحقق من مجلد البريد المزعج أو اتصل بالدعم إذا استمرت المشكلة.",
"password_updated": "تم تحديث كلمة المرور!",
"pending_payment": "في انتظار الدفع",
"pending_invites": "دعوات معلقة",
"confirmation_page_rainbow": "قم بتعيين رمز مميز لفتح الحدث الخاص بك باستخدام الرموز أو NFTs على Ethereum وPolygon وغيرها.",
"not_on_cal": "غير موجود على {{appName}}",
"no_calendar_installed": "لا يوجد تقويم مثبت",
@@ -1428,6 +1452,9 @@
"disabled_calendar": "إذا كان لديك تقويم آخر مثبت، ستُضاف الحجوزات الجديدة إليه. إذا لم يكن الأمر كذلك، قم بتوصيل تقويم جديد حتى لا تفوت أي حجوزات جديدة.",
"enable_apps": "تمكين التطبيقات",
"enable_apps_description": "تمكين التطبيقات التي يمكن للمستخدمين دمجها مع Cal.com",
"purchase_license": "شراء ترخيص",
"already_have_key": "لدي بالفعل مفتاح:",
"already_have_key_suggestion": "يرجى نسخ متغير البيئة الحالي CALCOM_LICENSE_KEY هنا.",
"app_is_enabled": "تم تمكين {{appName}}",
"app_is_disabled": "تم تعطيل {{appName}}",
"keys_have_been_saved": "تم حفظ المفاتيح",
@@ -1448,6 +1475,7 @@
"individual": "فردي",
"all_bookings_filter_label": "جميع الحجوزات",
"all_users_filter_label": "جميع المستخدمين",
"your_bookings_filter_label": "الحجوزات لديك",
"meeting_url_variable": "رابط الاجتماع",
"meeting_url_info": "رابط فعالية اجتماع المؤتمر",
"date_overrides": "تجاوز التاريخ",
@@ -1486,6 +1514,7 @@
"round_robin_hosts": "مضيفون بنظام دوار",
"minimum_round_robin_hosts_count": "عدد المضيفين المطلوب حضورهم",
"hosts": "المضيفون",
"upgrade_to_enable_feature": "تحتاج إلى إنشاء فريق لتمكين هذه الميزة. انقر لإنشاء فريق.",
"new_attendee": "حضور جديد",
"awaiting_approval": "في انتظار الموافقة",
"requires_google_calendar": "يتطلب هذا التطبيق اتصال تقويم Google",
@@ -1494,9 +1523,99 @@
"continue_to_install_google_calendar": "متابعة تثبيت تقويم Google",
"install_google_meet": "تثبيت Google Meet",
"install_google_calendar": "تثبيت تقويم Google",
"sender_name": "اسم المرسل",
"already_invited": "تمت دعوة هذا الحاضر بالفعل",
"no_recordings_found": "لم يتم العثور على تسجيلات",
"reporting": "تقديم التقارير",
"reporting_feature": "عرض كل ما هو وارد من بيانات وتحميلها بصيغة CSV",
"teams_plan_required": "خطة الفرق مطلوبة",
"routing_forms_are_a_great_way": "نماذج التوجيه هي طريقة رائعة لتوجيه الوارد إليك إلى الشخص الصحيح. قم بالترقية إلى خطة \"الفرق\" للوصول إلى هذه الميزة.",
"choose_a_license": "اختر ترخيص",
"choose_license_description": "يأتي Cal.com مع رخصة AGPLv3 متاحة ومجانية والتي يمكن ترقيتها إلى رخصة \"المؤسسة\" في أي وقت. يمكنك الترقية في أي وقت لاحق.",
"license": "الترخيص",
"agplv3_license": "ترخيص AGPLv3",
"ee_enterprise_license": "رخصة المؤسسة \"/ee\"",
"enterprise_booking_fee": "يبدأ في {{enterprise_booking_fee}}/ الشهر",
"enterprise_license_includes": "كل شيء لأغراض الاستخدام التجاري",
"no_need_to_keep_your_code_open_source": "لا حاجة للحفاظ على تعليماتك البرمجية مفتوحة المصدر",
"repackage_rebrand_resell": "إعادة التعبئة وإعادة الترويج وإعادة البيع بسهولة",
"a_vast_suite_of_enterprise_features": "مجموعة كبيرة من سمات المشاريع",
"free_license_fee": "$0.00/شهر",
"forever_open_and_free": "مفتوح و مجاني إلى الأبد",
"required_to_keep_your_code_open_source": "مطلوب للإبقاء على تعليماتك البرمجية مفتوحة المصدر",
"cannot_repackage_and_resell": "لا يمكن إعادة التعبئة وإعادة العلامة التجارية وإعادة البيع بسهولة",
"no_enterprise_features": "ميزات \"المؤسسة\" غير موجودة",
"step_enterprise_license": "ترخيص المؤسسة",
"step_enterprise_license_description": "كل شيء موجود ضمن الرخصة التجارية مع الاستضافة الخاصة وإعادة التغليف وإعادة التوسيم وإعادة البيع والوصول إلى مكونات المؤسسة الحصرية.",
"setup": "الإعداد",
"setup_description": "إعداد مثال Cal.com",
"configure": "تهيئة",
"sso_configuration": "تسجيل الدخول لمرة واحدة",
"sso_configuration_description": "إعداد SAML/OIDC SSO والسماح لأعضاء الفريق بتسجيل الدخول باستخدام \"موفر الهوية\"",
"sso_oidc_heading": "SSO مع OIDC",
"sso_oidc_description": "تكوين OIDC SSO مع موفر هوية من اختيارك.",
"sso_oidc_configuration_title": "تكوين OIDC",
"sso_oidc_configuration_description": "قم بتكوين اتصال OIDC مع موفر هويتك الشخصية. يمكنك العثور على المعلومات المطلوبة في موفر هويتك الشخصية.",
"sso_oidc_callback_copied": "تم نسخ رابط رد اتصال URL",
"sso_saml_heading": "SSO مع SAML",
"sso_saml_description": "تكوين SAML SSO مع موفر هوية من اختيارك.",
"sso_saml_configuration_title": "تكوين SAML",
"sso_saml_configuration_description": "تكوين اتصال SAML مع مزود هويتك الشخصية. يمكنك العثور على المعلومات المطلوبة في مزود هويتك الشخصية.",
"sso_saml_acsurl_copied": "تم نسخ رابط ACS",
"sso_saml_entityid_copied": "تم نسخ معرف الكيان",
"sso_connection_created_successfully": "تم إنشاء تكوين {{connectionType}} بنجاح",
"sso_connection_deleted_successfully": "تم حذف تكوين {{connectionType}} بنجاح",
"delete_sso_configuration": "حذف اعدادات {{connectionType}}",
"delete_sso_configuration_confirmation": "نعم، احذف اعدادات {{connectionType}}",
"delete_sso_configuration_confirmation_description": "هل أنت متأكد من أنك تريد حذف تكوين {{connectionType}}؟ لن يكون لأعضاء فريقك الذين يستخدمون {{connectionType}} لتسجيل الدخول القدرة بعد الآن على الوصول إلى Cal.com.",
"organizer_timezone": "منظم المناطق الزمنية",
"email_no_user_cta": "إنشاء حسابك",
"email_user_cta": "عرض الدعوة",
"email_no_user_invite_heading": "لقد تمت دعوتك للانضمام إلى فريق في {{appName}}",
"email_no_user_invite_subheading": "قام {{invitedBy}} بدعوتك للانضمام إلى فريقهم على {{appName}}. {{appName}} هو أداة لجدولة الأحداث تعطيك أنت وفريقك القدرة على جدولة اجتماعات من دون الحاجة إلى البريد الإلكتروني.",
"email_no_user_invite_steps_intro": "سنقطع معك بضع خطوات قصيرة وستستمتع بالجدولة المجانية مع فريقك في أي وقت.",
"email_no_user_step_one": "اختر اسم المستخدم الخاص بك",
"email_no_user_step_two": "ربط حساب التقويم الخاص بك",
"email_no_user_step_three": "تعيين توافرك",
"email_no_user_step_four": "انضم إلى {{teamName}}",
"email_no_user_signoff": "جدولة سعيدة من فريق {{appName}}",
"impersonation_user_tip": "أنت على وشك انتحال شخصية مستخدم، مما يعني أنه يمكنك إجراء تغييرات نيابة عنهم. يرجى توخي الحذر.",
"available_variables": "المتغيرات المتوفرة",
"scheduler": "{Scheduler}",
"recommended_next_steps": "الخطوات التالية الموصى بها",
"create_a_managed_event": "إنشاء نوع من الأحداث المُدارة",
"meetings_are_better_with_the_right": "الاجتماعات أفضل مع أعضاء الفريق الصحيحين. ادعوهم الآن.",
"collective_or_roundrobin": "جماعية أو ذهاب وإياب",
"book_your_team_members": "احجز أعضاء فريقك مع الأحداث الجماعية أو الدورة للحصول على الشخص المناسب مع خيار الذهاب والإياب.",
"default_app_link_title": "تعيين رابط تطبيق افتراضي",
"default_app_link_description": "إعداد رابط التطبيق الافتراضي يسمح لجميع أنواع الأحداث التي تم إنشاؤها حديثا باستخدام رابط التطبيق الذي قمت بتعيينه.",
"change_default_conferencing_app": "تعيين كافتراضي",
"booking_confirmation_failed": "فشل تأكيد الحجز"
"under_maintenance": "معطل للصيانة",
"under_maintenance_description": "فريق {{appName}} يقوم بأعمال الصيانة المجدولة. إذا كان لديك أي أسئلة، يرجى الاتصال بالدعم.",
"event_type_seats": "{{numberOfSeats}} مقعد",
"booking_with_payment_cancelled": "الدفع لهذا الحدث لم يعد ممكناً",
"booking_with_payment_cancelled_already_paid": "جاري العمل على استرداد دفعة الحجز هذه.",
"booking_with_payment_cancelled_refunded": "تم استرداد هذه الدفعة للحجز.",
"booking_confirmation_failed": "فشل تأكيد الحجز",
"get_started_zapier_templates": "ابدأ مع قوالب Zapier",
"a_routing_form": "نموذج توجيه",
"form_description_placeholder": "وصف النموذج",
"keep_me_connected_with_form": "ابقني متصلاً بالنموذج",
"fields_in_form_duplicated": "سيعكس التكرار أية تغييرات تطرأ على التدوير والحقول المكررة في النموذج.",
"form_deleted": "تم حذف النموذج",
"delete_form": "حذف النموذج",
"delete_form_action": "نعم، احذف النموذج",
"delete_form_confirmation": "هل أنت متأكد من أنك تريد حذف هذا النموذج؟ لن يتمكن أي شخص تشارك الرابط معه من الحجز بعد الآن. كما ستحذف جميع الردود المرتبطة بذلك.",
"typeform_redirect_url_copied": "تم نسخ عنوان URL في Typeform Redirect! يمكنك الذهاب وتعيين عنوان URL في نموذج Typeform.",
"modifications_in_fields_warning": "سيعكس هذا الشكل التعديلات التي ستطرأ على الحقول والطرق التي تسلكها النماذج اللاحقة.",
"connected_forms": "النماذج المتصلة",
"form_modifications_warning": "ستتأثر النماذج التالية عند تعديل الحقول أو المسارات هنا.",
"responses_collection_waiting_description": "انتظر لبعض الوقت لجمع الردود. يمكنك الذهاب وتقديم النموذج بنفسك أيضاً.",
"this_is_what_your_users_would_see": "هذا ما سيراه المستخدمون لديك",
"identifies_name_field": "تحديد الحقل بهذا الاسم.",
"add_1_option_per_line": "إضافة خيار واحد لكل سطر",
"select_a_router": "حدد مساراً",
"add_a_new_route": "إضافة مسار جديد",
"no_responses_yet": "لا توجد ردود بعد",
"this_will_be_the_placeholder": "هذا سيكون العنصر النائب"
}
@@ -589,7 +589,6 @@
"new_event_type_heading": "Vytvořte svůj první typ události",
"new_event_type_description": "Typy událostí vám umožňují sdílet odkazy, které ukazují časy v kalendáři, kdy jste dostupní, a lidí skrz ně mohou rezervovat váš čas.",
"new_event_title": "Přidat nový typ události",
"new_event_subtitle": "Vytvořit nový typ události pod vaším jménem nebo týmem.",
"new_team_event": "Přidat nový typ týmové události",
"new_event_description": "Vytvořit nový typ události, ve které si lidé mohou rezervovat časy.",
"event_type_created_successfully": "Typ události {{eventTypeTitle}} byl úspěšně vytvořen",
+134 -20
View File
@@ -166,7 +166,7 @@
"optional": "Valgfrit",
"input_type": "Input type",
"rejected": "Afvist",
"unconfirmed": "Ubekræftet",
"unconfirmed": "Ubekræftede",
"guests": "Gæster",
"guest": "Gæst",
"web_conferencing_details_to_follow": "Webkonference detaljer følger i bekræftelses-e-mailen.",
@@ -189,8 +189,8 @@
"check_spelling_mistakes_or_go_back": "Tjek for stavefejl eller gå tilbage til forrige side.",
"404_page_not_found": "404: Denne side blev ikke fundet.",
"getting_started": "Kom I Gang",
"15min_meeting": "15 min Møde",
"30min_meeting": "30 min Møde",
"15min_meeting": "15 min. Møde",
"30min_meeting": "30 min. Møde",
"secret": "Hemmeligt",
"leave_blank_to_remove_secret": "Lad feltet stå tomt for at fjerne hemmelig",
"webhook_secret_key_description": "Sørg for, at din server kun modtager de forventede {{appName}} anmodninger af sikkerhedsmæssige årsager",
@@ -437,7 +437,7 @@
"24_h": "24h",
"use_setting": "Brug indstilling",
"am_pm": "am/pm",
"time_options": "Tidsindstillinger",
"time_options": "Tidsmuligheder",
"january": "Januar",
"february": "Februar",
"march": "Marts",
@@ -527,7 +527,7 @@
"invite_new_team_member": "Invitér nogen til dit team.",
"change_member_role": "Skift teammedlems rolle",
"disable_cal_branding": "Deaktivér {{appName}} branding",
"disable_cal_branding_description": "Skjul alle {{appName}} branding fra dine offentlige sider.",
"disable_cal_branding_description": "Skjul al {{appName}} branding fra dine offentlige sider.",
"hide_book_a_team_member": "Skjul Book et Team Medlem-knap",
"hide_book_a_team_member_description": "Skjul Book et Team medlem-knap på dine offentlige sider.",
"danger_zone": "Farezone",
@@ -548,12 +548,12 @@
"manage_your_team": "Administrér dit team",
"no_teams": "Du har ikke nogen teams endnu.",
"no_teams_description": "Teams giver andre mulighed for at booke begivenheder, der deles mellem dine kolleger.",
"submit": "Send",
"submit": "Videre",
"delete": "Slet",
"update": "Opdatér",
"save": "Gem",
"pending": "Afventer",
"open_options": "Åbn indstillinger",
"open_options": "Åbn muligheder",
"copy_link": "Kopiér link til begivenhed",
"share": "Del",
"share_event": "Kan du booke min Cal eller sende mig dit link?",
@@ -598,7 +598,6 @@
"new_event_type_heading": "Opret din første begivenhedstype",
"new_event_type_description": "Begivenhedstyper gør det muligt for dig at dele links, der viser tilgængelige tider i din kalender, og give folk mulighed for at foretage bookinger med dig.",
"new_event_title": "Tilføj en ny begivenhedstype",
"new_event_subtitle": "Opret en begivenhedstype under dit navn eller et team.",
"new_team_event": "Tilføj en ny team begivenhedstype",
"new_event_description": "Opret en ny begivenhedstype for folk til at booke tider med.",
"event_type_created_successfully": "{{eventTypeTitle}} begivenhedstype blev oprettet",
@@ -647,6 +646,15 @@
"team_billing_description": "Administrér fakturering for dit team",
"upgrade_to_flexible_pro_title": "Vi har ændret faktureringen for teams",
"upgrade_to_flexible_pro_message": "Der er medlemmer i dit team uden en plads. Opgradér din pro plan for at dække manglende pladser.",
"changed_team_billing_info": "Fra januar 2022 opkræver vi pr. plads for teammedlemmer. Medlemmer af Deres team, der havde PRO gratis, er nu på en 14 dages prøveperiode. Når deres prøveperiode udløber, vil disse medlemmer blive skjult fra dit team, medmindre du opgraderer nu.",
"create_manage_teams_collaborative": "Opret og administrer teams for at bruge samarbejdsfunktioner.",
"only_available_on_pro_plan": "Denne funktion er kun tilgængelig i Pro plan",
"remove_cal_branding_description": "For at fjerne {{appName}} branding fra dine bookingsider, skal du opgradere til en Pro-konto.",
"edit_profile_info_description": "Redigér dine profiloplysninger, der vises på dit planlægningslink.",
"change_email_tip": "Det kan være nødvendigt at logge ud og ind igen for at se ændringerne træde i kraft.",
"little_something_about": "Lidt om dig selv.",
"profile_updated_successfully": "Profilen blev opdateret",
"your_user_profile_updated_successfully": "Din brugerprofil er blevet opdateret.",
"user_cannot_found_db": "Bruger ser ud til at være logget ind, men kan ikke findes i db",
"embed_and_webhooks": "Indlejr & Webhooks",
"enabled": "Aktiveret",
@@ -656,7 +664,7 @@
"manage_your_billing_info": "Administrer dine faktureringsoplysninger og annuller dit abonnement.",
"availability": "Tilgængelighed",
"edit_availability": "Redigér tilgængelighed",
"configure_availability": "Konfigurér tidspunkter, hvor du er tilgængelig for bookinger.",
"configure_availability": "Indstil tidspunkter, hvor du er tilgængelig for bookinger.",
"copy_times_to": "Kopiér tidspunkter til",
"copy_times_to_tooltip": "Kopiér tidspunkter til …",
"change_weekly_schedule": "Redigér din ugentlige tidsplan",
@@ -754,7 +762,7 @@
"embed_your_calendar": "Indlejr din kalender på din hjemmeside",
"connect_your_favourite_apps": "Tilslut dine foretrukne apps.",
"automation": "Automatisering",
"configure_how_your_event_types_interact": "Konfigurér hvordan dine begivenhedstyper skal interagere med dine kalendere.",
"configure_how_your_event_types_interact": "Indstil hvordan dine begivenhedstyper skal interagere med dine kalendere.",
"toggle_calendars_conflict": "Skift mellem de kalendere du vil tjekke for konflikter, for at forhindre dobbeltbookinger.",
"select_destination_calendar": "Opret begivenheder på",
"connect_additional_calendar": "Forbind yderligere kalender",
@@ -803,11 +811,11 @@
"no_category_apps_description_web3": "Tilføj en web3 app til dine bookingsider",
"installed_app_calendar_description": "Indstil kalenderne til at tjekke for konflikter, for at forhindre dobbeltbookinger.",
"installed_app_conferencing_description": "Tilføj dine foretrukne videokonference apps til dine møder",
"installed_app_payment_description": "Konfigurer hvilke betalingsbehandlings tjenester der skal bruges, når du opkræver betaling fra dine kunder.",
"installed_app_analytics_description": "Konfigurer hvilke analyseapps der skal bruges til dine bookingsider",
"installed_app_payment_description": "Indstil hvilke betalingsbehandlingstjenester der skal bruges, når du opkræver betaling fra dine kunder.",
"installed_app_analytics_description": "Indstil hvilke analyseapps der skal bruges til dine bookingsider",
"installed_app_other_description": "Alle dine installerede apps fra andre kategorier.",
"installed_app_automation_description": "Konfigurer hvilke automatiserings apps der skal bruges",
"installed_app_web3_description": "Konfigurer hvilke web3 apps der skal bruges til dine bookingsider",
"installed_app_automation_description": "Indstil hvilke automatiseringsapps der skal bruges",
"installed_app_web3_description": "Indstil hvilke web3 apps der skal bruges til dine bookingsider",
"analytics": "Analyser",
"empty_installed_apps_headline": "Ingen apps installeret",
"empty_installed_apps_description": "Apps giver dig mulighed for at forbedre din arbejdsgang og forbedre dit planlægningsliv betydeligt.",
@@ -867,6 +875,7 @@
"24_hour_short": "24h",
"redirect_success_booking": "Omdirigér ved booking ",
"you_are_being_redirected": "Du bliver omdirigeret til {{ url }} om $t(second, {\"count\": {{seconds}} }).",
"external_redirect_url": "https://eksempel.com/omdiriger-til-min-succes-side",
"redirect_url_description": "Omdiriger til en brugerdefineret URL efter en vellykket booking",
"duplicate": "Duplikér",
"offer_seats": "Tilbyd pladser",
@@ -1006,7 +1015,7 @@
"new_event_trigger": "når ny begivenhed er booket",
"email_host_action": "send e-mail til vært",
"email_attendee_action": "send e-mail til deltager",
"sms_attendee_action": "send SMS til deltager",
"sms_attendee_action": "Send SMS til deltager",
"sms_number_action": "send SMS til et specifikt nummer",
"workflows": "Workflows",
"new_workflow_btn": "Nyt Workflow",
@@ -1092,6 +1101,24 @@
"new_seat_subject": "Ny Deltager {{name}} på {{eventType}} den {{date}}",
"new_seat_title": "Nogen har tilføjet sig selv til en begivenhed",
"variable": "Variabel",
"event_name_variable": "Begivenhedsnavn",
"organizer_name_variable": "Arrangør",
"attendee_name_variable": "Deltager",
"event_date_variable": "Dato for begivenhed",
"event_time_variable": "Tidspunkt for begivenhed",
"location_variable": "Placering",
"additional_notes_variable": "Yderligere bemærkninger",
"app_upgrade_description": "For at bruge denne funktion skal du opgradere til en Pro-konto.",
"invalid_number": "Ugyldigt telefonnummer",
"navigate": "Navigér",
"open": "Åbn",
"close": "Luk",
"upgrade": "Opgradér",
"upgrade_to_access_recordings_title": "Opgradér for at tilgå optagelser",
"upgrade_to_access_recordings_description": "Optagelser er kun tilgængelige som en del af vores teams plan. Opgradér for at begynde at optage dine opkald",
"recordings_are_part_of_the_teams_plan": "Optagelser er en del af teams planen",
"team_feature_teams": "Dette er en Team funktion. Opgradér til Team for at se dit teams tilgængelighed.",
"team_feature_workflows": "Dette er en Team funktion. Opgradér til Team for at automatisere dine begivenhedsnotifikationer og påmindelser med Workflows.",
"show_eventtype_on_profile": "Vis på profil",
"embed": "Indlejr",
"new_username": "Nyt brugernavn",
@@ -1161,7 +1188,37 @@
"profile_picture": "Profilbillede",
"upload": "Upload",
"add_profile_photo": "Tilføj profilbillede",
"web3": "Web3",
"rainbow_token_gated": "Denne begivenhedstype er token gated.",
"rainbow_connect_wallet_gate": "Forbind din wallet, hvis du ejer <1>{{name}}</1> (<3>{{symbol}}</3>).",
"rainbow_insufficient_balance": "Din tilsluttede wallet indeholder ikke nok <1>{{symbol}}</1>.",
"rainbow_sign_message_request": "Signér beskedanmodningen på din wallet.",
"rainbow_signature_error": "Fejl ved anmodning om signatur fra din wallet.",
"token_address": "Token Adresse",
"blockchain": "Blockchain",
"old_password": "Gammel adgangskode",
"secure_password": "Din nye super sikre adgangskode",
"error_updating_password": "Fejl under opdatering af adgangskode",
"two_factor_auth": "To-faktor autentificering",
"recurring_event_tab_description": "Opret en gentagende tidsplan",
"today": "i dag",
"appearance": "Udseende",
"appearance_subtitle": "Administrér indstillinger for din booking visning",
"my_account": "Min konto",
"general": "Generelt",
"calendars": "Kalendere",
"2fa_auth": "To-faktor aut",
"invoices": "Fakturaer",
"embeds": "Indlejringer",
"impersonation": "Impersonation",
"impersonation_description": "Indstillinger til håndtering af bruger-impersonation",
"users": "Brugere",
"profile_description": "Administrér indstillinger for din {{appName}} profil",
"users_description": "Her kan du finde en liste over alle brugere",
"users_listing": "Bruger liste",
"general_description": "Administrér indstillinger for dit sprog og tidszone",
"calendars_description": "Indstil hvordan dine begivenhedstyper interagerer med dine kalendere",
"appearance_description": "Administrér indstillinger for din booking visning",
"conferencing_description": "Tilføj dine foretrukne videokonference apps til dine møder",
"password_description": "Administrér indstillinger for dine kontoadgangskoder",
"2fa_description": "Administrér indstillinger for dine kontoadgangskoder",
@@ -1337,7 +1394,7 @@
"saml_sp_entity_id": "SP Entity ID",
"saml_sp_acs_url_copied": "ACS URL kopieret!",
"saml_sp_entity_id_copied": "SP Entity ID kopieret!",
"saml_btn_configure": "Konfigurér",
"saml_btn_configure": "Indstil",
"add_calendar": "Tilføj Kalender",
"limit_future_bookings": "Begræns fremtidige bookinger",
"limit_future_bookings_description": "Begræns hvor langt i fremtiden denne begivenhed kan bookes",
@@ -1378,7 +1435,7 @@
"edit_event_type": "Redigér begivenhedstype",
"collective_scheduling": "Kollektiv Planlægning",
"make_it_easy_to_book": "Gør det nemt at booke dit team, når alle er tilgængelige.",
"find_the_best_person": "Find den bedste tilgængelige person, og cykl gennem dit team.",
"find_the_best_person": "Find den bedste person tilgængelig, og cykl gennem dit team.",
"fixed_round_robin": "Fast round robin",
"add_one_fixed_attendee": "Tilføj en fast deltager og round robin gennem en række deltagere.",
"calcom_is_better_with_team": "Cal.com er bedre med teams",
@@ -1389,6 +1446,41 @@
"disable_payment_app": "Administratoren har deaktiveret {{appName}}, hvilket påvirker din begivenhedstype {{title}}. Deltagere er stadig i stand til at booke denne type begivenhed, men vil ikke blive bedt om at betale. Du kan skjule begivenhedstypen for at forhindre dette, indtil din administrator genaktiverer din betalingsmetode.",
"payment_disabled_still_able_to_book": "Deltagere kan stadig booke denne type begivenhed, men vil ikke blive bedt om at betale. Du kan skjule begivenhedstypen for at forhindre dette, indtil din administrator genaktiverer din betalingsmetode.",
"app_disabled_with_event_type": "Administratoren har deaktiveret {{appName}}, hvilket påvirker din begivenhedstype {{title}}.",
"app_disabled_video": "Administratoren har deaktiveret {{appName}}, hvilket kan påvirke dine begivenhedstyper. Hvis du har begivenhedstyper med {{appName}} som placering, vil den som standard gå til Cal Video.",
"app_disabled_subject": "{{appName}} er blevet deaktiveret",
"navigate_installed_apps": "Gå til installerede apps",
"disabled_calendar": "Hvis du har en anden kalender installeret vil nye bookinger blive tilføjet til den. Hvis ikke, så forbind en ny kalender, så du ikke går glip af nye bookinger.",
"enable_apps": "Aktivér Apps",
"enable_apps_description": "Aktivér apps, som brugere kan integrere med Cal.com",
"purchase_license": "Køb en licens",
"already_have_key": "Jeg har allerede en nøgle:",
"already_have_key_suggestion": "Kopiér venligst din eksisterende CALCOM_LICENSE_KEY environment variabel her.",
"app_is_enabled": "{{appName}} er aktiveret",
"app_is_disabled": "{{appName}} er deaktiveret",
"keys_have_been_saved": "Nøgler er blevet gemt",
"disable_app": "Deaktivér App",
"disable_app_description": "Deaktivering af denne app kan forårsage problemer med, hvordan dine brugere interagerer med Cal",
"edit_keys": "Redigér Nøgler",
"admin_apps_description": "Aktivér apps for din instans af Cal",
"no_available_apps": "Der er ingen tilgængelige apps",
"no_available_apps_description": "Sørg for, at der er apps i din implementering under 'packages/app-store'",
"no_apps": "Der er ingen apps aktiveret i denne instans af Cal",
"apps_settings": "Indstillinger for apps",
"fill_this_field": "Udfyld venligst dette felt",
"options": "Muligheder",
"enter_option": "Angiv Mulighed {{index}}",
"add_an_option": "Tilføj en mulighed",
"radio": "Radio",
"google_meet_warning": "For at kunne bruge Google Meet skal du indstille din destinationskalender til en Google-kalender",
"individual": "Individuel",
"all_bookings_filter_label": "Alle Bookinger",
"all_users_filter_label": "Alle Brugere",
"your_bookings_filter_label": "Dine Bookinger",
"meeting_url_variable": "Møde url",
"meeting_url_info": "Begivenhedens mødekonference url",
"date_overrides": "Overskrivning af dato",
"date_overrides_subtitle": "Tilføj datoer, når din tilgængelighed skifter fra dine daglige timer.",
"date_overrides_info": "Overskrivninger af dato arkiveres automatisk efter datoen er passeret",
"date_overrides_dialog_which_hours": "Hvilke timer er du ledig?",
"date_overrides_dialog_which_hours_unavailable": "Hvilke timer er du optaget?",
"date_overrides_dialog_title": "Vælg de datoer der skal tilsidesættes",
@@ -1434,15 +1526,31 @@
"sender_name": "Afsenders navn",
"already_invited": "Deltager allerede inviteret",
"no_recordings_found": "Ingen optagelser fundet",
"new_workflow_subtitle": "Nyt Workflow for...",
"reporting": "Indberetning",
"reporting_feature": "Se alt indkommende fra data og download det som en CSV",
"teams_plan_required": "Teams plan påkrævet",
"routing_forms_are_a_great_way": "Routingformularer er en fantastisk måde at dirigere dine indgående leads til den rigtige person. Opgradér til en Teams-plan for at få adgang til denne funktion.",
"choose_a_license": "Vælg en licens",
"choose_license_description": "Cal.com kommer med en tilgængelig og gratis AGPLv3-licens med nogle begrænsninger, som til enhver tid kan opgraderes til en Enterprise-licens. Du kan opgradere når som helst senere.",
"license": "Licens",
"agplv3_license": "AGPLv3 Licens",
"ee_enterprise_license": "“/ee” Virksomhedslicens",
"enterprise_booking_fee": "Fra {{enterprise_booking_fee}}/måned",
"enterprise_license_includes": "Alt til en kommerciel brugssag",
"no_need_to_keep_your_code_open_source": "Ingen grund til at holde din kode open source",
"repackage_rebrand_resell": "Ompak, rebrand og videresælg nemt",
"a_vast_suite_of_enterprise_features": "En bred vifte af virksomhedsfunktioner",
"free_license_fee": "0,00kr./måned",
"forever_open_and_free": "Åbent og Gratis for altid",
"required_to_keep_your_code_open_source": "Påkrævet for at holde din kode open source",
"cannot_repackage_and_resell": "Kan ikke ompakke, rebrande og videresælge nemt",
"no_enterprise_features": "Ingen virksomhedsfunktioner",
"step_enterprise_license": "Virksomhedslicens",
"step_enterprise_license_description": "Alt til en kommerciel brugssag med privat hosting, ompakning, rebranding og videresalg, og adgang til eksklusive virksomhedskomponenter.",
"setup": "Opsætning",
"setup_description": "Opsæt Cal.com instans",
"configure": "Konfigurér",
"configure": "Indstil",
"sso_configuration": "Enkelt Log På",
"sso_configuration_description": "Konfigurer SAML/OIDC SSO og tillad teammedlemmer at logge ind ved hjælp af en Identitetsudbyder",
"sso_oidc_heading": "SSO med OIDC",
@@ -1473,14 +1581,17 @@
"email_no_user_step_four": "Tilmeld dig {{teamName}}",
"email_no_user_signoff": "Glædelig planlægning fra {{appName}} teamet",
"impersonation_user_tip": "Du er ved at efterligne en bruger, hvilket betyder, at du kan foretage ændringer på deres vegne. Vær venligst forsigtig.",
"scheduler": "{Scheduler}",
"available_variables": "Tilgængelige variabler",
"scheduler": "{Scheduler}",
"no_workflows": "Ingen workflows",
"change_filter": "Skift filter for at se dine personlige- og team workflows.",
"recommended_next_steps": "Anbefalede næste trin",
"create_a_managed_event": "Opret en administreret begivenhedstype",
"meetings_are_better_with_the_right": "Møder er bedre med de rigtige teammedlemmer. Invitér dem nu.",
"create_a_one_one_template": "Opret en one-one skabelon for en begivenhedstype og distribuér den til flere medlemmer.",
"collective_or_roundrobin": "Kollektiv eller round-robin",
"book_your_team_members": "Book dine teammedlemmer sammen med kollektive arrangementer eller cykl igennem for at få den rigtige person med round-robin.",
"create_event_on": "Opret begivenhed den",
"default_app_link_title": "Angiv et standard app-link",
"default_app_link_description": "Indstilling af et standard app-link gør det muligt for alle nyoprettede begivenhedstyper at bruge det app-link, du har angivet.",
"change_default_conferencing_app": "Angiv som standard",
@@ -1492,6 +1603,7 @@
"booking_with_payment_cancelled_refunded": "Denne booking betaling er blevet refunderet.",
"booking_confirmation_failed": "Reservationsbekræftelse mislykkedes",
"get_started_zapier_templates": "Kom i gang med Zapier skabeloner",
"team_member": "Teammedlem",
"a_routing_form": "En Routing Formular",
"form_description_placeholder": "Formular Beskrivelse",
"keep_me_connected_with_form": "Hold mig i forbindelse med formularen",
@@ -1511,5 +1623,7 @@
"select_a_router": "Vælg en router",
"add_a_new_route": "Tilføj en ny rute",
"no_responses_yet": "Ingen svar endnu",
"this_will_be_the_placeholder": "Dette vil være pladsholderen"
"this_will_be_the_placeholder": "Dette vil være pladsholderen",
"verification_code": "Bekræftelseskode",
"verify": "Bekræft"
}
+41 -42
View File
@@ -25,7 +25,7 @@
"rejection_confirmation": "Buchung ablehnen",
"manage_this_event": "Diesen Termin verwalten",
"invite_team_member": "Teammitglied einladen",
"invite_team_notifcation_badge": "Inv.",
"invite_team_notifcation_badge": "Eing.",
"your_event_has_been_scheduled": "Ihr Termin wurde gebucht",
"your_event_has_been_scheduled_recurring": "Ihr wiederkehrender Termin wurde gebucht",
"accept_our_license": "Akzeptieren Sie unsere Lizenz, indem Sie die .env-Variable <1>NEXT_PUBLIC_LICENSE_CONSENT</1> auf '{{agree}} ' ändern.",
@@ -413,8 +413,8 @@
"password_updated_successfully": "Passwort erfolgreich aktualisiert",
"password_has_been_changed": "Ihr Passwort wurde erfolgreich geändert.",
"error_changing_password": "Fehler beim Ändern des Passworts",
"session_timeout_changed": "Ihre Session Konfiguration wurde erfolgreich aktualisiert.",
"session_timeout_change_error": "Fehler beim Aktualisieren der Session Konfiguration",
"session_timeout_changed": "Ihre Sitzungskonfiguration wurde erfolgreich aktualisiert.",
"session_timeout_change_error": "Fehler beim Aktualisieren der Sitzungskonfiguration",
"something_went_wrong": "Etwas ist schief gelaufen",
"something_doesnt_look_right": "Irgendwas sieht nicht richtig aus?",
"please_try_again": "Bitte erneut versuchen",
@@ -515,7 +515,7 @@
"admin": "Administrator",
"administrator_user": "Administrator Benutzer",
"lets_create_first_administrator_user": "Lass uns als erstes den Administrator Benutzer anlegen.",
"admin_user_created": "Administrator-Benutzereinstellungen",
"admin_user_created": "Administrator-Benutzereinrichtung",
"admin_user_created_description": "Sie haben bereits einen Administrator-Benutzer erstellt. Sie können sich nun in Ihr Konto einloggen.",
"new_member": "Neues Mitglied",
"invite": "Einladen",
@@ -598,7 +598,6 @@
"new_event_type_heading": "Erstellen Sie Ihren ersten Ereignistyp",
"new_event_type_description": "Mit Ereignistypen kann man verfügbare Zeiten im Kalendar freigeben, die andere Personen dann buchen können.",
"new_event_title": "Neuen Ereignistyp hinzufügen",
"new_event_subtitle": "Erstellen Sie einen Ereignistyp unter Ihrem Namen oder einem Team.",
"new_team_event": "Neuen Ereignistyp hinzufügen",
"new_event_description": "Erstellen Sie einen neuen Ereignistyp, mit dem Personen Zeiten buchen können.",
"event_type_created_successfully": "{{eventTypeTitle}} Ereignistyp erfolgreich erstellt",
@@ -944,11 +943,11 @@
"current_location": "Aktueller Ort",
"user_phone": "Ihre Telefonnummer",
"new_location": "Neuer Ort",
"session": "Session",
"session_description": "Verwalten Sie Ihre Account-Session",
"session": "Sitzung",
"session_description": "Verwalten Sie Ihre Kontositzung",
"session_timeout_after": "Timeout Session nach",
"session_timeout": "Session Timeout",
"session_timeout_description": "Nach einer bestimmten Zeit die Session löschen.",
"session_timeout_description": "Erklärt nach einer bestimmten Zeit Ihre Sitzung für ungültig.",
"no_location": "Kein Ort definiert",
"set_location": "Ort festlegen",
"update_location": "Ort aktualisieren",
@@ -1295,7 +1294,7 @@
"routing_forms_send_email_owner_description": "Sendet eine E-Mail an den Eigentümer, wenn das Formular abgeschickt wird",
"add_new_form": "Neues Formular hinzufügen",
"create_your_first_route": "Erstellen Sie Ihre erste Route",
"route_to_the_right_person": "Route an die richtige Person anhand der Formularantworten",
"route_to_the_right_person": "Anhand der Formularantworten an die richtige Person weiterleiten",
"form_description": "Erstellen Sie Ihr Formular, um einen Bucher zu anzuleiten",
"copy_link_to_form": "Link in Formular kopieren",
"theme": "Theme",
@@ -1454,8 +1453,8 @@
"enable_apps": "Apps aktivieren",
"enable_apps_description": "Apps aktivieren, die Benutzer mit Cal.com integrieren können",
"purchase_license": "Lizenz kaufen",
"already_have_key": "Ich habe bereits einen Key:",
"already_have_key_suggestion": "Bitte fügen Sie Ihre vorhandene CALCOM_LICENSE_KEY Lizenz hier ein.",
"already_have_key": "Ich habe bereits einen Schlüssel:",
"already_have_key_suggestion": "Bitte fügen Sie Ihre vorhandene CALCOM_LICENSE_KEY Umgebungsvariable hier ein.",
"app_is_enabled": "{{appName}} ist aktiviert",
"app_is_disabled": "{{appName}} ist deaktiviert",
"keys_have_been_saved": "Schlüssel wurden gespeichert",
@@ -1526,28 +1525,28 @@
"install_google_calendar": "Google Calendar installieren",
"sender_name": "Absendername",
"already_invited": "Teilnehmer bereits eingeladen",
"no_recordings_found": "Keine Aufzeichnungen gefunden",
"reporting": "Berichte",
"reporting_feature": "Alle Daten anzeigen und als CSV herunterladen",
"teams_plan_required": "Teamplan benötigt",
"routing_forms_are_a_great_way": "Routing-Formulare sind eine gute Möglichkeit, Ihre eingehenden Leads zur richtigen Person zu leiten. Upgrade auf einen Teamplan, um auf diese Funktion zuzugreifen.",
"no_recordings_found": "Keine Aufnahmen gefunden",
"reporting": "Meldungen",
"reporting_feature": "Alle eingehenden Daten anzeigen und als CSV herunterladen",
"teams_plan_required": "Teams-Plan benötigt",
"routing_forms_are_a_great_way": "Routing-Formulare sind eine gute Möglichkeit, Ihre eingehenden Leads zur richtigen Person zu leiten. Upgraden Sie auf einen Teamplan, um auf diese Funktion zuzugreifen.",
"choose_a_license": "Wählen Sie eine Lizenz",
"choose_license_description": "Cal.com enthält eine kostenlose AGPLv3-Lizenz, die einige Einschränkungen enthält, die jederzeit auf eine Enterprise-Lizenz aktualisiert werden können. Sie können später jederzeit upgraden.",
"choose_license_description": "Cal.com enthält eine kostenlose AGPLv3-Lizenz, mit einigen Einschränkungen, die jederzeit auf eine Enterprise-Lizenz aufgerüstet werden können. Sie können später jederzeit upgraden.",
"license": "Lizenz",
"agplv3_license": "AGPLv3 Lizenz",
"ee_enterprise_license": "„/ee“ Enterprise-Lizenz",
"enterprise_booking_fee": "Ab {{enterprise_booking_fee}}/Monat",
"enterprise_license_includes": "Alles für einen kommerziellen Anwendungsfall",
"no_need_to_keep_your_code_open_source": "Sie müssen Ihren Code nicht Open-Source lassen",
"repackage_rebrand_resell": "Einfaches neuverpacken, rebranding und weiterverkaufen",
"no_need_to_keep_your_code_open_source": "Ihr Code muss nicht open-source bleiben",
"repackage_rebrand_resell": "Einfaches Neuverpacken, Rebranding und Weiterverkaufen",
"a_vast_suite_of_enterprise_features": "Eine große Auswahl an Enterprise-Funktionen",
"free_license_fee": "0.00€/Monat",
"free_license_fee": "0,00€/Monat",
"forever_open_and_free": "Für immer offen & kostenlos",
"required_to_keep_your_code_open_source": "Ihr Code muss Open-Source bleiben",
"cannot_repackage_and_resell": "Kann nicht einfach neu verpackt, gerebrandet und weiterverkauft werden",
"required_to_keep_your_code_open_source": "Ihr Code muss open-source bleiben",
"cannot_repackage_and_resell": "Kann nicht einfach neuverpackt, rebrandet und weiterverkauft werden",
"no_enterprise_features": "Keine Enterprise-Funktionen",
"step_enterprise_license": "Enterprise-Lizenz",
"step_enterprise_license_description": "Alles für einen kommerziellen Einsatzfall mit privatem Hosting, Umverpackung, Rebranding, Wiederverkauf und Zugriff auf exklusive Enterprise Komponenten.",
"step_enterprise_license_description": "Alles für einen kommerziellen Einsatzfall mit privatem Hosting, Umverpacken, Rebranding, Wiederverkauf und Zugriff auf exklusive Enterprise-Komponenten.",
"setup": "Einrichtung",
"setup_description": "Cal.com Instanz einrichten",
"configure": "Konfigurieren",
@@ -1566,28 +1565,28 @@
"sso_saml_entityid_copied": "Entitäts-ID kopiert",
"sso_connection_created_successfully": "{{connectionType}} Konfiguration erfolgreich erstellt",
"sso_connection_deleted_successfully": "{{connectionType}} Konfiguration erfolgreich gelöscht",
"delete_sso_configuration": "Lösche {{connectionType}} Konfiguration",
"delete_sso_configuration": "{{connectionType}}-Konfiguration löschen",
"delete_sso_configuration_confirmation": "Ja, {{connectionType}} Konfiguration löschen",
"delete_sso_configuration_confirmation_description": "Sind Sie sicher, dass Sie die {{connectionType}} -Konfiguration löschen möchten? Ihre Teammitglieder, die {{connectionType}} anmelden, werden keinen Zugriff mehr auf Cal.com haben.",
"delete_sso_configuration_confirmation_description": "Sind Sie sicher, dass Sie die {{connectionType}}-Konfiguration löschen möchten? Ihre Teammitglieder, die sich über {{connectionType}} anmelden, werden keinen Zugriff mehr auf Cal.com haben.",
"organizer_timezone": "Zeitzone des Veranstalters",
"email_no_user_cta": "Erstellen Sie Ihr Konto",
"email_user_cta": "Einladung anzeigen",
"email_no_user_invite_heading": "Sie wurden eingeladen dem Team {{appName}} beizutreten",
"email_no_user_invite_subheading": "{{invitedBy}} hat Sie eingeladen, dem Team {{appName}} beizutreten. {{appName}} ist der Event-Planer, der es Ihnen und Ihrem Team ermöglicht, Meetings ohne E-Mail Tennis zu planen.",
"email_no_user_invite_steps_intro": "Wir begleiten Sie durch ein paar Schritte und Sie genießen stressfreie Planung mit Ihrem Team in kürzester Zeit.",
"email_no_user_step_one": "Benutzernamen auswählen",
"email_no_user_step_two": "Kalenderkonto verbinden",
"email_no_user_step_three": "Verfügbarkeit festlegen",
"email_no_user_step_four": "{{teamName}} beitreten",
"email_no_user_signoff": "Glückliche Terminplanung wünscht das {{appName}} Team",
"impersonation_user_tip": "Sie sind dabei einen Benutzer zu identifizieren, was bedeutet, dass Sie Änderungen in ihrem Namen vornehmen können. Bitte seien Sie vorsichtig.",
"scheduler": "{Scheduler}",
"email_no_user_invite_heading": "Sie wurden eingeladen, dem Team in {{appName}} beizutreten",
"email_no_user_invite_subheading": "{{invitedBy}} hat Sie eingeladen, dem Team {{appName}} beizutreten. {{appName}} ist der Event-Planer, der es Ihnen und Ihrem Team ermöglicht, Meetings ohne ständiges hin und her zu planen.",
"email_no_user_invite_steps_intro": "Wir begleiten Sie durch ein paar Schritte, danach können Sie stressfreie Planung mit Ihrem Team in kürzester Zeit genießen.",
"email_no_user_step_one": "Wählen Sie Ihren Benutzernamen",
"email_no_user_step_two": "Verbinden Sie Ihr Kalenderkonto",
"email_no_user_step_three": "Legen Sie Ihre Verfügbarkeit fest",
"email_no_user_step_four": "Treten Sie {{teamName}} bei",
"email_no_user_signoff": "Fröhliche Terminplanung wünscht das {{appName}} Team",
"impersonation_user_tip": "Sie sind dabei einen Benutzer zu identifizieren, was bedeutet, dass Sie Änderungen an ihrem Namen vornehmen können. Bitte seien Sie vorsichtig.",
"available_variables": "Verfügbare Variablen",
"scheduler": "{Scheduler}",
"recommended_next_steps": "Empfohlene nächste Schritte",
"create_a_managed_event": "Erstellen Sie ein verwalteten Ereignistyp",
"meetings_are_better_with_the_right": "Treffen sind besser mit den richtigen Teammitgliedern. Laden Sie sie jetzt ein.",
"create_a_managed_event": "Einen verwalteten Ereignistyp erstellen",
"meetings_are_better_with_the_right": "Meetings sind mit den richtigen Teammitgliedern besser. Laden Sie sie jetzt ein.",
"collective_or_roundrobin": "Kollektiv oder Round-Robin",
"book_your_team_members": "Buchen Sie Ihre Teammitglieder zusammen mit Kollektivveranstaltungen oder wählen Sie anhand Round-Robin die richtige Person.",
"book_your_team_members": "Buchen Sie Ihre Teammitglieder zusammen mit Kollektivveranstaltungen oder wählen Sie mit Round-Robin die richtige Person.",
"default_app_link_title": "Standard-App-Link festlegen",
"default_app_link_description": "Mit dem Festlegen eines Standard-App-Links können alle neu erstellten Ereignistypen den App-Link verwenden, den Sie gesetzt haben.",
"change_default_conferencing_app": "Als Standard festlegen",
@@ -1598,15 +1597,15 @@
"booking_with_payment_cancelled_already_paid": "Eine Rückerstattung für diese Buchungszahlung ist auf dem Weg.",
"booking_with_payment_cancelled_refunded": "Diese Buchungszahlung wurde zurückerstattet.",
"booking_confirmation_failed": "Buchungsbestätigung fehlgeschlagen",
"get_started_zapier_templates": "Beginnen Sie mit Zapier-Templates",
"get_started_zapier_templates": "Legen Sie mit Zapier-Templates los",
"a_routing_form": "Ein Routing-Formular",
"form_description_placeholder": "Formularbeschreibung",
"keep_me_connected_with_form": "Mich mit dem Formular verbunden lassen",
"fields_in_form_duplicated": "Alle Änderungen in Router und Felder des Formulars, die dupliziert werden, werden auch im Duplikat wiedergegeben.",
"keep_me_connected_with_form": "Behalte mich mit dem Formular verbunden",
"fields_in_form_duplicated": "Alle Änderungen in Router und in den Feldern des Formulars, die dupliziert werden, werden auch im Duplikat wiedergegeben.",
"form_deleted": "Formular gelöscht",
"delete_form": "Formular löschen",
"delete_form_action": "Ja, Formular löschen",
"delete_form_confirmation": "Sind Sie sicher, dass Sie dieses Formular löschen möchten? Jede Person, mit dem Link kann nicht mehr mit dem Formular buchen. Außerdem wurden alle zugehörigen Antworten gelöscht.",
"delete_form_confirmation": "Sind Sie sicher, dass Sie dieses Formular löschen möchten? Jede Person, mit der Sie den Link geteilt haben, kann nicht mehr über das Formular buchen. Außerdem würden alle zugehörigen Antworten gelöscht werden.",
"typeform_redirect_url_copied": "Typeform Umleitungs-URL kopiert! Sie können die URL in Typeform einstellen.",
"modifications_in_fields_warning": "Änderungen in den Feldern und Routen der folgenden Formulare werden in diesem Formular reflektiert.",
"connected_forms": "Verbundene Formulare",
+22 -3
View File
@@ -598,7 +598,6 @@
"new_event_type_heading": "Create your first event type",
"new_event_type_description": "Event types enable you to share links that show available times on your calendar and allow people to make bookings with you.",
"new_event_title": "Add a new event type",
"new_event_subtitle": "Create an event type under your name or a team.",
"new_team_event": "Add a new team event type",
"new_event_description": "Create a new event type for people to book times with.",
"event_type_created_successfully": "{{eventTypeTitle}} event type created successfully",
@@ -861,6 +860,7 @@
"add_new_calendar": "Add a new calendar",
"set_calendar": "Set where to add new events to when you're booked.",
"delete_schedule": "Delete schedule",
"delete_schedule_description":"Deleting a schedule will remove it from all event types. This action cannot be undone.",
"schedule_created_successfully": "{{scheduleName}} schedule created successfully",
"availability_updated_successfully": "{{scheduleName}} schedule updated successfully",
"schedule_deleted_successfully": "Schedule deleted successfully",
@@ -1527,6 +1527,7 @@
"sender_name": "Sender name",
"already_invited": "Attendee already invited",
"no_recordings_found": "No recordings found",
"new_workflow_subtitle": "New workflow for...",
"reporting": "Reporting",
"reporting_feature": "See all incoming from data and download it as a CSV",
"teams_plan_required": "Teams plan required",
@@ -1581,25 +1582,37 @@
"email_no_user_step_four":"Join {{teamName}}",
"email_no_user_signoff":"Happy Scheduling from the {{appName}} team",
"impersonation_user_tip": "You are about to impersonate a user, which means you can make changes on their behalf. Please be careful.",
"scheduler": "{Scheduler}",
"available_variables": "Available variables",
"scheduler": "{Scheduler}",
"no_workflows": "No workflows",
"change_filter": "Change filter to see your personal and team workflows.",
"recommended_next_steps": "Recommended next steps",
"create_a_managed_event": "Create a managed event type",
"meetings_are_better_with_the_right": "Meetings are better with the right team members there. Invite them now.",
"create_a_one_one_template": "Create a one-one one template for an event type and distribute it to multiple members.",
"collective_or_roundrobin": "Collective or round-robin",
"book_your_team_members": "Book your team members together with collective events or cycle through to get the right person with round-robin.",
"create_event_on": "Create event on",
"default_app_link_title":"Set a default app link",
"default_app_link_description":"Setting a default app link allows all newly created event types to use the app link you set.",
"change_default_conferencing_app":"Set as default",
"under_maintenance": "Down for maintenance",
"under_maintenance_description": "The {{appName}} team are performing scheduled maintenance. If you have any questions, please contact support.",
"event_type_seats": "{{numberOfSeats}} seats",
"booking_questions_title": "Booking questions",
"booking_questions_description": "Customize the questions asked on the booking page",
"add_a_booking_question": "Add a question",
"duplicate_email": "Email is duplicate",
"booking_with_payment_cancelled": "Paying for this event is no longer possible",
"booking_with_payment_cancelled_already_paid": "A refund for this booking payment it's on the way.",
"booking_with_payment_cancelled_refunded": "This booking payment has been refunded.",
"booking_confirmation_failed": "Booking confirmation failed",
"form_builder_field_already_exists": "A field with this name already exists",
"form_builder_field_add_subtitle": "Customize the questions asked on the booking page",
"form_builder_system_field_cant_delete": "This system field can't be removed.",
"form_builder_system_field_cant_toggle": "This system field can't be toggled.",
"get_started_zapier_templates": "Get started with Zapier templates",
"team_member": "Team member",
"a_routing_form": "A Routing Form",
"form_description_placeholder": "Form Description",
"keep_me_connected_with_form": "Keep me connected with the form",
@@ -1619,5 +1632,11 @@
"select_a_router": "Select a router",
"add_a_new_route": "Add a new Route",
"no_responses_yet": "No responses yet",
"this_will_be_the_placeholder": "This will be the placeholder"
"this_will_be_the_placeholder": "This will be the placeholder",
"verification_code": "Verification code",
"verify": "Verify",
"select_all": "Select All",
"default_conferencing_bulk_title": "Bulk update existing event types",
"default_conferencing_bulk_description": "Update the locations for the selected event types",
"invalid_event_name_variables": "There is an invalid variable in your event name"
}
@@ -589,7 +589,6 @@
"new_event_type_heading": "Crea tu primer tipo de evento",
"new_event_type_description": "Los tipos de eventos te permiten compartir enlaces que muestran las horas disponibles en tu calendario y permitir que la gente haga reservas contigo.",
"new_event_title": "Crear Nuevo Tipo de Evento",
"new_event_subtitle": "Crea un tipo de evento bajo tu nombre o equipo.",
"new_team_event": "Crear Nuevo Tipo de Evento de Equipo",
"new_event_description": "Crea un nuevo tipo de evento con el que la gente pueda hacer reservas.",
"event_type_created_successfully": "{{eventTypeTitle}} tipo de evento creado con éxito",
+22 -3
View File
@@ -598,7 +598,6 @@
"new_event_type_heading": "Créez votre premier type d'événement",
"new_event_type_description": "Les types d'événements vous permettent de partager des liens qui affichent vos disponibilités sur votre calendrier et permettent aux personnes de réserver des créneaux.",
"new_event_title": "Ajouter un nouveau type d'événement",
"new_event_subtitle": "Créez un type d'événement pour vous ou votre équipe.",
"new_team_event": "Ajouter un nouveau type d'événement d'équipe",
"new_event_description": "Créez un nouveau type d'événement avec lequel les personnes peuvent réserver des créneaux.",
"event_type_created_successfully": "Type d'événement {{eventTypeTitle}} créé avec succès",
@@ -861,6 +860,7 @@
"add_new_calendar": "Ajouter un nouveau calendrier",
"set_calendar": "Définissez où ajouter les nouveaux événements après une réservation.",
"delete_schedule": "Supprimer le planning",
"delete_schedule_description": "La suppression d'un planning l'enlèvera de tous les types d'événements. Cette action ne peut pas être annulée.",
"schedule_created_successfully": "Planning {{scheduleName}} créé avec succès",
"availability_updated_successfully": "Planning {{scheduleName}} mis à jour avec succès",
"schedule_deleted_successfully": "Planning supprimé avec succès",
@@ -1527,6 +1527,7 @@
"sender_name": "Nom de l'expéditeur",
"already_invited": "Participant déjà invité",
"no_recordings_found": "Aucun enregistrement trouvé",
"new_workflow_subtitle": "Nouveau workflow pour...",
"reporting": "Rapports",
"reporting_feature": "Voir toutes les données entrantes et les télécharger au format CSV.",
"teams_plan_required": "Plan Équipes requis",
@@ -1581,25 +1582,37 @@
"email_no_user_step_four": "Rejoignez {{teamName}}",
"email_no_user_signoff": "Bonne planification de la part de l'équipe {{appName}}",
"impersonation_user_tip": "Vous êtes sur le point d'emprunter l'identité d'un utilisateur, ce qui signifie que vous pouvez effectuer des modifications en son nom. S'il vous plaît, soyez prudent(e).",
"scheduler": "{Scheduler}",
"available_variables": "Variables disponibles",
"scheduler": "{Scheduler}",
"no_workflows": "Aucun workflow",
"change_filter": "Changez le filtre pour voir vos workflows personnels et d'équipe.",
"recommended_next_steps": "Étapes suivantes recommandées",
"create_a_managed_event": "Créer un type d'événement géré",
"meetings_are_better_with_the_right": "Les rendez-vous sont meilleurs avec les bons membres d'équipe. Invitez-les maintenant.",
"create_a_one_one_template": "Créez un modèle unique pour un type d'événement et distribuez-le à plusieurs membres.",
"collective_or_roundrobin": "Collective ou round-robin",
"book_your_team_members": "Réservez les membres de votre équipe entre eux avec des événements collectifs ou mettez en place un cycle pour obtenir la bonne personne avec le round-robin.",
"create_event_on": "Créer un événement dans",
"default_app_link_title": "Définir un lien d'application par défaut",
"default_app_link_description": "Définir un lien d'application par défaut permet à tous les nouveaux types d'événements d'utiliser le lien d'application que vous avez défini.",
"change_default_conferencing_app": "Définir par défaut",
"under_maintenance": "En cours de maintenance",
"under_maintenance_description": "L'équipe {{appName}} effectue une maintenance planifiée. Si vous avez des questions, veuillez contacter l'assistance.",
"event_type_seats": "{{numberOfSeats}} places",
"booking_questions_title": "Questions de réservation",
"booking_questions_description": "Personnalisez les questions posées sur la page de réservation.",
"add_a_booking_question": "Ajouter une question",
"duplicate_email": "L'adresse e-mail existe déjà",
"booking_with_payment_cancelled": "Payer pour cet événement n'est plus possible",
"booking_with_payment_cancelled_already_paid": "Un remboursement pour ce paiement de réservation est en route.",
"booking_with_payment_cancelled_refunded": "Ce paiement de réservation a été remboursé.",
"booking_confirmation_failed": "Échec de la confirmation de la réservation",
"form_builder_field_already_exists": "Un champ avec ce nom existe déjà",
"form_builder_field_add_subtitle": "Personnalisez les questions posées sur la page de réservation.",
"form_builder_system_field_cant_delete": "Ce champ système ne peut pas être supprimé.",
"form_builder_system_field_cant_toggle": "Ce champ système ne peut pas être activé.",
"get_started_zapier_templates": "Démarrer avec les modèles Zapier",
"team_member": "Membre d'équipe",
"a_routing_form": "Un formulaire de routage",
"form_description_placeholder": "Description du formulaire",
"keep_me_connected_with_form": "Rester connecté avec le formulaire",
@@ -1619,5 +1632,11 @@
"select_a_router": "Sélectionner un routeur",
"add_a_new_route": "Ajouter une nouvelle route",
"no_responses_yet": "Aucune réponse pour l'instant",
"this_will_be_the_placeholder": "Ce sera le texte de substitution"
"this_will_be_the_placeholder": "Ce sera le texte de substitution",
"verification_code": "Code de vérification",
"verify": "Vérifier",
"select_all": "Tout sélectionner",
"default_conferencing_bulk_title": "Mise à jour multiple des types d'événements existants",
"default_conferencing_bulk_description": "Mettez à jour les emplacements pour les types d'événements sélectionnés.",
"invalid_event_name_variables": "Il y a une variable invalide dans le nom de votre événement"
}
+230 -90
View File
@@ -1,5 +1,5 @@
{
"trial_days_left": "נותרו לך $t(day, {\"count\": {{days}} }) לשימוש בגרסת הניסיון של גרסת ה-PRO",
"trial_days_left": "נותרו לך $t(day, {\"count\": {{days}} }) לשימוש בגרסת הניסיון של רישוי PRO",
"day_one": "יום {{count}}",
"day_other": "{{count}} ימים",
"second_one": "שנייה {{count}}",
@@ -7,10 +7,11 @@
"upgrade_now": "לשדרג עכשיו",
"accept_invitation": "אישור הזמנה",
"calcom_explained": "{{appName}} מספקת תשתית ליצירת לוחות זמנים שתתאים לכולם.",
"calcom_explained_new_user": "סיים להגדיר את החשבון של {{appName}}! אתה רק מספר צעדים מפתרון כל בעיות התזמון שלך.",
"have_any_questions": "יש לך שאלות? אנחנו כאן כדי לעזור.",
"reset_password_subject": "{{appName}}: הנחיות לאיפוס סיסמה",
"event_declined_subject": "נדחה: {{title}} בתאריך {{date}}",
"event_cancelled_subject": "בוטל: {{title}} בתאריך {{date}}",
"event_declined_subject": "נדחה: {{title}} ב- {{date}}",
"event_cancelled_subject": "בוטל: {{title}} ב- {{date}}",
"event_request_declined": "בקשת האירוע שלך נדחתה",
"event_request_declined_recurring": "בקשת האירוע החוזר שלך נדחתה",
"event_request_cancelled": "האירוע המתוכנן בוטל",
@@ -23,6 +24,8 @@
"rejection_reason_description": "בטוח שברצונך לדחות את ההזמנה? אנחנו ניידע את האדם שניסה להזמין. יש לך אפשרות להוסיף את הסיבה לדחייה למטה.",
"rejection_confirmation": "לדחות את ההזמנה",
"manage_this_event": "נהל את האירוע הזה",
"invite_team_member": "הזמן חבר צוות",
"invite_team_notifcation_badge": "הזמנה",
"your_event_has_been_scheduled": "נקבע מועד לאירוע שלך",
"your_event_has_been_scheduled_recurring": "נקבע מועד לאירוע החוזר שלך",
"accept_our_license": "יש לאשר את תנאי הרישיון שלנו על ידי שינוי המשתנה מסוג ‎.env בשם <1>NEXT_PUBLIC_LICENSE_CONSENT</1> ל-'{{agree}}'.",
@@ -32,7 +35,7 @@
"refund_failed": "ההחזר הכספי עבור האירוע {{eventType}} עם {{userName}} בתאריך {{date}} נכשל.",
"check_with_provider_and_user": "יש לברר עם ספק התשלום ועם {{user}} כיצד לטפל בעניין.",
"a_refund_failed": "ההחזר הכספי נכשל",
"awaiting_payment_subject": "בהמתנה לתשלום: {{title}} בתאריך {{date}}",
"awaiting_payment_subject": "ממתין לתשלום: {{title}} ב- {{date}}",
"meeting_awaiting_payment": "התשלום על הפגישה שלך טרם בוצע",
"help": "עזרה",
"price": "מחיר",
@@ -63,11 +66,11 @@
"someone_requested_an_event": "מישהו ביקש לקבוע מועד לאירוע בלוח השנה שלך.",
"someone_requested_password_reset": "מישהו ביקש קישור כדי לשנות את הסיסמה שלך.",
"password_reset_instructions": "אם הבקשה לא היתה שלך, ניתן להתעלם מהודעת הדוא\"ל הזו בבטחה, והסיסמה שלך לא תשתנה.",
"event_awaiting_approval_subject": "בהמתנה לאישור: {{title}} בתאריך {{date}}",
"event_awaiting_approval_subject": "ממתין לאישור: {{title}} ב- {{date}}",
"event_still_awaiting_approval": "אירוע עדיין ממתין לאישורך",
"booking_submitted_subject": "ההזמנה נשלחה: {{title}} בתאריך {{date}}",
"booking_submitted_subject": "ההזמנה נשלחה: {{title}} ב- {{date}}",
"your_meeting_has_been_booked": "הפגישה שלך הוזמנה",
"event_type_has_been_rescheduled_on_time_date": "{{title}} הועבר לתאריך {{date}}.",
"event_type_has_been_rescheduled_on_time_date": "{{title}} תוזמן מחדש לתאריך {{date}}.",
"event_has_been_rescheduled": "עודכן – מועד האירוע שלך השתנה",
"request_reschedule_title_attendee": "בקשה לשינוי מועד ההזמנה",
"request_reschedule_subtitle": "{{organizer}} ביטל/ה את ההזמנה וביקש/ה שתבחר/י מועד אחר.",
@@ -101,7 +104,7 @@
"team_info": "מידע על הצוות",
"request_another_invitation_email": "אם אתה מעדיף לא להשתמש בכתובת {{toEmail}} ככתובת הדוא\"ל שלך עבור {{appName}} או שכבר יש לך חשבון {{appName}}, יש לבקש לקבל הזמנה נוספת לכתובת הדוא\"ל הרצויה.",
"you_have_been_invited": "הוזמנת להצטרף לצוות {{teamName}}",
"user_invited_you": "{{user}} הזמין/ה אותך להצטרף לצוות {{team}} ב-{{appName}}",
"user_invited_you": "{{user}} הזמין אותך להצטרף לצוות {{team}} ב-{{appName}}",
"hidden_team_member_title": "אתה מוסתר בצוות זה",
"hidden_team_member_message": "לא בוצע תשלום עבור המקום שלך. ניתן לשדרג ל-PRO או ליידע את הבעלים של הצוות שהוא או היא יכולים לשלם עבור המקום שלך.",
"hidden_team_owner_message": "נדרש חשבון Pro כדי להשתמש בתכונות הצוותים. תהיה מוסתר עד שתבצע שידרוג.",
@@ -240,7 +243,7 @@
"credentials_stored_and_encrypted": "פרטי הכניסה שלך יישמרו ויוצפנו.",
"connect": "להתחבר",
"try_for_free": "לנסות בחינם",
"create_booking_link_with_calcom": "צור קישור הזמנה משלך ב-{{appName}}",
"create_booking_link_with_calcom": "צור קישור תזמון משלך עם {{appName}}",
"who": "מי",
"what": "מה",
"when": "מתי",
@@ -410,6 +413,8 @@
"password_updated_successfully": "עדכון הסיסמה בוצע בהצלחה",
"password_has_been_changed": "שינוי הסיסמה בוצע בהצלחה.",
"error_changing_password": "שגיאה בעת שינוי הסיסמה",
"session_timeout_changed": "הגדרות החיבור שלך עודכנו בהצלחה.",
"session_timeout_change_error": "שגיאה בעדכון הגדרות החיבור",
"something_went_wrong": "משהו השתבש.",
"something_doesnt_look_right": "משהו נראה לא כשורה?",
"please_try_again": "נא לנסות שוב.",
@@ -418,16 +423,17 @@
"your_old_password": "הסיסמה הקודמת",
"current_password": "הסיסמה הנוכחית",
"change_password": "שינוי סיסמה",
"change_secret": "שינוי הסוד",
"change_secret": "שנה סוד",
"new_password_matches_old_password": "הסיסמה החדשה זהה לישנה. יש לבחור סיסמה אחרת.",
"forgotten_secret_description": "אם שכחת או איבדת את הסוד, יש לך אפשרות לשנות אותו, אבל חשוב לזכור שיהיה צורך לעדכן את כל השילובים שבהם נעשה שימוש בסוד זה",
"current_incorrect_password": "הסיסמה הנוכחית שגויה",
"password_hint_caplow": "שילוב של אותיות רישיות וקטנות",
"password_hint_min": "באורך 7 תווים לפחות",
"password_hint_admin_min": "באורך של 15 תווים לפחות",
"password_hint_admin_min": "באורך 15 תווים לפחות",
"password_hint_num": "לכלול לפחות ספרה אחת",
"invalid_password_hint": "הסיסמה חייבת להיות באורך של {{passwordLength}} תווים לפחות ולכלול לפחות ספרה אחת ושילוב של אותיות רישיות וקטנות",
"incorrect_password": "הסיסמה שגויה.",
"incorrect_username_password": "שם משתמש או סיסמה שגויים.",
"24_h": "24 שעות",
"use_setting": "השתמש בהגדרה",
"am_pm": "לפנה\"צ/אחה\"צ",
@@ -455,12 +461,13 @@
"slots_load_fail": "לא ניתן לטעון את חלונות הזמן הזמינים.",
"additional_guests": "הוספת אורחים",
"your_name": "שמך",
"your_full_name": "שמך המלא",
"email_address": "כתובת דוא\"ל",
"enter_valid_email": "יש להזין כתובת דוא״ל תקינה",
"location": "מיקום",
"address": "כתובת",
"enter_address": "יש להזין כתובת",
"in_person_attendee_address": "פנים-אל-פנים (הכתובת של המשתתף/ת)",
"in_person_attendee_address": "פנים-אל-פנים (בכתובת של המשתתף)",
"yes": "כן",
"no": "לא",
"additional_notes": "הערות נוספות",
@@ -470,14 +477,14 @@
"booking_confirmation": "אשר את ה{{eventTypeTitle}} עם {{profileName}}",
"booking_reschedule_confirmation": "שנה את המועד של ה{{eventTypeTitle}} עם {{profileName}}",
"in_person_meeting": "פגישה אישית",
"attendeeInPerson": "פנים-אל-פנים (הכתובת של המשתתף/ת)",
"inPerson": "פנים-אל-פנים (הכתובת של המארגן/ת)",
"attendeeInPerson": "פנים-אל-פנים (בכתובת של המשתתף)",
"inPerson": "פנים-אל-פנים (בכתובת של המארגן)",
"link_meeting": "פגישה עם קישור",
"phone_call": "מספר הטלפון של המשתתף/ת",
"your_number": "מספר הטלפון שלך",
"phone_number": "מספר טלפון",
"attendee_phone_number": "מספר הטלפון של המשתתף/ת",
"organizer_phone_number": "מספר הטלפון של המארגן/ת",
"organizer_phone_number": "מספר הטלפון של המארגן",
"host_phone_number": "מספר הטלפון שלך",
"enter_phone_number": "נא להזין מספר טלפון",
"reschedule": "שינוי המועד שנקבע",
@@ -508,6 +515,8 @@
"admin": "מנהל/ת מערכת",
"administrator_user": "משתמש המוגדר כמנהל מערכת",
"lets_create_first_administrator_user": "עכשיו ניצור את המשתמש הראשון המוגדר כמנהל מערכת.",
"admin_user_created": "הגדרת משתמש מנהל",
"admin_user_created_description": "כבר יצרת משתמש מנהל. אתה יכול לבצע כניסה לחשבונך.",
"new_member": "חבר/ה חדש/ה",
"invite": "להזמין",
"add_team_members": "הוספת חברי צוות",
@@ -517,8 +526,8 @@
"invite_new_member_description": "הערה: פעולה זו <1>תחייב את המינוי שלך במקום נוסף ($15 לחודש)</1>.",
"invite_new_team_member": "להזמין מישהו לצוות שלך.",
"change_member_role": "לשנות את תפקיד החבר/ה בצוות",
"disable_cal_branding": "להשבית את מיתוג {{appName}}",
"disable_cal_branding_description": "הסתר את כל מיתוגי {{appName}} מהדפים הציבוריים שלך.",
"disable_cal_branding": "הסר מיתוג {{appName}}",
"disable_cal_branding_description": "הסתר את כל המיתוג של {{appName}} מהדפים הציבוריים שלך.",
"hide_book_a_team_member": "הסתרת הלחצן לשריון זמן של חבר/ת צוות",
"hide_book_a_team_member_description": "הסתר/י את הלחצן לשריון זמן של חבר/ת צוות מהדפים הציבוריים שלך.",
"danger_zone": "אזור מסוכן",
@@ -568,7 +577,7 @@
"duration": "משך זמן",
"available_durations": "משכי זמן זמינים",
"default_duration": "משך הזמן המוגדר כברירת מחדל",
"default_duration_no_options": "יש לבחור תחילה משכי זמן זמינים",
"default_duration_no_options": "ראשית, אנא בחר משך זמינות",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "דקות",
"round_robin": "לפי תורות",
@@ -589,7 +598,6 @@
"new_event_type_heading": "צור את סוג האירוע הראשון שלך",
"new_event_type_description": "סוגי אירועים מאפשרים לך לשתף קישורים המציגים את המועדים הפנויים בלוח השנה שלך ומאפשרים לאנשים לשריין זמן איתך.",
"new_event_title": "הוסף סוג אירוע חדש",
"new_event_subtitle": "צור סוג אירוע בשמך או עבור צוות.",
"new_team_event": "הוסף סוג אירוע חדש עבור הצוות",
"new_event_description": "צור סוג אירוע חדש שאנשים יוכלו לקבוע באמצעותו מועדים.",
"event_type_created_successfully": "יצירת האירוע מסוג {{eventTypeTitle}} בוצעה בהצלחה",
@@ -641,7 +649,7 @@
"changed_team_billing_info": "החל מינואר 2022, התחלנו לגבות תשלום עבור כל חבר צוות שמשתתף בפגישה. חברי צוות שעבדו עם גרסת PRO בחינם, הועברו לגרסת ניסיון למשך 14 יום. ברגע שהתוקף של גרסת הניסיון יסתיים, חברי צוות אלה יוסתרו מהצוות, אלא אם תשדרג כעת.",
"create_manage_teams_collaborative": "צור ונהל צוותים כדי להשתמש בתכונות של שיתוף פעולה.",
"only_available_on_pro_plan": "תכונה זו זמינה רק בחבילת Pro",
"remove_cal_branding_description": "כדי להסיר את המיתוג של {{appName}} מדפי ההזמנות שלך, עליך לשדרג לחשבון Pro.",
"remove_cal_branding_description": "כדי להסיר את המיתוג של {{appName}} מדפי הזימון שלך, עליך לשדרג לחשבון Pro.",
"edit_profile_info_description": "ערוך את פרטי הפרופיל שלך שיופיעו בקישור לקביעת מועד.",
"change_email_tip": "ייתכן שיהיה צורך לצאת ולהיכנס שוב כדי לראות את השינויים.",
"little_something_about": "משהו קטן אודותיך.",
@@ -658,6 +666,7 @@
"edit_availability": "עריכת נתוני זמינות",
"configure_availability": "הגדר מועדים שבהם תהיה זמין להזמנות.",
"copy_times_to": "להעתיק שעות ל",
"copy_times_to_tooltip": "העתק שעות אל …",
"change_weekly_schedule": "שינוי לוח הזמנים השבועי שלך",
"logo": "לוגו",
"error": "שגיאה",
@@ -669,10 +678,11 @@
"add_attendees": "הוסף משתתפים",
"show_advanced_settings": "הצגת הגדרות מתקדמות",
"event_name": "שם האירוע",
"event_name_in_calendar": "שם הארוע בלוח השנה",
"event_name_tooltip": "השם שיופיע בלוחות השנה",
"meeting_with_user": "פגישה עם {{attendeeName}}",
"meeting_with_user": "{Event type title} פגישה בין {Organiser} ו- {Scheduler}",
"additional_inputs": "אפשרויות קלט נוספות",
"additional_input_description": "לדרוש מכלי התזמון להוסיף פרטים לפני אישור ההזמנה",
"additional_input_description": "דרוש מהמתזמן להזין נתונים נוספים לפני אישור התזמון",
"label": "תווית",
"placeholder": "מציין מיקום",
"type": "סוג",
@@ -690,21 +700,21 @@
"private_link_label": "קישור פרטי",
"private_link_hint": "הקישור הפרטי שלך ייווצר מחדש לאחר כל שימוש",
"copy_private_link": "העתקת קישור פרטי",
"private_link_description": "ניתן ליצור כתובת URL פרטית על מנת לשתף את האירוע מבלי לחשוף את שם המשתמש/ת שלך ב-{{appName}}",
"private_link_description": "ניתן ליצור כתובת קישור פרטית מבלי לחשוף את המשתמש שלך- {{appName}}",
"invitees_can_schedule": "המוזמנים יכולים לקבוע מועד",
"date_range": "טווח תאריכים",
"calendar_days": "ימים קלנדריים",
"business_days": "ימי עסקים",
"set_address_place": "הגדר כתובת או מקום",
"set_link_meeting": "הגדר קישור לפגישה",
"cal_invitee_phone_number_scheduling": "{{appName}} יציג למוזמן/ת בקשה להזין מספר טלפון לפני קביעת מועד.",
"cal_invitee_phone_number_scheduling": "{{appName}} יציג לנמען בקשה להזנת מספר טלפון לפני קביעת ביצוע התזמון.",
"cal_provide_google_meet_location": "{{appName}} יספק מיקום Google Meet.",
"cal_provide_zoom_meeting_url": "{{appName}} יספק כתובת URL לפגישת Zoom.",
"cal_provide_tandem_meeting_url": "{{appName}} יספק כתובת URL לפגישת Tandem.",
"cal_provide_video_meeting_url": "{{appName}} יספק כתובת URL לפגישת וידאו.",
"cal_provide_zoom_meeting_url": "{{appName}} יספק קישור לפגישת Zoom.",
"cal_provide_tandem_meeting_url": "{{appName}} יספק כתובת קישור לפגישת Tandem.",
"cal_provide_video_meeting_url": "{{appName}} יספק כתובת קישור לפגישת וידאו.",
"cal_provide_jitsi_meeting_url": "אנחנו ניצור עבורך כתובת URL לפגישת Jitsi Meet.",
"cal_provide_huddle01_meeting_url": "{{appName}} יספק כתובת URL לפגישת וידאו ב-Huddle01 web3.",
"cal_provide_teams_meeting_url": "{{appName}} יספק כתובת URL לפגישת MS Teams. הערה: חייב להיות חשבון של מקום עבודה או בית ספר",
"cal_provide_huddle01_meeting_url": "{{appName}} יספק כתובת קישור לפגישת וידאו web3 ב- Huddle01.",
"cal_provide_teams_meeting_url": "{{appName}} יספק כתובת קישור לפגישת MS Teams. הערה: חייב להיות חשבון של מקום עבודה או בית ספר",
"require_payment": "דרישת תשלום",
"commission_per_transaction": "עמלה לעסקה",
"event_type_updated_successfully_description": "סוג האירוע שלך עודכן בהצלחה.",
@@ -787,6 +797,7 @@
"number_apps_one": "אפליקציה {{count}}",
"number_apps_other": "{{count}} אפליקציות",
"trending_apps": "אפליקציות פופולריות",
"most_popular": "הכי פופולרי",
"explore_apps": "אפליקציות בקטגוריה {{category}}",
"installed_apps": "אפליקציות מותקנות",
"free_to_use_apps": "חינם",
@@ -797,12 +808,14 @@
"no_category_apps_description_analytics": "להוסיף אפליקציה לניתוח נתונים עבור דפי ההזמנות שלך",
"no_category_apps_description_automation": "להוסיף אפליקציית אוטומציה שברצונך להשתמש בה",
"no_category_apps_description_other": "הוסף/הוסיפי אפליקציה מכל סוג אחר לביצוע פעולות שונות",
"no_category_apps_description_web3": "הוסף אפליקצית web3 לעמודי תזמון הפגישות שלך",
"installed_app_calendar_description": "הגדר/הגדירח את לוחות השנה כדי לבדוק אם יש התנגשויות על מנת למנוע כפל הזמנות.",
"installed_app_conferencing_description": "הוסף/הוסיפי את האפליקציות האהובות עליך לשיחות ועידה לשימוש בפגישות שלך",
"installed_app_payment_description": "הגדר/הגידירי את השירותים לעיבוד תשלומים שבהם יש להשתמש לחיוב הלקוחות.",
"installed_app_analytics_description": "הגדרת האפליקציות לניתוח נתונים שבהן יש להשתמש עבור דפי ההזמנות שלך",
"installed_app_other_description": "כל האפליקציות המותקנות שלך מקטגוריות אחרות.",
"installed_app_automation_description": "הגדרת אפליקציות האוטומציה שבהן ברצונך להשתמש",
"installed_app_web3_description": "הגדר באיזה אפליקציות web3 להשתמש בעמודי תזמון הפגישות שלך",
"analytics": "ניתוח נתונים",
"empty_installed_apps_headline": "אין אפליקציות מותקנות",
"empty_installed_apps_description": "אפליקציות מאפשרות לך לשפר משמעותית את תהליכי העבודה שלך ואת תהליך קביעת המועדים.",
@@ -828,11 +841,11 @@
"verify_wallet": "אימות הארנק",
"connect_metamask": "קישור Metamask",
"create_events_on": "ליצור אירועים ב-",
"enterprise_license": "תכונה זו זמינה רק במינוי ארגוני",
"enterprise_license_description": "כדי להפעיל תכונה זו, עליך לקבל מפתח פריסה בכתובת {{consoleUrl}} ולהוסיף אותו לסביבת ‎.env שלך בתור CALCOM_LICENSE_KEY. אם כבר יש לצוות שלך רישיון, ניתן לפנות אל {{supportMail}} לקבלת עזרה.",
"enterprise_license": "תכונה זו זמינה רק במינוי Enterprise",
"enterprise_license_description": "כדי להפעיל את יכולת זו, בקש ממנהל לגשת אל הקישור {{setupUrl}} והקלדת הרישיון. אם כבר יש רישיון מוגדר, צור קשר עם {{supportMail}} לעזרה.",
"missing_license": "חסר רישיון",
"signup_requires": "נדרש רישיון לשימוש מסחרי",
"signup_requires_description": "בשלב זה, {{companyName}} אינה מציעה גרסת קוד פתוח חינמית של דף ההרשמה. לקבלת גישה מלאה לרכיבי ההרשמה, תצטרך לקנות רישיון לשימוש מסחרי. ליציר חשבונות המיועדים לשימוש אישי, אנחנו ממליצים על Prisma Data Platform או על כל ממשק אחר של Postgres ליצירת חשבונות.",
"signup_requires_description": "בשלב זה, {{companyName}} אינה מציעה גרסת קוד פתוח חינמית של דף ההרשמה. לקבלת גישה מלאה לרכיבי ההרשמה, תצטרך לקנות רישיון לשימוש מסחרי. לצרכים אישיים אנו ממליצים על Prisma Data Platform או כל ממשק Postgres אחר ליצירת חשבונות.",
"next_steps": "השלבים הבאים",
"acquire_commercial_license": "רכישת רישיון לשימוש מסחרי",
"the_infrastructure_plan": "חבילת הבסיס מחויבת לפי שימוש ומוצעת בהנחות למתחילים.",
@@ -847,6 +860,7 @@
"add_new_calendar": "הוספת לוח שנה חדש",
"set_calendar": "הגדר/י למתי יש להוסיף אירועים חדשים כשאת/ה עסוק/ה.",
"delete_schedule": "מחק תזמון",
"delete_schedule_description": "מחיקת תזמון תמחק אותו מכל סוגי הארועים. לא ניתן לבטל פעולה זו.",
"schedule_created_successfully": "יצירת התזמון {{scheduleName}} בוצעה בהצלחה",
"availability_updated_successfully": "עדכון התזמון {{scheduleName}} בוצע בהצלחה",
"schedule_deleted_successfully": "מחיקת התזמון בוצעה בהצלחה",
@@ -858,7 +872,7 @@
"time_format": "פורמט תצוגת זמן",
"12_hour": "12 שעות",
"24_hour": "24 שעות",
"12_hour_short": "12 ש'",
"12_hour_short": "12 שעות",
"24_hour_short": "24 שעות",
"redirect_success_booking": "הפניה מחדש בעת הזמנה ",
"you_are_being_redirected": "אנחנו ננתב אותך אל {{ url }} בעוד $t(second, {\"count\": {{seconds}} }).",
@@ -921,7 +935,7 @@
"user_impersonation_heading": "התחזות למשתמשים",
"user_impersonation_description": "מצב זה מאפשר לצוות התמיכה שלנו להתחבר באופן זמני לחשבונך כדי שנוכל לפתור במהירות את הבעיות שתדווח/י לנו עליהם.",
"team_impersonation_description": "מאפשר לבעלים של הצוות/מנהלים להיכנס זמנית בשמך.",
"allow_booker_to_select_duration": "אפשר/י למזמין/ה לבחור משך זמן",
"allow_booker_to_select_duration": "אפשר למזמין לבחור את משך הזמן",
"impersonate_user_tip": "כל השימושים בתכונה זו נבדקים.",
"impersonating_user_warning": "בהתחזות לשם המשתמש \"{{user}}\".",
"impersonating_stop_instructions": "לחץ/י כאן כדי להפסיק",
@@ -930,20 +944,25 @@
"current_location": "המיקום הנוכחי",
"user_phone": "מספר הטלפון שלך",
"new_location": "המיקום החדש",
"session": "חיבור",
"session_description": "שלוט בהגדרות החיבור של החשבון שלך",
"session_timeout_after": "נתק חיבור לאחר",
"session_timeout": "ניתוק חיבור לאחר",
"session_timeout_description": "בטל הזדהות של החיבור לאחר פרק זמן מסויים.",
"no_location": "לא הוגדר מיקום",
"set_location": "הגדרת מיקום",
"update_location": "עדכון מיקום",
"location_updated": "המיקום עודכן",
"email_validation_error": "המידע שהזנת לא נראה כמו כתובת דוא\"ל",
"place_where_cal_widget_appear": "מקם/י את הקוד הזה בתוך דף ה-HTML במקום שבו ברצונך שהווידג'ט של {{appName}} יופיע.",
"place_where_cal_widget_appear": "מקם את הקוד הזה בתוך דף ה-HTML במקום שבו ברצונך שהווידג'ט של {{appName}} יופיע.",
"create_update_react_component": "צור או עדכן רכיב React קיים כפי שמוצג להלן.",
"copy_code": "העתקת הקוד",
"code_copied": "הקוד הועתק!",
"how_you_want_add_cal_site": "איך ברצונך להוסיף את {{appName}} לאתר שלך?",
"choose_ways_put_cal_site": "בחר/י אחת מהדרכים הבאות כדי להציב את {{appName}} באתר שלך.",
"choose_ways_put_cal_site": "בחר/י אחת מהדרכים הבאות כדי לשלב את {{appName}} באתר שלך.",
"setting_up_zapier": "המערכת מגדירה את השילוב שלך עם Zapier",
"generate_api_key": "יצירת מפתח ממשק תכנות יישומים (API)",
"generate_api_key_description": "יצירת מפתח ממשק תכנות יישומים (API) לשימוש עם {{appName}} ב-",
"generate_api_key_description": "צור מפתח ממשק מפתחים, API, לשימוש עם {{appName}} ב-",
"your_unique_api_key": "מפתח ה-API הייחודי שלך",
"copy_safe_api_key": "העתק/י את מפתח ה-API הזה ושמור/י אותו במקום בטוח. אם תאבד/י אותו, יהיה עליך ליצור מפתח חדש.",
"zapier_setup_instructions": "<0>התחבר/י לחשבון Zapier שלך וצור/י Zap חדש.</0><1>בחר/י את Cal.com כאפליקציית ה-Trigger. בנוסף, בחר/י אירוע Trigger.</1><2>בחר/י את החשבון שלך ולאחר מכן הזן/י את מפתח ה-API הייחודי שלך.</2><3>בדוק/י את ה-Trigger.</3><4>וזהו, הכל מוכן!</4>",
@@ -951,7 +970,7 @@
"connect_apple_server": "חיבור לשרת Apple",
"connect_caldav_server": "חיבור לשרת CalDav (בטא)",
"calendar_url": "כתובת ה-URL של לוח השנה",
"apple_server_generate_password": "צור סיסמה ספציפית לאפליקציה כדי להשתמש ב-{{appName}} ב:",
"apple_server_generate_password": "צור סיסמה ספציפית לאפליקציה שתשמש עבור {{appName}} ב-",
"credentials_stored_encrypted": "פרטי הכניסה שלך יישמרו ויוצפנו.",
"it_stored_encrypted": "הוא יאוחסן ויוצפן.",
"go_to_app_store": "מעבר אל ה-App Store",
@@ -995,13 +1014,13 @@
"before_event_trigger": "לפני שהאירוע מתחיל",
"event_cancelled_trigger": "כאשר אירוע מבוטל",
"new_event_trigger": "כאשר אירוע חדש מוזמן",
"email_host_action": "יש לשלוח דוא\"ל למארח/ת",
"email_attendee_action": "יש לשלוח דוא\"ל למשתתף/ת",
"sms_attendee_action": "יש לשלוח SMS למשתתף/ת",
"sms_number_action": "יש לשלוח SMS למספר מסוים",
"workflows": "תהליכי עבודה",
"email_host_action": "שלח מייל למארח",
"email_attendee_action": "שלח מייל למשתתף",
"sms_attendee_action": "שלח SMS למשתתף",
"sms_number_action": "שלח SMS למספר מסוים",
"workflows": "תהליכים",
"new_workflow_btn": "תהליך עבודה חדש",
"add_new_workflow": "הוספת תהליך עבודה חדש",
"add_new_workflow": "הוסף תהליך חדש",
"reschedule_event_trigger": "כאשר נקבע מועד חדש לאירוע",
"trigger": "גורם מפעיל",
"triggers": "גורמים מפעילים",
@@ -1026,7 +1045,7 @@
"current": "נוכחי",
"premium": "פרימיום",
"standard": "רגיל",
"confirm_username_change_dialog_title": "אישור השינוי של שם המשתמש",
"confirm_username_change_dialog_title": "אשר את שינוי שם המשתמש",
"change_username_standard_to_premium": "מאחר שאתה משנה את שם המשתמש מסטנדרטי לפרימיום, תועבר אל הקופה לצורך שדרוג.",
"change_username_premium_to_standard": "מאחר שאתה משנה את שם המשתמש מפרימיום לסטנדרטי, תועבר אל הקופה לצורך שנמוך.",
"go_to_stripe_billing": "למעבר אל דף החיוב",
@@ -1062,37 +1081,38 @@
"browse_our_docs": "לדפדף במסמכים שלנו",
"choose_template": "בחירת תבנית",
"reminder": "תזכורת",
"custom": "בהתאמה אישית",
"custom": "התאמה אישית",
"reminder_email": "תזכורת: {{eventType}} עם {{name}} בתאריך {{date}}",
"not_triggering_existing_bookings": "לא יופעל עבור הזמנות קיימות מאחר שהמשתמש יתבקש למסור מספר טלפון בעת הזמנת האירוע.",
"minute_one": "דקה {{count}}",
"minute_one": "{{count}} דקה",
"minute_other": "{{count}} דקות",
"hour_one": "שעה {{count}}",
"hour_one": "{{count}} שעה",
"hour_other": "{{count}} שעות",
"invalid_input": "קלט לא חוקי",
"broken_video_action": "לא יכולנו להוסיף לאירוע המתוכנן שלך את הקישור לפגישה ב-<1>{{location}}</1>. צור/י קשר עם המוזמנים או עדכן/י את הפרטים באירוע בלוח השנה. ניתן <3> לשנות את המיקום בסוג האירוע </3> או לנסות <5>להסיר את האפליקציה ולהוסיף אותה שוב.</5>",
"broken_calendar_action": "לא הצלחנו לעדכן את <1>{{calendar}}</1>. <2> נא לבדוק את הגדרות לוח השנה שלך או להסיר את לוח השנה ולהוסיף אותו שוב </2>",
"attendee_name": "שם המשתתף/ת",
"attendee_name": "שם המשתתף",
"scheduler_full_name": "השם המלא של האדם שמבצע את התזמון",
"broken_integration": "שילוב מנותק",
"problem_adding_video_link": "הייתה בעיה בהוספת קישור לסרטון",
"problem_updating_calendar": "הייתה בעיה בעדכון לוח השנה",
"active_on_event_types_one": "פעיל בסוג אירוע {{count}}",
"active_on_event_types_one": "פעיל ב-{{count}} סוגי אירועים",
"active_on_event_types_other": "פעיל ב-{{count}} סוגי אירועים",
"no_active_event_types": "אין סוגי אירועים פעילים",
"new_seat_subject": "משתתף/ת חדש/ה {{name}} ב{{eventType}} בתאריך {{date}}",
"new_seat_title": "מישהו הוסיף את עצמו לאירוע",
"variable": "משתנה",
"event_name_variable": "שם האירוע",
"organizer_name_variable": "שם המארגן/ת",
"attendee_name_variable": "שם המשתתף/ת",
"organizer_name_variable": "שם המארגן",
"attendee_name_variable": "שם המשתתף",
"event_date_variable": "תאריך האירוע",
"event_time_variable": "שעת האירוע",
"event_time_variable": "מועד האירוע",
"location_variable": "מיקום",
"additional_notes_variable": "הערות נוספות",
"app_upgrade_description": "כדי להשתמש בתכונה זו, עליך לשדרג לחשבון Pro.",
"invalid_number": "מספר טלפון לא תקין",
"navigate": "ניווט",
"open": "פתיחה",
"open": "פתח",
"close": "סגירה",
"upgrade": "שדרוג",
"upgrade_to_access_recordings_title": "שדרג/י כדי לגשת להקלטות",
@@ -1110,25 +1130,25 @@
"company_size": "גודל החברה",
"what_help_needed": "במה דרושה לך עזרה?",
"variable_format": "הפורמט של המשתנה",
"webhook_subscriber_url_reserved": "כתובת ה-URL של מנוי/מנויית Webhook כבר מוגדרת",
"webhook_subscriber_url_reserved": "כתובת ה-URL של מנוי Webhook כבר מוגדרת",
"custom_input_as_variable_info": "התעלמות מכל התווים המיוחדים של תווית הקלט הנוספת (שימוש באותיות ובספרות בלבד), שימוש באותיות רישיות עבור כל האותיות והחלפת רווחים במקפים תחתונים.",
"using_additional_inputs_as_variables": "איך ניתן להשתמש בסוגי קלט נוספים כמשתנים?",
"download_desktop_app": "הורדת האפליקציה למחשבים שולחניים",
"set_ping_link": "הגדרת קישור Ping",
"rate_limit_exceeded": "חריגה מההגבלה",
"rate_limit_exceeded": "חריגה מהגבלת קצב",
"when_something_happens": "כאשר משהו קורה",
"action_is_performed": "פעולה מבוצעת",
"test_action": "בדיקת פעולה",
"notification_sent": "נשלח עדכון",
"test_action": "בדוק פעולה",
"notification_sent": "התראה נשלחה",
"no_input": "אין קלט",
"test_workflow_action": "בדיקת פעולת תהליך עבודה",
"test_workflow_action": "בדוק פעולת תהליך עבודה",
"send_sms": "שליחת SMS",
"send_sms_to_number": "בטוח שברצונך לשלוח SMS למספר {{number}}?",
"missing_connected_calendar": "אף לוח שנה המוגדר כברירת מחדל לא מחובר",
"missing_connected_calendar": "אין לוח שנה מחובר המוגדר כברירת מחדל",
"connect_your_calendar_and_link": "ניתן לחבר את לוח השנה שלך מ<1>כאן</1>.",
"default_calendar_selected": "לוח השנה המוגדר כברירת מחדל",
"hide_from_profile": "להסתיר מהפרופיל",
"event_setup_tab_title": "הגדרת האירוע",
"hide_from_profile": "הסתר מהפרופיל",
"event_setup_tab_title": "הגדרת ארוע",
"event_limit_tab_title": "מגבלות",
"event_limit_tab_description": "באיזו תדירות ניתן להזמין אותך",
"event_advanced_tab_description": "הגדרות לוח שנה ועוד...",
@@ -1136,36 +1156,36 @@
"event_setup_multiple_duration_error": "הגדרת אירוע: משכי זמן מרובים מחייבים הגדרה של אפשרות אחת לפחות.",
"event_setup_multiple_duration_default_error": "הגדרת אירוע: יש לבחור משך זמן חוקי המוגדר כברירת מחדל.",
"event_setup_booking_limits_error": "הגבלות על הזמנות חייבות להיות מוגדרות בסדר עולה. [יום,שבוע,חודש,שנה]",
"select_which_cal": "בחר/י את לוח השנה שאליו יש להוסיף הזמנות",
"select_which_cal": "בחר את לוח השנה שאליו יש להוסיף הזמנות",
"custom_event_name": "שם אירוע מותאם אישית",
"custom_event_name_description": "ניתן ליצור שמות אירועים בהתאמה אישית שיוצגו באירוע בלוח השנה",
"2fa_required": "נדרש אימות בשני גורמים",
"incorrect_2fa": "קוד שגוי של אימות בשני גורמים",
"custom_event_name_description": "צור שמות אירועים בהתאמה אישית שיוצגו באירוע בלוח השנה",
"2fa_required": "נדרש אימות דו-גורמי",
"incorrect_2fa": "קוד אימות דו-גורמי שגוי",
"which_event_type_apply": "על איזה סוג אירוע זה יחול?",
"no_workflows_description": "תהליכי עבודה מאפשרים שימוש באוטומציה פשוטה לשליחת עדכונים ותזכורות, כך שאת/ה יכול/ה לתכנן תהליכים שקשורים לאירועים שלך.",
"no_workflows_description": "תהליכי עבודה מאפשרים שימוש באוטומציה פשוטה לשליחת התראות ותזכורות, כך שאתה יכול לתכנן תהליכים שקשורים לאירועים שלך.",
"timeformat_profile_hint": "זוהי הגדרה פנימית שלא תשפיע על אופן התצוגה של תאריכים ושעות בדפים של הזמנות ציבוריות עבורך או עבור מי שמבצע הזמנות בשמך.",
"create_workflow": "יצירת תהליך עבודה",
"do_this": "יש לבצע את הפעולה הזו",
"turn_off": "השבתה",
"do_this": "עשה את זה",
"turn_off": "כבה",
"settings_updated_successfully": "עדכון ההגדרות בוצע בהצלחה",
"error_updating_settings": "שגיאה במהלך עדכון ההגדרות",
"personal_cal_url": "כתובת ה-URL של ה-{{appName}} האישי שלי",
"error_updating_settings": "שגיאה בעדכון ההגדרות",
"personal_cal_url": "כתובת ה-URL האישית שלי של {{appName}}",
"bio_hint": "מספר משפטים אודותיך. המידע הזה יופיע בדף ה-URL האישי שלך.",
"delete_account_modal_title": "מחיקת חשבון",
"delete_account_modal_title": "מחיקת החשבון",
"confirm_delete_account_modal": "בטוח שברצונך למחוק את חשבון {{appName}} שלך?",
"delete_my_account": "מחיקת החשבון שלי",
"start_of_week": "תחילת השבוע",
"recordings_title": "הקלטות",
"recording": "הקלטה",
"select_calendars": "בחר/י את לוחות השנה שבהם ברצונך לבדוק אם יש התנגשויות כדי למנוע כפל הזמנות.",
"select_calendars": "בחר את לוחות השנה שבהם ברצונך לבדוק אם יש התנגשויות, כדי למנוע כפל הזמנות.",
"check_for_conflicts": "בדיקת התנגשויות",
"view_recordings": "צפייה בהקלטות",
"adding_events_to": "הוספת אירועים ל",
"follow_system_preferences": "פעל לפי העדפות המערכת",
"custom_brand_colors": "צבעי מותג בהתאמה אישית",
"customize_your_brand_colors": "בצע/י התאמה אישית של דף ההזמנות שלך עם צבעי מותג משלך.",
"customize_your_brand_colors": "בצע התאמה אישית של דף ההזמנות שלך עם צבעי מותג משלך.",
"pro": "Pro",
"removes_cal_branding": "הסרת מיתוגים הקשורים ל-{{appName}}, כלומר 'מופעל על ידי {{appName}}'.",
"removes_cal_branding": "הסרת מיתוגים הקשורים ל-{{appName}}, כגון 'מופעל על ידי {{appName}}'",
"profile_picture": "תמונת פרופיל",
"upload": "העלאה",
"add_profile_photo": "הוספת תמונת פרופיל",
@@ -1177,11 +1197,11 @@
"rainbow_signature_error": "שגיאה בעת בקשת חתימה מהארנק.",
"token_address": "כתובת הטוקן",
"blockchain": "בלוקצ'יין",
"old_password": "הסיסמה הקודמת",
"old_password": "סיסמה ישנה",
"secure_password": "הסיסמה החדשה והסופר מאובטחת שלך",
"error_updating_password": "שגיאה בעת עדכון הסיסמה",
"two_factor_auth": "אימות בשני גורמים",
"recurring_event_tab_description": "הגדרת לוח זמנים חוזר",
"two_factor_auth": "אימות דו-גורמי",
"recurring_event_tab_description": "הגדרת ארוע חוזר",
"today": "היום",
"appearance": "מראה",
"appearance_subtitle": "ניהול ההגדרות של מראה ההזמנות שלך",
@@ -1215,6 +1235,7 @@
"connect_automation_apps": "קישור אפליקציות אוטומציה",
"connect_analytics_apps": "קישור אפליקציות ניתוח נתונים",
"connect_other_apps": "חיבור אפליקציות אחרות",
"connect_web3_apps": "חבר אפליקציות web3",
"current_step_of_total": "שלב {{currentStep}} מתוך {{maxSteps}}",
"add_variable": "להוסיף משתנה",
"custom_phone_number": "מספר טלפון בהתאמה אישית",
@@ -1231,6 +1252,7 @@
"to": "אל",
"workflow_turned_on_successfully": "תהליך העבודה {{workflowName}} הועבר למצב {{offOn}} בהצלחה",
"download_responses": "להוריד תגובות",
"download_responses_description": "הורד את כל התגובות לטופס שלך בפורמט CSV.",
"download": "הורדה",
"create_your_first_form": "צור/צרי את הטופס הראשון שלך",
"create_your_first_form_description": "באמצעות טפסי ניתוב ניתן לשאול שאלות סיווג ולנתב אל האדם או אל סוג האירוע המתאימים.",
@@ -1251,7 +1273,7 @@
"workflow_example_4": "לשלוח למשתתף/ת תזכורת בדוא\"ל שעה אחת לפני תחילת האירועים",
"workflow_example_5": "לשלוח דוא\"ל מותאם אישית למארח/ת כאשר המועד של אירוע משתנה",
"workflow_example_6": "לשלוח הודעת SMS מותאמת אישית למארח/ת כאשר אירוע חדש מוזמן",
"welcome_to_cal_header": "ברוך בואך ל-{{appName}}!",
"welcome_to_cal_header": "ברוך הבא אל {{appName}}!",
"edit_form_later_subtitle": "ניתן יהיה לערוך את זה מאוחר יותר.",
"connect_calendar_later": "אקשר את לוח השנה שלי מאוחר יותר",
"problem_saving_user_profile": "אירעה בעיה בשמירת הנתונים שלך. נסה/י שוב או פנה/י לשירות התמיכה.",
@@ -1260,7 +1282,7 @@
"booking_appearance": "מראה ההזמנה",
"appearance_team_description": "ניהול ההגדרות של מראה הזמנות הצוות שלך",
"only_owner_change": "רק הבעלים של הצוות יכולים לבצע שינויים בהזמנת הצוות ",
"team_disable_cal_branding_description": "הסרת מיתוגים הקשורים ל-{{appName}}, כמו למשל 'מופעל על ידי {{appName}}'",
"team_disable_cal_branding_description": "הסרת מיתוגים הקשורים ל-{{appName}}, כגון 'מופעל על ידי {{appName}}'",
"invited_by_team": "הוזמנת על ידי {{teamName}} להצטרף לצוות בתפקיד {{role}}",
"token_invalid_expired": "הטוקן לא חוקי או שתוקפו פג.",
"exchange_add": "התחבר/י אל Microsoft Exchange",
@@ -1272,6 +1294,8 @@
"routing_forms_send_email_owner": "שליחת דוא\"ל לבעלים",
"routing_forms_send_email_owner_description": "דוא\"ל נשלח לבעלים לאחר שליחת הטופס",
"add_new_form": "הוספת טופס חדש",
"create_your_first_route": "צור את המסלול הראשון שלך",
"route_to_the_right_person": "מסלל לאדם הנכון בהתאם לתשובות בטופס שלך",
"form_description": "צור/י טופס משלך כדי לנתב אל מזמין/ה",
"copy_link_to_form": "להעתיק את הקישור לטופס",
"theme": "ערכת נושא",
@@ -1299,9 +1323,10 @@
"back_to_signin": "חזרה לדף הכניסה",
"reset_link_sent": "נשלח קישור לאיפוס",
"password_reset_email": "שלחנו לך הודעת דוא\"ל עם הוראות לאיפוס הסיסמה לכתובת {{email}}.",
"password_reset_leading": "אם הודעת הדוא\"ל לא תגיע בקרוב, ודא/י שכתובת הדוא\"ל שהזנת נכונה, בדוק/י אם ההודעה הגיעה לתיקיית דואר הזבל או פנה/י לתמיכה אם הבעיה נמשכת.",
"password_reset_leading": "אם הודעת המייל לא תגיע בקרוב, ודא שכתובת הדוא\"ל שהזנת נכונה, בדוק אם ההודעה הגיעה לתיקיית דואר הזבל או פנה לתמיכה אם הבעיה נמשכת.",
"password_updated": "הסיסמה עודכנה!",
"pending_payment": "המתנה לתשלום",
"pending_invites": "הזמנות ממתינות",
"confirmation_page_rainbow": "הגן/י על האירוע שלך באמצעות טוקנים או אסימוני NFT במטבע Ethereum, Polygon ועוד.",
"not_on_cal": "לא ב-{{appName}}",
"no_calendar_installed": "לא מותקן לוח שנה",
@@ -1315,7 +1340,7 @@
"limit_booking_frequency": "הגבלת תדירות ההזמנות",
"limit_booking_frequency_description": "הגבלת מספר הפעמים שבהן ניתן להזמין את האירוע הזה",
"add_limit": "הוספת הגבלה",
"team_name_required": "שם הצוות נדרש",
"team_name_required": "נדרש שם צוות",
"show_attendees": "שיתוף האורחים בפרטי המשתתפים",
"how_additional_inputs_as_variables": "איך ניתן להשתמש בסוגי קלט נוספים כמשתנים",
"format": "פורמט",
@@ -1372,7 +1397,7 @@
"saml_sp_entity_id_copied": "המזהה של ישות SP הועתק!",
"saml_btn_configure": "הגדרה",
"add_calendar": "הוספת לוח שנה",
"limit_future_bookings": "הגבלת הזמנות עתידיות",
"limit_future_bookings": "הגבלת תזמונים עתידיים",
"limit_future_bookings_description": "הגבלת התאריך הרחוק ביותר בעתיד שעד אליו ניתן להזמין את האירוע הזה",
"no_event_types": "לא הוגדר אף סוג אירוע",
"no_event_types_description": "לא הוגדר על ידי {{name}} אף סוג אירוע שבאפשרותך להזמין.",
@@ -1391,8 +1416,8 @@
"team_url_required": "יש להזין כתובת URL של הצוות",
"team_url_taken": "כתובת ה-URL הזו כבר תפוסה",
"team_publish": "פרסום צוות",
"number_sms_notifications": "מספר טלפון (הודעות SMS)",
"attendee_email_variable": "כתובת הדוא\"ל של המשתתף/ת",
"number_sms_notifications": "מספר טלפון (התראות SMS)",
"attendee_email_variable": "כתובת הדוא\"ל של המשתתף",
"attendee_email_info": "כתובת הדוא\"ל של האדם שביצע את ההזמנה",
"kbar_search_placeholder": "הקלד/י פקודה או חפש/י...",
"invalid_credential": "נראה שתוקף ההרשאה פג או שההרשאה בוטלה. יש להתקין מחדש שוב.",
@@ -1428,6 +1453,9 @@
"disabled_calendar": "אם התקנת לוח שנה אחר, ההזמנות החדשות יתווספו אליו. אם לא, קשר/י לוח שנה חדש כדי לא להחמיץ הזמנות חדשות.",
"enable_apps": "הפעלת אפליקציות",
"enable_apps_description": "להפעיל אפליקציות שמשתמשים יכולים לשלב עם Cal.com",
"purchase_license": "רכוש רישיון",
"already_have_key": "כבר יש לי מפתח:",
"already_have_key_suggestion": "אנא העתק את משתנה הסביבה CALCOM_LICENSE_KEY הקיים שלך לכאן.",
"app_is_enabled": "האפליקציה {{appName}} מופעלת",
"app_is_disabled": "האפליקציה {{appName}} מושבתת",
"keys_have_been_saved": "המפתחות נשמרו",
@@ -1439,18 +1467,19 @@
"no_available_apps_description": "ודא/י שיש אפליקציות בפריסה שלך בנתיב 'packages/app-store'",
"no_apps": "אף אפליקציה לא מופעלת במופע הזה של Cal",
"apps_settings": "הגדרות אפליקציות",
"fill_this_field": "יש להזין מידע בשדה הזה",
"fill_this_field": "נא מלא שדה זה",
"options": "אפשרויות",
"enter_option": "הזנת ה-{{index}} של האפשרות",
"add_an_option": "הוספת אפשרות",
"radio": "לחצן אפשרויות",
"radio": "רדיו",
"google_meet_warning": "כדי להשתמש ב-Google Meet, יש להגדיר את Google Calendar כלוח השנה של המארח",
"individual": "משתמש בודד",
"all_bookings_filter_label": "כל ההזמנות",
"all_users_filter_label": "כל המשתמשים",
"your_bookings_filter_label": "התזמונים שלך",
"meeting_url_variable": "כתובת ה-URL של הפגישה",
"meeting_url_info": "כתובת ה-URL של שיחת הוועידה באירוע",
"date_overrides": "חפיפת תאריכים",
"date_overrides": "תאריכים חופפים",
"date_overrides_subtitle": "הוסף/י תאריכים שבהם הזמינות שלך שונה משעות העבודה הרגילות שלך מדי יום.",
"date_overrides_info": "תאריכים שסומנו למעקף מועברים לארכיון באופן אוטומטי לאחר שהתאריך חולף",
"date_overrides_dialog_which_hours": "באילו שעות את/ה פנוי/ה?",
@@ -1486,6 +1515,7 @@
"round_robin_hosts": "מארחים לפי סבב",
"minimum_round_robin_hosts_count": "מספר המארחים שחייבים להשתתף",
"hosts": "מארחים",
"upgrade_to_enable_feature": "אתה צריך לייצר צוות כדי להפעיל את היכולת. לחץ ליצירת צוות.",
"new_attendee": "משתתף/ת חדש/ה",
"awaiting_approval": "בהמתנה לאישור",
"requires_google_calendar": "האפליקציה הזו מחייבת חיבור ל-Google Calendar",
@@ -1494,9 +1524,119 @@
"continue_to_install_google_calendar": "המשך להתקנת Google Calendar",
"install_google_meet": "התקנת Google Meet",
"install_google_calendar": "התקנת Google Calendar",
"sender_name": "שם השולח",
"already_invited": "הנמען כבר הוזמן",
"no_recordings_found": "לא נמצאו הקלטות",
"new_workflow_subtitle": "צור תהליך חדש עבור...",
"reporting": "דוחות",
"reporting_feature": "ראה את כל המידע הנכנס והורד אותו כ- CSV",
"teams_plan_required": "נדרש תוכנית צוותים",
"routing_forms_are_a_great_way": "טפסי ניתוב הם דרך מעולה לנתב לידים נכנסים לאדם המתאים. שדרג לתוכנית צוותים לגישה ליכולת זו.",
"choose_a_license": "בחר רישיון",
"choose_license_description": "Cal.com מגיע עם רישיון AGPLv3 נגיש וחינמי עם כמה מגבלות שניתן לשדרג לתוכנית Enterprise בכל עת לקבלת גישה אליהן. אתה יכול לשדרג בכל מועד עתידי.",
"license": "רישיון",
"agplv3_license": "רישיון AGPLv3",
"ee_enterprise_license": "\"ee/\" רישיון Enterprise",
"enterprise_booking_fee": "החל מ- {{enterprise_booking_fee}}/לחודש",
"enterprise_license_includes": "הכל לשימוש מסחרי",
"no_need_to_keep_your_code_open_source": "אין צורך לשמור את הקוד שלך כקוד פתוח",
"repackage_rebrand_resell": "אריזה מחדש, מיתוג מחדש ומכירה בקלות",
"a_vast_suite_of_enterprise_features": "מגוון יכולות Enterprise עצום",
"free_license_fee": "$0.00/לחודש",
"forever_open_and_free": "פתוח וחינם לתמיד",
"required_to_keep_your_code_open_source": "נדרש על מנת לשמור את הקוד שלך כקוד פתוח",
"cannot_repackage_and_resell": "לא ניתן לבצע אריזה מחדש, מיתוג מחדש ומכירה בקלות",
"no_enterprise_features": "אין יכולות Enterprise",
"step_enterprise_license": "רישיון Enterprise",
"step_enterprise_license_description": "כל מה שמימוש מסחרי צריך עם אחסון פרטי, אריזה מחדש, מיתוג מחדש ומכירה עם גישה בלעדית ליכולות Enterprise.",
"setup": "התקנה",
"setup_description": "התקנת מופע Cal.com",
"configure": "הגדרה",
"sso_configuration": "כניסה בודדת",
"sso_configuration": "כניסת הזדהות אחידה SSO",
"sso_configuration_description": "הגדר SSO מסוג SAML/OIDC ואפשר לחברי הצוות לבצע כניסה עם ספק הזדהות",
"sso_oidc_heading": "SSO עם OIDC",
"sso_oidc_description": "הגדר SSO מסוג OIDC עם ספק ההזדהות שאתה רוצה.",
"sso_oidc_configuration_title": "הגדרות OIDC",
"sso_oidc_configuration_description": "הגדרת חיבור OIDC לספק ההזדהות שלך. אתה יכול למצוא את כל המידע הדרוש אצל ספק ההזדהות שלך.",
"sso_oidc_callback_copied": "הועתק כתובת קישור Callback",
"sso_saml_heading": "SSO עם SAML",
"sso_saml_description": "הגדר SSO מסוג SAML עם ספק ההזדהות שאתה רוצה.",
"sso_saml_configuration_title": "הגדרות SAML",
"sso_saml_configuration_description": "הגדרת חיבור SAML לספק ההזדהות שלך. אתה יכול למצוא את כל המידע הדרוש אצל ספק ההזדהות שלך.",
"sso_saml_acsurl_copied": "הועתק כתובת קישור ACS",
"sso_saml_entityid_copied": "הועתק Entity ID",
"sso_connection_created_successfully": "הגדרות {{connectionType}} נוצרו בהצלחה",
"sso_connection_deleted_successfully": "הגדרות {{connectionType}} נמחקו בהצלחה",
"delete_sso_configuration": "מחק את הגדרות {{connectionType}}",
"delete_sso_configuration_confirmation": "כן, מחק את הגדרות {{connectionType}}",
"delete_sso_configuration_confirmation_description": "אתה בטוח שאתה רוצה למחוק את הגדרות {{connectionType}}? חברי הצוות שלך שמשתמשים ב- {{connectionType}} להזדהות לא יוכלו להשתמש בו להכנס ל- Cal.com.",
"organizer_timezone": "מארגן אזורי זמן",
"email_no_user_cta": "צור את החשבון שלך",
"email_user_cta": "צפה בהזמנה",
"email_no_user_invite_heading": "הוזמנת להצטרף לצוות ב- {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} הזמין אותך להצטרף לצוות שלו ב- {{appName}}. {{appName}} הינה מתזמן זימונים שמאפשר לך ולצוות שלך לזמן פגישות בלי כל הפינג פונג במיילים.",
"email_no_user_invite_steps_intro": "אנחנו נדריך אותך במספר צעדים קצרים ואתה תהנה תזמון נטול מתח עם הצוות שלך כהרף עין.",
"email_no_user_step_one": "בחר שם משתמש",
"email_no_user_step_two": "קשר את לוח השנה שלך",
"email_no_user_step_three": "הגדר את הזמינות שלך",
"email_no_user_step_four": "הצטרף ל- {{teamName}}",
"email_no_user_signoff": "תזמון פגישות נעים מצוות {{appName}}",
"impersonation_user_tip": "אתה עומד להתחזות למשתמש, זה אומר שאתה יכול לעשות שינויים בשמו. אנא הזהר.",
"available_variables": "משתנים זמינים",
"scheduler": "{Scheduler}",
"no_workflows": "אין תהליכים",
"change_filter": "שנה את הסנן כדי לראות תהליכים אישיים וצוותים.",
"recommended_next_steps": "הצעדים הבאים המומלצים",
"create_a_managed_event": "צור ארוע מנוהל",
"meetings_are_better_with_the_right": "פגישות טובות יותר עם אנשי הצוות הנכונים נמצאים בו. הזמן אותם עכשיו.",
"create_a_one_one_template": "צור תבנית לתזמון פגישות אחד על אחד והפץ אותם למספר משתמשים.",
"collective_or_roundrobin": "כולם או טורי",
"book_your_team_members": "הזמן את אנשי הצוות שלך ביחד עם ארועים מסוג כולם או עבור על פניהם לבחירת האדם הנכון עם טורי.",
"create_event_on": "צור ארוע ב-",
"default_app_link_title": "צור קישור אפליקציה ברירת מחדל",
"default_app_link_description": "הגדרת קישור אפליקציה ברירת מחדל מאפשר לכל הארועים החדשים להשתמש בקישור שהגדרת.",
"change_default_conferencing_app": "להגדיר כברירת מחדל",
"booking_confirmation_failed": "אישור ההזמנה נכשל"
"under_maintenance": "אינו זמין עקב תחזוקה",
"under_maintenance_description": "צוות {{appName}} מבצע עבודות תחזוקה שתוכננו מראש. אם יש לך שאלות, נא צור קשר עם התמיכה.",
"event_type_seats": "{{numberOfSeats}} מושבים",
"booking_questions_title": "שאלות תזמון",
"booking_questions_description": "התאמת השאלות שנשאלות בדף התזמון",
"add_a_booking_question": "הוסף שאלה",
"duplicate_email": "מייל כבר קיים",
"booking_with_payment_cancelled": "תשלום עבור ארוע זה כבר אינו נתמך",
"booking_with_payment_cancelled_already_paid": "זיכוי עבור התשלום של תזמון זה כבר בדרך.",
"booking_with_payment_cancelled_refunded": "התשלום עבור תזמון זה זוכה.",
"booking_confirmation_failed": "אישור ההזמנה נכשל",
"form_builder_field_already_exists": "שדה עם שם זה כבר קיים",
"form_builder_field_add_subtitle": "התאמת השאלות שנשאלות בדף התזמון",
"form_builder_system_field_cant_delete": "שדה מערכת זה לא ניתן להסרה.",
"form_builder_system_field_cant_toggle": "שדה מערכת זה לא ניתן לשינוי.",
"get_started_zapier_templates": "התחל עם תבניות Zapier",
"team_member": "חבר צוות",
"a_routing_form": "טופס ניתוב",
"form_description_placeholder": "תיאור הטופס",
"keep_me_connected_with_form": "שמור אותי מחובר לטופס",
"fields_in_form_duplicated": "כל שינוי בניתוב ובשדות הטופס שמשוכפל, ישפיעו על הטופס המשוכפל.",
"form_deleted": "הטופס נמחק",
"delete_form": "מחק טופס",
"delete_form_action": "כן, מחק את הטופס",
"delete_form_confirmation": "אתה בטוח שאתה רוצה למחוק את הטופס? כל מי ששיתפת עמו את הקישור לא יוכל יותר לתזמן באמצעותו. בנוסף, כל התגובות הקשורות אליו ימחקו.",
"typeform_redirect_url_copied": "קישור הפניה TypeForm הועתק! אתה יכול ללכת ולהגדיר את הקישור בטופס ה- Typeform שלך.",
"modifications_in_fields_warning": "שינויים בשדות וניתובים של הטפסים הבאים ישפיעו על טופס זה.",
"connected_forms": "טפסים מחוברים",
"form_modifications_warning": "הטפסים הבאים יושפעו כשאתה משנה שדות או ניתובים כאן.",
"responses_collection_waiting_description": "חכה זמן מה עד לאיסוף תגובות. אתה גם יכול ללכת ולשלוח טופס בעצמך.",
"this_is_what_your_users_would_see": "זה מה שהמשתמשים שלך היו רואים",
"identifies_name_field": "מזהה שדה לפי שם זה.",
"add_1_option_per_line": "הוסף אפשרות 1 לכל שורה",
"select_a_router": "בחר נתב",
"add_a_new_route": "הוסף נתיב חדש",
"no_responses_yet": "אין עדיין תגובות",
"this_will_be_the_placeholder": "זה יהיה הממלא מקום",
"verification_code": "קוד אימות",
"verify": "אמת",
"select_all": "בחר הכל",
"default_conferencing_bulk_title": "בצע עדכון אצווה של סוגי הארועים הקיימים",
"default_conferencing_bulk_description": "עדכן את המיקומים עבור סוגי הארועים שנבחרו",
"invalid_event_name_variables": "יש משתנה שגוי בשם הארוע שלך"
}
@@ -7,6 +7,7 @@
"upgrade_now": "Upgrade sekarang",
"accept_invitation": "Terima undangan",
"calcom_explained": "{{appName}} menyediakan infrastruktur penjadwalan untuk semua orang.",
"calcom_explained_new_user": "Selesaikan pengaturan akun {{appName}}! Tinggal selangkah lagi untuk solusi penjadwalan kamu.",
"have_any_questions": "Punya pertanyaan? Kami siap membantu.",
"reset_password_subject": "{{appName}}: Petunjuk pengaturan ulang kata sandi",
"event_declined_subject": "Ditolak: {{title}} pada {{date}}",
@@ -23,6 +24,7 @@
"rejection_reason_description": "Kamu yakin menolak pemesanan? Kami akan memberitahu orang yang memesan. Kamu bisa menyediakan alasan di bawah ini.",
"rejection_confirmation": "Tolak pesanan",
"manage_this_event": "Kelola acara ini",
"invite_team_member": "Undang anggota tim",
"your_event_has_been_scheduled": "Acara kamu telah terjadwal",
"your_event_has_been_scheduled_recurring": "Acara berulang kamu telah terjadwal",
"accept_our_license": "Setujui lisensi dengan mengganti variabel .env <1>NEXT_PUBLIC_LICENSE_CONSENT</1> menjadi {{agree}}.",
@@ -48,6 +50,32 @@
"open_env": "Buka .env dan setujui lisensi kami",
"env_changed": "Saya telah mengubah .env saya",
"accept_license": "Terima Lisensi",
"still_waiting_for_approval": "Satu acara menunggu persetujuan",
"event_is_still_waiting": "Permintaan acara masih menunggu: {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "Tidak ada lagi",
"no_results": "Tidak ada hasil",
"load_more_results": "Muat lagi",
"integration_meeting_id": "{{integrationName}} ID pertemuan: {{meetingId}}",
"confirmed_event_type_subject": "Terkonfirmasi: {{eventType}} dengan {{name}} pada {{date}}",
"new_event_request": "Permintaan acara baru: {{attendeeName}} - {{date}} - {{eventType}}",
"confirm_or_reject_request": "Konfirmasi atau tolak permintaan",
"check_bookings_page_to_confirm_or_reject": "Periksa halaman penjadwalan untuk mengkonfirmasi atau menolak.",
"event_awaiting_approval": "Sebuah acara menunggu persetujuanmu",
"event_awaiting_approval_recurring": "Acara berulang menunggu persetujuanmu",
"someone_requested_an_event": "Sesorang mengirimkan permintaan penjadwalan acara di kalender kamu.",
"someone_requested_password_reset": "Seseorang mengirimkan link permintaan penggantian kata sandi.",
"password_reset_instructions": "Jika kamu tidak meminta ini, hiraukan email ini dan kata sandi tidak akan diganti.",
"event_awaiting_approval_subject": "Menunggu persetujuan: {{title}} pada {{date}}",
"event_still_awaiting_approval": "Sebuah acara masih menunggu persetujuanmu",
"booking_submitted_subject": "Pendaftaran Terkirim: {{title}} pada {{date}}",
"your_meeting_has_been_booked": "Pertemuanmu telah terdaftar",
"event_type_has_been_rescheduled_on_time_date": "{{title}} kamu telah dijadwalkan ulang menjadi {{date}}.",
"event_has_been_rescheduled": "Terkini - Acara kamu telah dijadwalkan ulang",
"request_reschedule_title_attendee": "Minta untuk penjadwalan ulang",
"request_reschedule_subtitle": "{{organizer}} telah membatalkan acara dan meminta kamu untuk memilih waktu lain.",
"request_reschedule_title_organizer": "Kamu telah meminta {{attendee}} untuk menjadwalkan ulang",
"request_reschedule_subtitle_organizer": "Kamu telah membatalkan acara dan {{attendee}} akan memilih waktu lain.",
"rescheduled_event_type_subject": "Permintaan penjadwalan ulang terkirim: {{eventType}} dengan {{name}} pada {{date}}",
"team_info": "Informasi Tim",
"request_another_invitation_email": "Jika kamu lebih suka tidak menggukan {{toEmail}} sebagai email {{appName}} atau telah memiliki akun {{appName}} silakan membuat permintaan lain kepada email tersebut.",
"you_have_been_invited": "Kamu telah diundang untuk bergabung ke tim {{teamName}}",
+128 -2
View File
@@ -7,6 +7,7 @@
"upgrade_now": "Cambia piano",
"accept_invitation": "Accetta Invito",
"calcom_explained": "{{appName}} fornisce un'infrastruttura di panificazione aperta a tutti.",
"calcom_explained_new_user": "Completa la configurazione del tuo account {{appName}}! Mancano solo pochi passi per risolvere tutti i problemi di pianificazione.",
"have_any_questions": "Hai domande? Siamo qui per aiutare.",
"reset_password_subject": "{{appName}}: istruzioni per reimpostare la password",
"event_declined_subject": "Rifiutato: {{title}} il {{date}}",
@@ -23,6 +24,8 @@
"rejection_reason_description": "Sei sicuro di voler rifiutare la prenotazione? Informeremo la persona che ha cercato di prenotare. Puoi indicare il motivo qui sotto.",
"rejection_confirmation": "Rifiuta la prenotazione",
"manage_this_event": "Gestisci questo evento",
"invite_team_member": "Invia un membro del team",
"invite_team_notifcation_badge": "Inv.",
"your_event_has_been_scheduled": "Il tuo evento è stato programmato",
"your_event_has_been_scheduled_recurring": "Il tuo evento ricorrente è stato programmato",
"accept_our_license": "Accetta la nostra licenza cambiando la variabile .env <1>NEXT_PUBLIC_LICENSE_CONSENT</1> a '{{agree}}'.",
@@ -410,6 +413,8 @@
"password_updated_successfully": "Webhook aggiornato con successo",
"password_has_been_changed": "La password è stata cambiata con successo.",
"error_changing_password": "Errore nel cambiare la password",
"session_timeout_changed": "La configurazione della sessione è stata aggiornata correttamente.",
"session_timeout_change_error": "Errore durante l'aggiornamento della configurazione della sessione",
"something_went_wrong": "Qualcosa è andato storto.",
"something_doesnt_look_right": "Qualcosa non sembra giusto?",
"please_try_again": "Per favore riprova.",
@@ -428,6 +433,7 @@
"password_hint_num": "Deve contenere almeno 1 numero",
"invalid_password_hint": "La password deve avere una lunghezza minima di {{passwordLength}} caratteri e deve contenere almeno 1 numero e una combinazione di caratteri maiuscoli e minuscoli",
"incorrect_password": "La password non è corretta.",
"incorrect_username_password": "Nome utente o password errati.",
"24_h": "24 ore",
"use_setting": "Usa impostazione",
"am_pm": "am/pm",
@@ -455,6 +461,7 @@
"slots_load_fail": "Impossibile caricare gli intervalli di tempo disponibili.",
"additional_guests": "Aggiungi ospiti",
"your_name": "Il tuo nome",
"your_full_name": "Nome completo",
"email_address": "Indirizzo email",
"enter_valid_email": "Immettere un indirizzo e-mail valido",
"location": "Luogo",
@@ -508,6 +515,8 @@
"admin": "Amministratore",
"administrator_user": "Utente amministratore",
"lets_create_first_administrator_user": "Creiamo il primo utente amministratore.",
"admin_user_created": "Impostazione utente amministratore",
"admin_user_created_description": "Hai già creato un utente amministratore. Puoi effettuare l'accesso al tuo account.",
"new_member": "Nuovo Membro",
"invite": "Invita",
"add_team_members": "Aggiungi membri del team",
@@ -589,7 +598,6 @@
"new_event_type_heading": "Crea il tuo primo tipo di evento",
"new_event_type_description": "I tipi di eventi ti permettono di condividere i link che mostrano gli orari disponibili sul tuo calendario e consentire alle persone di effettuare prenotazioni con te.",
"new_event_title": "Aggiungi un nuovo tipo di evento",
"new_event_subtitle": "Crea un tipo di evento sotto il tuo nome o un team.",
"new_team_event": "Aggiungi un nuovo tipo di evento del team",
"new_event_description": "Crea un nuovo tipo di evento per le persone con cui prenotare.",
"event_type_created_successfully": "{{eventTypeTitle}} tipo di evento creato con successo",
@@ -658,6 +666,7 @@
"edit_availability": "Modifica disponibilità",
"configure_availability": "Cambia quando sei disponibile per le prenotazioni.",
"copy_times_to": "Copia orari in",
"copy_times_to_tooltip": "Copia orari in…",
"change_weekly_schedule": "Cambia il tuo programma settimanale",
"logo": "Logo",
"error": "Errore",
@@ -669,6 +678,7 @@
"add_attendees": "Aggiungi partecipanti",
"show_advanced_settings": "Mostra impostazioni avanzate",
"event_name": "Nome Dell'Evento",
"event_name_in_calendar": "Nome dell'evento nel calendario",
"event_name_tooltip": "Il nome che apparirà nei calendari",
"meeting_with_user": "Riunione con {{attendeeName}}",
"additional_inputs": "Input Aggiuntivi",
@@ -787,6 +797,7 @@
"number_apps_one": "{{count}} App",
"number_apps_other": "{{count}} app",
"trending_apps": "App di tendenza",
"most_popular": "Più popolari",
"explore_apps": "App {{category}}",
"installed_apps": "App installate",
"free_to_use_apps": "Gratuito",
@@ -797,12 +808,14 @@
"no_category_apps_description_analytics": "Aggiungi un'app di analisi per le tue pagine di prenotazione",
"no_category_apps_description_automation": "Aggiungi un'app di automazione da usare",
"no_category_apps_description_other": "Aggiungi qualsiasi altra app per fare altre attività",
"no_category_apps_description_web3": "Aggiungi un'app Web3 per le tue pagine di prenotazione",
"installed_app_calendar_description": "Imposta uno o più calendari per controllare i conflitti ed evitare doppie prenotazioni.",
"installed_app_conferencing_description": "Aggiungi le tue app di videoconferenza preferite per le tue riunioni",
"installed_app_payment_description": "Configura quale servizio di elaborazione dei pagamenti usare per addebitare i clienti.",
"installed_app_analytics_description": "Imposta quali app di analisi usare per le tue pagine di prenotazione",
"installed_app_other_description": "Tutte le app installate appartenenti ad altre categorie.",
"installed_app_automation_description": "Imposta quali app di automazione usare",
"installed_app_web3_description": "Imposta quali app Web3 usare per le tue pagine di prenotazione",
"analytics": "Analisi",
"empty_installed_apps_headline": "Nessuna app installata",
"empty_installed_apps_description": "Le app consentono di ottimizzare il flusso di lavoro e di migliorare significativamente la pianificazione.",
@@ -930,6 +943,11 @@
"current_location": "Luogo corrente",
"user_phone": "Il tuo numero di telefono",
"new_location": "Nuovo luogo",
"session": "Sessione",
"session_description": "Controlla la sessione del tuo account",
"session_timeout_after": "Timeout sessione dopo",
"session_timeout": "Timeout sessione",
"session_timeout_description": "Invalida la sessione dopo un determinato periodo di tempo.",
"no_location": "Nessun luogo definito",
"set_location": "Imposta luogo",
"update_location": "Aggiorna luogo",
@@ -1073,6 +1091,7 @@
"broken_video_action": "Non è stato possibile aggiungere il link della riunione di <1>{{location}}</1> al tuo evento programmato. Contatta gli invitati o aggiorna l'evento sul tuo calendario per aggiungere i dettagli. È possibile <3> cambiare il luogo del tipo di evento </3> oppure provare a <5>rimuovere l'app e aggiungerla nuovamente.</5>",
"broken_calendar_action": "Non è stato possibile aggiornare il tuo <1>{{calendar}}</1>. <2> Controlla le impostazioni del tuo calendario oppure rimuovi il calendario e aggiungilo nuovamente </2>",
"attendee_name": "Nome del partecipante",
"scheduler_full_name": "Nome completo della persona che prenota",
"broken_integration": "Integrazione non funzionante",
"problem_adding_video_link": "Si è verificato un problema durante l'aggiunta di un collegamento video",
"problem_updating_calendar": "Si è verificato un problema durante l'aggiornamento del calendario",
@@ -1215,6 +1234,7 @@
"connect_automation_apps": "Connetti app di automazione",
"connect_analytics_apps": "Connetti app di analisi",
"connect_other_apps": "Connetti altre app",
"connect_web3_apps": "Connetti app Web3",
"current_step_of_total": "Passo {{currentStep}} di {{maxSteps}}",
"add_variable": "Aggiungi variabile",
"custom_phone_number": "Numero di telefono personalizzato",
@@ -1231,6 +1251,7 @@
"to": "A",
"workflow_turned_on_successfully": "Il flusso di lavoro {{workflowName}} è stato impostato come {{offOn}}",
"download_responses": "Scarica risposte",
"download_responses_description": "Scarica tutte le risposte al tuo modulo in formato CSV.",
"download": "Scarica",
"create_your_first_form": "Crea il tuo primo modulo",
"create_your_first_form_description": "I Moduli di instradamento consentono di porre domande qualificanti agli utenti per indirizzarli a persone o eventi pertinenti.",
@@ -1272,6 +1293,8 @@
"routing_forms_send_email_owner": "Invia e-mail al proprietario",
"routing_forms_send_email_owner_description": "Invia un'e-mail al proprietario quando il modulo viene inviato",
"add_new_form": "Aggiungi nuovo modulo",
"create_your_first_route": "Crea il tuo primo percorso",
"route_to_the_right_person": "Instrada gli utenti alla persona giusta in base alle risposte al modulo",
"form_description": "Crea un modulo per instradare le persone che prenotano",
"copy_link_to_form": "Copia il link del modulo",
"theme": "Tema",
@@ -1302,6 +1325,7 @@
"password_reset_leading": "Se un'e-mail non ti dovesse arrivare a breve, assicurati che l'indirizzo specificato sia corretto e controlla la cartella della posta indesiderata. Se il problema persiste, contatta l'assistenza clienti.",
"password_updated": "Password aggiornata!",
"pending_payment": "Pagamento in sospeso",
"pending_invites": "Inviti in sospeso",
"confirmation_page_rainbow": "Proteggi i tuoi eventi con token o NFT su Ethereum, Polygon e altri.",
"not_on_cal": "Non presente su {{appName}}",
"no_calendar_installed": "Nessun calendario installato",
@@ -1428,6 +1452,9 @@
"disabled_calendar": "Se hai un altro calendario installato, le nuove prenotazioni saranno aggiunte a quel calendario. Altrimenti, collega un nuovo calendario in modo da non perdere nessuna nuova prenotazione.",
"enable_apps": "Abilita app",
"enable_apps_description": "Abilita le app che gli utenti possono integrare con Cal.com",
"purchase_license": "Acquista una licenza",
"already_have_key": "Ho già una chiave:",
"already_have_key_suggestion": "Copia qui la variabile di ambiente CALCOM_LICENSE_KEY esistente.",
"app_is_enabled": "{{appName}} è abilitato",
"app_is_disabled": "{{appName}} è disabilitato",
"keys_have_been_saved": "Le chiavi sono state salvate",
@@ -1448,6 +1475,7 @@
"individual": "Persona",
"all_bookings_filter_label": "Tutte le prenotazioni",
"all_users_filter_label": "Tutti gli utenti",
"your_bookings_filter_label": "Le tue prenotazioni",
"meeting_url_variable": "URL della riunione",
"meeting_url_info": "URL dell'evento, dell'incontro o della conferenza",
"date_overrides": "Configurazione date specifiche",
@@ -1486,6 +1514,7 @@
"round_robin_hosts": "Organizzatori round robin",
"minimum_round_robin_hosts_count": "Numero di organizzatori necessario per partecipare",
"hosts": "Organizzatori",
"upgrade_to_enable_feature": "Per abilitare questa funzione, è necessario creare un team. Fai clic per creare un team.",
"new_attendee": "Nuovo partecipante",
"awaiting_approval": "In attesa di approvazione",
"requires_google_calendar": "L'app richiede una connessione a Google Calendar",
@@ -1494,9 +1523,106 @@
"continue_to_install_google_calendar": "Continua e installa Google Calendar",
"install_google_meet": "Installa Google Meet",
"install_google_calendar": "Installa Google Calendar",
"sender_name": "Nome mittente",
"already_invited": "Partecipante già invitato",
"no_recordings_found": "Nessuna registrazione trovata",
"new_workflow_subtitle": "Nuovo flusso di lavoro per...",
"reporting": "Reporting",
"reporting_feature": "Visualizza tutti i dati in arrivo e scaricali in formato CSV",
"teams_plan_required": "Piano Team richiesto",
"routing_forms_are_a_great_way": "I moduli di instradamento sono un ottimo strumento per instradare i lead in arrivo alle persone giuste. Per accedere a questa funzione, effettuare l'upgrade al piano Team.",
"choose_a_license": "Scegli una licenza",
"choose_license_description": "Cal.com viene fornito con una licenza AGPLv3 accessibile e gratuita che ha alcune limitazioni e può essere aggiornata a Enterprise in qualsiasi momento. È possibile effettuare l'upgrade in un secondo tempo.",
"license": "Licenza",
"agplv3_license": "Licenza AGPLv3",
"ee_enterprise_license": "Licenza Enterprise “/ee”",
"enterprise_booking_fee": "A partire da {{enterprise_booking_fee}}/mese",
"enterprise_license_includes": "Tutto il necessario per l'utilizzo commerciale",
"no_need_to_keep_your_code_open_source": "Non c'è bisogno di mantenere il codice open-source",
"repackage_rebrand_resell": "Rimonta e rivendi sotto un nuovo marchio",
"a_vast_suite_of_enterprise_features": "Un'estesa suite di funzionalità enterprise",
"free_license_fee": "0,00 $/mese",
"forever_open_and_free": "Aperto e gratuito per sempre",
"required_to_keep_your_code_open_source": "È necessario mantenere il codice open-source",
"cannot_repackage_and_resell": "Impossibile rimontare e rivendere sotto un nuovo marchio",
"no_enterprise_features": "Nessuna funzionalità enterprise",
"step_enterprise_license": "Licenza Enterprise",
"step_enterprise_license_description": "Tutto il necessario per l'utilizzo commerciale con hosting privato, rimontaggio, rebranding e rivendita e accesso ai componenti enterprise esclusivi.",
"setup": "Impostazione",
"setup_description": "Imposta istanza Cal.com",
"configure": "Configura",
"sso_configuration": "Configurazione SAML",
"sso_configuration_description": "Configura accesso SSO tramite SAML/OIDC e consenti ai membri del team di accedere utilizzando un provider di identità",
"sso_oidc_heading": "SSO con OIDC",
"sso_oidc_description": "Configura accesso SSO tramite OIDC con un provider di identità di tua scelta.",
"sso_oidc_configuration_title": "Configurazione OIDC",
"sso_oidc_configuration_description": "Configura la connessione OIDC al tuo provider di identità. Puoi trovare le informazioni necessarie presso il tuo provider di identità.",
"sso_oidc_callback_copied": "URL di callback copiato",
"sso_saml_heading": "SSO con SAML",
"sso_saml_description": "Configura accesso SSO tramite SAML con un provider di identità di tua scelta.",
"sso_saml_configuration_title": "Configurazione SAML",
"sso_saml_configuration_description": "Configura la connessione SAML al tuo provider di identità. Puoi trovare le informazioni necessarie presso il tuo provider di identità.",
"sso_saml_acsurl_copied": "URL dell'ACS copiato",
"sso_saml_entityid_copied": "ID entità copiato",
"sso_connection_created_successfully": "Configurazione {{connectionType}} creata correttamente",
"sso_connection_deleted_successfully": "Configurazione {{connectionType}} eliminata correttamente",
"delete_sso_configuration": "Eliminare la configurazione {{connectionType}}",
"delete_sso_configuration_confirmation": "Sì, elimina la configurazione {{connectionType}}",
"delete_sso_configuration_confirmation_description": "Eliminare la configurazione {{connectionType}}? I membri del tuo team che utilizzano l'accesso {{connectionType}} non saranno più in grado di accedere a Cal.com.",
"organizer_timezone": "Fuso orario organizzatore",
"email_no_user_cta": "Crea il tuo account",
"email_user_cta": "Visualizza invito",
"email_no_user_invite_heading": "Sei stato invitato a unirti al team su {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} ti ha invitato a unirti al suo team su {{appName}}. {{appName}} è uno strumento di pianificazione di eventi che permette al tuo team di pianificare le riunioni senza scambiare decine di e-mail.",
"email_no_user_invite_steps_intro": "Ti assisteremo nei passaggi iniziali in modo che tu possa pianificare eventi per il tuo team senza stress e in pochissimo tempo.",
"email_no_user_step_one": "Scegli il tuo nome utente",
"email_no_user_step_two": "Collega il tuo account di calendario",
"email_no_user_step_three": "Imposta la tua disponibilità",
"email_no_user_step_four": "Unirsi a {{teamName}}",
"email_no_user_signoff": "Il team {{appName}} ti augura una piacevole esperienza di pianificazione",
"impersonation_user_tip": "Stai per effettuare l'accesso con altra identità, vuol dire che potrai apportare modifiche a nome di un altro utente. Sei pregato di farlo con cautela.",
"available_variables": "Variabili disponibili",
"scheduler": "{Scheduler}",
"no_workflows": "Nessun flusso di lavoro",
"change_filter": "Modifica il filtro per vedere i flussi di lavoro personali e di squadra.",
"recommended_next_steps": "Passaggi successivi consigliati",
"create_a_managed_event": "Crea un tipo di evento gestito",
"meetings_are_better_with_the_right": "Le riunioni sono efficaci quando sono presenti le persone giuste. Invitale adesso.",
"create_a_one_one_template": "Crea un modello di incontro uno a uno per un tipo di evento e distribuiscilo a più membri.",
"collective_or_roundrobin": "Di gruppo o round robin",
"book_your_team_members": "Usa eventi di gruppo per riunire tutti i membri del team o eventi round robin per farli intervenire a turno e individuare la persona giusta.",
"create_event_on": "Crea evento in data",
"default_app_link_title": "Imposta il link dell'app predefinita",
"default_app_link_description": "L'impostazione di un link dell'app predefinita permette a tutti i nuovi tipi di eventi di usare il link dell'app impostato.",
"change_default_conferencing_app": "Imposta come predefinito",
"booking_confirmation_failed": "Conferma prenotazione non riuscita"
"under_maintenance": "Inattivo per manutenzione",
"under_maintenance_description": "Il team di {{appName}} sta eseguendo la manutenzione programmata. Per qualsiasi domanda, contattare il supporto.",
"event_type_seats": "{{numberOfSeats}} posti",
"booking_with_payment_cancelled": "Non è più possibile pagare per questo evento",
"booking_with_payment_cancelled_already_paid": "È in corso il rimborso del pagamento per questa prenotazione.",
"booking_with_payment_cancelled_refunded": "Il pagamento per questa prenotazione è stato rimborsato.",
"booking_confirmation_failed": "Conferma prenotazione non riuscita",
"get_started_zapier_templates": "Inizia con i modelli Zapier",
"a_routing_form": "Un modulo di instradamento",
"form_description_placeholder": "Descrizione modulo",
"keep_me_connected_with_form": "Mantieni la connessione con il modulo",
"fields_in_form_duplicated": "Le eventuali modifiche al router e ai campi del modulo da duplicare si rifletteranno nel duplicato.",
"form_deleted": "Modulo eliminato",
"delete_form": "Elimina modulo",
"delete_form_action": "Sì, elimina modulo",
"delete_form_confirmation": "Eliminare questo modulo? Gli utenti con cui hai condiviso il link non saranno più in grado di usarlo per prenotare. Inoltre, verranno eliminate tutte le risposte a esso associate.",
"typeform_redirect_url_copied": "URL di reindirizzamento Typeform copiato! È possibile andare a impostare l'URL ne modulo Typeform.",
"modifications_in_fields_warning": "Le eventuali modifiche apportate ai campi e ai percorsi dei seguenti moduli si rifletteranno in questo modulo.",
"connected_forms": "Moduli connessi",
"form_modifications_warning": "Le modifiche qui apportate a campi e percorsi si rifletteranno nei seguenti moduli.",
"responses_collection_waiting_description": "Attendere che vengano raccolte alcune risposte. È possibile anche compilare e inviare il modulo personalmente.",
"this_is_what_your_users_would_see": "Ecco cosa vedranno gli utenti",
"identifies_name_field": "Identifica il campo con questo nome.",
"add_1_option_per_line": "Aggiungi 1 opzione per riga",
"select_a_router": "Seleziona un router",
"add_a_new_route": "Aggiungi un nuovo percorso",
"no_responses_yet": "Per ora non ci sono risposte",
"this_will_be_the_placeholder": "Questo sarà un segnaposto",
"verification_code": "Codice di verifica",
"verify": "Verifica"
}
+121 -2
View File
@@ -7,6 +7,7 @@
"upgrade_now": "今すぐアップグレード",
"accept_invitation": "招待を受け入れる",
"calcom_explained": "{{appName}} は、すべての人が使えるスケジュール管理インフラを提供します。",
"calcom_explained_new_user": "{{appName}} アカウントの設定を完了してください!あと数ステップでスケジューリングの問題をすべて解決できます。",
"have_any_questions": "ご不明な点があれば、お気軽にお問い合わせください。",
"reset_password_subject": "{{appName}}: パスワードのリセット手順",
"event_declined_subject": "却下: {{title}} {{date}}",
@@ -23,6 +24,8 @@
"rejection_reason_description": "この予約を拒否してよろしいですか?予約を試みたユーザーに通知を送ります。拒否の理由は、下にご記入いただけます。",
"rejection_confirmation": "予約を拒否する",
"manage_this_event": "このイベントを管理する",
"invite_team_member": "チームメンバーを招待",
"invite_team_notifcation_badge": "招待済み",
"your_event_has_been_scheduled": "イベントがスケジュールされました",
"your_event_has_been_scheduled_recurring": "繰り返しイベントがスケジュールされています",
"accept_our_license": ".env 変数 <1>NEXT_PUBLIC_LICENSE_CONSENT</1> を '{{agree}} ' に変更することで、ライセンスに同意してください。",
@@ -410,6 +413,8 @@
"password_updated_successfully": "パスワードが正常に更新されました",
"password_has_been_changed": "パスワードが正常に変更されました。",
"error_changing_password": "パスワードの変更中にエラーが発生しました",
"session_timeout_changed": "セッションの構成が正常に更新されました。",
"session_timeout_change_error": "セッションの構成の更新中にエラーが発生しました",
"something_went_wrong": "問題が発生しました。",
"something_doesnt_look_right": "何かおかしいことがありますか?",
"please_try_again": "もう一度お試しください",
@@ -428,6 +433,7 @@
"password_hint_num": "少なくとも 1 文字は数字を含めてください",
"invalid_password_hint": "パスワードには少なくとも 1 文字以上数字を含め、アルファベットの大文字と小文字を両方使用した上で {{passwordLength}} 文字以上の長さに設定してください",
"incorrect_password": "パスワードが正しくありません。",
"incorrect_username_password": "ユーザー名またはパスワードが正しくありません。",
"24_h": "24時間",
"use_setting": "設定を使用",
"am_pm": "午前/午後",
@@ -455,6 +461,7 @@
"slots_load_fail": "利用可能な時間帯を読み込めませんでした。",
"additional_guests": "ゲストを追加",
"your_name": "あなたの名前",
"your_full_name": "あなたのフルネーム",
"email_address": "メールアドレス",
"enter_valid_email": "有効なメールアドレスを入力してください",
"location": "所在地",
@@ -508,6 +515,8 @@
"admin": "管理者",
"administrator_user": "管理者ユーザー",
"lets_create_first_administrator_user": "最初の管理者ユーザーを作成しましょう。",
"admin_user_created": "管理者ユーザー設定",
"admin_user_created_description": "すでに管理者ユーザーを作成しています。アカウントにログインできます。",
"new_member": "新しいメンバー",
"invite": "招待する",
"add_team_members": "チームメンバーを追加",
@@ -589,7 +598,6 @@
"new_event_type_heading": "最初のイベントタイプを作成する",
"new_event_type_description": "イベントタイプを使用すると、カレンダーに利用可能な時間を表示し、人々があなたの時間の予約を行うことができるリンクを共有できます。",
"new_event_title": "新しいイベント種別を追加",
"new_event_subtitle": "あなたの名前またはチームの下にイベントタイプを作成します。",
"new_team_event": "新しいイベント種別を追加",
"new_event_description": "人々が時間を予約できる新しいイベントタイプを作成します。",
"event_type_created_successfully": "{{eventTypeTitle}} イベント種別が正常に作成されました",
@@ -658,6 +666,7 @@
"edit_availability": "参加可能な時間を編集",
"configure_availability": "予約が可能な時間を設定する。",
"copy_times_to": "時間を以下にコピー",
"copy_times_to_tooltip": "時間を…にコピー",
"change_weekly_schedule": "ウィークリースケジュールを変更する",
"logo": "ロゴ",
"error": "エラー",
@@ -669,6 +678,7 @@
"add_attendees": "出席者を追加",
"show_advanced_settings": "詳細設定を表示",
"event_name": "イベント名",
"event_name_in_calendar": "カレンダーのイベント名",
"event_name_tooltip": "カレンダーに表示される名前",
"meeting_with_user": "{{attendeeName}} とのミーティング",
"additional_inputs": "追加入力",
@@ -787,6 +797,7 @@
"number_apps_one": "{{count}} 個のアプリ",
"number_apps_other": "{{count}} 個のアプリ",
"trending_apps": "人気のアプリ",
"most_popular": "最も人気",
"explore_apps": "{{category}} アプリ",
"installed_apps": "インストールされているアプリ",
"free_to_use_apps": "無料",
@@ -797,12 +808,14 @@
"no_category_apps_description_analytics": "予約ページに分析アプリを追加する",
"no_category_apps_description_automation": "使用する自動化アプリを追加する",
"no_category_apps_description_other": "その他のアプリを追加して、様々なことを実現しましょう",
"no_category_apps_description_web3": "予約ページに Web3 アプリを追加",
"installed_app_calendar_description": "ダブルブッキングを防ぐために、カレンダーの重複をチェックするように設定します。",
"installed_app_conferencing_description": "会議用にお気に入りのビデオ会議アプリを追加しましょう",
"installed_app_payment_description": "顧客に請求をする際に使用する決済処理サービスを設定しましょう。",
"installed_app_analytics_description": "予約ページでどの分析アプリを使用するかを構成する",
"installed_app_other_description": "その他のカテゴリーからインストールしたすべてのアプリ。",
"installed_app_automation_description": "どの自動化アプリを使用するかを構成する",
"installed_app_web3_description": "予約ページでどの Web3 アプリを使用するかを設定します",
"analytics": "分析",
"empty_installed_apps_headline": "アプリがインストールされていません",
"empty_installed_apps_description": "アプリを使えば、ワークフローの質を高め、スケジュールライフを大幅に改善することができます。",
@@ -930,6 +943,11 @@
"current_location": "現在の場所",
"user_phone": "あなたの電話番号",
"new_location": "新しい場所",
"session": "セッション",
"session_description": "アカウントのセッションを管理します",
"session_timeout_after": "後にセッションをタイムアウト",
"session_timeout": "セッションタイムアウト",
"session_timeout_description": "一定時間後にセッションを無効にする",
"no_location": "場所が定義されていません",
"set_location": "場所を設定",
"update_location": "場所を更新",
@@ -1073,6 +1091,7 @@
"broken_video_action": "スケジュールされたイベントに<1>{{location}}</1>ミーティングへのリンクを追加できませんでした。招待者に連絡するか、カレンダーのイベントを更新して詳細を追加してください。 <3>イベントタイプで場所を変更するか</3>、<5>アプリを削除して再度追加</5>してください。",
"broken_calendar_action": "<1>{{calendar}}</1>を更新できませんでした。<2>カレンダーの設定を確認するか、カレンダーを削除してもう一度追加してください</2>",
"attendee_name": "出席者の名前",
"scheduler_full_name": "予約者のフルネーム",
"broken_integration": "連携が切断されています",
"problem_adding_video_link": "動画リンクの追加中に問題が発生しました",
"problem_updating_calendar": "カレンダーの更新中に問題が発生しました",
@@ -1215,6 +1234,7 @@
"connect_automation_apps": "自動化アプリを接続する",
"connect_analytics_apps": "分析アプリを接続する",
"connect_other_apps": "その他のアプリを接続",
"connect_web3_apps": "Web3 アプリを接続",
"current_step_of_total": "ステップ {{currentStep}}/{{maxSteps}}",
"add_variable": "変数を追加",
"custom_phone_number": "カスタム電話番号",
@@ -1231,6 +1251,7 @@
"to": "送信先",
"workflow_turned_on_successfully": "{{workflowName}} ワークフローが正常に {{offOn}} になりました",
"download_responses": "回答をダウンロード",
"download_responses_description": "CSV形式ですべての回答をフォームにダウンロードします。",
"download": "ダウンロード",
"create_your_first_form": "最初のフォームを作成",
"create_your_first_form_description": "ルーティングフォームを使用すれば、絞り込みの質問を通して適切な人物やイベントの種類へとルーティングを行うことができます。",
@@ -1272,6 +1293,8 @@
"routing_forms_send_email_owner": "所有者にメールを送信",
"routing_forms_send_email_owner_description": "フォームの送信時に所有者にメールを送信します",
"add_new_form": "新しいフォームを追加",
"create_your_first_route": "最初のルートを作成",
"route_to_the_right_person": "フォームへの回答に基づいて適切な人にルーティングする",
"form_description": "予約者をルーティングするフォームを作成します",
"copy_link_to_form": "フォームへのリンクをコピー",
"theme": "テーマ",
@@ -1302,6 +1325,7 @@
"password_reset_leading": "すぐにメールが届かない場合は、入力したメールアドレスが正しいかや迷惑メールフォルダを確認し、それでも問題が解決しない場合はサポートにご連絡ください。",
"password_updated": "パスワードが更新されました!",
"pending_payment": "支払い待ち",
"pending_invites": "保留中の招待",
"confirmation_page_rainbow": "Ethereum や Polygon などのトークンや NFT を使用してイベントに参加しましょう。",
"not_on_cal": "{{appName}} にはいません",
"no_calendar_installed": "カレンダーがインストールされていません",
@@ -1428,6 +1452,9 @@
"disabled_calendar": "他のカレンダーがインストールされている場合、新しい予約はそのカレンダーに追加されます。そうでない場合には、新しいカレンダーへと接続します。そのため、新しい予約を見逃すことはなくなります。",
"enable_apps": "アプリを有効にする",
"enable_apps_description": "ユーザーが Cal.com と統合可能なアプリを有効にします",
"purchase_license": "ライセンスを購入",
"already_have_key": "すでにキーを持っています:",
"already_have_key_suggestion": "ここに既存の CALCOM_LICENSE_KEY 環境変数をコピーしてください。",
"app_is_enabled": "{{appName}} は有効です",
"app_is_disabled": "{{appName}} は無効です",
"keys_have_been_saved": "キーが保存されました",
@@ -1448,6 +1475,7 @@
"individual": "個人",
"all_bookings_filter_label": "すべての予約",
"all_users_filter_label": "すべてのユーザー",
"your_bookings_filter_label": "あなたの予約",
"meeting_url_variable": "ミーティング URL",
"meeting_url_info": "イベントミーティング会議 URL",
"date_overrides": "日付の上書き",
@@ -1486,6 +1514,7 @@
"round_robin_hosts": "ラウンドロビンホスト",
"minimum_round_robin_hosts_count": "出席が必要なホストの数",
"hosts": "ホスト",
"upgrade_to_enable_feature": "この機能を有効にするにはチームを作成する必要があります。クリックしてチームを作成してください。",
"new_attendee": "新規参加者",
"awaiting_approval": "承認を待っています",
"requires_google_calendar": "このアプリは Google カレンダーとの接続が必要です",
@@ -1494,9 +1523,99 @@
"continue_to_install_google_calendar": "Google カレンダーのインストールを続行",
"install_google_meet": "Google Meet をインストール",
"install_google_calendar": "Google カレンダーをインストール",
"sender_name": "送信者名",
"already_invited": "出席者はすでに招待されています",
"no_recordings_found": "録音が見つかりません",
"reporting": "報告",
"reporting_feature": "データからの受信情報をすべて表示し、CSV形式でダウンロード",
"teams_plan_required": "チームプランが必要です",
"routing_forms_are_a_great_way": "ルーティングフォームは入ってきたリードを適切な人にルーティングするのに最適な方法です。この機能にアクセスするにはチームプランにアップグレードしてください。",
"choose_a_license": "ライセンスを選択する",
"choose_license_description": "Cal.com にはアクセス可能な無料の AGPLv3 ライセンスが付属していますが、これにはいくつか制限があり、いつでも企業向けライセンスにアップグレードすることができます。後でいつでもアップグレード可能です。",
"license": "ライセンス",
"agplv3_license": "AGPLv3 ライセンス",
"ee_enterprise_license": "「/ee」企業向けライセンス",
"enterprise_booking_fee": "{{enterprise_booking_fee}}/月で開始",
"enterprise_license_includes": "商用ユースケースのためのあらゆるもの",
"no_need_to_keep_your_code_open_source": "コードをオープンソースにしておく必要はありません",
"repackage_rebrand_resell": "再パッケージ、リブランド、再販が簡単です",
"a_vast_suite_of_enterprise_features": "膨大な企業向け機能のパッケージ",
"free_license_fee": "0.00ドル/月",
"forever_open_and_free": "永久にオープンかつ無料です",
"required_to_keep_your_code_open_source": "コードをオープンソースにしておく必要があります",
"cannot_repackage_and_resell": "再パッケージ、リブランド、再販が簡単ではありません",
"no_enterprise_features": "企業向け機能がありません",
"step_enterprise_license": "企業向けライセンス",
"step_enterprise_license_description": "プライベートホスティング、再パッケージ、リブラディング、再販、専用の企業向けコンポーネントなど、商用ユースケース用のあらゆるものが備わっています。",
"setup": "設定",
"setup_description": "Cal.com インスタンスを設定します",
"configure": "構成する",
"sso_configuration": "シングルサインオン",
"sso_configuration_description": "SAML/OIDC SSO を設定し、チームメンバーが ID プロバイダーを使ってログインできるようにします",
"sso_oidc_heading": "OIDCでのSSO",
"sso_oidc_description": "好みの ID プロバイダーを使用して OIDCでのSSO を設定します。",
"sso_oidc_configuration_title": "OIDC 設定",
"sso_oidc_configuration_description": "ID プラバイダーへの OIDC 接続を設定します。必要な情報は ID プロバイダーで見つけることができます。",
"sso_oidc_callback_copied": "コールバックURLがコピーされました",
"sso_saml_heading": "SAMLでのSSO",
"sso_saml_description": "好みの ID プロバイダーを使用して SAMLでのSSO を設定します。",
"sso_saml_configuration_title": "SAML 設定",
"sso_saml_configuration_description": "ID プロバイダーへの SAML 接続を設定します。必要な情報は ID プロバイダーで見つけることができます。",
"sso_saml_acsurl_copied": "ACS URL をコピーしました",
"sso_saml_entityid_copied": "エンティティ ID をコピーしました",
"sso_connection_created_successfully": "{{connectionType}} の設定が正常に作成されました",
"sso_connection_deleted_successfully": "{{connectionType}} の設定が正常に削除されました",
"delete_sso_configuration": "{{connectionType}} の設定を削除",
"delete_sso_configuration_confirmation": "はい、{{connectionType}} の設定を削除します",
"delete_sso_configuration_confirmation_description": "{{connectionType}} の設定を削除してもよろしいですか?{{connectionType}} でのログインを使用しているチームメンバーは Cal.com にアクセスすることができなくなります。",
"organizer_timezone": "主催者のタイムゾーン",
"email_no_user_cta": "アカウントを作成",
"email_user_cta": "招待を表示",
"email_no_user_invite_heading": "{{appName}} のチームに参加するよう招待されました",
"email_no_user_invite_subheading": "{{invitedBy}} は {{appName}} のチームに参加するようあなたを招待しました。{{appName}} はイベント調整スケジューラーで、あなたとチームは延々とメールをやりとりすることなくミーティングをスケジューリングできます。",
"email_no_user_invite_steps_intro": "いくつかの短い手順を踏むだけで、すぐにチームとのストレスフリーなスケジューリングをお楽しみいただけます。",
"email_no_user_step_one": "ユーザー名を選択する",
"email_no_user_step_two": "カレンダーアカウントを接続する",
"email_no_user_step_three": "空き状況を設定する",
"email_no_user_step_four": "{{teamName}} に参加する",
"email_no_user_signoff": "スケジューリングをお楽しみください {{appName}} チームより",
"impersonation_user_tip": "あるユーザーになりすまし、その人に代わって変更を加えることが可能になろうとしています。ご注意ください。",
"available_variables": "利用可能な変数",
"scheduler": "{Scheduler}",
"recommended_next_steps": "推奨される次のステップ",
"create_a_managed_event": "管理イベントタイプを作成",
"meetings_are_better_with_the_right": "ミーティングは適切なチームメンバーの参加でより良いものになります。今すぐ彼らを招待しましょう。",
"collective_or_roundrobin": "全体またはラウンドロビン",
"book_your_team_members": "全体イベントでチームメンバー全員を予約するか、ラウンドロビンで持ち回りにして適切な人を獲得します。",
"default_app_link_title": "デフォルトのアプリリンクを設定",
"default_app_link_description": "デフォルトのアプリリンクを設定すると、新たに作成したすべてのイベントタイプがそのアプリリンクを使用することができます。",
"change_default_conferencing_app": "デフォルトとして設定",
"booking_confirmation_failed": "予約の確認に失敗しました"
"under_maintenance": "メンテナンスのためダウン",
"under_maintenance_description": "{{appName}} チームが定期メンテナンスを行っています。ご質問がある場合は、サポートにお問い合わせください。",
"event_type_seats": "{{numberOfSeats}} 席",
"booking_with_payment_cancelled": "このイベントの支払いはもうできません",
"booking_with_payment_cancelled_already_paid": "この予約の支払いに対する払い戻しは処理中です。",
"booking_with_payment_cancelled_refunded": "この予約の支払いは返金されました。",
"booking_confirmation_failed": "予約の確認に失敗しました",
"get_started_zapier_templates": "Zapier テンプレートを使い始める",
"a_routing_form": "ルーティングフォーム",
"form_description_placeholder": "フォームの説明",
"keep_me_connected_with_form": "フォームに接続したままにする",
"fields_in_form_duplicated": "フォームのルーターとフィールドの変更は複製され、複製に反映されます。",
"form_deleted": "フォームが削除されました",
"delete_form": "フォームを削除",
"delete_form_action": "はい、フォームを削除します",
"delete_form_confirmation": "このフォームを削除してもよろしいですか?これまでにリンクを共有した相手の人はリンクを使って予約することができなくなります。また、関連する回答はすべて削除されます。",
"typeform_redirect_url_copied": "Typeform のリダイレクト URL をコピーしました!URL は Typeform のフォームで設定できます。",
"modifications_in_fields_warning": "以下のフォームのフィールドとルートの変更がこのフォームに反映されます。",
"connected_forms": "接続されているフォーム",
"form_modifications_warning": "ここでフィールドまたはルートを変更すると、以下のフォームが影響を受けます。",
"responses_collection_waiting_description": "回答が集められるまでしばらく待ってください。あなた自身でフォームを送信してくることもできます。",
"this_is_what_your_users_would_see": "これがユーザーに表示される内容です",
"identifies_name_field": "この名前でフィールドを識別します。",
"add_1_option_per_line": "1 行に 1 つのオプションを追加",
"select_a_router": "ルーターを選択",
"add_a_new_route": "新しいルートを追加",
"no_responses_yet": "回答はまだありません",
"this_will_be_the_placeholder": "これがプレースホルダーになります"
}
@@ -598,7 +598,6 @@
"new_event_type_heading": "첫 번째 이벤트 타입 만들기",
"new_event_type_description": "이벤트 타입을 사용하면 캘린더에서 사용 가능한 시간을 표시하는 링크를 공유하고, 사람들이 당신과 함께 예약할 수 있습니다.",
"new_event_title": "새 이벤트 타입 추가하기",
"new_event_subtitle": "귀당신의 이름 또는 팀 아래 이벤트 타입을 작성하십시오.",
"new_team_event": "새 팀 이벤트 타입 추가",
"new_event_description": "사람들이 시간을 예약할 수 있는 새 이벤트 타입을 만듭니다.",
"event_type_created_successfully": "{{eventTypeTitle}} 이벤트 타입이 성공적으로 생성되었습니다.",
@@ -1581,8 +1580,8 @@
"email_no_user_step_four": "{{teamName}} 가입",
"email_no_user_signoff": "{{appName}} 팀의 행복한 일정",
"impersonation_user_tip": "사용자를 가장하려고 합니다. 즉, 사용자를 대신하여 변경할 수 있는 것입니다. 조심하세요.",
"scheduler": "{Scheduler}",
"available_variables": "사용 가능 변수",
"scheduler": "{Scheduler}",
"recommended_next_steps": "추천 다음 단계",
"create_a_managed_event": "관리형 이벤트 유형 작성",
"meetings_are_better_with_the_right": "회의는 그곳에서 올바른 팀원과 함께 하는 것이 더 낫습니다. 지금 초대하세요.",
@@ -589,7 +589,6 @@
"new_event_type_heading": "Maak uw eerste evenement aan",
"new_event_type_description": "Evenementen stellen u in staat om links te delen die beschikbare tijden tonen op uw agenda en bezoekers hiervan kunnen deze gebruiken om afspraken te maken.",
"new_event_title": "Een evenement toevoegen",
"new_event_subtitle": "Maak een evenement aan onder uw profiel of team.",
"new_team_event": "Een nieuw type vergadering toevoegen",
"new_event_description": "Maak een nieuw evenement voor bezoekers om afspraken te maken.",
"event_type_created_successfully": "{{eventTypeTitle}} evenement met succes aangemaakt",
@@ -588,7 +588,6 @@
"new_event_type_heading": "Opprett din første hendelsestype",
"new_event_type_description": "Hendelsestyper lar deg dele lenker som viser tilgjengelige tider i kalenderen din og lar folk gjøre bookinger med deg.",
"new_event_title": "Legg til en ny hendelsestype",
"new_event_subtitle": "Opprett en hendelsestype under navnet ditt eller et team.",
"new_team_event": "Legg til en ny hendelsestype for team",
"new_event_description": "Opprett en ny hendelsestype som folk kan booke tider med.",
"event_type_created_successfully": "{{eventTypeTitle}} hendelsestype opprettet",
@@ -589,7 +589,6 @@
"new_event_type_heading": "Utwórz swój typ pierwszego wydarzenia",
"new_event_type_description": "Typy wydarzeń umożliwiają udostępnianie linków, które pokazują dostępne czasy w kalendarzu i pozwalają ludziom na dokonywanie rezerwacji.",
"new_event_title": "Dodaj nowy typ wydarzenia",
"new_event_subtitle": "Utwórz typ wydarzenia pod Twoim imieniem lub zespołem.",
"new_team_event": "Dodaj nowy typ wydarzenia drużynowego",
"new_event_description": "Utwórz nowy typ wydarzenia, z którym ludzie mogą rezerwować czasy.",
"event_type_created_successfully": "Utworzono {{eventTypeTitle}} typ wydarzenia pomyślnie",

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