Compare commits

...
63 Commits
Author SHA1 Message Date
zomars 3b896bee5c v2.4.0
release.yml / release (push) Canceled after 0s
2022-12-15 15:59:08 -07:00
zomars cc1985442e Submodule sync 2022-12-15 15:58:13 -07:00
Omar López 630f438b32 Upgrades prisma / Syncs packages (#6044)
* Syncs packages

* Upgrades zod
2022-12-15 15:56:48 -07:00
6fae01d45a added auto animate to installed apps, fixed container size (#6046)
* added auto animate to installed apps, fixed container size, horizontal scroll

* Update packages/ui/v2/modules/booker/DatePicker.tsx

Co-authored-by: Omar López <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-12-15 22:47:31 +00:00
dependabot[bot] 7009373b11 Bump loader-utils from 1.4.0 to 1.4.2 (#6045)
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.0 to 1.4.2.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.2/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v1.4.2)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-15 15:18:40 -07:00
c2f150dbab Feat/verify phone number (#6035)
* first implementation of verifying phone number

* add UI + logic for verifying phone number flow

* check if all phone numbers are verified before submit

* add numberVerification pending

* only send SMS to verified numbers

* fix design for mobile view

* check if phone number is verified before testing action

* add back message service id check

* add TWILIO_VERIFY_SID to .env.example

* code clean up

* add TWILIO_VERIFY_SID to README.md instructions

* save new verified numbers

* fix wrongly thrown error for new verified numbers

* use false as default value for isVerificationPending paramater

* add translations

* add migration file

* code clean up

* don't throw error if phone number is not needed

* Feedback

* Shows error when Twillio isn't properly setup

* Type fixes

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-12-15 21:54:40 +00:00
688541923b Save meeting url to metadata of bookings and add it to webhook payload (#5773)
* small design fix for time unit select

* save video cal url to booking in metaData

* save video url when bookings are confirmed

* fix e2e tests

* remove not needed waitForNavigation

* add metadata to booking created webhook payload

* add migration

* fix type error

* adjust e2e tests

* add bookingMetadataSchema

* add missing change from merge

* add new line to expected test result

* update snapshot

* fix e2e tests

* Update confirm.ts

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2022-12-15 21:43:07 +00:00
20402ac26c Fixes for testing workflow action (#6005)
* additional checks for testing workflow action

* fix old variable in testAction mutaiton

* remove workflowId

* only allow team plan to test action

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
2022-12-15 20:48:22 +00:00
Omar López a54952b41a Adds tests for date overrides (#6027) 2022-12-15 13:19:35 -07:00
github-actions[bot]andCrowdin Bot 987756c4d4 New Crowdin translations by Github Action (#6022)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-12-15 21:18:33 +01:00
Leo Giovanetti 3ee294ffc5 Fixing admin wizard translations (#6038) 2022-12-15 15:03:57 +00:00
Leo Giovanetti a638708871 Baikal CalDAV error hint (#6021) 2022-12-15 13:45:54 +00:00
Peer RichelsenandHariom Balhara 3456cc5521 vimcal app (#5791)
* wip vimcal app

* added vimcal blog link

* todo: filter out external calendar apps

* Make it a non calendar type app with category and variant as calendar

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-12-15 08:44:21 +00:00
GitStart 546f0c3881 A Playwright test for "Copy Typeform Redirect URL link" action (#5716)
Co-authored-by: niteshsingh1357 <niteshsingh1357@gmail.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: RubensRafael <rubensrafael2@live.com>
Co-authored-by: José Romary Brandão Jr <joseromarybrandao@gmail.com>
Co-authored-by: csangam <coolmagnas@gmail.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: gitstart-app[bot] <57568882+gitstart-app[bot]@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
Fixes https://github.com/calcom/cal.com/issues/4570
2022-12-15 11:15:09 +05:30
7b18272d60 SMS workflow actions only for Teams (#6003)
* disable sms actions for users without team

* add isTeamsPlan query

* disable SMS action if user is not in a team

* remove console log

* disable SMS actions also in AddActionDialog

* throw error is users wants to add sms or edit  to sms action

* code clean up

* Get actions options from backend. cleanup

* Typefix

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-12-15 00:11:57 +00:00
Peer Richelsen cf6efabdbb added layout for empty month to go to the next month (#6011) 2022-12-14 22:43:21 +00:00
Udit TakkarandPeer Richelsen 92d15a00f5 fix: use cal url (#6013)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-12-14 17:43:00 +00:00
2f2b72dd54 Feature/date overrides (#5991)
* Initial incomplete (but mostly functional) push of date override functions

* Fixed date shifting on load

* Bring back minDate (automatically disable all dates before current date)

* Fix type error

* Supply working hours to render available dates

* Converted to SSR

* moving defaultValues to the backend

* Improv. as filter can be achieved within the reduce

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

* Double inversion -> single, as it is an early return

* uniq() exit - not needed anymore

* Typefixes

* It's overriding dates :D

* Fixed duplication DateOverrides in list

* Implemented changing the month

* Make dateOverrides an optional param

* Fixed test (which requires dateOverrides due to auto-typing)

* Prevent a full update on set as default from list view

* Added some extra keys to keep ts happy

* Only allow a single date override per date

* Disallow editing excludedDates to the same date

* Bring back duplicate key ?.?

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-12-14 17:30:55 +00:00
8fd5d6b5b5 Calendar Weekly Scheduler (#5653)
* storybook v2 init

* Merge config into storybook vite build

* Remove path

* Storybook config tweaks

* Added styles and settings for storybook v2, and started working on button documentation and examples.

* Badges + flex wrap on mobile

* Breadcrumbs+button+avatar

* Checkbox

* Input + moving files around

* WIP table

* WIP table grid

* Replaced imports for new components.

* Added first steps for varianttable.

* Small alignment fix.

* Custom Args Table - With scrollbar

* Adding table to components that need it + darkmode

* Add intro

* Fix types

* Remove V1 storybook and replace with V2

* Fix badge type error

* Fixed storybook dependencies

* Added cover image to storybook

* Remove vita from ts config, we dont use vite.

* Fixed button import.

* Explained postcss pseudo plugin.

* Fixed badge import.

* Add Avatar Stories

* ButtonGroup Stories

* Fixed imports

* Add checkbox stories

* Inital state plannning

* Inital state combined with passed in props

* Start of UI work

* Able to change dates?

* Add dynamic hour props

* Get grid system setup correctly

* Show events on grid

* Weird sizing issue but events placed correctly gridstart

* CAL styled calendar event component

* availability WIP ish

* Blocking days! + Block days < today

* Kinda working time line

* Rename grid stop + formatting

* Handle sorting events if required.

* Add util for getting startDate bassed on weekday

* Remove event stories for now

* Implement gridstops per hour to be dyamic

* New CSS Grid + offsetbased positoning

* Fix weird Z-Index issues on hover

* Implement blocklist again with new format

* Side by side events working - styling needs work

* New design of overlap

* Overlapping? Working :O

* Cleanup

* WIP hover state

* Werid border issue

* fix translate issue

* Kinda working with overflow

* Fix overflow

* Progressive date blocking

* Cleanup

* Fix double render of blocked list

* WIP mobile implementaiton

* Trying to fix CSS

* Extract CSS to styles.css to allow media queries

* Improve documentation - allow args to be changed in storybook

* Fix hover showing even if disabled

* WIP cols auto approach

* Merge blocking dates

* Fix zindex

* Fix hover position

* Fix Z-Index issues on hover and blocking events

* Re add onclick handler

* Fix overlapping blocking dates

* Fix scaling for datevalues columns

* Date values closer to DS

* Blocked List Tidy up

* Storybook + file tidy up

* Little tidy up

* Fix offsets

* Remove event hover

* Fix random bg-red-500

* Fix import

* FIx blocking cells appearing above start Date

* Fix truncation

* Fix border overlap

* Overlap a little nicer

* Condtional 80% sizing

* Nitpicks

* Fix today height and top breaking

* Add text left to time stamp

* Support string dates

* Add shalow to reduce re-renders

* Rename to Calendar instead of scheduler

* Fix 3 overlapping events

* Fix merge type error

* Fix destructuring

* NITS

* Move to features package

Co-authored-by: Jeroen Reumkens <hello@jeroenreumkens.nl>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-12-14 13:36:10 +00:00
Joe Au-Yeung 2078f12a85 Outlook - list all calendars (#6004) 2022-12-14 11:04:32 +00:00
github-actions[bot]andCrowdin Bot 77b8eb8a7f New Crowdin translations by Github Action (#6018)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-12-14 11:57:02 +01:00
Peer Richelsenandsean-brydon 463787b607 team banner (#5956)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-12-14 09:04:45 +01:00
alannncandPeer Richelsen 1f4e7ca695 Adding new middleware and using it for test action workflow (#6017)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-12-13 21:34:21 +00:00
Jeroen ReumkensandPeer Richelsen 9eb52bbfb2 #5613 Optimise social images by utilising next image api. (#5997)
* #5613 Optimise social images by utilising next image api.

* removed unused import

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-12-13 21:22:38 +00:00
Leo GiovanettiandPeer Richelsen 61e6f76bc6 Direct links to accept/reject a booking (#5939)
* Impl

* Added reason

* Secondary reject CTA

* Update packages/emails/src/components/Info.tsx

* Adjusting recurring info

* Not showing reason for rejecting if none given

* Feedback

* Error handling + style fixes

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-12-13 22:09:28 +01:00
github-actions[bot]andCrowdin Bot 2e6c3a6068 New Crowdin translations by Github Action (#6001)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-12-13 19:11:45 +01:00
8f48ecafec Misc fixes (#5988)
* Address reports

* Clean up

* remove activeOn in where

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2022-12-13 17:44:53 +00:00
Peer Richelsen 8849d1148c removed z-index from topbanner (#6008) 2022-12-13 23:04:25 +05:30
98a4684a2b Impl (#5993)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
2022-12-13 10:25:30 -03:00
Omar López da0bdfacae Handle membership updates in Stripe (#5992) 2022-12-13 12:43:37 +00:00
github-actions[bot]andCrowdin Bot 68c7425922 New Crowdin translations by Github Action (#5996)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-12-13 11:07:00 +01:00
github-actions[bot]andCrowdin Bot c3418cbe1f New Crowdin translations by Github Action (#5990)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-12-13 10:57:22 +01:00
Hariom Balhara 3a641e0bc1 Feature/Embed/Add Embed ui instruction config to hide event type description (#5951)
* Add embed ui instruction to hide event type description

* Add EventTypeDetails option in Embed Snippet Generator
2022-12-13 07:23:26 +00:00
Carina Wollendorfer b7fcf6ade1 fix missing space in email (#5986) 2022-12-12 12:51:27 -07:00
github-actions[bot]andCrowdin Bot 53d160e495 New Crowdin translations by Github Action (#5984)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-12-12 14:24:10 +01:00
sean-brydon df4df6846b Required radio button (#5985) 2022-12-12 14:18:04 +01:00
Carina WollendorferandCarinaWolli c1bbb9b139 Fixes SMS not sending for country codes not supporting alphanumeric sender IDs (#5969)
* don't use alphanumeric sender ID for not support country codes

* fix import

* rename file

* fix spelling mistakes

* add twilio phone number to .env file

* add SENDER_ID to env variable and create function to get sender id

* add NEXT_PUBLIC_SENDER_ID env variable

* remove not needed Cal fallback

* fix readme

* code clean up

* code clean up

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2022-12-12 12:00:15 +01:00
Hariom Balhara 56c07ec351 Hide Branding in Embed. (#5975) 2022-12-12 08:37:36 +00:00
Hariom Balhara 3080b47aec Theming shouldnt depend on plan now as everyone is pro now (#5976) 2022-12-12 08:25:59 +00:00
c1e23bfdc6 fix/duplicate-event-types (#5888)
* Add new modal for duplicate and trpc action for it

* Adding event type duplication validations

* Fix type errors and id not being removed from rest obj

* Fix test for duplicating event

* Fix test for duplicating event

* Add connect to event type workflows

* fix button for adding new events

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-12-10 15:48:26 -07:00
e7ff5508d7 New Crowdin translations by Github Action (#5966)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-12-10 22:46:06 +01:00
Nafees Nazik 5850d227ee fix: render upgrade banner above ImpersonatingBanner (#5968) 2022-12-10 21:45:35 +00:00
sean-brydon ede987ba19 Fixing branding toggle (#5952)
* Fix form state not saving

* Always show false on toggle if you dont have an active team

* Correct filter
2022-12-09 22:33:49 +00:00
zomars bc0d83164c v2.3.8
release.yml / release (push) Canceled after 0s
2022-12-09 10:43:49 -07:00
zomars 30f0b080c5 SIU 2022-12-09 10:43:19 -07:00
Leo Giovanetti ee39ea316f Fixing app store search input (#5960) 2022-12-09 14:10:54 -03:00
Hariom BalharaandPeer Richelsen fbc2708259 Hotfix/Google Meet must be globally installed (#5948)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-12-09 12:51:23 +00:00
9bb83aba7e removed license consent (#5931)
Co-authored-by: alannnc <alannnc@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2022-12-09 11:05:42 +00:00
Omar López 3a67e5617e v2.3.7
release.yml / release (push) Canceled after 0s
2022-12-08 22:44:49 -07:00
f401aa7669 Harsh/fix avail time order (#5900)
* fix: 404 page for undefined events

* remove prev code merge

* add: sorting in available durations

* fix: sort only once

* remove: console logs

Co-authored-by: Guest <guest@pop-os.localdomain>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-12-09 00:16:08 +00:00
a6e0678e59 feat: use next/script tag for inline scripts (#5938)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: alannnc <alannnc@gmail.com>
2022-12-08 23:39:15 +00:00
Omar López e832015f26 Deprecates user plan (#5942)
* Remove isMissingSeat

* Removes user plan

* Deprecates User Plan

* Updates website

* Update eventTypes.tsx
2022-12-08 16:20:24 -07:00
zomars 5e86881e15 Adds missing package 2022-12-08 16:17:04 -07:00
zomars 3b71f98460 Submodule sync 2022-12-08 15:00:29 -07:00
144d57adb2 fix: make banner responsive and cover entire app (#5934)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-12-08 18:42:55 +00:00
github-actions[bot]andCrowdin Bot e1f1c15318 New Crowdin translations by Github Action (#5932)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-12-08 19:31:16 +01:00
d88b1b2f8f remove {{appName}} in translation (#5924)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-12-08 18:28:22 +00:00
2e61c15f2c fix: check input is not undefined (#5927)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2022-12-08 18:16:04 +00:00
Hariom Balharaandkodiakhq[bot] df73473c7f Avoid redirect in cancellation and visit from Bookings list (#5928)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2022-12-08 17:32:44 +00:00
github-actions[bot]andCrowdin Bot 7167973afd New Crowdin translations by Github Action (#5920)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-12-08 15:12:24 +01:00
Omar López 3c17bc2d1a Fixes for avatar payloads (#5921) 2022-12-08 09:50:03 +00:00
a9a295dc54 Admin apps UI (#5494)
* Abstract app category navigation

* Send key schema to frontend

Co-authored-by: Omar López <zomars@users.noreply.github.com>

* Render keys for apps on admin

* Add enabled col for apps

* Save app keys to DB

* Add checks for admin role

* Abstract setup components

* Add AdminAppsList to setup wizard

* Migrate to v10 tRPC

* Default hide keys

* Display enabled apps

* Merge branch 'main' into admin-apps-ui

* Toggle calendars

* WIP

* Add params and include AppCategoryNavigation

* Refactor getEnabledApps

* Add warning for disabling apps

* Fallback to cal video when a video app is disabled

* WIP send disabled email

* Send email to all users of  event types with payment app

* Disable Stripe when app is disabled

* Disable apps in event types

* Send email to users on disabled apps

* Send email based on what app was disabled

* WIP type fix

* Disable navigation to apps list if already setup

* UI import fixes

* Waits for session data before redirecting

* Updates admin seeded password

To comply with admin password requirements

* Update yarn.lock

* Flex fixes

* Adds admin middleware

* Clean up

* WIP

* WIP

* NTS

* Add dirName to app metadata

* Upsert app if not in db

* Upsert app if not in db

* Add dirName to app metadata

* Add keys to app packages w/ keys

* Merge with main

* Toggle show keys & on enable

* Fix empty keys

* Fix lark calendar metadata

* Fix some type errors

* Fix Lark metadata & check for category when upserting

* More type fixes

* Fix types & add keys to google cal

* WIP

* WIP

* WIP

* More type fixes

* Fix type errors

* Fix type errors

* More type fixes

* More type fixes

* More type fixes

* Feedback

* Fixes default value

* Feedback

* Migrate credential invalid col default value "false"

* Upsert app on saving keys

* Clean up

* Validate app keys on frontend

* Add nonempty to app keys schemas

* Add web3

* Listlocale filter on categories / category

* Grab app metadata via category or categories

* Show empty screen if no apps are enabled

* Fix type checks

* Fix type checks

* Fix type checks

* Fix type checks

* Fix type checks

* Fix type checks

* Replace .nonempty() w/ .min(1)

* Fix type error

* Address feedback

* Added migration to keep current apps enabled

* Update apps.tsx

* Fix bug

* Add keys schema to Plausible app

* Add appKeysSchema to zod.ts template

* Update AdminAppsList.tsx

Co-authored-by: Omar López <zomars@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2022-12-07 14:47:02 -07:00
+2 21dd1f4e95 [CAL-462] Team settings dropdown menu should be open when you navigate from Teams (#5868)
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: niteshsingh1357 <niteshsingh1357@gmail.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: gitstart-app[bot] <57568882+gitstart-app[bot]@users.noreply.github.com>
Co-authored-by: gitstart_bot <gitstart_bot@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: niteshsingh1357 <niteshsingh1357@gmail.com>
Co-authored-by: Rafael Toledo <87545086+Toledodev@users.noreply.github.com>
Co-authored-by: Rafael <rafael.toledo@engenharia.ufjf.br>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Matheus Benini Ferreira <88898100+MatheusBeniniF@users.noreply.github.com>
Co-authored-by: gitstart-app[bot] <57568882+gitstart-app[bot]@users.noreply.github.com>
Co-authored-by: gitstart_bot <gitstart_bot@users.noreply.github.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Matheus Muniz <87545749+matheusmuniz03@users.noreply.github.com>
2022-12-07 21:35:41 +00:00
339 changed files with 7953 additions and 2557 deletions
+5 -3
View File
@@ -7,14 +7,13 @@
# - E-MAIL SETTINGS
# - LICENSE *************************************************************************************************
# Set this value to 'agree' to accept our license:
# LICENSE: https://github.com/calendso/calendso/blob/main/LICENSE
# https://github.com/calendso/calendso/blob/main/LICENSE
#
# Summary of terms:
# - The codebase has to stay open source, whether it was modified or not
# - You can not repackage or sell the codebase
# - Acquire a commercial license to remove these terms by visiting: cal.com/sales
NEXT_PUBLIC_LICENSE_CONSENT=''
#
# To enable enterprise-only features, fill your license key in here.
# @see https://console.cal.com
CALCOM_LICENSE_KEY=
@@ -86,6 +85,9 @@ SENDGRID_EMAIL=
TWILIO_SID=
TWILIO_TOKEN=
TWILIO_MESSAGING_SID=
TWILIO_PHONE_NUMBER=
NEXT_PUBLIC_SENDER_ID=
TWILIO_VERIFY_SID=
# This is used so we can bypass emails in auth flows for E2E testing
# Set it to "1" if you need to run E2E tests locally
+13 -9
View File
@@ -292,7 +292,7 @@ You can deploy Cal on [Railway](https://railway.app/) using the button above. Th
Currently Vercel Pro Plan is required to be able to Deploy this application with Vercel, due to limitations on the number of serverless functions on the free plan.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fcalcom%2Fcal.com&env=DATABASE_URL,NEXT_PUBLIC_WEBAPP_URL,NEXTAUTH_URL,NEXTAUTH_SECRET,CRON_API_KEY,CALENDSO_ENCRYPTION_KEY,NEXT_PUBLIC_LICENSE_CONSENT&envDescription=See%20all%20available%20env%20vars&envLink=https%3A%2F%2Fgithub.com%2Fcalcom%2Fcal.com%2Fblob%2Fmain%2F.env.example&project-name=cal&repo-name=cal.com&build-command=cd%20../..%20%26%26%20yarn%20build&root-directory=apps%2Fweb%2F)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fcalcom%2Fcal.com&env=DATABASE_URL,NEXT_PUBLIC_WEBAPP_URL,NEXTAUTH_URL,NEXTAUTH_SECRET,CRON_API_KEY,CALENDSO_ENCRYPTION_KEY&envDescription=See%20all%20available%20env%20vars&envLink=https%3A%2F%2Fgithub.com%2Fcalcom%2Fcal.com%2Fblob%2Fmain%2F.env.example&project-name=cal&repo-name=cal.com&build-command=cd%20../..%20%26%26%20yarn%20build&root-directory=apps%2Fweb%2F)
<!-- ROADMAP -->
@@ -419,14 +419,18 @@ following
2. Click ‘Get a Twilio phone number’
3. Copy Account SID to your .env file into the TWILIO_SID field
4. Copy Auth Token to your .env file into the TWILIO_TOKEN field
5. Create a messaging service (Develop -> Messaging -> Services)
6. Choose any name for the messaging service
7. Click 'Add Senders'
8. Choose phone number as sender type
9. Add the listed phone number
10. Leave all other fields as they are
11. Complete setup and click ‘View my new Messaging Service’
12. Copy Messaging Service SID to your .env file into the TWILIO_MESSAGING_SID field
5. Copy your Twilio phone number to your .env file into the TWILIO_PHONE_NUMBER field
6. Add your own sender id to the .env file into the NEXT_PUBLIC_SENDER_ID field (fallback is Cal)
7. Create a messaging service (Develop -> Messaging -> Services)
8. Choose any name for the messaging service
9. Click 'Add Senders'
10. Choose phone number as sender type
11. Add the listed phone number
12. Leave all other fields as they are
13. Complete setup and click ‘View my new Messaging Service’
14. Copy Messaging Service SID to your .env file into the TWILIO_MESSAGING_SID field
15. Create a verify service
16. Copy Verify Service SID to your .env file into the TWILIO_VERIFY_SID field
<!-- LICENSE -->
-5
View File
@@ -33,11 +33,6 @@
"generator": "secret",
"required": "true"
},
"NEXT_PUBLIC_LICENSE_CONSENT": {
"description": "License consent",
"value": "true",
"required": "true"
},
"CRON_API_KEY": {
"description": "ApiKey for cronjobs",
"value": ""
+16
View File
@@ -4,6 +4,7 @@ module.exports = {
stories: [
"../intro.stories.mdx",
"../../../packages/ui/components/**/*.stories.mdx",
"../../../packages/features/**/*.stories.mdx",
"../../../packages/ui/components/**/*.stories.@(js|jsx|ts|tsx)",
],
addons: [
@@ -50,6 +51,21 @@ module.exports = {
vm: false,
zlib: false,
};
config.module.rules.push({
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
modules: true, // Enable modules to help you using className
},
},
],
include: path.resolve(__dirname, "../src"),
});
return config;
},
};
+5 -1
View File
@@ -15,7 +15,11 @@ export const parameters = {
},
};
addDecorator((storyFn) => <I18nextProvider i18n={i18n}>{storyFn()}</I18nextProvider>);
addDecorator((storyFn) => (
<I18nextProvider i18n={i18n}>
<div style={{ margin: "2rem" }}>{storyFn()}</div>
</I18nextProvider>
));
window.getEmbedNamespace = () => {
const url = new URL(document.URL);
+1 -1
View File
@@ -2,4 +2,4 @@
@tailwind components;
@tailwind utilities;
@import "../../../packages/ui/styles/shared-globals.css"
@import "../../../packages/ui/styles/shared-globals.css"
+3 -1
View File
@@ -1,3 +1,5 @@
@import url("../../../packages/features/calendars/weeklyview/styles/styles.css");
.sbdocs {
font-family: 'Inter var' !important;
padding: 0!important;
@@ -36,7 +38,7 @@
/** Docs table **/
.custom-args-wrapper{
max-height: 600px;
max-height: 400px;
overflow-y: scroll;
overflow-x: hidden;
margin-bottom: 1rem;
+24 -4
View File
@@ -44,6 +44,7 @@ type PreviewState = {
palette: {
brandColor: string;
};
hideEventTypeDetails: boolean;
};
const queryParamsForDialog = ["embedType", "embedTabName", "embedUrl"];
@@ -125,10 +126,12 @@ const getEmbedUIInstructionString = ({
apiName,
theme,
brandColor,
hideEventTypeDetails,
}: {
apiName: string;
theme?: string;
brandColor: string;
hideEventTypeDetails: boolean;
}) => {
theme = theme !== "auto" ? theme : undefined;
return getInstructionString({
@@ -141,6 +144,7 @@ const getEmbedUIInstructionString = ({
brandColor,
},
},
hideEventTypeDetails: hideEventTypeDetails,
},
});
};
@@ -253,18 +257,21 @@ const getEmbedTypeSpecificString = ({
apiName: string;
theme: PreviewState["theme"];
brandColor: string;
hideEventTypeDetails: boolean;
};
if (embedFramework === "react") {
uiInstructionStringArg = {
apiName: "cal",
theme: previewState.theme,
brandColor: previewState.palette.brandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails,
};
} else {
uiInstructionStringArg = {
apiName: "Cal",
theme: previewState.theme,
brandColor: previewState.palette.brandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails,
};
}
if (!frameworkCodes[embedType]) {
@@ -689,6 +696,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
theme: Theme.auto,
floatingPopup: {},
elementClick: {},
hideEventTypeDetails: false,
palette: {
brandColor: "#000000",
},
@@ -753,6 +761,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
name: "ui",
arg: {
theme: previewState.theme,
hideEventTypeDetails: previewState.hideEventTypeDetails,
styles: {
branding: {
...previewState.palette,
@@ -811,7 +820,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
{embed.title}
</h3>
<hr className={classNames("mt-4", embedType === "element-click" ? "hidden" : "")} />
<div className="max-h-97 flex flex-col overflow-y-auto">
<div className="flex flex-col overflow-y-auto">
<div className={classNames("mt-4 font-medium", embedType === "element-click" ? "hidden" : "")}>
<Collapsible
open={isEmbedCustomizationOpen}
@@ -838,7 +847,6 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
<div className="text-sm">Embed Window Sizing</div>
<div className="justify-left flex items-center">
<TextField
name="width"
labelProps={{ className: "hidden" }}
required
value={previewState.inline.width}
@@ -860,7 +868,6 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
<span className="p-2">×</span>
<TextField
labelProps={{ className: "hidden" }}
name="height"
value={previewState.inline.height}
required
onChange={(e) => {
@@ -888,7 +895,6 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
<div className="mb-2 text-sm">Button Text</div>
{/* Default Values should come from preview iframe */}
<TextField
name="buttonText"
labelProps={{ className: "hidden" }}
onChange={(e) => {
setPreviewState((previewState) => {
@@ -1004,6 +1010,20 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</CollapsibleTrigger>
<CollapsibleContent>
<div className="mt-6 text-sm">
<div className="mb-4 flex items-center justify-start space-x-2">
<Switch
checked={previewState.hideEventTypeDetails}
onCheckedChange={(checked) => {
setPreviewState((previewState) => {
return {
...previewState,
hideEventTypeDetails: checked,
};
});
}}
/>
<div className="text-sm">{t("hide_eventtype_details")}</div>
</div>
<Label className="">
<div className="mb-2">Theme</div>
<Select
+3 -1
View File
@@ -66,7 +66,9 @@ const Component = ({
}
);
const allowedMultipleInstalls = categories.indexOf("calendar") > -1;
// variant not other allows, an app to be shown in calendar category without requiring an actual calendar connection e.g. vimcal
// Such apps, can only be installed once.
const allowedMultipleInstalls = categories.indexOf("calendar") > -1 && variant !== "other";
return (
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
@@ -44,7 +44,7 @@ function IntegrationListItem(props: {
expanded={!!props.children}
className={classNames(
props.separate ? "rounded-md" : "first:rounded-t-md last:rounded-b-md",
"my-0 flex-col border transition-colors duration-500 ",
"my-0 flex-col border transition-colors duration-500",
highlight ? "bg-yellow-100" : ""
)}>
<div className={classNames("flex w-full flex-1 items-center space-x-2 p-4 rtl:space-x-reverse")}>
@@ -13,6 +13,7 @@ import dayjs, { Dayjs } from "@calcom/dayjs";
import {
useEmbedNonStylesConfig,
useEmbedStyles,
useEmbedUiConfig,
useIsBackgroundTransparent,
useIsEmbed,
} from "@calcom/embed-core/embed-iframe";
@@ -29,10 +30,7 @@ import { trpc } from "@calcom/trpc/react";
import { Icon, DatePicker } from "@calcom/ui";
import { timeZone as localStorageTimeZone } from "@lib/clock";
// import { timeZone } from "@lib/clock";
import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally";
import useRouterQuery from "@lib/hooks/useRouterQuery";
import { isBrandingHidden } from "@lib/isBrandingHidden";
import Gates, { Gate, GateState } from "@components/Gates";
import AvailableTimes from "@components/booking/AvailableTimes";
@@ -256,6 +254,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
useTheme(profile.theme);
const { t } = useLocale();
const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker");
//TODO: Plan to remove shouldAlignCentrallyInEmbed config
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left";
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
const isBackgroundTransparent = useIsBackgroundTransparent();
@@ -280,9 +279,6 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
setTimeZone(localStorageTimeZone() || dayjs.tz.guess());
}, []);
// TODO: Improve this;
useExposePlanGlobally(eventType.users.length === 1 ? eventType.users[0].plan : "PRO");
const [recurringEventCount, setRecurringEventCount] = useState(eventType.recurringEvent?.count);
const telemetry = useTelemetry();
@@ -295,7 +291,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
);
}
}, [telemetry]);
const embedUiConfig = useEmbedUiConfig();
// get dynamic user list here
const userList = eventType.users ? eventType.users.map((user) => user.username).filter(notEmpty) : [];
@@ -308,6 +304,8 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
const rawSlug = profile.slug ? profile.slug.split("/") : [];
if (rawSlug.length > 1) rawSlug.pop(); //team events have team name as slug, but user events have [user]/[type] as slug.
const showEventTypeDetails = (isEmbed && !embedUiConfig.hideEventTypeDetails) || !isEmbed;
// Define conditional gates here
const gates = [
// Rainbow gate is only added if the event has both a `blockchainId` and a `smartContractAddress`
@@ -341,7 +339,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
<div>
<main
className={classNames(
"flex-col md:mx-4 lg:flex",
"flex flex-col md:mx-4",
shouldAlignCentrally ? "items-center" : "items-start",
!isEmbed && classNames("mx-auto my-0 ease-in-out md:my-24")
)}>
@@ -356,54 +354,55 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
isEmbed && "mx-auto"
)}>
<div className="overflow-hidden md:flex">
<div
className={classNames(
"sm:dark:border-darkgray-200 flex flex-col border-gray-200 p-5 sm:border-r",
"min-w-full md:w-[230px] md:min-w-[230px]",
recurringEventCount && "xl:w-[380px] xl:min-w-[380px]"
)}>
<BookingDescription profile={profile} eventType={eventType} rescheduleUid={rescheduleUid}>
{!rescheduleUid && eventType.recurringEvent && (
<div className="flex items-start text-sm font-medium">
<Icon.FiRefreshCcw className="float-left mr-[10px] mt-[7px] ml-[2px] inline-block h-4 w-4 " />
<div>
<p className="mb-1 -ml-2 inline px-2 py-1">
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
</p>
<input
type="number"
min="1"
max={eventType.recurringEvent.count}
className="w-15 dark:bg-darkgray-200 h-7 rounded-sm border-gray-300 bg-white text-sm font-medium [appearance:textfield] ltr:mr-2 rtl:ml-2 dark:border-gray-500"
defaultValue={eventType.recurringEvent.count}
onChange={(event) => {
setRecurringEventCount(parseInt(event?.target.value));
}}
/>
<p className="inline">
{t("occurrence", {
count: recurringEventCount,
})}
</p>
{showEventTypeDetails && (
<div
className={classNames(
"sm:dark:border-darkgray-200 flex flex-col border-gray-200 p-5 sm:border-r",
"min-w-full md:w-[230px] md:min-w-[230px]",
recurringEventCount && "xl:w-[380px] xl:min-w-[380px]"
)}>
<BookingDescription profile={profile} eventType={eventType} rescheduleUid={rescheduleUid}>
{!rescheduleUid && eventType.recurringEvent && (
<div className="flex items-start text-sm font-medium">
<Icon.FiRefreshCcw className="float-left mr-[10px] mt-[7px] ml-[2px] inline-block h-4 w-4 " />
<div>
<p className="mb-1 -ml-2 inline px-2 py-1">
{getRecurringFreq({ t, recurringEvent: eventType.recurringEvent })}
</p>
<input
type="number"
min="1"
max={eventType.recurringEvent.count}
className="w-15 dark:bg-darkgray-200 h-7 rounded-sm border-gray-300 bg-white text-sm font-medium [appearance:textfield] ltr:mr-2 rtl:ml-2 dark:border-gray-500"
defaultValue={eventType.recurringEvent.count}
onChange={(event) => {
setRecurringEventCount(parseInt(event?.target.value));
}}
/>
<p className="inline">
{t("occurrence", {
count: recurringEventCount,
})}
</p>
</div>
</div>
</div>
)}
{stripeAppData.price > 0 && (
<p className="-ml-2 px-2 text-sm font-medium">
<Icon.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en">
<FormattedNumber
value={stripeAppData.price / 100.0}
style="currency"
currency={stripeAppData.currency.toUpperCase()}
/>
</IntlProvider>
</p>
)}
{timezoneDropdown}
</BookingDescription>
)}
{stripeAppData.price > 0 && (
<p className="-ml-2 px-2 text-sm font-medium">
<Icon.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en">
<FormattedNumber
value={stripeAppData.price / 100.0}
style="currency"
currency={stripeAppData.currency.toUpperCase()}
/>
</IntlProvider>
</p>
)}
{timezoneDropdown}
</BookingDescription>
{/* Temporarily disabled - booking?.startTime && rescheduleUid && (
{/* Temporarily disabled - booking?.startTime && rescheduleUid && (
<div>
<p
className="mt-4 mb-3 text-gray-600 dark:text-darkgray-600"
@@ -416,7 +415,8 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
</p>
</div>
)*/}
</div>
</div>
)}
<SlotPicker
weekStart={
typeof profile.weekStart === "string"
@@ -442,7 +442,8 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
/>
</div>
</div>
{(!restProps.isBrandingHidden || isEmbed) && <PoweredByCal />}
{/* FIXME: We don't show branding in Embed yet because we need to place branding on top of the main content. Keeping it outside the main content would have visibility issues because outside main content background is transparent */}
{!restProps.isBrandingHidden && !isEmbed && <PoweredByCal />}
</div>
</main>
</div>
+135 -107
View File
@@ -26,6 +26,7 @@ import { LocationObject, LocationType } from "@calcom/core/location";
import dayjs from "@calcom/dayjs";
import {
useEmbedNonStylesConfig,
useEmbedUiConfig,
useIsBackgroundTransparent,
useIsEmbed,
} from "@calcom/embed-core/embed-iframe";
@@ -89,6 +90,7 @@ const BookingPage = ({
const { t, i18n } = useLocale();
const { duration: queryDuration } = useRouterQuery("duration");
const isEmbed = useIsEmbed(restProps.isEmbed);
const embedUiConfig = useEmbedUiConfig();
const shouldAlignCentrallyInEmbed = useEmbedNonStylesConfig("align") !== "left";
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
const router = useRouter();
@@ -315,6 +317,22 @@ const BookingPage = ({
{}
);
if (eventType.customInputs.length > 0) {
// find all required custom inputs and ensure they are filled out in the booking form
const requiredCustomInputs = eventType.customInputs.filter((input) => input.required);
const missingRequiredCustomInputs = requiredCustomInputs.filter(
(input) => !booking?.customInputs?.[input.id]
);
if (missingRequiredCustomInputs.length > 0) {
missingRequiredCustomInputs.forEach((input) => {
bookingForm.setError(`customInputs.${input.id}`, {
type: "required",
});
});
return;
}
}
if (recurringDates.length) {
// Identify set of bookings to one intance of recurring event to support batch changes
const recurringEventId = uuidv4();
@@ -406,6 +424,7 @@ const BookingPage = ({
}
});
}
const showEventTypeDetails = (isEmbed && !embedUiConfig.hideEventTypeDetails) || !isEmbed;
const rainbowAppData = getEventTypeAppData(eventType, "rainbow") || {};
// Define conditional gates here
@@ -439,7 +458,7 @@ const BookingPage = ({
className={classNames(
shouldAlignCentrally ? "mx-auto" : "",
isEmbed ? "" : "sm:my-24",
"my-0 max-w-3xl "
"my-0 max-w-3xl"
)}>
<div
className={classNames(
@@ -448,95 +467,97 @@ const BookingPage = ({
"dark:border-darkgray-300 rounded-md sm:border"
)}>
<div className="sm:flex">
<div className="sm:dark:border-darkgray-300 dark:text-darkgray-600 flex flex-col px-6 pt-6 pb-0 text-gray-600 sm:w-1/2 sm:border-r sm:pb-6">
<BookingDescription isBookingPage profile={profile} eventType={eventType}>
{stripeAppData.price > 0 && (
<p className="text-bookinglight -ml-2 px-2 text-sm ">
<Icon.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en">
<FormattedNumber
value={stripeAppData.price / 100.0}
style="currency"
currency={stripeAppData.currency.toUpperCase()}
/>
</IntlProvider>
</p>
)}
{!rescheduleUid && eventType.recurringEvent?.freq && recurringEventCount && (
<div className="items-start text-sm font-medium text-gray-600 dark:text-white">
<Icon.FiRefreshCw className="mr-[10px] ml-[2px] inline-block h-4 w-4" />
<p className="-ml-2 inline-block items-center px-2">
{getEveryFreqFor({
t,
recurringEvent: eventType.recurringEvent,
recurringCount: recurringEventCount,
})}
{showEventTypeDetails && (
<div className="sm:dark:border-darkgray-300 dark:text-darkgray-600 flex flex-col px-6 pt-6 pb-0 text-gray-600 sm:w-1/2 sm:border-r sm:pb-6">
<BookingDescription isBookingPage profile={profile} eventType={eventType}>
{stripeAppData.price > 0 && (
<p className="text-bookinglight -ml-2 px-2 text-sm ">
<Icon.FiCreditCard className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
<IntlProvider locale="en">
<FormattedNumber
value={stripeAppData.price / 100.0}
style="currency"
currency={stripeAppData.currency.toUpperCase()}
/>
</IntlProvider>
</p>
</div>
)}
<div className="text-bookinghighlight flex items-start text-sm">
<Icon.FiCalendar className="mr-[10px] ml-[2px] mt-[2px] inline-block h-4 w-4" />
<div className="text-sm font-medium">
{(rescheduleUid || !eventType.recurringEvent?.freq) && `${parseDate(date, i18n)}`}
{!rescheduleUid &&
eventType.recurringEvent?.freq &&
recurringStrings.slice(0, 5).map((timeFormatted, key) => {
return <p key={key}>{timeFormatted}</p>;
})}
{!rescheduleUid && eventType.recurringEvent?.freq && recurringStrings.length > 5 && (
<div className="flex">
<Tooltip
content={recurringStrings.slice(5).map((timeFormatted, key) => (
<p key={key}>{timeFormatted}</p>
))}>
<p className="dark:text-darkgray-600 text-sm">
+ {t("plus_more", { count: recurringStrings.length - 5 })}
</p>
</Tooltip>
</div>
)}
</div>
</div>
{booking?.startTime && rescheduleUid && (
<div>
<p className="mt-8 mb-2 text-sm " data-testid="former_time_p">
{t("former_time")}
</p>
<p className="line-through ">
<Icon.FiCalendar className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
</p>
</div>
)}
{!!eventType.seatsPerTimeSlot && (
)}
{!rescheduleUid && eventType.recurringEvent?.freq && recurringEventCount && (
<div className="items-start text-sm font-medium text-gray-600 dark:text-white">
<Icon.FiRefreshCw className="mr-[10px] ml-[2px] inline-block h-4 w-4" />
<p className="-ml-2 inline-block items-center px-2">
{getEveryFreqFor({
t,
recurringEvent: eventType.recurringEvent,
recurringCount: recurringEventCount,
})}
</p>
</div>
)}
<div className="text-bookinghighlight flex items-start text-sm">
<Icon.FiUser
className={`mr-[10px] ml-[2px] mt-[2px] inline-block h-4 w-4 ${
booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.5
? "text-rose-600"
: booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.33
? "text-yellow-500"
: "text-bookinghighlight"
}`}
/>
<p
className={`${
booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.5
? "text-rose-600"
: booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.33
? "text-yellow-500"
: "text-bookinghighlight"
} mb-2 font-medium`}>
{booking
? eventType.seatsPerTimeSlot - booking.attendees.length
: eventType.seatsPerTimeSlot}{" "}
/ {eventType.seatsPerTimeSlot} {t("seats_available")}
</p>
<Icon.FiCalendar className="mr-[10px] ml-[2px] mt-[2px] inline-block h-4 w-4" />
<div className="text-sm font-medium">
{(rescheduleUid || !eventType.recurringEvent?.freq) && `${parseDate(date, i18n)}`}
{!rescheduleUid &&
eventType.recurringEvent?.freq &&
recurringStrings.slice(0, 5).map((timeFormatted, key) => {
return <p key={key}>{timeFormatted}</p>;
})}
{!rescheduleUid && eventType.recurringEvent?.freq && recurringStrings.length > 5 && (
<div className="flex">
<Tooltip
content={recurringStrings.slice(5).map((timeFormatted, key) => (
<p key={key}>{timeFormatted}</p>
))}>
<p className="dark:text-darkgray-600 text-sm">
+ {t("plus_more", { count: recurringStrings.length - 5 })}
</p>
</Tooltip>
</div>
)}
</div>
</div>
)}
</BookingDescription>
</div>
<div className="p-6 sm:w-1/2">
{booking?.startTime && rescheduleUid && (
<div>
<p className="mt-8 mb-2 text-sm " data-testid="former_time_p">
{t("former_time")}
</p>
<p className="line-through ">
<Icon.FiCalendar className="mr-[10px] ml-[2px] -mt-1 inline-block h-4 w-4" />
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
</p>
</div>
)}
{!!eventType.seatsPerTimeSlot && (
<div className="text-bookinghighlight flex items-start text-sm">
<Icon.FiUser
className={`mr-[10px] ml-[2px] mt-[2px] inline-block h-4 w-4 ${
booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.5
? "text-rose-600"
: booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.33
? "text-yellow-500"
: "text-bookinghighlight"
}`}
/>
<p
className={`${
booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.5
? "text-rose-600"
: booking && booking.attendees.length / eventType.seatsPerTimeSlot >= 0.33
? "text-yellow-500"
: "text-bookinghighlight"
} mb-2 font-medium`}>
{booking
? eventType.seatsPerTimeSlot - booking.attendees.length
: eventType.seatsPerTimeSlot}{" "}
/ {eventType.seatsPerTimeSlot} {t("seats_available")}
</p>
</div>
)}
</BookingDescription>
</div>
)}
<div className={classNames("p-6", showEventTypeDetails ? "sm:w-1/2" : "w-full")}>
<Form form={bookingForm} handleSubmit={bookEvent}>
<div className="mb-4">
<label htmlFor="name" className="block text-sm font-medium text-gray-700 dark:text-white">
@@ -679,8 +700,11 @@ const BookingPage = ({
{input.type !== EventTypeCustomInputType.BOOL && (
<label
htmlFor={"custom_" + input.id}
className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
{input.label}
className={classNames(
"mb-1 block text-sm font-medium text-gray-700 transition-colors dark:text-white",
bookingForm.formState.errors.customInputs?.[input.id] && "!text-red-700"
)}>
{input.label} {input.required && <span className="text-red-700">*</span>}
</label>
)}
{input.type === EventTypeCustomInputType.TEXTLONG && (
@@ -745,24 +769,28 @@ const BookingPage = ({
</div>
)}
{input.options && input.type === EventTypeCustomInputType.RADIO && (
<div className="">
<div className="flex">
<Group
onValueChange={(e) => {
bookingForm.setValue(`customInputs.${input.id}`, e);
}}>
<>
{input.options.map((option, i) => (
<RadioField
label={option.label}
key={`option.${i}.radio`}
value={option.label}
id={`option.${i}.radio`}
/>
))}
</>
</Group>
</div>
<div className="flex">
<Group
required={input.required}
onValueChange={(e) => {
bookingForm.setValue(`customInputs.${input.id}`, e);
}}>
<>
{input.options.map((option, i) => (
<RadioField
label={option.label}
key={`option.${i}.radio`}
value={option.label}
id={`option.${i}.radio`}
/>
))}
</>
{bookingForm.formState.errors.customInputs?.[input.id] && (
<div className="mt-px flex items-center text-xs text-red-700 ">
<p>{t("required")}</p>
</div>
)}
</Group>
</div>
)}
</div>
+12 -11
View File
@@ -250,23 +250,24 @@ export const EventSetupTab = (
isSearchable={false}
className="h-auto !min-h-[36px] text-sm"
options={multipleDurationOptions}
value={selectedMultipleDuration}
onChange={(options) => {
const values = options
.map((opt) => opt.value)
.sort(function (a, b) {
return a - b;
});
let newOptions = [...options];
newOptions = newOptions.sort((a, b) => {
return a?.value - b?.value;
});
const values = newOptions.map((opt) => opt.value);
setMultipleDuration(values);
setSelectedMultipleDuration(options);
if (!options.find((opt) => opt.value === defaultDuration?.value)) {
if (options.length > 0) {
setDefaultDuration(options[0]);
setSelectedMultipleDuration(newOptions);
if (!newOptions.find((opt) => opt.value === defaultDuration?.value)) {
if (newOptions.length > 0) {
setDefaultDuration(newOptions[0]);
} else {
setDefaultDuration(null);
}
}
if (options.length === 1 && defaultDuration === null) {
setDefaultDuration(options[0]);
if (newOptions.length === 1 && defaultDuration === null) {
setDefaultDuration(newOptions[0]);
}
formMethods.setValue("metadata.multipleDuration", values);
}}
@@ -35,23 +35,7 @@ interface ICustomUsernameProps {
setInputUsernameValue: (value: string) => void;
onSuccessMutation?: () => void;
onErrorMutation?: (error: TRPCClientErrorLike<AppRouter>) => void;
user: Pick<
User,
| "username"
| "name"
| "email"
| "bio"
| "avatar"
| "timeZone"
| "weekStart"
| "hideBranding"
| "theme"
| "plan"
| "brandColor"
| "darkBrandColor"
| "timeFormat"
| "metadata"
>;
user: Pick<User, "username" | "metadata">;
readonly?: boolean;
}
@@ -14,23 +14,7 @@ export const UsernameAvailability = IS_SELF_HOSTED ? UsernameTextfield : Premium
interface UsernameAvailabilityFieldProps {
onSuccessMutation?: () => void;
onErrorMutation?: (error: TRPCClientErrorLike<AppRouter>) => void;
user: Pick<
User,
| "username"
| "name"
| "email"
| "bio"
| "avatar"
| "timeZone"
| "weekStart"
| "hideBranding"
| "theme"
| "plan"
| "brandColor"
| "darkBrandColor"
| "timeFormat"
| "metadata"
>;
user: Pick<User, "username" | "metadata">;
}
export const UsernameAvailabilityField = ({
onSuccessMutation,
@@ -1,12 +0,0 @@
import { UserPlan } from "@prisma/client";
/**
* TODO: It should be exposed at a single place.
*/
export function useExposePlanGlobally(plan: UserPlan) {
// Don't wait for component to mount. Do it ASAP. Delaying it would delay UI Configuration.
if (typeof window !== "undefined") {
// This variable is used by embed-iframe to determine if we should allow UI configuration
window.CalComPlan = plan;
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "2.3.6",
"version": "2.4.0",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
@@ -61,7 +61,7 @@
"@stripe/stripe-js": "^1.35.0",
"@tanstack/react-query": "^4.3.9",
"@vercel/edge-functions-ui": "^0.2.1",
"@vercel/og": "^0.0.19",
"@vercel/og": "^0.0.21",
"accept-language-parser": "^1.5.0",
"async": "^3.2.4",
"bcryptjs": "^2.4.3",
@@ -121,7 +121,7 @@
"tailwindcss-radix": "^2.6.0",
"uuid": "^8.3.2",
"web3": "^1.7.5",
"zod": "^3.19.1"
"zod": "^3.20.2"
},
"devDependencies": {
"@babel/core": "^7.19.6",
+29 -13
View File
@@ -1,32 +1,48 @@
import Head from "next/head";
import { useRouter } from "next/router";
import { APP_NAME, WEBSITE_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui";
import { Button, Icon, showToast } from "@calcom/ui";
export default function Error500() {
const { t } = useLocale();
const router = useRouter();
return (
<div className="flex h-screen">
<div className="flex h-screen bg-gray-100">
<Head>
<title>Something unexpected occurred | {APP_NAME}</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<div className="m-auto text-center">
<h1 className="font-cal text-[250px] text-gray-900">
5
{
// eslint-disable-next-line @next/next/no-img-element
<img src="/error.svg" className="-mt-10 inline w-60" alt="0" />
}
0
</h1>
<h2 className="mb-2 -mt-16 text-3xl text-gray-600">It&apos;s not you, it&apos;s us.</h2>
<p className="mb-4 max-w-2xl text-gray-500">
<div className="rtl: m-auto rounded-md bg-white p-10 text-right ltr:text-left">
<h1 className="font-cal text-6xl text-black">500</h1>
<h2 className="mt-6 text-2xl font-medium text-black">It&apos;s not you, it&apos;s us.</h2>
<p className="mt-4 mb-6 max-w-2xl text-sm text-gray-600">
Something went wrong on our end. Get in touch with our support team, and we’ll get it fixed right
away for you.
</p>
{router.query.error && (
<div className="mb-8 flex flex-col">
<p className="mb-4 max-w-2xl text-sm text-gray-600">
Please provide the following text when contacting support to better help you:
</p>
<pre className="w-full max-w-2xl whitespace-normal break-words rounded-md bg-gray-200 p-4 text-gray-900">
{router.query.error}
<br />
<Button
color="secondary"
className="mt-2 border-0 font-sans font-normal hover:bg-gray-300"
StartIcon={Icon.FiCopy}
onClick={() => {
navigator.clipboard.writeText(router.query.error as string);
showToast("Link copied!", "success");
}}>
{t("copy")}
</Button>
</pre>
</div>
)}
<Button href={`${WEBSITE_URL}/support`}>{t("contact_support")}</Button>
<Button color="secondary" href="javascript:history.back()" className="ml-2">
Go back
-3
View File
@@ -28,7 +28,6 @@ import { baseEventTypeSelect } from "@calcom/prisma/selects";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { BadgeCheckIcon, EventTypeDescriptionLazy as EventTypeDescription, Icon } from "@calcom/ui";
import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import { EmbedProps } from "@lib/withEmbedSsr";
@@ -92,7 +91,6 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed;
const query = { ...router.query };
delete query.user; // So it doesn't display in the Link (and make tests fail)
useExposePlanGlobally("PRO");
const nameOrUsername = user.name || user.username || "";
const telemetry = useTelemetry();
@@ -272,7 +270,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
darkBrandColor: true,
avatar: true,
theme: true,
plan: true,
away: true,
verified: true,
allowDynamicBooking: true,
+5 -8
View File
@@ -4,7 +4,7 @@ import { JSONObject } from "superjson/dist/types";
import { z } from "zod";
import { privacyFilteredLocations, LocationObject } from "@calcom/app-store/locations";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
@@ -72,7 +72,6 @@ async function getUserPageProps(context: GetStaticPropsContext) {
id: true,
username: true,
away: true,
plan: true,
name: true,
hideBranding: true,
timeZone: true,
@@ -130,7 +129,7 @@ async function getUserPageProps(context: GetStaticPropsContext) {
if (!user || !user.eventTypes.length) return { notFound: true };
const [eventType]: (typeof user.eventTypes[number] & {
users: Pick<User, "name" | "username" | "hideBranding" | "plan" | "timeZone">[];
users: Pick<User, "name" | "username" | "hideBranding" | "timeZone">[];
})[] = [
{
...user.eventTypes[0],
@@ -139,7 +138,6 @@ async function getUserPageProps(context: GetStaticPropsContext) {
name: user.name,
username: user.username,
hideBranding: user.hideBranding,
plan: user.plan,
timeZone: user.timeZone,
},
],
@@ -159,9 +157,10 @@ async function getUserPageProps(context: GetStaticPropsContext) {
// Check if the user you are logging into has any active teams
const hasActiveTeam =
user.teams.filter((m) => {
if (!IS_TEAM_BILLING_ENABLED) return true;
const metadata = teamMetadataSchema.safeParse(m.team.metadata);
if (metadata.success && metadata.data?.subscriptionId) return false;
return true;
if (metadata.success && metadata.data?.subscriptionId) return true;
return false;
}).length > 0;
return {
@@ -226,7 +225,6 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
},
},
theme: true,
plan: true,
},
});
@@ -246,7 +244,6 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
name: user.name,
username: user.username,
hideBranding: user.hideBranding,
plan: user.plan,
timeZone: user.timeZone,
};
}),
+5 -1
View File
@@ -1,5 +1,6 @@
import { DefaultSeo } from "next-seo";
import Head from "next/head";
import Script from "next/script";
import superjson from "superjson";
import "@calcom/embed-core/src/embed-iframe";
@@ -36,8 +37,11 @@ function MyApp(props: AppProps) {
<AppProviders {...props}>
<DefaultSeo {...seoConfig.defaultNextSeo} />
<I18nLanguageHandler />
<Script
id="page-status"
dangerouslySetInnerHTML={{ __html: `window.CalComPageStatus = '${pageStatus}'` }}
/>
<Head>
<script dangerouslySetInnerHTML={{ __html: `window.CalComPageStatus = '${pageStatus}'` }} />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
</Head>
{getLayout(
+4 -1
View File
@@ -1,4 +1,5 @@
import Document, { DocumentContext, Head, Html, Main, NextScript, DocumentProps } from "next/document";
import Script from "next/script";
type Props = Record<string, unknown> & DocumentProps;
@@ -69,7 +70,9 @@ class MyDocument extends Document<Props> {
{/* Define isEmbed here so that it can be shared with App(embed-iframe) as well as the following code to change background and hide body
Persist the embed mode in sessionStorage because query param might get lost during browsing.
*/}
<script
<Script
id="run-before-client"
strategy="beforeInteractive"
dangerouslySetInnerHTML={{
__html: `(${toRunBeforeReactOnClient.toString()})()`,
}}
+4 -3
View File
@@ -14,7 +14,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, WEBAPP_URL } from "@calcom/lib/constants";
import { APP_NAME, IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
import { symmetricDecrypt } from "@calcom/lib/crypto";
import { defaultCookies } from "@calcom/lib/default-cookies";
import rateLimit from "@calcom/lib/rateLimit";
@@ -125,9 +125,10 @@ const providers: Provider[] = [
// Check if the user you are logging into has any active teams
const hasActiveTeams =
user.teams.filter((m) => {
if (!IS_TEAM_BILLING_ENABLED) return true;
const metadata = teamMetadataSchema.safeParse(m.team.metadata);
if (metadata.success && metadata.data?.subscriptionId) return false;
return true;
if (metadata.success && metadata.data?.subscriptionId) return true;
return false;
}).length > 0;
// authentication success- but does it meet the minimum password requirements?
-1
View File
@@ -46,7 +46,6 @@ async function handler(req: NextApiRequest) {
name: parsedQuery.data.full_name,
emailVerified: new Date(),
locale: "en", // TODO: We should revisit this
plan: "PRO",
identityProvider: IdentityProvider.CAL,
},
});
+1 -33
View File
@@ -1,10 +1,5 @@
import type { NextApiRequest, NextApiResponse } from "next";
import dayjs from "@calcom/dayjs";
import prisma from "@calcom/prisma";
import { TRIAL_LIMIT_DAYS } from "@lib/config/constants";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const apiKey = req.headers.authorization || req.query.apiKey;
if (process.env.CRON_API_KEY !== apiKey) {
@@ -18,35 +13,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
/**
* TODO:
* We should add and extra check for non-paying customers in Stripe so we can
* downgrade them here.
* Remove this endpoint
*/
await prisma.user.updateMany({
data: {
plan: "FREE",
},
where: {
plan: "TRIAL",
OR: [
/**
* If the user doesn't have a trial end date,
* use the default 14 day trial from creation.
*/
{
createdDate: {
lt: dayjs().subtract(TRIAL_LIMIT_DAYS, "day").toDate(),
},
trialEndsAt: null,
},
/** If it does, then honor the trial end date. */
{
trialEndsAt: {
lt: dayjs().toDate(),
},
},
],
},
});
res.json({ ok: true });
}
+2 -20
View File
@@ -13,10 +13,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
title: "30min between Pro Example and pro@example.com",
description: null,
additionalNotes: "asdasdas",
customInputs: {
"Custom input 01": "sadasdasdsadasd",
"Custom input 02": "asdasdasd",
},
startTime: "2022-06-03T09:00:00-06:00",
endTime: "2022-06-03T09:30:00-06:00",
organizer: {
@@ -32,26 +28,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
timeZone: "America/Chihuahua",
language,
},
{
email: "pro@example.com",
name: "pro@example.com",
timeZone: "America/Chihuahua",
language,
},
{
email: "pro@example.com",
name: "pro@example.com",
timeZone: "America/Chihuahua",
language,
},
],
location: "Zoom video",
destinationCalendar: null,
hideCalendarNotes: false,
uid: "bwPWLpjYrx4rZf6MCZdKgE",
uid: "xxyPr4cg2xx4XoS2KeMEQy",
metadata: {},
cancellationReason: "It got late",
paymentInfo: { id: "pi_12312", link: "https://cal.com", reason: "no reason" },
recurringEvent: null,
};
@@ -60,7 +42,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
res.setHeader("Content-Type", "text/html");
res.setHeader("Cache-Control", "no-cache, no-store, private, must-revalidate");
res.write(
renderEmail("AttendeeScheduledEmail", {
renderEmail("OrganizerRequestEmail", {
attendee: evt.attendees[0],
calEvent: evt,
})
+16 -4
View File
@@ -53,6 +53,8 @@ export default async function handler(req: NextApiRequest) {
interFontMedium,
]);
const ogConfig = {
width: 1200,
height: 630,
fonts: [
{ name: "inter", data: interFontData, weight: 400 },
{ name: "inter", data: interFontMediumData, weight: 500 },
@@ -71,7 +73,7 @@ export default async function handler(req: NextApiRequest) {
meetingImage: searchParams.get("meetingImage"),
imageType,
});
return new ImageResponse(
const img = new ImageResponse(
(
<Meeting
title={title}
@@ -80,7 +82,9 @@ export default async function handler(req: NextApiRequest) {
/>
),
ogConfig
);
) as { body: Buffer };
return new Response(img.body, { status: 200 });
}
case "app": {
const { name, description, slug } = appSchema.parse({
@@ -89,7 +93,11 @@ export default async function handler(req: NextApiRequest) {
slug: searchParams.get("slug"),
imageType,
});
return new ImageResponse(<App name={name} description={description} slug={slug} />, ogConfig);
const img = new ImageResponse(<App name={name} description={description} slug={slug} />, ogConfig) as {
body: Buffer;
};
return new Response(img.body, { status: 200 });
}
case "generic": {
@@ -99,7 +107,11 @@ export default async function handler(req: NextApiRequest) {
imageType,
});
return new ImageResponse(<Generic title={title} description={description} />, ogConfig);
const img = new ImageResponse(<Generic title={title} description={description} />, ogConfig) as {
body: Buffer;
};
return new Response(img.body, { status: 200 });
}
default:
@@ -47,7 +47,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
select: {
username: true,
id: true,
plan: true,
createdDate: true,
},
});
@@ -81,7 +80,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
? new Date(lastBooking.booking.createdAt).toLocaleDateString("en-US")
: "No info"
}</li>
<li><b>Plan:</b>&nbsp;${user.plan}</li>
<li><b>Account created:</b>&nbsp;${new Date(user.createdDate).toLocaleDateString("en-US")}</li>
</ul>
`,
+53 -67
View File
@@ -1,86 +1,72 @@
import crypto from "crypto";
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar";
import prisma from "@calcom/prisma";
import { defaultAvatarSrc } from "@lib/profile";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// const username = req.url?.substring(1, req.url.lastIndexOf("/"));
const username = req.query.username as string;
const teamname = req.query.teamname as string;
let identity;
let linksToThisRoute = false;
const querySchema = z
.object({
username: z.string(),
teamname: z.string(),
})
.partial();
async function getIdentityData(req: NextApiRequest) {
const { username, teamname } = querySchema.parse(req.query);
if (username) {
const user = await prisma.user.findUnique({
where: {
username: username,
},
select: {
avatar: true,
email: true,
},
where: { username },
select: { avatar: true, email: true },
});
identity = {
return {
name: username,
email: user?.email,
avatar: user?.avatar,
};
linksToThisRoute =
identity.avatar === `${CAL_URL}/${username}/avatar.png` ||
identity.avatar === `${WEBAPP_URL}/${username}/avatar.png`;
} else if (teamname) {
const team = await prisma.team.findUnique({
where: {
slug: teamname,
},
select: {
logo: true,
},
});
identity = {
name: teamname,
shouldDefaultBeNameBased: true,
avatar: team?.logo,
};
linksToThisRoute =
identity.avatar === `${CAL_URL}/team/${teamname}/avatar.png` ||
identity.avatar === `${WEBAPP_URL}/team/${teamname}/avatar.png`;
}
const emailMd5 = crypto
.createHash("md5")
.update((identity?.email as string) || "guest@example.com")
.digest("hex");
const img = identity?.avatar;
// If image isn't set or links to this route itself, use default avatar
if (!img || linksToThisRoute) {
let defaultSrc = defaultAvatarSrc({ md5: emailMd5 });
if (identity?.shouldDefaultBeNameBased) {
defaultSrc = getPlaceholderAvatar(null, identity.name);
}
res.writeHead(302, {
Location: defaultSrc,
if (teamname) {
const team = await prisma.team.findUnique({
where: { slug: teamname },
select: { logo: true },
});
res.end();
} else if (!img.includes("data:image")) {
res.writeHead(302, {
Location: img,
});
res.end();
} else {
const decoded = img
.toString()
.replace("data:image/png;base64,", "")
.replace("data:image/jpeg;base64,", "");
const imageResp = Buffer.from(decoded, "base64");
res.writeHead(200, {
"Content-Type": "image/png",
"Content-Length": imageResp.length,
});
res.end(imageResp);
return {
name: teamname,
email: null,
avatar: team?.logo || getPlaceholderAvatar(null, teamname),
};
}
}
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const identity = await getIdentityData(req);
const img = identity?.avatar;
// If image isn't set or links to this route itself, use default avatar
if (!img) {
res.writeHead(302, {
Location: defaultAvatarSrc({
md5: crypto
.createHash("md5")
.update(identity?.email || "guest@example.com")
.digest("hex"),
}),
});
return res.end();
}
if (!img.includes("data:image")) {
res.writeHead(302, { Location: img });
return res.end();
}
const decoded = img.toString().replace("data:image/png;base64,", "").replace("data:image/jpeg;base64,", "");
const imageResp = Buffer.from(decoded, "base64");
res.writeHead(200, {
"Content-Type": "image/png",
"Content-Length": imageResp.length,
});
res.end(imageResp);
}
+9 -6
View File
@@ -1,5 +1,4 @@
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { InferGetStaticPropsType, NextPageContext } from "next";
import { GetServerSideProps, InferGetServerSidePropsType } from "next";
import { ChangeEventHandler, useState } from "react";
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
@@ -31,7 +30,10 @@ function AppsSearch({
);
}
export default function Apps({ appStore, categories }: InferGetStaticPropsType<typeof getServerSideProps>) {
export default function Apps({
categories,
appStore,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
const { t } = useLocale();
const [searchText, setSearchText] = useState<string | undefined>(undefined);
@@ -42,7 +44,8 @@ export default function Apps({ appStore, categories }: InferGetStaticPropsType<t
subtitle={t("app_store_description")}
actions={(className) => (
<AppsSearch className={className} onChange={(e) => setSearchText(e.target.value)} />
)}>
)}
emptyStore={!appStore.length}>
{!searchText && (
<>
<AppStoreCategories categories={categories} />
@@ -54,7 +57,7 @@ export default function Apps({ appStore, categories }: InferGetStaticPropsType<t
);
}
export const getServerSideProps = async (context: NextPageContext) => {
export const getServerSideProps: GetServerSideProps = async (context) => {
const ssg = await ssgInit(context);
const session = await getSession(context);
@@ -77,7 +80,6 @@ export const getServerSideProps = async (context: NextPageContext) => {
}, {} as Record<string, number>);
return {
props: {
trpcState: ssg.dehydrate(),
categories: Object.entries(categories)
.map(([name, count]): { name: AppCategories; count: number } => ({
name: name as AppCategories,
@@ -87,6 +89,7 @@ export const getServerSideProps = async (context: NextPageContext) => {
return b.count - a.count;
}),
appStore,
trpcState: ssg.dehydrate(),
},
};
};
+48
View File
@@ -0,0 +1,48 @@
import { useState } from "react";
import AdminAppsList from "@calcom/features/apps/AdminAppsList";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import prisma from "@calcom/prisma";
import { inferSSRProps } from "@calcom/types/inferSSRProps";
import { WizardForm } from "@calcom/ui";
import SetupFormStep1 from "./steps/SetupFormStep1";
import StepDone from "./steps/StepDone";
export default function Setup(props: inferSSRProps<typeof getServerSideProps>) {
const { t } = useLocale();
const [isLoadingStep1, setIsLoadingStep1] = useState(false);
const shouldDisable = props.userCount !== 0;
const steps = [
{
title: t("administrator_user"),
description: t("lets_create_first_administrator_user"),
content: shouldDisable ? <StepDone /> : <SetupFormStep1 setIsLoading={setIsLoadingStep1} />,
isLoading: isLoadingStep1,
},
{
title: t("enable_apps"),
description: t("enable_apps_description"),
content: <AdminAppsList baseURL="/auth/setup" />,
isLoading: false,
},
];
return (
<>
<main className="flex items-center bg-gray-100 print:h-full">
<WizardForm href="/auth/setup" steps={steps} disableNavigation={shouldDisable} />
</main>
</>
);
}
export const getServerSideProps = async () => {
const userCount = await prisma.user.count();
return {
props: {
userCount,
},
};
};
@@ -1,34 +1,14 @@
import { CheckIcon } from "@heroicons/react/solid";
import { zodResolver } from "@hookform/resolvers/zod";
import classNames from "classnames";
import { GetServerSidePropsContext } from "next";
import { signIn } from "next-auth/react";
import { useRouter } from "next/router";
import { useState } from "react";
import { Controller, FormProvider, useForm } from "react-hook-form";
import * as z from "zod";
import { isPasswordValid } from "@calcom/lib/auth";
import { WEBSITE_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import prisma from "@calcom/prisma";
import { inferSSRProps } from "@calcom/types/inferSSRProps";
import { EmailField, Label, PasswordField, TextField, WizardForm } from "@calcom/ui";
import { ssrInit } from "@server/lib/ssr";
const StepDone = () => {
const { t } = useLocale();
return (
<div className="min-h-36 my-6 flex flex-col items-center justify-center">
<div className="flex h-[72px] w-[72px] items-center justify-center rounded-full bg-gray-600 dark:bg-white">
<CheckIcon className="inline-block h-10 w-10 text-white dark:bg-white dark:text-gray-600" />
</div>
<div className="max-w-[420px] text-center">
<h2 className="mt-6 mb-1 text-lg font-medium dark:text-gray-300">{t("all_done")}</h2>
</div>
</div>
);
};
import { EmailField, Label, PasswordField, TextField } from "@calcom/ui";
const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => {
const router = useRouter();
@@ -83,13 +63,19 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => {
},
});
if (response.status === 200) {
router.replace(`/auth/login?email=${data.email_address.toLowerCase()}`);
await signIn("credentials", {
redirect: false,
callbackUrl: "/",
email: data.email_address.toLowerCase(),
password: data.password,
});
router.replace(`/auth/setup?step=2&category=calendar`);
} else {
router.replace("/auth/setup");
}
}, onError);
const longWebsiteUrl = process.env.NEXT_PUBLIC_WEBSITE_URL.length > 30;
const longWebsiteUrl = WEBSITE_URL.length > 30;
return (
<FormProvider {...formMethods}>
@@ -201,37 +187,4 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => {
);
};
export default function Setup(props: inferSSRProps<typeof getServerSideProps>) {
const { t } = useLocale();
const [isLoadingStep1, setIsLoadingStep1] = useState(false);
const steps = [
{
title: t("administrator_user"),
description: t("lets_create_first_administrator_user"),
content: props.userCount !== 0 ? <StepDone /> : <SetupFormStep1 setIsLoading={setIsLoadingStep1} />,
enabled: props.userCount === 0, // to check if the wizard should show buttons to navigate through more steps
isLoading: isLoadingStep1,
},
];
return (
<>
<main className="flex h-screen items-center bg-gray-100 print:h-full">
<WizardForm href="/auth/setup" steps={steps} containerClassname="max-w-sm" />
</main>
</>
);
}
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const ssr = await ssrInit(context);
const userCount = await prisma.user.count();
return {
props: {
trpcState: ssr.dehydrate(),
userCount,
},
};
};
export default SetupFormStep1;
@@ -0,0 +1,19 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui";
const StepDone = () => {
const { t } = useLocale();
return (
<div className="min-h-36 my-6 flex flex-col items-center justify-center">
<div className="flex h-[72px] w-[72px] items-center justify-center rounded-full bg-gray-600 dark:bg-white">
<Icon.FiCheck className="inline-block h-10 w-10 text-white dark:bg-white dark:text-gray-600" />
</div>
<div className="max-w-[420px] text-center">
<h2 className="mt-6 mb-1 text-lg font-medium dark:text-gray-300">{t("all_done")}</h2>
</div>
</div>
);
};
export default StepDone;
+74 -43
View File
@@ -1,15 +1,16 @@
import { GetStaticPaths, GetStaticProps } from "next";
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { GetServerSidePropsContext } from "next";
import { Controller, useFieldArray, useForm } from "react-hook-form";
import { z } from "zod";
import { DateOverrideInputDialog, DateOverrideList } from "@calcom/features/schedules";
import Schedule from "@calcom/features/schedules/components/Schedule";
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 { trpc } from "@calcom/trpc/react";
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
import type { Schedule as ScheduleType } from "@calcom/types/schedule";
import type { Schedule as ScheduleType, TimeRange, WorkingHours } from "@calcom/types/schedule";
import {
Button,
Form,
@@ -21,6 +22,7 @@ import {
SkeletonText,
Switch,
TimezoneSelect,
Tooltip,
VerticalDivider,
} from "@calcom/ui";
@@ -29,7 +31,7 @@ import { HttpError } from "@lib/core/http/error";
import { SelectSkeletonLoader } from "@components/availability/SkeletonLoader";
import EditableHeading from "@components/ui/EditableHeading";
import { ssgInit } from "@server/lib/ssg";
import { ssrInit } from "@server/lib/ssr";
const querySchema = z.object({
schedule: stringOrNumber,
@@ -38,31 +40,59 @@ const querySchema = z.object({
type AvailabilityFormValues = {
name: string;
schedule: ScheduleType;
dateOverrides: { ranges: TimeRange[] }[];
timeZone: string;
isDefault: boolean;
};
const DateOverride = ({ workingHours }: { workingHours: WorkingHours[] }) => {
const { remove, append, update, fields } = useFieldArray<AvailabilityFormValues, "dateOverrides">({
name: "dateOverrides",
});
const { t } = useLocale();
return (
<div className="px-4 py-5 sm:p-6">
<h3 className="font-medium leading-6 text-gray-900">
{t("date_overrides")}{" "}
<Tooltip content={t("date_overrides_info")}>
<span className="inline-block">
<Icon.FiInfo />
</span>
</Tooltip>
</h3>
<p className="mb-4 text-sm text-neutral-500 ltr:mr-4 rtl:ml-4">{t("date_overrides_subtitle")}</p>
<div className="mt-1 space-y-2">
<DateOverrideList
excludedDates={fields.map((field) => yyyymmdd(field.ranges[0].start))}
remove={remove}
update={update}
items={fields}
workingHours={workingHours}
/>
<DateOverrideInputDialog
workingHours={workingHours}
excludedDates={fields.map((field) => yyyymmdd(field.ranges[0].start))}
onChange={(ranges) => append({ ranges })}
Trigger={
<Button color="secondary" StartIcon={Icon.FiPlus} data-testid="add-override">
Add an override
</Button>
}
/>
</div>
</div>
);
};
export default function Availability({ schedule }: { schedule: number }) {
const { t, i18n } = useLocale();
const utils = trpc.useContext();
const me = useMeQuery();
const { timeFormat } = me.data || { timeFormat: null };
const { data, isLoading } = trpc.viewer.availability.schedule.get.useQuery({ scheduleId: schedule });
const form = useForm<AvailabilityFormValues>();
const { control, reset } = form;
useEffect(() => {
if (!isLoading && data) {
reset({
name: data?.schedule?.name,
schedule: data.availability,
timeZone: data.timeZone,
isDefault: data.isDefault,
});
}
}, [data, isLoading, reset]);
const { data: defaultValues } = trpc.viewer.availability.defaultValues.useQuery({ scheduleId: schedule });
const form = useForm<AvailabilityFormValues>({ defaultValues });
const { control } = form;
const updateMutation = trpc.viewer.availability.schedule.update.useMutation({
onSuccess: async ({ prevDefaultId, currentDefaultId, ...data }) => {
if (prevDefaultId && currentDefaultId) {
@@ -73,7 +103,7 @@ export default function Availability({ schedule }: { schedule: number }) {
utils.viewer.availability.schedule.get.refetch({ scheduleId: prevDefaultId });
}
}
utils.viewer.availability.schedule.get.setData({ scheduleId: data.schedule.id }, data);
utils.viewer.availability.schedule.get.invalidate({ scheduleId: data.schedule.id });
utils.viewer.availability.list.invalidate();
showToast(
t("availability_updated_successfully", {
@@ -98,17 +128,21 @@ export default function Availability({ schedule }: { schedule: number }) {
<Controller
control={form.control}
name="name"
render={({ field }) => <EditableHeading isReady={!isLoading} {...field} />}
render={({ field }) => (
<EditableHeading isReady={!isLoading} {...field} data-testid="availablity-title" />
)}
/>
}
subtitle={
data ? (
data.schedule.availability.map((availability) => (
<span key={availability.id}>
{availabilityAsString(availability, { locale: i18n.language, hour12: timeFormat === 12 })}
<br />
</span>
))
data.schedule.availability
.filter((availability) => !!availability.days.length)
.map((availability) => (
<span key={availability.id}>
{availabilityAsString(availability, { locale: i18n.language, hour12: timeFormat === 12 })}
<br />
</span>
))
) : (
<SkeletonText className="h-4 w-48" />
)
@@ -147,15 +181,16 @@ export default function Availability({ schedule }: { schedule: number }) {
<Form
form={form}
id="availability-form"
handleSubmit={async (values) => {
handleSubmit={async ({ dateOverrides, ...values }) => {
updateMutation.mutate({
scheduleId: schedule,
dateOverrides: dateOverrides.flatMap((override) => override.ranges),
...values,
});
}}
className="-mx-4 flex flex-col pb-16 sm:mx-0 xl:flex-row xl:space-x-6">
<div className="flex-1">
<div className="rounded-md border-gray-200 bg-white py-5 pr-4 sm:border sm:p-6">
<div className="flex-1 divide-y divide-neutral-200 rounded-md border">
<div className=" py-5 pr-4 sm:p-6">
<h3 className="mb-5 text-base font-medium leading-6 text-gray-900">
{t("change_start_end")}
</h3>
@@ -171,6 +206,7 @@ export default function Availability({ schedule }: { schedule: number }) {
/>
)}
</div>
{data?.workingHours && <DateOverride workingHours={data.workingHours} />}
</div>
<div className="min-w-40 col-span-3 space-y-2 lg:col-span-1">
<div className="xl:max-w-80 mt-4 w-full pr-4 sm:p-0">
@@ -211,24 +247,19 @@ export default function Availability({ schedule }: { schedule: number }) {
);
}
export const getStaticProps: GetStaticProps = async (ctx) => {
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const params = querySchema.safeParse(ctx.params);
const ssg = await ssgInit(ctx);
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: params.data.schedule,
trpcState: ssg.dehydrate(),
schedule: scheduleId,
trpcState: ssr.dehydrate(),
},
revalidate: 10, // seconds
};
};
export const getStaticPaths: GetStaticPaths = () => {
return {
paths: [],
fallback: "blocking",
};
};
+58 -37
View File
@@ -1,10 +1,10 @@
import { useState } from "react";
import dayjs from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { RouterOutputs, trpc } from "@calcom/trpc/react";
import { Shell, SkeletonText } from "@calcom/ui";
import useRouterQuery from "@lib/hooks/useRouterQuery";
type User = RouterOutputs["viewer"]["me"];
export interface IBusySlot {
@@ -16,7 +16,9 @@ export interface IBusySlot {
const AvailabilityView = ({ user }: { user: User }) => {
const { t } = useLocale();
const [selectedDate, setSelectedDate] = useState(dayjs());
const { date, setQuery: setSelectedDate } = useRouterQuery("date");
const selectedDate = dayjs(date);
const formattedSelectedDate = selectedDate.format("YYYY-MM-DD");
const { data, isLoading } = trpc.viewer.availability.user.useQuery(
{
@@ -30,6 +32,17 @@ const AvailabilityView = ({ user }: { user: User }) => {
}
);
const overrides =
data?.dateOverrides.reduce((acc, override) => {
if (
formattedSelectedDate !== dayjs(override.start).format("YYYY-MM-DD") &&
formattedSelectedDate !== dayjs(override.end).format("YYYY-MM-DD")
)
return acc;
acc.push({ ...override, source: "Date override" });
return acc;
}, [] as IBusySlot[]) || [];
return (
<div className="max-w-xl overflow-hidden rounded-md bg-white shadow">
<div className="px-4 py-5 sm:p-6">
@@ -37,9 +50,9 @@ const AvailabilityView = ({ user }: { user: User }) => {
<input
type="date"
className="inline h-8 border-none p-0"
defaultValue={selectedDate.format("YYYY-MM-DD")}
defaultValue={formattedSelectedDate}
onChange={(e) => {
if (e.target.value) setSelectedDate(dayjs(e.target.value));
if (e.target.value) setSelectedDate(e.target.value);
}}
/>
<small className="block text-neutral-400">{t("hover_over_bold_times_tip")}</small>
@@ -49,39 +62,47 @@ const AvailabilityView = ({ user }: { user: User }) => {
{t("your_day_starts_at")} {convertMinsToHrsMins(user.startTime)}
</div>
</div>
{isLoading ? (
<>
<SkeletonText className="block h-16 w-full" />
<SkeletonText className="block h-16 w-full" />
</>
) : data && data.busy.length > 0 ? (
data.busy
.sort((a: IBusySlot, b: IBusySlot) => (a.start > b.start ? -1 : 1))
.map((slot: IBusySlot) => (
<div
key={dayjs(slot.start).format("HH:mm")}
className="overflow-hidden rounded-md bg-neutral-100">
<div className="px-4 py-5 text-black sm:p-6">
{t("calendar_shows_busy_between")}{" "}
<span className="font-medium text-neutral-800" title={dayjs(slot.start).format("HH:mm")}>
{dayjs(slot.start).format("HH:mm")}
</span>{" "}
{t("and")}{" "}
<span className="font-medium text-neutral-800" title={dayjs(slot.end).format("HH:mm")}>
{dayjs(slot.end).format("HH:mm")}
</span>{" "}
{t("on")} {dayjs(slot.start).format("D")}{" "}
{t(dayjs(slot.start).format("MMMM").toLowerCase())} {dayjs(slot.start).format("YYYY")}
{slot.title && ` - (${slot.title})`}
{slot.source && <small>{` - (source: ${slot.source})`}</small>}
{(() => {
if (isLoading)
return (
<>
<SkeletonText className="block h-16 w-full" />
<SkeletonText className="block h-16 w-full" />
</>
);
if (data && (data.busy.length > 0 || overrides.length > 0))
return [...data.busy, ...overrides]
.sort((a: IBusySlot, b: IBusySlot) => (a.start > b.start ? -1 : 1))
.map((slot: IBusySlot) => (
<div
key={dayjs(slot.start).format("HH:mm")}
className="overflow-hidden rounded-md bg-neutral-100"
data-testid="troubleshooter-busy-time">
<div className="px-4 py-5 text-black sm:p-6">
{t("calendar_shows_busy_between")}{" "}
<span
className="font-medium text-neutral-800"
title={dayjs(slot.start).format("HH:mm")}>
{dayjs(slot.start).format("HH:mm")}
</span>{" "}
{t("and")}{" "}
<span className="font-medium text-neutral-800" title={dayjs(slot.end).format("HH:mm")}>
{dayjs(slot.end).format("HH:mm")}
</span>{" "}
{t("on")} {dayjs(slot.start).format("D")}{" "}
{t(dayjs(slot.start).format("MMMM").toLowerCase())} {dayjs(slot.start).format("YYYY")}
{slot.title && ` - (${slot.title})`}
{slot.source && <small>{` - (source: ${slot.source})`}</small>}
</div>
</div>
</div>
))
) : (
<div className="overflow-hidden rounded-md bg-neutral-100">
<div className="px-4 py-5 text-black sm:p-6">{t("calendar_no_busy_slots")}</div>
</div>
)}
));
return (
<div className="overflow-hidden rounded-md bg-neutral-100">
<div className="px-4 py-5 text-black sm:p-6">{t("calendar_no_busy_slots")}</div>
</div>
);
})()}
<div className="bg-brand dark:bg-darkmodebrand overflow-hidden rounded-md">
<div className="text-brandcontrast dark:text-darkmodebrandcontrast px-4 py-2 sm:px-6">
+37 -20
View File
@@ -57,26 +57,41 @@ function redirectToExternalUrl(url: string) {
function RedirectionToast({ url }: { url: string }) {
const [timeRemaining, setTimeRemaining] = useState(10);
const [isToastVisible, setIsToastVisible] = useState(true);
const parsedSuccessUrl = new URL(document.URL);
const parsedExternalUrl = new URL(url);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
/* @ts-ignore */ //https://stackoverflow.com/questions/49218765/typescript-and-iterator-type-iterableiteratort-is-not-an-array-type
for (const [name, value] of parsedExternalUrl.searchParams.entries()) {
parsedSuccessUrl.searchParams.set(name, value);
}
const urlWithSuccessParams =
parsedExternalUrl.origin +
parsedExternalUrl.pathname +
"?" +
parsedSuccessUrl.searchParams.toString() +
parsedExternalUrl.hash;
const { t } = useLocale();
const timerRef = useRef<number | null>(null);
const router = useRouter();
const { cancel: isCancellationMode } = querySchema.parse(router.query);
const urlWithSuccessParamsRef = useRef<string | null>();
if (isCancellationMode && timerRef.current) {
setIsToastVisible(false);
}
useEffect(() => {
if (!isToastVisible && timerRef.current) {
window.clearInterval(timerRef.current);
}
}, [isToastVisible]);
useEffect(() => {
const parsedExternalUrl = new URL(url);
const parsedSuccessUrl = new URL(document.URL);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
/* @ts-ignore */ //https://stackoverflow.com/questions/49218765/typescript-and-iterator-type-iterableiteratort-is-not-an-array-type
for (const [name, value] of parsedExternalUrl.searchParams.entries()) {
parsedSuccessUrl.searchParams.set(name, value);
}
const urlWithSuccessParams =
parsedExternalUrl.origin +
parsedExternalUrl.pathname +
"?" +
parsedSuccessUrl.searchParams.toString() +
parsedExternalUrl.hash;
urlWithSuccessParamsRef.current = urlWithSuccessParams;
timerRef.current = window.setInterval(() => {
if (timeRemaining > 0) {
setTimeRemaining((timeRemaining) => {
@@ -90,7 +105,7 @@ function RedirectionToast({ url }: { url: string }) {
return () => {
window.clearInterval(timerRef.current as number);
};
}, [timeRemaining, urlWithSuccessParams]);
}, [timeRemaining, url]);
if (!isToastVisible) {
return null;
@@ -113,7 +128,9 @@ function RedirectionToast({ url }: { url: string }) {
<div className="order-3 mt-2 w-full flex-shrink-0 sm:order-2 sm:mt-0 sm:w-auto">
<button
onClick={() => {
redirectToExternalUrl(urlWithSuccessParams);
if (urlWithSuccessParamsRef.current) {
redirectToExternalUrl(urlWithSuccessParamsRef.current);
}
}}
className="flex w-full items-center justify-center rounded-sm border border-transparent bg-white px-4 py-2 text-sm font-medium text-green-600 hover:bg-green-50">
{t("continue")}
@@ -124,7 +141,6 @@ function RedirectionToast({ url }: { url: string }) {
type="button"
onClick={() => {
setIsToastVisible(false);
window.clearInterval(timerRef.current as number);
}}
className="-mr-1 flex rounded-md p-2 hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-white">
<Icon.FiX className="h-6 w-6 text-white" />
@@ -349,7 +365,9 @@ export default function Success(props: SuccessProps) {
<CustomBranding lightVal={props.profile.brandColor} darkVal={props.profile.darkBrandColor} />
<main className={classNames(shouldAlignCentrally ? "mx-auto" : "", isEmbed ? "" : "max-w-3xl")}>
<div className={classNames("overflow-y-auto", isEmbed ? "" : "z-50 ")}>
{eventType.successRedirectUrl ? <RedirectionToast url={eventType.successRedirectUrl} /> : null}{" "}
{isSuccessBookingPage && !isCancellationMode && eventType.successRedirectUrl ? (
<RedirectionToast url={eventType.successRedirectUrl} />
) : null}{" "}
<div
className={classNames(
shouldAlignCentrally ? "text-center" : "",
@@ -840,7 +858,6 @@ const getEventTypesFromDB = async (id: number) => {
name: true,
username: true,
hideBranding: true,
plan: true,
theme: true,
brandColor: true,
darkBrandColor: true,
+450
View File
@@ -0,0 +1,450 @@
import { BookingStatus } from "@prisma/client";
import { createHmac } from "crypto";
import { instance } from "gaxios";
import { GetServerSidePropsContext } from "next";
import { useRouter } from "next/router";
import { useState } from "react";
import z from "zod";
import { getEventLocationValue, getSuccessPageLocationMessage } from "@calcom/app-store/locations";
import dayjs from "@calcom/dayjs";
import { getRecurringWhen } from "@calcom/emails/src/components/WhenInfo";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
import { processBookingConfirmation } from "@calcom/lib/server/queries/bookings/confirm";
import prisma from "@calcom/prisma";
import { TRPCError } from "@calcom/trpc/server";
import { inferSSRProps } from "@calcom/types/inferSSRProps";
import { Button, Icon, TextArea } from "@calcom/ui";
import { HeadSeo } from "@components/seo/head-seo";
enum DirectAction {
"accept" = "accept",
"reject" = "reject",
}
const actionSchema = z.nativeEnum(DirectAction);
const refineParse = (result: z.SafeParseReturnType<any, any>, context: z.RefinementCtx) => {
if (result.success === false) {
result.error.issues.map((issue) => context.addIssue(issue));
}
};
const CALENDSO_ENCRYPTION_KEY = process.env.CALENDSO_ENCRYPTION_KEY || "";
const pageErrors = {
signature_mismatch: "Direct link signature doesn't match signed data",
booking_not_found: "Direct link booking not found",
user_not_found: "Direct link booking user not found",
};
const requestSchema = z.object({
link: z
.array(z.string())
.max(4)
.superRefine((data, ctx) => {
refineParse(actionSchema.safeParse(data[0]), ctx);
const signedData = `${data[1]}/${data[2]}`;
const sig = createHmac("sha1", CALENDSO_ENCRYPTION_KEY).update(signedData).digest("base64");
if (data[3] !== sig) {
ctx.addIssue({
message: pageErrors.signature_mismatch,
code: "custom",
});
console.log(signedData, data, data[3], "==", sig);
}
}),
reason: z.string().optional(),
});
function bookingContent(status: BookingStatus | undefined | null) {
switch (status) {
case BookingStatus.PENDING:
// Trying to reject booking without reason
return {
iconColor: "gray",
Icon: Icon.FiCalendar,
titleKey: "event_awaiting_approval",
subtitleKey: "someone_requested_an_event",
};
case BookingStatus.ACCEPTED:
// Booking was acepted successfully
return {
iconColor: "green",
Icon: Icon.FiCheck,
titleKey: "booking_confirmed",
subtitleKey: "emailed_you_and_any_other_attendees",
};
case BookingStatus.REJECTED:
// Booking was rejected successfully
return {
iconColor: "red",
Icon: Icon.FiX,
titleKey: "booking_rejection_success",
subtitleKey: "emailed_you_and_any_other_attendees",
};
default:
// Booking was already accepted or rejected
return {
iconColor: "yellow",
Icon: Icon.FiAlertTriangle,
titleKey: "booking_already_accepted_rejected",
};
}
}
export default function Directlink({ booking, reason, status }: inferSSRProps<typeof getServerSideProps>) {
const { t } = useLocale();
const router = useRouter();
const acceptPath = router.asPath.replace("reject", "accept");
const rejectPath = router.asPath.replace("accept", "reject");
const [cancellationReason, setCancellationReason] = useState("");
function getRecipientStart(format: string) {
return dayjs(booking.startTime).tz(booking?.user?.timeZone).format(format);
}
function getRecipientEnd(format: string) {
return dayjs(booking.endTime).tz(booking?.user?.timeZone).format(format);
}
const organizer = {
...booking.attendees[0],
language: {
translate: t,
locale: booking.attendees[0].locale ?? "en",
},
};
const location: ReturnType<typeof getEventLocationValue> = Array.isArray(booking.location)
? booking.location[0]
: // If there is no location set then we default to Cal Video
"integrations:daily";
const locationToDisplay = getSuccessPageLocationMessage(location, t);
const content = bookingContent(status);
const recurringInfo = getRecurringWhen({
recurringEvent: booking.eventType?.recurringEvent,
attendee: organizer,
});
return (
<>
<HeadSeo
title={t(content.titleKey)}
description=""
nextSeoProps={{
nofollow: true,
noindex: true,
}}
/>
<div className="dark:bg-darkgray-50 desktop-transparent min-h-screen bg-gray-100 px-4">
<main className="mx-auto max-w-3xl">
<div className="z-50 overflow-y-auto ">
<div className="flex items-end justify-center px-4 pt-4 pb-20 text-center sm:block sm:p-0">
<div className="inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
<div
className="main dark:bg-darkgray-100 inline-block transform overflow-hidden rounded-lg border bg-white px-8 pt-5 pb-4 text-left align-bottom transition-all dark:border-neutral-700 sm:my-[68px] sm:w-full sm:max-w-xl sm:py-8 sm:align-middle"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
<div
className={`flex h-12 w-12 items-center justify-center rounded-full sm:mx-auto bg-${content.iconColor}-100`}>
<content.Icon className={`h-5 w-5 text-${content.iconColor}-600`} />
</div>
<div className="mt-6 mb-8 last:mb-0 sm:text-center">
<h3
className="text-2xl font-semibold leading-6 text-neutral-900 dark:text-white"
id="modal-headline">
{t(content.titleKey)}
</h3>
{content.subtitleKey && (
<div className="mt-3">
<p className="text-neutral-600 dark:text-gray-300">{t(content.subtitleKey)}</p>
</div>
)}
<div className="dark:border-darkgray-300 mt-8 grid grid-cols-3 border-t border-[#e1e1e1] pt-8 text-left text-[#313131] dark:text-gray-300">
<div className="col-span-3 font-medium sm:col-span-1">{t("what")}</div>
<div className="col-span-3 mb-6 last:mb-0 sm:col-span-2">{booking.title}</div>
<div className="col-span-3 font-medium sm:col-span-1">{t("when")}</div>
<div className="col-span-3 mb-6 last:mb-0 sm:col-span-2">
{recurringInfo !== "" && (
<>
{recurringInfo}
<br />
</>
)}
{booking.eventType.recurringEvent?.count ? `${t("starting")} ` : ""}
{t(getRecipientStart("dddd").toLowerCase())},{" "}
{t(getRecipientStart("MMMM").toLowerCase())} {getRecipientStart("D, YYYY")}
<br />
{getRecipientStart("h:mma")} - {getRecipientEnd("h:mma")}{" "}
<span style={{ color: "#888888" }}>({booking.attendees[0].timeZone})</span>
</div>
{(booking?.user || booking?.attendees) && (
<>
<div className="col-span-3 font-medium sm:col-span-1">{t("who")}</div>
<div className="col-span-3 last:mb-0 sm:col-span-2">
<>
{booking?.user && (
<div className="mb-3">
<p>{booking.user.name}</p>
<p className="text-[#888888]">{booking.user.email}</p>
</div>
)}
{booking?.attendees.map((attendee) => (
<div key={attendee.name} className="mb-3 last:mb-0">
{attendee.name && <p>{attendee.name}</p>}
<p className="text-[#888888]">{attendee.email}</p>
</div>
))}
</>
</div>
</>
)}
{locationToDisplay && (
<>
<div className="col-span-3 mt-6 font-medium sm:col-span-1">{t("where")}</div>
<div className="col-span-3 mt-6 sm:col-span-2">
{locationToDisplay.startsWith("http") ? (
<a title="Meeting Link" href={locationToDisplay}>
{locationToDisplay}
</a>
) : (
locationToDisplay
)}
</div>
</>
)}
{booking?.description && (
<>
<div className="col-span-3 mt-9 font-medium sm:col-span-1">
{t("additional_notes")}
</div>
<div className="col-span-3 mb-2 mt-9 sm:col-span-2">
<p>{booking.description}</p>
</div>
</>
)}
{status === BookingStatus.REJECTED && reason && (
<>
<div className="col-span-3 mt-9 font-medium sm:col-span-1">
{t("rejection_reason")}
</div>
<div className="col-span-3 mb-2 mt-9 sm:col-span-2">
<p>{reason}</p>
</div>
</>
)}
</div>
{status === BookingStatus.PENDING && reason === undefined && (
<>
<hr className="mt-6" />
<div className="mt-5 text-left sm:mt-6">
<label className="font-medium text-[#313131] dark:text-white">
{`${t("rejection_reason")} (${t("optional").toLowerCase()})`}
</label>
<TextArea
value={cancellationReason}
onChange={(e) => setCancellationReason(e.target.value)}
className="mt-2 mb-4 w-full dark:border-gray-900 dark:bg-gray-700 dark:text-white "
rows={3}
/>
<div className="flex flex-col-reverse rtl:space-x-reverse">
<div className="ml-auto flex w-full justify-end space-x-4">
<Button
color="secondary"
className="hidden text-center sm:block"
href={acceptPath}>
{t("booking_accept_intent")}
</Button>
<Button
className="hidden sm:block"
onClick={async () => {
router.push(
`${rejectPath}?reason=${encodeURIComponent(cancellationReason)}`
);
}}>
{t("rejection_confirmation")}
</Button>
<Button
color="secondary"
className="block text-center sm:hidden"
href={acceptPath}>
{t("accept")}
</Button>
<Button
className="block sm:hidden"
onClick={async () => {
router.push(
`${rejectPath}?reason=${encodeURIComponent(cancellationReason)}`
);
}}>
{t("reject")}
</Button>
</div>
</div>
</div>
</>
)}
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</>
);
}
export async function getServerSideProps(context: GetServerSidePropsContext) {
const parsedQuery = requestSchema.safeParse(context.query);
// Parsing error, showing error 500 with message
if (parsedQuery.success === false) {
return {
redirect: {
destination: `/500?error=${parsedQuery.error.errors[0].message.concat(
" accessing " + context.resolvedUrl
)}`,
permanent: false,
},
};
}
const {
link: [action, email, bookingUid],
reason,
} = parsedQuery.data;
const isAccept = action === DirectAction.accept;
const bookingRaw = await prisma?.booking.findFirst({
where: {
uid: bookingUid,
user: {
email,
},
},
select: {
location: true,
description: true,
id: true,
recurringEventId: true,
status: true,
title: true,
startTime: true,
endTime: true,
eventType: {
select: {
recurringEvent: true,
},
},
attendees: {
select: {
locale: true,
name: true,
email: true,
timeZone: true,
},
},
user: {
select: {
id: true,
email: true,
name: true,
timeZone: true,
locale: true,
destinationCalendar: true,
credentials: true,
username: true,
},
},
},
});
// Booking not found, showing error 500 with message
if (!bookingRaw) {
return {
redirect: {
destination: `/500?error=${pageErrors.booking_not_found.concat(" accessing " + context.resolvedUrl)}`,
permanent: false,
},
};
}
const booking = {
...bookingRaw,
startTime: bookingRaw.startTime.toString(),
endTime: bookingRaw.endTime.toString(),
eventType: {
...bookingRaw.eventType,
recurringEvent: parseRecurringEvent(bookingRaw?.eventType?.recurringEvent),
},
attendees: bookingRaw?.attendees.map((att) => ({
...att,
language: {
locale: att.locale ?? "en",
},
})),
};
// Booking user not found, showing error 500 with message
if (booking.user === null) {
return {
redirect: {
destination: `/500?error=${pageErrors.user_not_found.concat(" accessing " + context.resolvedUrl)}`,
permanent: false,
},
};
}
// Booking already accepted or rejected
if (booking.status !== BookingStatus.PENDING) {
return {
props: {
booking,
status: null,
},
};
}
// Trying to reject booking without reason
if (!isAccept && reason === undefined) {
return {
props: {
booking,
status: BookingStatus.PENDING,
},
};
}
// Booking good to be accepted or rejected, proceeding to mark it
let result: { status: BookingStatus | undefined } = { status: undefined };
try {
result = await processBookingConfirmation(
{
bookingId: booking.id,
user: booking.user,
recurringEventId: booking.recurringEventId,
confirmed: action === DirectAction.accept,
rejectionReason: reason,
},
prisma
);
} catch (e) {
if (e instanceof TRPCError) {
return {
redirect: {
destination: `/500?error=${e.message.concat(" accessing " + context.resolvedUrl)}`,
permanent: false,
},
};
}
}
return {
props: {
booking,
status: result.status,
reason: context.query.reason ?? null,
},
};
}
-3
View File
@@ -90,7 +90,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
},
},
theme: true,
plan: true,
},
});
@@ -117,7 +116,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
name: u.name,
username: u.username,
hideBranding: u.hideBranding,
plan: u.plan,
timeZone: u.timeZone,
})),
});
@@ -158,7 +156,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
away: user.away,
isDynamicGroup: false,
profile,
plan: user.plan,
date,
eventType: eventTypeObject,
workingHours,
+8 -4
View File
@@ -8,9 +8,10 @@ import { useForm } from "react-hook-form";
import { z } from "zod";
import { StripeData } from "@calcom/app-store/stripepayment/lib/server";
import getApps, { getEventTypeAppData, getLocationOptions } from "@calcom/app-store/utils";
import { getEventTypeAppData, getLocationOptions } from "@calcom/app-store/utils";
import { EventLocationType, LocationObject } from "@calcom/core/location";
import { parseBookingLimit, parseRecurringEvent, validateBookingLimitOrder } from "@calcom/lib";
import getEnabledApps from "@calcom/lib/apps/getEnabledApps";
import { CAL_URL } from "@calcom/lib/constants";
import convertToNewDurationType from "@calcom/lib/convertToNewDurationType";
import findDurationType from "@calcom/lib/findDurationType";
@@ -282,7 +283,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
if (metadata?.multipleDuration.length < 1) {
throw new Error(t("event_setup_multiple_duration_error"));
} else {
if (!metadata?.multipleDuration?.includes(input.length)) {
if (input.length && !metadata?.multipleDuration?.includes(input.length)) {
throw new Error(t("event_setup_multiple_duration_default_error"));
}
}
@@ -340,7 +341,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
id: true,
avatar: true,
email: true,
plan: true,
locale: true,
defaultScheduleId: true,
});
@@ -467,6 +467,9 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
const credentials = await prisma.credential.findMany({
where: {
userId: session.user.id,
app: {
enabled: true,
},
},
select: {
id: true,
@@ -474,6 +477,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
key: true,
userId: true,
appId: true,
invalid: true,
},
});
@@ -526,7 +530,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
}
const currentUser = eventType.users.find((u) => u.id === session.user.id);
const t = await getTranslation(currentUser?.locale ?? "en", "common");
const integrations = getApps(credentials);
const integrations = await getEnabledApps(credentials);
const locationOptions = getLocationOptions(integrations, t);
const eventTypeObject = Object.assign({}, eventType, {
+13 -16
View File
@@ -7,6 +7,7 @@ import React, { Fragment, useEffect, useState } from "react";
import CreateEventTypeButton from "@calcom/features/eventtypes/components/CreateEventTypeButton";
import { APP_NAME, CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import isCalcom from "@calcom/lib/isCalcom";
import { RouterOutputs, trpc, TRPCClientError } from "@calcom/trpc/react";
import {
Badge,
@@ -28,6 +29,7 @@ import {
showToast,
Switch,
Tooltip,
TipBanner,
} from "@calcom/ui";
import { withQuery } from "@lib/QueryCell";
@@ -177,22 +179,17 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
}
// inject selection data into url for correct router history
const openModal = (group: EventTypeGroup, type: EventType) => {
const openDuplicateModal = (eventType: EventType) => {
const query = {
...router.query,
dialog: "new-eventtype",
eventPage: group.profile.slug,
title: type.title,
slug: type.slug,
description: type.description,
length: type.length,
type: type.schedulingType,
teamId: group.teamId,
locations: encodeURIComponent(JSON.stringify(type.locations)),
dialog: "duplicate-event-type",
title: eventType.title,
description: eventType.description,
slug: eventType.slug,
id: eventType.id,
length: eventType.length,
};
if (!group.teamId) {
delete query.teamId;
}
router.push(
{
pathname: router.pathname,
@@ -361,7 +358,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
type="button"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiCopy}
onClick={() => openModal(group, type)}>
onClick={() => openDuplicateModal(type)}>
{t("duplicate") as string}
</DropdownItem>
</DropdownMenuItem>
@@ -469,7 +466,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
className="w-full rounded-none"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiCopy}
onClick={() => openModal(group, type)}>
onClick={() => openDuplicateModal(type)}>
{t("duplicate") as string}
</Button>
</DropdownMenuItem>
@@ -592,6 +589,7 @@ const EventTypesPage = () => {
customLoader={<SkeletonLoader />}
success={({ data }) => (
<>
{isCalcom && <TipBanner />}
{data.eventTypeGroups.map((group, index) => (
<Fragment key={group.profile.slug}>
{/* hide list heading when there is only one (current user) */}
@@ -610,7 +608,6 @@ const EventTypesPage = () => {
/>
</Fragment>
))}
{data.eventTypeGroups.length === 0 && <CreateFirstEventTypeView />}
<EmbedDialog />
</>
@@ -164,7 +164,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
weekStart: true,
hideBranding: true,
theme: true,
plan: true,
brandColor: true,
darkBrandColor: true,
metadata: true,
@@ -1,5 +1,6 @@
import { GetServerSidePropsContext } from "next";
import AdminAppsList from "@calcom/features/apps/AdminAppsList";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { getAdminLayout as getLayout, Meta } from "@calcom/ui";
@@ -9,8 +10,8 @@ function AdminAppsView() {
const { t } = useLocale();
return (
<>
<Meta title={t("apps")} description={t("apps_description")} />
<h1>{t("apps_listing")}</h1>
<Meta title={t("apps")} description={t("admin_apps_description")} />
<AdminAppsList baseURL="/settings/admin/apps" className="w-0" />
</>
);
}
+1 -7
View File
@@ -36,7 +36,6 @@ const CtaRow = ({ title, description, className, children }: CtaRowProps) => {
const BillingView = () => {
const { t } = useLocale();
const { data: user } = trpc.viewer.me.useQuery();
const isPro = user?.plan === "PRO";
const [, loadChat] = useChat();
const [showChat, setShowChat] = useState(false);
const router = useRouter();
@@ -53,14 +52,9 @@ const BillingView = () => {
<Meta title={t("billing")} description={t("manage_billing_description")} />
<div className="space-y-6 text-sm sm:space-y-8">
<CtaRow
className={classNames(!isPro && "pointer-events-none opacity-30")}
title={t("billing_manage_details_title")}
description={t("billing_manage_details_description")}>
<Button
color={isPro ? "primary" : "secondary"}
href={billingHref}
target="_blank"
EndIcon={Icon.FiExternalLink}>
<Button color="primary" href={billingHref} target="_blank" EndIcon={Icon.FiExternalLink}>
{t("billing_portal")}
</Button>
</CtaRow>
@@ -189,7 +189,7 @@ const AppearanceView = () => {
onCheckedChange={(checked) =>
formMethods.setValue("hideBranding", checked, { shouldDirty: true })
}
checked={value}
checked={!session.data?.user.belongsToActiveTeam ? false : value}
/>
</div>
</div>
+164 -154
View File
@@ -2,7 +2,7 @@ import { IdentityProvider } from "@prisma/client";
import crypto from "crypto";
import { GetServerSidePropsContext } from "next";
import { signOut } from "next-auth/react";
import { BaseSyntheticEvent, useEffect, useRef, useState } from "react";
import { BaseSyntheticEvent, useRef, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { ErrorCode } from "@calcom/lib/auth";
@@ -63,14 +63,25 @@ interface DeleteAccountValues {
totpCode: string;
}
type FormValues = {
username: string;
avatar: string;
name: string;
email: string;
bio: string;
};
const ProfileView = () => {
const { t } = useLocale();
const utils = trpc.useContext();
const { data: user, isLoading } = trpc.viewer.me.useQuery();
const { data: avatar, isLoading: isLoadingAvatar } = trpc.viewer.avatar.useQuery();
const mutation = trpc.viewer.updateProfile.useMutation({
onSuccess: () => {
showToast(t("settings_updated_successfully"), "success");
utils.viewer.me.invalidate();
utils.viewer.avatar.invalidate();
setTempFormValues(null);
},
onError: () => {
showToast(t("error_updating_settings"), "error");
@@ -78,18 +89,14 @@ const ProfileView = () => {
});
const [confirmPasswordOpen, setConfirmPasswordOpen] = useState(false);
const [tempFormValues, setTempFormValues] = useState<FormValues | null>(null);
const [confirmPasswordErrorMessage, setConfirmPasswordDeleteErrorMessage] = useState("");
const [deleteAccountOpen, setDeleteAccountOpen] = useState(false);
const [hasDeleteErrors, setHasDeleteErrors] = useState(false);
const [deleteErrorMessage, setDeleteErrorMessage] = useState("");
const form = useForm<DeleteAccountValues>();
const emailMd5 = crypto
.createHash("md5")
.update(user?.email || "example@example.com")
.digest("hex");
const onDeleteMeSuccessMutation = async () => {
await utils.viewer.me.invalidate();
showToast(t("Your account was deleted"), "success");
@@ -104,7 +111,7 @@ const ProfileView = () => {
const confirmPasswordMutation = trpc.viewer.auth.verifyPassword.useMutation({
onSuccess() {
mutation.mutate(formMethods.getValues());
if (tempFormValues) mutation.mutate(tempFormValues);
setConfirmPasswordOpen(false);
},
onError() {
@@ -150,6 +157,7 @@ const ProfileView = () => {
deleteMeWithoutPasswordMutation.mutate();
}
};
const onConfirm = ({ totpCode }: DeleteAccountValues, e: BaseSyntheticEvent | undefined) => {
e?.preventDefault();
if (isCALIdentityProviver) {
@@ -160,38 +168,6 @@ const ProfileView = () => {
}
};
const formMethods = useForm({
defaultValues: {
username: "",
avatar: "",
name: "",
email: "",
bio: "",
},
});
const {
reset,
formState: { isSubmitting, isDirty },
} = formMethods;
useEffect(() => {
if (user) {
reset(
{
avatar: user?.avatar || "",
username: user?.username || "",
name: user?.name || "",
email: user?.email || "",
bio: user?.bio || "",
},
{
keepDirtyValues: true,
}
);
}
}, [reset, user]);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const passwordRef = useRef<HTMLInputElement>(null!);
@@ -203,133 +179,100 @@ const ProfileView = () => {
[ErrorCode.InternalServerError]: `${t("something_went_wrong")} ${t("please_try_again_and_contact_us")}`,
[ErrorCode.ThirdPartyIdentityProviderEnabled]: t("account_created_with_identity_provider"),
};
const onSuccessfulUsernameUpdate = async () => {
showToast(t("settings_updated_successfully"), "success");
await utils.viewer.me.invalidate();
};
const onErrorInUsernameUpdate = () => {
showToast(t("error_updating_settings"), "error");
};
if (isLoading || !user)
if (isLoading || !user || isLoadingAvatar || !avatar)
return <SkeletonLoader title={t("profile")} description={t("profile_description")} />;
const isDisabled = isSubmitting || !isDirty;
const defaultValues = {
username: user.username || "",
avatar: avatar.avatar || "",
name: user.name || "",
email: user.email || "",
bio: user.bio || "",
};
return (
<>
<Meta title={t("profile")} description={t("profile_description", { appName: APP_NAME })} />
<Form
form={formMethods}
handleSubmit={(values) => {
if (values.email !== user?.email && isCALIdentityProviver) {
<ProfileForm
key={JSON.stringify(defaultValues)}
defaultValues={defaultValues}
onSubmit={(values) => {
if (values.email !== user.email && isCALIdentityProviver) {
setTempFormValues(values);
setConfirmPasswordOpen(true);
} else {
mutation.mutate(values);
}
}}>
<div className="flex items-center">
<Controller
control={formMethods.control}
name="avatar"
render={({ field: { value } }) => (
<>
<Avatar alt="" imageSrc={value} gravatarFallbackMd5={emailMd5} size="lg" />
<div className="ml-4">
<ImageUploader
target="avatar"
id="avatar-upload"
buttonMsg={t("change_avatar")}
handleAvatarChange={(newAvatar) => {
formMethods.setValue("avatar", newAvatar, { shouldDirty: true });
}}
imageSrc={value}
/>
</div>
</>
}}
extraField={
<div className="mt-8">
<UsernameAvailabilityField
user={user}
onSuccessMutation={async () => {
showToast(t("settings_updated_successfully"), "success");
await utils.viewer.me.invalidate();
}}
onErrorMutation={() => {
showToast(t("error_updating_settings"), "error");
}}
/>
</div>
}
/>
<hr className="my-6 border-neutral-200" />
<Label>{t("danger_zone")}</Label>
{/* Delete account Dialog */}
<Dialog open={deleteAccountOpen} onOpenChange={setDeleteAccountOpen}>
<DialogTrigger asChild>
<Button
data-testid="delete-account"
color="destructive"
className="mt-1 border-2"
StartIcon={Icon.FiTrash2}>
{t("delete_account")}
</Button>
</DialogTrigger>
<DialogContent
title={t("delete_account_modal_title")}
description={t("confirm_delete_account_modal", { appName: APP_NAME })}
type="creation"
Icon={Icon.FiAlertTriangle}>
<>
<p className="mb-7">{t("delete_account_confirmation_message", { appName: APP_NAME })}</p>
{isCALIdentityProviver && (
<PasswordField
data-testid="password"
name="password"
id="password"
autoComplete="current-password"
required
label="Password"
ref={passwordRef}
/>
)}
/>
</div>
<div className="mt-8">
<UsernameAvailabilityField
user={user}
onSuccessMutation={onSuccessfulUsernameUpdate}
onErrorMutation={onErrorInUsernameUpdate}
/>
</div>
<div className="mt-8">
<TextField label={t("full_name")} {...formMethods.register("name")} />
</div>
<div className="mt-8">
<TextField label={t("email")} hint={t("change_email_hint")} {...formMethods.register("email")} />
</div>
<div className="mt-8">
<TextField label={t("about")} hint={t("bio_hint")} {...formMethods.register("bio")} />
</div>
<Button
disabled={isDisabled}
color="primary"
className="mt-8"
type="submit"
loading={mutation.isLoading}>
{t("update")}
</Button>
{user?.twoFactorEnabled && isCALIdentityProviver && (
<Form handleSubmit={onConfirm} className="pb-4" form={form}>
<TwoFactor center={false} />
</Form>
)}
<hr className="my-6 border-neutral-200" />
<Label>{t("danger_zone")}</Label>
{/* Delete account Dialog */}
<Dialog open={deleteAccountOpen} onOpenChange={setDeleteAccountOpen}>
<DialogTrigger asChild>
<Button
data-testid="delete-account"
color="destructive"
className="mt-1 border-2"
StartIcon={Icon.FiTrash2}>
{t("delete_account")}
</Button>
</DialogTrigger>
<DialogContent
title={t("delete_account_modal_title")}
description={t("confirm_delete_account_modal", { appName: APP_NAME })}
type="creation"
Icon={Icon.FiAlertTriangle}>
<>
<p className="mb-7">{t("delete_account_confirmation_message", { appName: APP_NAME })}</p>
{isCALIdentityProviver && (
<PasswordField
data-testid="password"
name="password"
id="password"
autoComplete="current-password"
required
label="Password"
ref={passwordRef}
/>
)}
{user?.twoFactorEnabled && isCALIdentityProviver && (
<Form handleSubmit={onConfirm} className="pb-4" form={form}>
<TwoFactor center={false} />
</Form>
)}
{hasDeleteErrors && <Alert severity="error" title={deleteErrorMessage} />}
<DialogFooter>
<Button
color="primary"
data-testid="delete-account-confirm"
onClick={(e) => onConfirmButton(e)}>
{t("delete_my_account")}
</Button>
<DialogClose />
</DialogFooter>
</>
</DialogContent>
</Dialog>
</Form>
{hasDeleteErrors && <Alert severity="error" title={deleteErrorMessage} />}
<DialogFooter>
<Button
color="primary"
data-testid="delete-account-confirm"
onClick={(e) => onConfirmButton(e)}>
{t("delete_my_account")}
</Button>
<DialogClose />
</DialogFooter>
</>
</DialogContent>
</Dialog>
{/* If changing email, confirm password */}
<Dialog open={confirmPasswordOpen} onOpenChange={setConfirmPasswordOpen}>
@@ -363,6 +306,73 @@ const ProfileView = () => {
);
};
const ProfileForm = ({
defaultValues,
onSubmit,
extraField,
}: {
defaultValues: FormValues;
onSubmit: (values: FormValues) => void;
extraField?: React.ReactNode;
}) => {
const { t } = useLocale();
const emailMd5 = crypto
.createHash("md5")
.update(defaultValues.email || "example@example.com")
.digest("hex");
const formMethods = useForm<FormValues>({
defaultValues,
});
const {
formState: { isSubmitting, isDirty },
} = formMethods;
const isDisabled = isSubmitting || !isDirty;
return (
<Form form={formMethods} handleSubmit={onSubmit}>
<div className="flex items-center">
<Controller
control={formMethods.control}
name="avatar"
render={({ field: { value } }) => (
<>
<Avatar alt="" imageSrc={value} gravatarFallbackMd5={emailMd5} size="lg" />
<div className="ml-4">
<ImageUploader
target="avatar"
id="avatar-upload"
buttonMsg={t("change_avatar")}
handleAvatarChange={(newAvatar) => {
formMethods.setValue("avatar", newAvatar, { shouldDirty: true });
}}
imageSrc={value || undefined}
/>
</div>
</>
)}
/>
</div>
{extraField}
<div className="mt-8">
<TextField label={t("full_name")} {...formMethods.register("name")} />
</div>
<div className="mt-8">
<TextField label={t("email")} hint={t("change_email_hint")} {...formMethods.register("email")} />
</div>
<div className="mt-8">
<TextField label={t("about")} hint={t("bio_hint")} {...formMethods.register("bio")} />
</div>
<Button disabled={isDisabled} color="primary" className="mt-8" type="submit">
{t("update")}
</Button>
</Form>
);
};
ProfileView.getLayout = getLayout;
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
-7
View File
@@ -1,4 +1,3 @@
import { UserPlan } from "@prisma/client";
import classNames from "classnames";
import { GetServerSidePropsContext } from "next";
import Link from "next/link";
@@ -14,7 +13,6 @@ import { getTeamWithMembers } from "@calcom/lib/server/queries/teams";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { Avatar, Button, EventTypeDescription, Icon } from "@calcom/ui";
import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally";
import { useToggleQuery } from "@lib/hooks/useToggleQuery";
import { inferSSRProps } from "@lib/types/inferSSRProps";
@@ -27,7 +25,6 @@ function TeamPage({ team }: TeamPageProps) {
useTheme();
const showMembers = useToggleQuery("members");
const { t } = useLocale();
useExposePlanGlobally("PRO");
const isEmbed = useIsEmbed();
const telemetry = useTelemetry();
const router = useRouter();
@@ -137,10 +134,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
if (!team) return { notFound: true } as { notFound: true };
const members = team.members.filter((member) => member.plan !== UserPlan.FREE);
team.members = members ?? [];
team.eventTypes = team.eventTypes.map((type) => ({
...type,
users: type.users.map((user) => ({
-5
View File
@@ -1,4 +1,3 @@
import { UserPlan } from "@prisma/client";
import { GetServerSidePropsContext } from "next";
import { privacyFilteredLocations, LocationObject } from "@calcom/core/location";
@@ -60,7 +59,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
username: true,
timeZone: true,
hideBranding: true,
plan: true,
brandColor: true,
darkBrandColor: true,
},
@@ -130,7 +128,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
name: user.name,
username: user.username,
hideBranding: user.hideBranding,
plan: user.plan,
timeZone: user.timeZone,
})),
});
@@ -144,8 +141,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
return {
props: {
// Team is always pro
plan: "PRO" as UserPlan,
profile: {
name: team.name || team.slug,
slug: team.slug,
+54
View File
@@ -0,0 +1,54 @@
import { expect } from "@playwright/test";
import dayjs from "@calcom/dayjs";
import { test } from "./lib/fixtures";
test.describe.configure({ mode: "parallel" });
test.describe("Availablity tests", () => {
test.beforeEach(async ({ page, users }) => {
const user = await users.create();
await user.login();
await page.goto("/availability");
// We wait until loading is finished
await page.waitForSelector('[data-testid="schedules"]');
});
test.afterEach(async ({ users }) => {
await users.deleteAll();
});
test("Date Overrides", async ({ page }) => {
await test.step("Can add a date override", async () => {
await page.locator('[data-testid="schedules"] > li a').click();
await page.locator('[data-testid="add-override"]').click();
await page.locator('[id="modal-title"]').waitFor();
await page.locator('[data-testid="incrementMonth"]').click();
await page.locator('[data-testid="day"][data-disabled="false"]').nth(0).click();
await page.locator('[data-testid="date-override-mark-unavailable"]').click();
await page.locator('[data-testid="add-override-submit-btn"]').click();
await expect(page.locator('[data-testid="date-overrides-list"] > li')).toHaveCount(1);
await page.locator('[form="availability-form"][type="submit"]').click();
});
await test.step("Date override is displayed in troubleshooter", async () => {
const response = await page.waitForResponse("**/api/trpc/viewer.availability.schedule.update?batch=1");
const json = await response.json();
// @ts-expect-error trust me bro
const date = json[0].result.data.json.schedule.availability.find((a) => !!a.date);
const troubleshooterURL = `/availability/troubleshoot?date=${dayjs(date.date).format("YYYY-MM-DD")}`;
await page.goto(troubleshooterURL);
await expect(page.locator('[data-testid="troubleshooter-busy-time"]')).toHaveCount(1);
});
});
test("Availablity pages", async ({ page }) => {
await test.step("Can add a new schedule", async () => {
await page.locator('[data-testid="new-schedule"]').click();
await page.locator('[id="name"]').fill("More working hours");
page.locator('[type="submit"]').click();
await expect(page.locator("[data-testid=availablity-title]")).toHaveValue("More working hours");
});
});
});
+1 -1
View File
@@ -13,7 +13,7 @@ test.describe.configure({ mode: "parallel" });
test.describe("free user", () => {
test.beforeEach(async ({ page, users }) => {
const free = await users.create({ plan: "FREE" });
const free = await users.create();
await page.goto(`/${free.username}`);
});
test.afterEach(async ({ users }) => {
@@ -1,7 +1,5 @@
import { expect } from "@playwright/test";
import { UserPlan } from "@prisma/client";
import { getPremiumMonthlyPlanPriceId } from "@calcom/app-store/stripepayment/lib/utils";
import stripe from "@calcom/features/ee/payments/server/stripe";
import { WEBAPP_URL } from "@calcom/lib/constants";
@@ -10,7 +10,7 @@ import {
test("dynamic booking", async ({ page, users }) => {
const pro = await users.create();
await pro.login();
const free = await users.create({ plan: "FREE" });
const free = await users.create({ username: "free" });
await page.goto(`/${pro.username}+${free.username}`);
await test.step("book an event first day in next month", async () => {
+6 -36
View File
@@ -8,10 +8,10 @@ import { test } from "./lib/fixtures";
test.describe.configure({ mode: "parallel" });
test.describe("Event Types tests", () => {
test.describe("pro user", () => {
test.describe("user", () => {
test.beforeEach(async ({ page, users }) => {
const proUser = await users.create();
await proUser.login();
const user = await users.create();
await user.login();
await page.goto("/event-types");
// We wait until loading is finished
await page.waitForSelector('[data-testid="event-types"]');
@@ -101,13 +101,13 @@ test.describe("Event Types tests", () => {
const params = new URLSearchParams(url);
expect(params.get("title")).toBe(firstTitle);
expect(params.get("slug")).toBe(firstSlug);
expect(params.get("slug")).toContain(firstSlug);
const formTitle = await page.inputValue("[name=title]");
const formSlug = await page.inputValue("[name=slug]");
expect(formTitle).toBe(firstTitle);
expect(formSlug).toBe(firstSlug);
expect(formSlug).toContain(firstSlug);
});
test("edit first event", async ({ page }) => {
const $eventTypes = page.locator("[data-testid=event-types] > li a");
@@ -120,37 +120,7 @@ test.describe("Event Types tests", () => {
});
await page.locator("[data-testid=update-eventtype]").click();
const toast = await page.waitForSelector("div[class*='data-testid-toast-success']");
await expect(toast).toBeTruthy();
});
});
test.describe("free user", () => {
test.beforeEach(async ({ page, users }) => {
const free = await users.create({ plan: "FREE" });
await free.login();
await page.goto("/event-types");
// We wait until loading is finished
await page.waitForSelector('[data-testid="event-types"]');
});
test("has at least 2 events where first is enabled", async ({ page }) => {
const $eventTypes = page.locator("[data-testid=event-types] > li a");
const count = await $eventTypes.count();
expect(count).toBeGreaterThanOrEqual(2);
});
test("edit first event", async ({ page }) => {
const $eventTypes = page.locator("[data-testid=event-types] > li a");
const firstEventTypeElement = $eventTypes.first();
await firstEventTypeElement.click();
await page.waitForNavigation({
url: (url) => {
return !!url.pathname.match(/\/event-types\/.+/);
},
});
await page.locator("[data-testid=update-eventtype]").click();
const toast = await page.waitForSelector("div[class*='data-testid-toast-success']");
await expect(toast).toBeTruthy();
expect(toast).toBeTruthy();
});
});
});
+4 -7
View File
@@ -1,6 +1,6 @@
import type { Page, WorkerInfo } from "@playwright/test";
import type Prisma from "@prisma/client";
import { Prisma as PrismaType, UserPlan } from "@prisma/client";
import { Prisma as PrismaType } from "@prisma/client";
import { hash } from "bcryptjs";
import dayjs from "@calcom/dayjs";
@@ -238,7 +238,7 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => {
};
};
type CustomUserOptsKeys = "username" | "password" | "plan" | "completedOnboarding" | "locale" | "name";
type CustomUserOptsKeys = "username" | "password" | "completedOnboarding" | "locale" | "name";
type CustomUserOpts = Partial<Pick<Prisma.User, CustomUserOptsKeys>> & { timeZone?: TimeZoneEnum };
// creates the actual user in the db.
@@ -247,13 +247,10 @@ const createUser = async (
opts?: CustomUserOpts | null
): Promise<PrismaType.UserCreateInput> => {
// build a unique name for our user
const uname = `${opts?.username ?? opts?.plan?.toLocaleLowerCase() ?? UserPlan.PRO.toLowerCase()}-${
workerInfo.workerIndex
}-${Date.now()}`;
const uname = `${opts?.username || "user"}-${workerInfo.workerIndex}-${Date.now()}`;
return {
username: uname,
name: opts?.name === undefined ? (opts?.plan ?? UserPlan.PRO).toUpperCase() : opts?.name,
plan: opts?.plan ?? UserPlan.PRO,
name: opts?.name,
email: `${uname}@example.com`,
password: await hashPassword(uname),
emailVerified: new Date(),
+2 -29
View File
@@ -1,5 +1,4 @@
import { expect } from "@playwright/test";
import { UserPlan } from "@prisma/client";
import { login } from "./fixtures/users";
import { test } from "./lib/fixtures";
@@ -13,10 +12,10 @@ test.describe("user can login & logout succesfully", async () => {
test.afterAll(async ({ users }) => {
await users.deleteAll();
});
test("login flow TRAIL user & logout using dashboard", async ({ page, users }) => {
test("login flow user & logout using dashboard", async ({ page, users }) => {
// log in trail user
await test.step("Log in", async () => {
const user = await users.create({ plan: UserPlan.TRIAL });
const user = await users.create();
await user.login();
const shellLocator = page.locator(`[data-testid=dashboard-shell]`);
@@ -24,11 +23,6 @@ test.describe("user can login & logout succesfully", async () => {
// expects the home page for an authorized user
await page.goto("/");
await expect(shellLocator).toBeVisible();
// Asserts to read the tested plan
const planLocator = shellLocator.locator(`[data-testid=plan-trial]`);
await expect(planLocator).toBeVisible();
await expect(planLocator).toHaveText("-TRIAL");
});
//
@@ -64,27 +58,6 @@ test.describe("Login and logout tests", () => {
await expect(page.locator(`[data-testid=login-form]`)).toBeVisible();
});
// Test login with all plans
const plans = [UserPlan.PRO, UserPlan.FREE];
plans.forEach((plan) => {
test(`Should login with a ${plan} account`, async ({ page, users }) => {
// Create user and login
const user = await users.create({ plan });
await user.login();
const shellLocator = page.locator(`[data-testid=dashboard-shell]`);
// expects the home page for an authorized user
await page.goto("/");
await expect(shellLocator).toBeVisible();
// Asserts to read the tested plan
const planLocator = shellLocator.locator(`[data-testid=plan-${plan.toLowerCase()}]`);
await expect(planLocator).toBeVisible();
await expect(planLocator).toHaveText(`-${plan}`);
});
});
test.describe("Login flow validations", async () => {
test("Should warn when user does not exist", async ({ page }) => {
const alertMessage = (await localize("en"))("no_account_exists");
+1 -2
View File
@@ -1,6 +1,5 @@
/* eslint-disable playwright/no-skipped-test */
import { expect } from "@playwright/test";
import { UserPlan } from "@prisma/client";
import { test } from "./lib/fixtures";
@@ -9,7 +8,7 @@ test.describe.configure({ mode: "serial" });
test.describe("Onboarding", () => {
test.describe("Onboarding v2", () => {
test("Onboarding Flow", async ({ page, users }) => {
const user = await users.create({ plan: UserPlan.TRIAL, completedOnboarding: false, name: null });
const user = await users.create({ completedOnboarding: false, name: null });
await user.login();
// tests whether the user makes it to /getting-started
+1
View File
@@ -67,6 +67,7 @@ test("add webhook & test that creating an event triggers a webhook call", async
body.payload.eventTypeId = dynamic;
body.payload.videoCallData = dynamic;
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);
@@ -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]","videoCallData":"[redacted/dynamic]","appsStatus":"[redacted/dynamic]","eventTitle":"30 min","eventDescription":null,"price":0,"currency":"usd","length":30,"bookingId":"[redacted/dynamic]","metadata":{},"status":"ACCEPTED","additionalInformation":"[redacted/dynamic]"}}
{"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":{"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,"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]"}}
+5
View File
@@ -0,0 +1,5 @@
<svg stroke="#888888" fill="none" stroke-width="2.5" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
<polyline points="15 3 21 3 21 9"></polyline>
<line x1="10" y1="14" x2="21" y2="3"></line>
</svg>

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 35 KiB

+69 -2
View File
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "يوجد حدث لا يزال في انتظار الموافقة",
"event_is_still_waiting": "لا يزال طلب الحدث في الانتظار: {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "لا يوجد مزيد من النتائج",
"no_results": "لا نتائج",
"load_more_results": "تحميل المزيد من النتائج",
"integration_meeting_id": "مُعرّف اجتماع {{integrationName}}:‏ {{meetingId}}",
"confirmed_event_type_subject": "تم التأكيد: {{eventType}} مع {{name}} في {{date}}",
@@ -248,6 +249,7 @@
"add_to_calendar": "إضافة إلى التقويم",
"add_another_calendar": "إضافة تقويم آخر",
"other": "آخر",
"email_sign_in_subject": "رابط تسجيل الدخول بك لـ{{appName}}",
"emailed_you_and_attendees": "لقد أرسلنا إليك وإلى الحضور الآخرين دعوة للتقويم عبر البريد الإلكتروني تتضمن كل التفاصيل.",
"emailed_you_and_attendees_recurring": "لقد أرسلنا إليك وإلى الحضور الآخرين دعوة تقويم عبر البريد الإلكتروني لأول هذه الأحداث المتكررة.",
"emailed_you_and_any_other_attendees": "تم إرسال هذه المعلومات إليك وإلى الحضور الآخرين عبر البريد الإلكتروني.",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "انتقل إلى بوابة الفوترة",
"need_anything_else": "هل تحتاج إلى أي شيء آخر؟",
"further_billing_help": "إذا كنت تحتاج إلى أي مساعدة إضافية بخصوص الفوترة، فإن فريق الدعم لدينا في انتظارك لتقديم المساعدة.",
"contact_our_support_team": "الاتصال بفريق الدعم لدينا",
"uh_oh": "عذرًا!",
"no_event_types_have_been_setup": "لم يقم هذا المستخدم بإعداد أي أنواع للحدث حتى الآن.",
"edit_logo": "تعديل الشعار",
@@ -420,6 +421,7 @@
"current_incorrect_password": "كلمة المرور الحالية غير صحيحة",
"password_hint_caplow": "مزيج من الحروف الكبيرة والحروف الصغيرة",
"password_hint_min": "8 أحرف كحد أدنى",
"password_hint_admin_min": "15 محرفًا كحد أدنى",
"password_hint_num": "يحتوي على رقم واحد على الأقل",
"invalid_password_hint": "يجب أن تتألف كلمة المرور على الأقل من 7 أحرف، ورقم واحد على الأقل، ومزيج من الحروف الكبيرة والصغيرة",
"incorrect_password": "كلمة المرور غير صحيحة.",
@@ -463,11 +465,14 @@
"booking_confirmation": "قم بتأكيد {{eventTypeTitle}} مع {{profileName}}",
"booking_reschedule_confirmation": "أعد جدولة {{eventTypeTitle}} مع {{profileName}}",
"in_person_meeting": "الرابط أو الاجتماع الشخصي",
"attendeeInPerson": "شخصيًا (عنوان من سيحضر)",
"inPerson": "شخصيًا (عنوان المنظم)",
"link_meeting": "رابط الاجتماع",
"phone_call": "رقم هاتف الحضور",
"your_number": "رقم هاتفك",
"phone_number": "رقم الهاتف",
"attendee_phone_number": "رقم هاتف الحضور",
"organizer_phone_number": "رقم هاتف المنظم",
"host_phone_number": "رقم هاتفك",
"enter_phone_number": "أدخل رقم الهاتف",
"reschedule": "إعادة الجدولة",
@@ -554,6 +559,10 @@
"collective": "جماعي",
"collective_description": "جدولة الاجتماعات عندما يكون جميع أعضاء الفريق المحددين متاحين.",
"duration": "المدة",
"available_durations": "المدد المتاحة",
"default_duration": "المدة الافتراضية",
"default_duration_no_options": "يرجى اختيار المدد المتاحة أولاً",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "الدقائق",
"round_robin": "الترتيب الدوري",
"round_robin_description": "نقل الاجتماعات بشكل دوري بين أعضاء الفريق المتعددين.",
@@ -619,6 +628,7 @@
"teams": "الفرق",
"team": "فريق",
"team_billing": "الفوترة الخاصة بالفريق",
"team_billing_description": "إدارة الفواتير لفريقك",
"upgrade_to_flexible_pro_title": "لقد قمنا بتغيير الفوترة الخاصة بالفرق",
"upgrade_to_flexible_pro_message": "يوجد أعضاء في فريقك من دون مقاعد. قم بترقية خطة Pro خاصتك لتوفير المقاعد الناقصة.",
"changed_team_billing_info": "بدءًا من يناير 2022، أصبحنا نفرض الرسوم على مقاعد أعضاء الفريق بشكل منفرد. حاليًا، أعضاء فريقك الذين كانت لديهم خطة Pro مجانية أصبحوا في فترة تجريبية مدتها 14 يومًا. وبمجرد انتهاء الفترة التجريبية الخاصة بهم، سيجري إخفاء هؤلاء الأعضاء من فريقك ما لم تقم بالترقية الآن.",
@@ -694,6 +704,7 @@
"hide_event_type": "إخفاء نوع الحدث",
"edit_location": "تعديل الموقع",
"into_the_future": "في المستقبل",
"when_booked_with_less_than_notice": "عند الحجز بأقل من إشعار <time></time>",
"within_date_range": "خلال فترة زمنية",
"indefinitely_into_future": "إلى أجل غير مسمى في المستقبل",
"add_new_custom_input_field": "إضافة خانة إدخال مخصص جديدة",
@@ -713,6 +724,8 @@
"delete_account_confirmation_message": "هل تريد بالتأكيد حذف حسابك على {{appName}}؟ أي شخص شاركت رابط حسابك معه لن يستطيع بعد الآن الحجز باستخدامه، وستفقد أي تفضيلات حفظتها.",
"integrations": "التكاملات",
"apps": "التطبيقات",
"apps_description": "تمكين تطبيقات مثيل Cal الخاص بك",
"apps_listing": "قائمة التطبيقات",
"category_apps": "تطبيقات {{category}}",
"app_store": "متجر التطبيقات",
"app_store_description": "الربط بين الناس، والتكنولوجيا، ومكان العمل.",
@@ -736,6 +749,7 @@
"toggle_calendars_conflict": "قم بتبديل التقويمات التي تريد التحقق من وجود تضاربات فيها لمنع حدوث حجز مزدوج.",
"select_destination_calendar": "إنشاء أحداث في",
"connect_additional_calendar": "ربط رزنامة إضافية",
"calendar_updated_successfully": "تم تحديث الرزنامة بنجاح",
"conferencing": "المؤتمرات عبر الفيديو",
"calendar": "التقويم",
"payments": "المدفوعات",
@@ -768,6 +782,7 @@
"trending_apps": "التطبيقات الرائجة",
"explore_apps": "تطبيقات {{category}}",
"installed_apps": "التطبيقات المثبتة",
"free_to_use_apps": "متاح",
"no_category_apps": "لا توجد تطبيقات {{category}}",
"no_category_apps_description_calendar": "إضافة تطبيق تقويم للتحقق من أي تضارب لمنع أي حجوزات مزدوجة",
"no_category_apps_description_conferencing": "جرب إضافة تطبيق مؤتمرات لإنشاء مكالمات فيديو مع عملائك",
@@ -806,6 +821,8 @@
"verify_wallet": "تأكيد المحفظة",
"connect_metamask": "توصيل Metamask",
"create_events_on": "إنشاء أحداث في",
"enterprise_license": "هذه هي ميزة للمؤسسات",
"enterprise_license_description": "لتمكين هذه الميزة، احصل على مفتاح نشر في وحدة التحكم {{consoleUrl}} وأضفه إلى وحدة التحكم الخاصة بك. nv باسم CALCOM_LICENSE_KEY. إذا كان لدى فريقك بالفعل ترخيص، يرجى الاتصال بـ {{supportMail}} للحصول على المساعدة.",
"missing_license": "الترخيص مفقود",
"signup_requires": "يلزم ترخيص تجاري",
"signup_requires_description": "لا تقدم شركة {{companyName}} حاليًا إصدارًا مجانيًا مفتوح المصدر لصفحة التسجيل. للحصول على حق الوصول الكامل إلى مكونات التسجيل، يجب أن تحصل على ترخيص تجاري. للاستخدام الشخصي، نوصي باستخدام منصة Prisma Data أو أي واجهة أخرى من واجهات Postgres لإنشاء الحسابات.",
@@ -897,6 +914,7 @@
"user_impersonation_heading": "انتحال شخصية مستخدم",
"user_impersonation_description": "يسمح لفريق الدعم الخاص بنا بتسجيل الدخول مؤقتًا نيابة عنك لمساعدتنا على حل أي مشاكل تبلغ عنها بسرعة.",
"team_impersonation_description": "يسمح لأعضاء فريقك بتسجيل الدخول نيابة عنك مؤقتًا.",
"allow_booker_to_select_duration": "السماح لمن يقوم بالحجز بتحديد المدة",
"impersonate_user_tip": "يتم مراجعة جميع استخدامات هذه الميزة.",
"impersonating_user_warning": "انتحال اسم مستخدم \"{{user}}\".",
"impersonating_stop_instructions": "<0>انقر هنا لإيقاف </0>.",
@@ -1014,6 +1032,9 @@
"error_removing_app": "خطأ في إزالة التطبيق",
"web_conference": "مؤتمر عبر الإنترنت",
"requires_confirmation": "يتطلب التأكيد",
"always_requires_confirmation": "دائمًا",
"requires_confirmation_threshold": "يتطلب تأكيد إذا تم حجزه مع إشعار $t({{unit}}_timeUnit){{time}}",
"may_require_confirmation": "قد يتطلب التأكيد",
"nr_event_type_one": "{{count}} نوع حدث",
"nr_event_type_other": "{{count}} من أنواع الحدث",
"add_action": "إضافة إجراء",
@@ -1101,6 +1122,9 @@
"event_limit_tab_description": "ما تواتر مرات الحجز",
"event_advanced_tab_description": "إعدادات الرزنامة والمزيد...",
"event_advanced_tab_title": "متقدم",
"event_setup_multiple_duration_error": "إعداد الحدث: تستلزم فترات متعددة خيارًا واحدًا على الأقل.",
"event_setup_multiple_duration_default_error": "إعداد الحدث: الرجاء تحديد مدة افتراضية صالحة.",
"event_setup_booking_limits_error": "يجب أن تكون حدود الحجز بترتيب تصاعدي. [يوم،أسبوع،شهر،سنة]",
"select_which_cal": "اختر أي رزنامة تريد إضافة الحجز إليها",
"custom_event_name": "اسم حدث مخصص",
"custom_event_name_description": "إنشاء أسماء أحداث مخصصة لعرضها في حدث الرزنامة",
@@ -1154,8 +1178,11 @@
"invoices": "الفواتير",
"embeds": "التضمينات",
"impersonation": "انتحال الشخصية",
"impersonation_description": "إعدادات إدارة انتحال شخصية مستخدم",
"users": "المستخدمون",
"profile_description": "إدارة الإعدادات لملفك الشخصي على {{appName}}",
"users_description": "يمكنك هنا العثور على قائمة بجميع المستخدمين",
"users_listing": "قائمة المستخدمين",
"general_description": "إدارة الإعدادات للغة والمنطقة الزمنية الخاصة بك",
"calendars_description": "قم بتهيئة كيفية تفاعل أنواع الأحداث مع التقويمات الخاصة بك",
"appearance_description": "إدارة إعدادات مظهر الحجز الخاص بك",
@@ -1360,6 +1387,7 @@
"number_sms_notifications": "رقم الهاتف (إشعارات الرسائل النصية)",
"attendee_email_workflow": "اسم الحاضر",
"attendee_email_info": "البريد الإلكتروني للشخص الحجز",
"kbar_search_placeholder": "اكتب أمرًا أو بحثًا...",
"invalid_credential": "لا! يبدو أن الصلاحية انتهت أو تم إلغاؤها. يرجى إعادة التثبيت مرة أخرى.",
"choose_common_schedule_team_event": "اختر جدولاً زمنياً مشتركاً",
"choose_common_schedule_team_event_description": "قم بتمكين هذا الخيار إذا كنت ترغب في استخدام جدول زمني مشترك بين المضيفين. عند تعطيل هذا الخيار، سيتم حجز كل مضيف على أساس جدوله الافتراضي.",
@@ -1370,5 +1398,44 @@
"test_preview": "اختبار المعاينة",
"route_to": "التوجيه إلى",
"test_preview_description": "اختبار نموذج التوجيه الخاص بك دون تقديم أي بيانات",
"test_routing": "اختبار التوجيه"
"test_routing": "اختبار التوجيه",
"payment_app_disabled": "قام المشرف بتعطيل تطبيق دفع",
"edit_event_type": "تعديل نوع الحدث",
"collective_scheduling": "جدولة جماعية",
"make_it_easy_to_book": "سهّل على فريقك الحجز عندما يكون الجميع متاحين.",
"find_the_best_person": "ابحث عن أفضل شخص متوفر، وقم بالتدوير بين أعضاء فريقك.",
"fixed_round_robin": "روبين دائري ثابت",
"add_one_fixed_attendee": "أضف حاضرًا ثابتًا وروبين مستدير من خلال عدد من الحاضرين.",
"calcom_is_better_with_team": "Cal.com أفضل مع الفرق",
"add_your_team_members": "أضف أعضاء فريقك إلى أنواع الأحداث الخاصة بك. استخدم الجدولة الجماعية لإدراج الجميع أو العثور على أنسب شخص مع جدولة روبن الدائرية.",
"booking_limit_reached": "تم الوصول إلى الحد الأقصى للحجز لهذا النوع من الأحداث",
"admin_has_disabled": "قام المشرف بتعطيل {{appName}}",
"disabled_app_affects_event_type": "قام المشرف بتعطيل {{appName}} مما يؤثر على نوع الحدث {{eventType}} لديك",
"disable_payment_app": "قام المشرف بتعطيل {{appName}} الذي يؤثر على نوع الحدث {{title}} لديك. ولا يزال بإمكان الحاضرين حجز هذا النوع من الأحداث، ولكن لن يُطالبوا بالدفع. يمكنك إخفاء نوع الحدث لمنع حدوث هذا إلى أن يقوم المشرف بتغيير طريقة الدفع لك.",
"payment_disabled_still_able_to_book": "لا يزال بإمكان الحاضرين حجز هذا النوع من الأحداث، ولكن لن يُطالبوا بالدفع. يمكنك إخفاء نوع الحدث لمنع حدوث هذا إلى أن يقوم المشرف بتغيير طريقة الدفع لك.",
"app_disabled_with_event_type": "عطّل المشرف {{appName}} مما يؤثر على نوع الحدث {{title}} لديك.",
"app_disabled_video": "عطّل المشرف {{appName}} الذي قد يؤثر على أنواع الأحداث لديك. إذا كان لديك أنواع من الأحداث مع {{appName}} كموقع، سيكون افتراضيًا من أجل فيديو Cal.",
"app_disabled_subject": "تم تعطيل {{appName}}",
"navigate_installed_apps": "الذهاب إلى التطبيقات المثبتة",
"disabled_calendar": "إذا كان لديك تقويم آخر مثبت، ستُضاف الحجوزات الجديدة إليه. إذا لم يكن الأمر كذلك، قم إذن بتوصيل تقويم جديد لكيلا تفوت أي حجوزات جديدة.",
"enable_apps": "تمكين التطبيقات",
"enable_apps_description": "تمكين التطبيقات التي يمكن للمستخدمين دمجها مع Cal.com",
"app_is_enabled": "تم تمكين {{appName}}",
"app_is_disabled": "تم تعطيل {{appName}}",
"keys_have_been_saved": "تم حفظ المفاتيح",
"disable_app": "تعطيل التطبيق",
"disable_app_description": "يمكن لتعطيل هذا التطبيق أن يسبب مشاكل مع كيفية تفاعل المستخدمين مع Cal",
"edit_keys": "تحرير المفاتيح",
"admin_apps_description": "تمكين تطبيقات مثيل Cal الخاص بك",
"no_available_apps": "لا توجد تطبيقات متاحة",
"no_available_apps_description": "الرجاء التأكد من وجود تطبيقات في نشرك تحت 'حزم/متجر تطبيقات'",
"no_apps": "لا توجد تطبيقات مفعلة في هذا المظهر من Cal",
"apps_settings": "إعدادات التطبيقات",
"fill_this_field": "يرجى ملء هذا الحقل",
"options": "خيارات",
"enter_option": "أدخل خيار {{index}}",
"add_an_option": "إضافة خيار",
"radio": "الراديو",
"event_type_duplicate_copy_text": "{{slug}}-نسخ",
"set_as_default": "تعيين كافتراضي"
}
@@ -383,7 +383,6 @@
"go_to_billing_portal": "Přejít na fakturační portál",
"need_anything_else": "Potřebujete ještě něco?",
"further_billing_help": "Pokud potřebujete další pomoc s fakturací, náš tým podpory je zde, aby vám pomohl.",
"contact_our_support_team": "Kontaktovat tým podpory",
"uh_oh": "Ale ne!",
"no_event_types_have_been_setup": "Tento uživatel zatím nezaložil žádné typy událostí.",
"edit_logo": "Upravit logo",
+13 -12
View File
@@ -14,7 +14,7 @@
"event_request_cancelled": "Ihr geplanter Termin wurde abgesagt",
"organizer": "Organisator",
"need_to_reschedule_or_cancel": "Möchten Sie einen neuen Termin finden oder absagen?",
"cancellation_reason": "Grund für Ihre Absage",
"cancellation_reason": "Grund für Ihre Absage (optional)",
"cancellation_reason_placeholder": "Warum sagen Sie ab? (optional)",
"rejection_reason": "Grund für die Ablehnung",
"rejection_reason_title": "Die Buchungsanfrage ablehnen?",
@@ -370,7 +370,6 @@
"go_to_billing_portal": "Zum Rechnungsportal gehen",
"need_anything_else": "Brauchen Sie etwas anderes?",
"further_billing_help": "Wenn Sie weitere Hilfe bei der Rechnungsstellung benötigen, hilft Ihnen unser Support-Team gerne weiter.",
"contact_our_support_team": "Kontaktieren Sie unser Support-Team",
"uh_oh": "Uh oh!",
"no_event_types_have_been_setup": "Dieser Benutzer hat noch keine Termintypen eingerichtet.",
"edit_logo": "Logo bearbeiten",
@@ -406,7 +405,7 @@
"forgotten_secret_description": "Wenn Sie dieses Passwort verloren oder vergessen haben, können Sie es ändern. Bitte beachten Sie, dass alle Integrationen, die dieses Geheimnis nutzen, aktualisiert werden müssen",
"current_incorrect_password": "Aktuelles Passwort ist falsch",
"password_hint_caplow": "Mischung aus Groß- und Kleinbuchstaben",
"password_hint_min": "Mindestens 7 Zeichen lang",
"password_hint_min": "Mindestens 8 Zeichen lang",
"password_hint_num": "Enthält mindestens 1 Zahl",
"invalid_password_hint": "Das Passwort muss mindestens 7 Zeichen lang sein und mindestens eine Nummer beinhalten, sowie eine Mischung aus Groß- und Kleinbuchstaben sein",
"incorrect_password": "Passwort ist falsch",
@@ -746,6 +745,7 @@
"trending_apps": "Angesagte Apps",
"explore_apps": "{{category}} Apps",
"installed_apps": "Installierte Apps",
"free_to_use_apps": "Kostenlos",
"no_category_apps": "Keine {{category}} Apps",
"no_category_apps_description_calendar": "Fügen Sie eine Kalender-App hinzu, in der nach Konflikten gesucht werden soll, um Doppelbuchungen zu vermeiden",
"no_category_apps_description_conferencing": "Versuchen Sie, eine Konferenz-App hinzuzufügen, um Videoanrufe mit Ihren Kunden zu integrieren",
@@ -762,7 +762,7 @@
"analytics": "Analyse",
"empty_installed_apps_headline": "Keine Apps installiert",
"empty_installed_apps_description": "Apps erlauben es Ihnen, Ihren Arbeitsfluss zu verbessern und Ihre Planung viel besser zu gestalten.",
"empty_installed_apps_button": "Entdecken Sie den App Store",
"empty_installed_apps_button": "Durchsuchen Sie den App Store",
"manage_your_connected_apps": "Verwalten Sie Ihre installierten Apps oder ändern Sie die Einstellungen",
"browse_apps": "Apps durchsuchen",
"features": "Funktionen",
@@ -784,6 +784,8 @@
"verify_wallet": "Wallet verifizieren",
"connect_metamask": "Metamask verbinden",
"create_events_on": "Erstelle Termine in:",
"enterprise_license": "Das ist eine Enterprise-Funktion",
"enterprise_license_description": "Um diese Funktion zu aktivieren, holen Sie sich einen Deployment-Schlüssel von der {{consoleUrl}}-Konsole und fügen Sie ihn als CALCOM_LICENSE_KEY zu Ihrer .env hinzu. Wenn Ihr Team bereits eine Lizenz hat, wenden Sie sich bitte an {{supportMail}} für Hilfe.",
"missing_license": "Lizenz fehlt",
"signup_requires": "Kommerzielle Lizenz erforderlich",
"next_steps": "Nächste Schritte",
@@ -894,6 +896,7 @@
"generate_api_key": "Api Key generieren",
"your_unique_api_key": "Ihr eindeutiger API-Key",
"copy_safe_api_key": "Kopieren Sie diesen API-Schlüssel und speichern Sie ihn an einem sicheren Ort. Wenn Sie diesen Schlüssel verlieren, müssen Sie einen neuen generieren.",
"zapier_setup_instructions": "<0>Gehen Sie zu: <1>Zapier-Einladungs-Link</1></0><1>Logge Sie sich in Ihr Zapier-Konto ein und erstelle einen neuen Zap.</1><2>Wählen Sie Cal.com als ihre Trigger-App aus. Wählen Sie auch ein Auslöserereignis.</2><3>Wähle Sie ihr Konto aus und geben dann ihren eindeutigen API-Key ein.</3><4>Teste Sie den Trigger.</4><5>Alles fertig!</5>",
"install_zapier_app": "Bitte installieren Sie zuerst die Zapier-App im App Store.",
"connect_apple_server": "Mit Apple-Server verbinden",
"connect_caldav_server": "Mit CalDav-Server verbinden",
@@ -1300,10 +1303,6 @@
"saml_sp_acs_url_copied": "ACS-URL kopiert!",
"saml_sp_entity_id_copied": "SP-Entitäts-ID kopiert!",
"saml_btn_configure": "Konfigurieren",
"kbar_search_placeholder": "Geben Sie einen Befehl ein oder suchen Sie...",
"free_to_use_apps": "Kostenlos",
"enterprise_license": "Das ist eine Enterprise-Funktion",
"enterprise_license_description": "Um diese Funktion zu aktivieren, holen Sie sich einen Deployment-Schlüssel von der {{consoleUrl}}-Konsole und fügen Sie ihn als CALCOM_LICENSE_KEY zu Ihrer .env hinzu. Wenn Ihr Team bereits eine Lizenz hat, wenden Sie sich bitte an {{supportMail}} für Hilfe.",
"add_calendar": "Kalender hinzufügen",
"limit_future_bookings": "Zukünftige Termine begrenzen",
"limit_future_bookings_description": "Begrenzt, wie weit in die Zukunft dieser Termin gebucht werden kann",
@@ -1327,7 +1326,8 @@
"number_sms_notifications": "Telefonnummer (SMS-Benachrichtigungen)",
"attendee_email_workflow": "Teilnehmer E-Mail",
"attendee_email_info": "Die E-Mail-Adresse der buchenden Person",
"invalid_credential": "Oh nein! Es sieht so aus, als ob die Berechtigung abgelaufen sind oder entzogen wurden. Bitte erneut installieren.",
"kbar_search_placeholder": "Geben Sie einen Befehl ein oder suchen Sie...",
"invalid_credential": "Oh nein! Es sieht so aus, als ob die Berechtigung abgelaufen ist oder entzogen wurden. Bitte erneut installieren.",
"choose_common_schedule_team_event": "Gemeinsamen Zeitplan auswählen",
"choose_common_schedule_team_event_description": "Aktivieren Sie dies, wenn Sie einen gemeinsamen Zeitplan zwischen Hosts verwenden möchten. Wenn deaktiviert, ist jeder Host basierend auf seinen Standardplan buchbar.",
"reason": "Grund",
@@ -1335,7 +1335,8 @@
"sender_id_error_message": "Nur Buchstaben, Zahlen und Leerzeichen erlaubt (max. 11 Zeichen)",
"test_routing_form": "Testweiterleitungsformular",
"test_preview": "Vorschau testen",
"route_to": "Route zu",
"test_preview_description": "Testen Sie Ihr Leitungsformular, ohne Daten zu senden",
"test_routing": "Testweiterleitung"
"route_to": "Weiterleiten zu",
"test_preview_description": "Testen Sie Ihr Weiterleitungsformular, ohne Daten zu senden",
"test_routing": "Testweiterleitung",
"admin_apps_description": "Hier findest du eine Auflistung deiner Apps"
}
@@ -0,0 +1,111 @@
{
"day_one": "{{count}} ημέρα",
"day_other": "{{count}} ημέρες",
"second_one": "{{count}} δευτερόλεπτο",
"second_other": "{{count}} δευτερόλεπτα",
"upgrade_now": "Αναβαθμίστε τώρα",
"accept_invitation": "Αποδοχή Πρόσκλησης",
"have_any_questions": "Έχετε ερωτήσεις? Είμαστε εδώ για να βοηθήσουμε.",
"reset_password_subject": "{{appName}}: Οδηγίες επαναφοράς κωδικού πρόσβασης",
"event_declined_subject": "Απορρίφθηκε: {{eventType}} με {{name}} στις {{date}}",
"event_cancelled_subject": "Ακύρωση: {{eventType}} με {{name}} στις {{date}}",
"need_to_reschedule_or_cancel": "Χρειάζεται να επαναπρογραμματίσετε ή να ακυρώσετε;",
"cancellation_reason": "Λόγος ακύρωσης (προαιρετικό)",
"rejection_reason": "Λόγος απόρριψης",
"rejection_reason_title": "Απόρριψη του αιτήματος κράτησης;",
"rejection_confirmation": "Απόρριψη κράτησης",
"error_message": "Το μήνυμα σφάλματος ήταν: '{{errorMessage}}'",
"refund_failed_subject": "Η επιστροφή χρημάτων απέτυχε: {{name}} - {{date}} - {{eventType}}",
"refund_failed": "Η επιστροφή χρημάτων για την εκδήλωση {{eventType}} με {{userName}} στις {{date}} απέτυχε.",
"a_refund_failed": "Αποτυχία επιστροφής χρημάτων",
"awaiting_payment_subject": "Σε αναμονή Πληρωμής: {{eventType}} με {{name}} στις {{date}}",
"meeting_awaiting_payment": "Η πληρωμή της συνάντησής σας εκκρεμεί",
"help": "Βοήθεια",
"price": "Τιμή",
"payment": "Πληρωμή",
"missing_card_fields": "Λείπουν τα πεδία της κάρτας",
"pay_now": "Πληρωμή τώρα",
"terms_summary": "Περίληψη όρων",
"open_env": "Ανοίξτε το .env και συμφωνήστε με την Άδεια χρήσης μας",
"env_changed": "Έχω αλλάξει το .env μου",
"accept_license": "Αποδοχή Άδειας",
"no_more_results": "Δεν υπάρχουν άλλα αποτελέσματα",
"no_results": "Δεν υπάρχουν αποτελέσματα",
"load_more_results": "Φόρτωση περισσότερων αποτελεσμάτων",
"integration_meeting_id": "ID συνάντησης {{integrationName}} : {{meetingId}}",
"confirmed_event_type_subject": "Επιβεβαίωση: {{eventType}} με {{name}} στις {{date}}",
"new_event_request": "Νέο αίτημα εκδήλωσης: {{attendeeName}} - {{date}} - {{eventType}}",
"confirm_or_reject_request": "Επιβεβαιώστε ή απορρίψτε το αίτημα",
"check_bookings_page_to_confirm_or_reject": "Ελέγξτε τη σελίδα κρατήσεων για να επιβεβαιώσετε ή να απορρίψετε την κράτηση.",
"event_awaiting_approval": "Ένα γεγονός περιμένει την έγκρισή σας",
"event_type": "Τύπος Συμβάντος",
"meeting_password": "Συνθηματικό Συνάντησης",
"meeting_url": "URL Συνάντησης",
"meeting_request_rejected": "Το αίτημα συνάντησής σας απορρίφθηκε",
"hi": "Γεια",
"manage_this_team": "Διαχείριση αυτής της ομάδας",
"team_info": "Πληροφορίες Ομάδας",
"hidden_team_member_title": "Είστε κρυμμένοι σε αυτήν την ομάδα",
"edit_webhook": "Επεξεργασία Webhook",
"delete_webhook": "Διαγραφή Webhook",
"webhook_enabled": "Ενεργοποιημένο Webhook",
"webhook_disabled": "Απενεργοποιημένο Webhook",
"webhook_response": "Απάντηση Webhook",
"webhook_test": "Έλεγχος Webhook",
"webhook_created_successfully": "Το Webhook δημιουργήθηκε επιτυχώς!",
"webhook_updated_successfully": "Το Webhook ενημερώθηκε επιτυχώς!",
"webhook_removed_successfully": "Το Webhook αφαιρέθηκε επιτυχώς!",
"dismiss": "Παράβλεψη",
"no_data_yet": "Δεν υπάρχουν δεδομένα ακόμη",
"upcoming": "Επερχόμενα",
"recurring": "Επαναλαμβανόμενα",
"past": "Παρελθοντικά",
"choose_a_file": "Επιλογή αρχείου...",
"upload_image": "Μεταφόρτωση εικόνας",
"username": "Όνομα χρήστη",
"is_still_available": "είναι ακόμα διαθέσιμο.",
"documentation": "Τεκμηρίωση",
"blog": "Ιστολόγιο",
"blog_description": "Διαβάστε τα τελευταία μας νέα και άρθρα",
"popular_pages": "Δημοφιλείς σελίδες",
"register_now": "Εγγραφή τώρα",
"register": "Εγγραφή",
"page_doesnt_exist": "Η σελίδα δεν υπάρχει.",
"check_spelling_mistakes_or_go_back": "Έλεγχος για ορθογραφικά λάθη ή επιστροφή στην προηγούμενη σελίδα.",
"404_page_not_found": "404: Η σελίδα δεν βρέθηκε.",
"getting_started": "Ξεκινήστε",
"already_have_an_account": "Έχετε ήδη λογαριασμό;",
"create_account": "Δημιουργία Λογαριασμού",
"confirm_password": "Επιβεβαίωση κωδικού πρόσβασης",
"create_your_account": "Δημιουργία λογαριασμού",
"sign_up": "Εγγραφή",
"youve_been_logged_out": "Έχετε αποσυνδεθεί",
"hope_to_see_you_soon": "Ελπίζουμε να σας ξαναδούμε σύντομα!",
"please_try_again_and_contact_us": "Παρακαλούμε δοκιμάστε ξανά και επικοινωνήστε μαζί μας αν το πρόβλημα παραμένει.",
"no_account_exists": "Δεν υπάρχει λογαριασμός που να ταιριάζει με τη διεύθυνση email.",
"2fa_enter_six_digit_code": "Εισάγετε τον εξαψήφιο κωδικό από την εφαρμογή ελέγχου ταυτότητας παρακάτω.",
"create_an_account": "Δημιουργία λογαριασμού",
"dont_have_an_account": "Δεν έχετε λογαριασμό;",
"sign_in_account": "Συνδεθείτε στο λογαριασμό σας",
"sign_in": "Είσοδος",
"connect": "Σύνδεση",
"try_for_free": "Δοκιμάστε δωρεάν",
"add_to_calendar": "Προσθήκη στο ημερολόγιο",
"add_another_calendar": "Προσθήκη άλλου ημερολογίου",
"meeting_is_scheduled": "Η συνάντηση έχει προγραμματιστεί",
"submitted": "Η κράτησή σας έχει υποβληθεί",
"reset_password": "Επαναφορά Κωδικού Πρόσβασης",
"change_your_password": "Αλλαγή κωδικού πρόσβασης",
"show_password": "Εμφάνιση κωδικού πρόσβασης",
"hide_password": "Απόκρυψη κωδικού πρόσβασης",
"try_again": "Δοκιμάστε ξανά",
"sunday_time_error": "Μη έγκυρη ώρα την Κυριακή",
"monday_time_error": "Μη έγκυρη ώρα τη Δευτέρα",
"tuesday_time_error": "Μη έγκυρη ώρα την Τρίτη",
"wednesday_time_error": "Μη έγκυρη ώρα την Τετάρτη",
"thursday_time_error": "Μη έγκυρη ώρα την Πέμπτη",
"friday_time_error": "Μη έγκυρη ώρα την Παρασκευή",
"saturday_time_error": "Μη έγκυρη ώρα το Σάββατο",
"no_meeting_found": "Δε βρέθηκε συνάντηση",
"bookings": "Κρατήσεις"
}
+57 -14
View File
@@ -6,7 +6,7 @@
"second_other": "{{count}} seconds",
"upgrade_now": "Upgrade now",
"accept_invitation": "Accept Invitation",
"calcom_explained": "{{appName}} is the open source Calendly alternative putting you in control of your own data, workflow and appearance.",
"calcom_explained": "{{appName}} provides scheduling infrastructure for absolutely everyone.",
"have_any_questions": "Have questions? We're here to help.",
"reset_password_subject": "{{appName}}: Reset password instructions",
"event_declined_subject": "Declined: {{eventType}} with {{name}} at {{date}}",
@@ -303,6 +303,7 @@
"cancelling_all_recurring": "These are all remaining instances in the recurring event.",
"error_with_status_code_occured": "An error with status code {{status}} occurred.",
"booking_already_cancelled": "This booking was already cancelled",
"booking_already_accepted_rejected": "This booking was already accepted or rejected",
"go_back_home": "Go back home",
"or_go_back_home": "Or go back home",
"no_availability": "Unavailable",
@@ -385,7 +386,8 @@
"go_to_billing_portal": "Go to the billing portal",
"need_anything_else": "Need anything else?",
"further_billing_help": "If you need any further help with billing, our support team are here to help.",
"contact_our_support_team": "Contact our support team",
"contact": "Contact",
"our_support_team": "our support team",
"uh_oh": "Uh oh!",
"no_event_types_have_been_setup": "This user hasn't set up any event types yet.",
"edit_logo": "Edit logo",
@@ -750,7 +752,7 @@
"toggle_calendars_conflict": "Toggle the calendars you want to check for conflicts to prevent double bookings.",
"select_destination_calendar": "Create events on",
"connect_additional_calendar": "Connect additional calendar",
"calendar_updated_successfully":"Calendar updated successfully",
"calendar_updated_successfully": "Calendar updated successfully",
"conferencing": "Conferencing",
"calendar": "Calendar",
"payments": "Payments",
@@ -791,7 +793,7 @@
"no_category_apps_description_analytics": "Add an analytics app for your booking pages",
"no_category_apps_description_automation": "Add an automation app to use",
"no_category_apps_description_other": "Add any other type of app to do all sorts of things",
"installed_app_calendar_description": "Set the calendar(s) to check for conflicts to prevent double bookings.",
"installed_app_calendar_description": "Set the calendars to check for conflicts to prevent double bookings.",
"installed_app_conferencing_description": "Add your favourite video conferencing apps for your meetings",
"installed_app_payment_description": "Configure which payment processing services to use when charging your clients.",
"installed_app_analytics_description": "Configure which analytics apps to use for your booking pages",
@@ -918,7 +920,7 @@
"allow_booker_to_select_duration": "Allow booker to select duration",
"impersonate_user_tip": "All uses of this feature is audited.",
"impersonating_user_warning": "Impersonating username \"{{user}}\".",
"impersonating_stop_instructions": "<0>Click Here to stop</0>.",
"impersonating_stop_instructions": "Click here to stop",
"event_location_changed": "Updated - Your event changed the location",
"location_changed_event_type_subject": "Location Changed: {{eventType}} with {{name}} at {{date}}",
"current_location": "Current Location",
@@ -940,7 +942,7 @@
"generate_api_key_description": "Generate an API key to use with {{appName}} at",
"your_unique_api_key": "Your unique API key",
"copy_safe_api_key": "Copy this API key and save it somewhere safe. If you lose this key you have to generate a new one.",
"zapier_setup_instructions": "<0>Log into your Zapier account and create a new Zap.</0><1>Select {{appName}} as your Trigger app. Also choose a Trigger event.</1><2>Choose your account and then enter your Unique API Key.</2><3>Test your Trigger.</3><4>You're set!</4>",
"zapier_setup_instructions": "<0>Log into your Zapier account and create a new Zap.</0><1>Select Cal.com as your Trigger app. Also choose a Trigger event.</1><2>Choose your account and then enter your Unique API Key.</2><3>Test your Trigger.</3><4>You're set!</4>",
"install_zapier_app": "Please first install the Zapier App in the app store.",
"connect_apple_server": "Connect to Apple Server",
"connect_caldav_server": "Connect to CalDav (Beta)",
@@ -1326,6 +1328,8 @@
"booking_confirmation_success": "Booking confirmation succeeded",
"booking_rejection_success": "Booking rejection succeeded",
"booking_confirmation_fail": "Booking confirmation failed",
"booking_tentative": "This booking is tentative",
"booking_accept_intent": "Oops, I want to accept",
"we_wont_show_again": "We won't show this again",
"couldnt_update_timezone": "We couldn't update the timezone",
"updated_timezone_to": "Updated timezone to {{formattedCurrentTz}}",
@@ -1389,9 +1393,6 @@
"attendee_email_workflow": "Attendee email",
"attendee_email_info": "The person booking's email",
"kbar_search_placeholder": "Type a command or search...",
"free_to_use_apps": "Free",
"enterprise_license": "This is an enterprise feature",
"enterprise_license_description": "To enable this feature, get a deployment key at {{consoleUrl}} console and add it to your .env as CALCOM_LICENSE_KEY. If your team already has a license, please contact {{supportMail}} for help.",
"invalid_credential": "Oh no! Looks like permission expired or was revoked. Please reinstall again.",
"choose_common_schedule_team_event": "Choose a common schedule",
"choose_common_schedule_team_event_description": "Enable this if you want to use a common schedule between hosts. When disabled, each host will be booked based on their default schedule.",
@@ -1403,8 +1404,8 @@
"route_to": "Route to",
"test_preview_description": "Test your routing form without submitting any data",
"test_routing": "Test Routing",
"booking_limit_reached": "Booking Limit for this event type has been reached",
"fill_this_field": "Please fill in this field",
"payment_app_disabled": "An admin has disabled a payment app",
"edit_event_type": "Edit event type",
"collective_scheduling": "Collective Scheduling",
"make_it_easy_to_book": "Make it easy to book your team when everyone is available.",
"find_the_best_person": "Find the best person available and cycle through your team.",
@@ -1412,12 +1413,54 @@
"add_one_fixed_attendee": "Add one fixed attendee and round robin through a number of attendees.",
"calcom_is_better_with_team": "Cal.com is better with teams",
"add_your_team_members": "Add your team members to your event types. Use collective scheduling to include everyone or find the most suitable person with round robin scheduling.",
"booking_limit_reached":"Booking Limit for this event type has been reached",
"booking_limit_reached": "Booking Limit for this event type has been reached",
"admin_has_disabled": "An admin has disabled {{appName}}",
"disabled_app_affects_event_type": "An admin has disabled {{appName}} which affects your event type {{eventType}}",
"disable_payment_app": "The admin has disabled {{appName}} which affects your event type {{title}}. Attendees are still able to book this type of event but will not be prompted to pay. You may hide hide the event type to prevent this until your admin renables your payment method.",
"payment_disabled_still_able_to_book": "Attendees are still able to book this type of event but will not be prompted to pay. You may hide hide the event type to prevent this until your admin reenables your payment method.",
"app_disabled_with_event_type": "The admin has disabled {{appName}} which affects your event type {{title}}.",
"app_disabled_video": "The admin has disabled {{appName}} which may affect your event types. If you have event types with {{appName}} as the location then it will default to Cal Video.",
"app_disabled_subject": "{{appName}} has been disabled",
"navigate_installed_apps": "Go to installed apps",
"disabled_calendar": "If you have another calendar installed new bookings will be added to it. If not then connect a new calendar so you do not miss any new bookings.",
"enable_apps": "Enable Apps",
"enable_apps_description": "Enable apps that users can integrate with Cal.com",
"app_is_enabled": "{{appName}} is enabled",
"app_is_disabled": "{{appName}} is disabled",
"keys_have_been_saved": "Keys have been saved",
"disable_app": "Disable App",
"disable_app_description": "Disabling this app could cause problems with how your users interact with Cal",
"edit_keys": "Edit Keys",
"admin_apps_description": "Enable apps for your instance of Cal",
"no_available_apps": "There are no available apps",
"no_available_apps_description": "Please ensure there are apps in your deployment under 'packages/app-store'",
"no_apps": "There are no apps enabled in this instance of Cal",
"apps_settings": "Apps settings",
"fill_this_field": "Please fill in this field",
"options": "Options",
"enter_option": "Enter Option {{index}}",
"add_an_option": "Add an option",
"radio": "Radio",
"kbar_search_placeholder" : "Start typing to search",
"set_as_default": "Set as default"
"date_overrides": "Date overrides",
"date_overrides_subtitle": "Add dates when your availability changes from your daily hours.",
"date_overrides_info": "Date overrides are archived automatically after the date has passed",
"date_overrides_dialog_which_hours": "Which hours are you free?",
"date_overrides_dialog_which_hours_unavailable": "Which hours are you busy?",
"date_overrides_dialog_title": "Select the dates to override",
"date_overrides_unavailable": "Unavailable all day",
"date_overrides_mark_all_day_unavailable_one": "Mark unavailable (All day)",
"date_overrides_mark_all_day_unavailable_other": "Mark unavailable on selected dates",
"date_overrides_add_btn": "Add Override",
"date_overrides_update_btn": "Update Override",
"event_type_duplicate_copy_text": "{{slug}}-copy",
"set_as_default": "Set as default",
"hide_eventtype_details": "Hide EventType Details",
"verification_code_sent": "Verification code sent",
"verified_successfully": "Verified successfully",
"wrong_code": "Wong verification code",
"not_verified": "Not yet verified",
"no_availability_in_month": "No availability in {{month}}",
"view_next_month": "View next month",
"send_code" : "Send code",
"number_verified": "Number Verified"
}
@@ -16,8 +16,8 @@
"event_request_cancelled": "Tu evento programado fue cancelado",
"organizer": "Organizador",
"need_to_reschedule_or_cancel": "¿Necesita reprogramar o cancelar?",
"cancellation_reason": "Motivo de la cancelación",
"cancellation_reason_placeholder": "¿Por qué razón estás cancelando? (opcional)",
"cancellation_reason": "Motivo de la cancelación (opcional)",
"cancellation_reason_placeholder": "¿Por qué razón está cancelando?",
"rejection_reason": "Motivo del rechazo",
"rejection_reason_title": "¿Rechazar la solicitud de reserva?",
"rejection_reason_description": "¿Estás seguro de que deseas rechazar la reserva? Le haremos saber a la persona que ha intentado reservar. Puedes indicar una razón a continuación.",
@@ -383,7 +383,6 @@
"go_to_billing_portal": "Ir al portal de facturación",
"need_anything_else": "¿Necesita algo más?",
"further_billing_help": "Si necesita más ayuda con la facturación, nuestro equipo de soporte está aquí para ayudarlo.",
"contact_our_support_team": "Contacta con nuestro equipo de soporte",
"uh_oh": "Uh oh!",
"no_event_types_have_been_setup": "Este usuario aún no ha configurado ningún tipo de evento.",
"edit_logo": "Cambiar la marca",
@@ -1228,8 +1227,8 @@
"exchange_authentication_ntlm": "Autenticación NTLM",
"exchange_compression": "Compresión GZip",
"routing_forms_description": "Aquí puedes ver todos los formularios y rutas que has creado.",
"routing_forms_send_email_owner": "Enviar email al propietario",
"routing_forms_send_email_owner_description": "Envía un correo electrónico al propietario cuando se envía el formulario",
"routing_forms_send_email_owner": "Enviar correo electrónico al propietario",
"routing_forms_send_email_owner_description": "Mande un correo electrónico al propietario cuando se envía el formulario",
"add_new_form": "Añadir formulario nuevo",
"form_description": "Crea tu formulario para dirigirlo a un agente de reservas",
"copy_link_to_form": "Copiar enlace al formulario",
@@ -1361,13 +1360,13 @@
"attendee_email_workflow": "Correo electrónico del asistente",
"attendee_email_info": "El correo electrónico de la persona que reserva",
"invalid_credential": "¡Oh no! Parece que el permiso ha caducado o se ha revocado. Vuelva a instalarlo.",
"choose_common_schedule_team_event": "Elija un horario común",
"choose_common_schedule_team_event_description": "Habilite esto si desea utilizar un cronograma común entre anfitriones. Cuando esté desactivado, cada anfitrión se reservará con base en su horario predeterminado.",
"choose_common_schedule_team_event": "Elige un horario común para todos",
"choose_common_schedule_team_event_description": "Habilite esto si desea utilizar un cronograma común entre anfitriones. Cuando esté desactivado, cada anfitrión se reservará en relación a su horario predeterminado.",
"reason": "Motivo",
"sender_id": "ID del remitente",
"sender_id_error_message": "Sólo se permiten letras, números y espacios (máx. 11 caracteres)",
"test_routing_form": "Prueba de formulario de ruta",
"test_preview": "Vista previa de prueba",
"test_preview": "Vista previa de la prueba",
"route_to": "Ruta hacia",
"test_preview_description": "Pruebe su formulario de ruta sin enviar ningún dato",
"test_routing": "Prueba de ruta"
+42 -12
View File
@@ -16,8 +16,8 @@
"event_request_cancelled": "Votre événement programmé a été annulé",
"organizer": "Organisateur",
"need_to_reschedule_or_cancel": "Besoin de reprogrammer ou d'annuler ?",
"cancellation_reason": "Motif de l'annulation",
"cancellation_reason_placeholder": "Pourquoi annulez-vous ? (facultatif)",
"cancellation_reason": "Motif de l'annulation (facultatif)",
"cancellation_reason_placeholder": "Pourquoi annulez-vous ?",
"rejection_reason": "Motif de rejet",
"rejection_reason_title": "Rejeter la demande de réservation ?",
"rejection_reason_description": "Êtes-vous sûr de vouloir rejeter la réservation ? Nous informerons la personne qui a essayé de réserver. Vous pouvez fournir une raison ci-dessous.",
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "Un événement est toujours en attente d'approbation",
"event_is_still_waiting": "La demande d'événement est toujours en attente : {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "Plus de résultats",
"no_results": "Aucun résultat",
"load_more_results": "Charger plus de résultats",
"integration_meeting_id": "Identifiant de la réunion {{integrationName}} : {{meetingId}}",
"confirmed_event_type_subject": "Confirmé : {{eventType}} avec {{name}} le {{date}}",
@@ -248,6 +249,7 @@
"add_to_calendar": "Ajouter au calendrier",
"add_another_calendar": "Ajouter un autre calendrier",
"other": "Autre",
"email_sign_in_subject": "Votre lien de connexion pour {{appName}}",
"emailed_you_and_attendees": "Nous vous avons envoyé, à vous et aux autres participants, une invitation par e-mail avec tous les détails.",
"emailed_you_and_attendees_recurring": "Nous vous avons envoyé, à vous et aux autres participants, une invitation au calendrier pour le premier de ces événements récurrents.",
"emailed_you_and_any_other_attendees": "Ces informations vous ont été envoyées par e-mail, ainsi qu'à tous les autres participants.",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "Accéder au portail de facturation",
"need_anything_else": "Besoin d'autre chose ?",
"further_billing_help": "Si vous avez besoin d'aide pour la facturation, notre équipe d'assistance est là pour vous aider.",
"contact_our_support_team": "Contactez notre équipe d'assistance",
"uh_oh": "Oups !",
"no_event_types_have_been_setup": "Cet utilisateur n'a pas encore configuré de types d'événements.",
"edit_logo": "Modifier le logo",
@@ -419,7 +420,8 @@
"forgotten_secret_description": "Si vous avez perdu ou oublié ce code secret, vous pouvez le modifer, mais soyez conscient que toutes les intégrations l'utilisant devront être actualisées",
"current_incorrect_password": "Le mot de passe actuel est incorrect",
"password_hint_caplow": "Mélange de lettres majuscules et minuscules",
"password_hint_min": "Minimum 7 caractères",
"password_hint_min": "Minimum 8 caractères",
"password_hint_admin_min": "Minimum 15 caractères",
"password_hint_num": "Contient au moins 1 chiffre",
"invalid_password_hint": "Le mot de passe doit comporter un minimum de 7 caractères, dont au moins un chiffre, et avoir un mélange de lettres majuscules et minuscules",
"incorrect_password": "Le mot de passe est incorrect.",
@@ -463,11 +465,14 @@
"booking_confirmation": "Confirmez votre {{eventTypeTitle}} avec {{profileName}}",
"booking_reschedule_confirmation": "Reporté votre {{eventTypeTitle}} avec {{profileName}}",
"in_person_meeting": "Lien ou réunion en présentiel",
"attendeeInPerson": "En personne (adresse du participant)",
"inPerson": "En personne (adresse de l'organisateur)",
"link_meeting": "Lier une réunion",
"phone_call": "Appel téléphonique",
"your_number": "Votre numéro de téléphone",
"phone_number": "Numéro de téléphone",
"attendee_phone_number": "Numéro de téléphone du participant",
"organizer_phone_number": "Numéro de téléphone de l'organisateur",
"host_phone_number": "Votre numéro de téléphone",
"enter_phone_number": "Saisissez le numéro de téléphone",
"reschedule": "Reporter",
@@ -554,6 +559,10 @@
"collective": "Collectif",
"collective_description": "Planifiez des réunions lorsque tous les membres de l'équipe sélectionnés sont disponibles.",
"duration": "Durée",
"available_durations": "Durées disponibles",
"default_duration": "Durée par défaut",
"default_duration_no_options": "Veuillez d'abord choisir les durées disponibles",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "Minutes",
"round_robin": "Round Robin",
"round_robin_description": "Faites tourner les réunions entre plusieurs membres de l'équipe.",
@@ -619,6 +628,7 @@
"teams": "Équipes",
"team": "Équipe",
"team_billing": "Facturation d'équipe",
"team_billing_description": "Gérer la facturation pour votre équipe",
"upgrade_to_flexible_pro_title": "Nous avons modifié la facturation pour les équipes",
"upgrade_to_flexible_pro_message": "Des membres dans votre équipe n'ont pas de place. Mettez à niveau votre offre pro pour couvrir les places manquantes.",
"changed_team_billing_info": "Depuis janvier 2022, nous facturons chaque place aux membres de l'équipe. Les membres de votre équipe qui ont eu la version Pro gratuitement disposent maintenant d'un essai de 14 jours. Une fois leur période d'essai expirée, ces membres seront cachés pour votre équipe, sauf si vous mettez à niveau maintenant.",
@@ -694,6 +704,7 @@
"hide_event_type": "Masquer le type d'événement",
"edit_location": "Modifier le lieu",
"into_the_future": "dans le futur",
"when_booked_with_less_than_notice": "Si réservé avec moins de <time></time> de préavis",
"within_date_range": "Dans une plage de dates",
"indefinitely_into_future": "Sans doute dans le futur",
"add_new_custom_input_field": "Ajouter un nouveau champ de saisie personnalisé",
@@ -713,6 +724,7 @@
"delete_account_confirmation_message": "Êtes-vous sûr de vouloir supprimer votre compte {{appName}} ? Toute personne avec qui vous avez partagé le lien de votre compte ne pourra plus réserver en utilisant ce lien et toutes les préférences que vous avez enregistrées seront perdues.",
"integrations": "Intégrations",
"apps": "Applications",
"apps_listing": "Liste des applications",
"category_apps": "Applications {{category}}",
"app_store": "App Store",
"app_store_description": "Connecter les personnes, la technologie et l'espace de travail.",
@@ -736,6 +748,7 @@
"toggle_calendars_conflict": "Activer/désactiver les calendriers pour lesquels vous souhaiter vérifier les conflits afin d'éviter les doubles réservations.",
"select_destination_calendar": "Créer des événements le",
"connect_additional_calendar": "Connecter un calendrier supplémentaire",
"calendar_updated_successfully": "Calendrier mis à jour avec succès",
"conferencing": "Conférence",
"calendar": "Calendrier",
"payments": "Paiements",
@@ -768,6 +781,7 @@
"trending_apps": "Applications populaires",
"explore_apps": "{{category}} applications",
"installed_apps": "Applications installées",
"free_to_use_apps": "Gratuit",
"no_category_apps": "Aucune application {{category}}",
"no_category_apps_description_calendar": "Ajouter une application de calendrier pour vérifier les conflits et éviter les doubles réservations",
"no_category_apps_description_conferencing": "Essayez d'ajouter une application de conférence pour interconnecter les appels vidéo avec vos clients",
@@ -806,6 +820,8 @@
"verify_wallet": "Vérifier le portefeuille",
"connect_metamask": "Connecter Metamask",
"create_events_on": "Créer des événements le :",
"enterprise_license": "Il s'agit d'une fonctionnalité d'entreprise",
"enterprise_license_description": "Pour activer cette fonctionnalité, obtenez une clé de déploiement sur la console {{consoleUrl}} et ajoutez-la à votre .env en tant que CALCOM_LICENSE_KEY. Si votre équipe a déjà une licence, veuillez contacter {{supportMail}} pour obtenir de l'aide.",
"missing_license": "Licence manquante",
"signup_requires": "Licence commerciale requise",
"signup_requires_description": "{{companyName}} ne propose pas actuellement de version open source gratuite de la page d'inscription. Pour obtenir un accès complet aux composants d'inscription, vous devez acquérir une licence commerciale. Pour une utilisation personnelle, nous recommandons la Plateforme de Données Prisma ou toute autre interface Postgres pour créer des comptes.",
@@ -897,6 +913,7 @@
"user_impersonation_heading": "Connexion en tant qu'autre utilisateur",
"user_impersonation_description": "Permet à notre équipe d'assistance de se connecter temporairement lorsque vous nous aidez à résoudre rapidement tous les problèmes que vous nous signalez.",
"team_impersonation_description": "Permet aux administrateurs de votre équipe de se connecter temporairement en tant que vous-même.",
"allow_booker_to_select_duration": "Autoriser l'organisateur à sélectionner la durée",
"impersonate_user_tip": "Toutes les utilisations de cette fonctionnalité sont vérifiées.",
"impersonating_user_warning": "Identification du nom d'utilisateur \"{{user}}\".",
"impersonating_stop_instructions": "<0>Cliquez ici pour arrêter</0>.",
@@ -1014,6 +1031,9 @@
"error_removing_app": "Erreur lors de la suppression de l'application",
"web_conference": "Conférence en ligne",
"requires_confirmation": "Nécessite une confirmation",
"always_requires_confirmation": "Toujours",
"requires_confirmation_threshold": "Nécessite une confirmation si réservé avec un préavis de < {{time}} $t({{unit}}_timeUnit)",
"may_require_confirmation": "Peut nécessiter une confirmation",
"nr_event_type_one": "{{count}} type d'événement",
"nr_event_type_other": "{{count}} types d'événements",
"add_action": "Ajouter une action",
@@ -1101,6 +1121,9 @@
"event_limit_tab_description": "Fréquence de réservation",
"event_advanced_tab_description": "Paramètres du calendrier & plus...",
"event_advanced_tab_title": "Avancé",
"event_setup_multiple_duration_error": "Configuration de l'événement : plusieurs durées requièrent au moins une option.",
"event_setup_multiple_duration_default_error": "Configuration de l'événement: veuillez sélectionner une durée par défaut valide.",
"event_setup_booking_limits_error": "Les limites de réservation doivent être en ordre croissant. [jour,semaine,mois,année]",
"select_which_cal": "Sélectionnez le calendrier auquel ajouter des réservations",
"custom_event_name": "Nom de l'événement personnalisé",
"custom_event_name_description": "Créer des noms d'événements personnalisés à afficher sur l'événement du calendrier",
@@ -1154,8 +1177,11 @@
"invoices": "Factures",
"embeds": "Intègre",
"impersonation": "Identification",
"impersonation_description": "Paramètres de gestion de l'identité de l'utilisateur",
"users": "Utilisateurs",
"profile_description": "Gérer les paramètres de votre profil {{appName}}",
"users_description": "Vous trouverez ici une liste de tous les utilisateurs",
"users_listing": "Liste des utilisateurs",
"general_description": "Gérer les paramètres pour votre langue et votre fuseau horaire",
"calendars_description": "Configurez la manière dont vos types d'événements interagissent avec vos calendriers",
"appearance_description": "Gérer les paramètres pour votre apparence de réservation",
@@ -1228,8 +1254,8 @@
"exchange_authentication_ntlm": "Authentification NTLM",
"exchange_compression": "Compression GZip",
"routing_forms_description": "Vous pouvez voir tous les formulaires et tous les parcours que vous avez créés ici.",
"routing_forms_send_email_owner": "Envoyer un e-mail au responsable",
"routing_forms_send_email_owner_description": "Envoie un e-mail au responsable lorsque le formulaire est soumis",
"routing_forms_send_email_owner": "Envoyer un e-mail au propriétaire",
"routing_forms_send_email_owner_description": "Envoie un e-mail au propriétaire lorsque le formulaire est envoyé",
"add_new_form": "Ajouter un nouveau formulaire",
"form_description": "Créez votre formulaire pour acheminer un organisateur",
"copy_link_to_form": "Copier le lien vers le formulaire",
@@ -1296,7 +1322,7 @@
"fetching_calendars_error": "Un problème est survenu lors de la récupération de vos calendriers. Veuillez <1>réessayer</1> ou contacter le service client.",
"calendar_connection_fail": "Échec de la connexion au calendrier",
"booking_confirmation_success": "Confirmation de réservation réussie",
"booking_rejection_success": "Rejet de réservation réussi",
"booking_rejection_success": "Refus de réservation réussi",
"booking_confirmation_fail": "Échec de la confirmation de la réservation",
"we_wont_show_again": "Nous n'afficherons plus ceci",
"couldnt_update_timezone": "Nous n'avons pas pu mettre à jour le fuseau horaire",
@@ -1346,21 +1372,22 @@
"monthly": "Mensuellement",
"yearly": "Annuellement",
"checkout": "Paiement",
"your_team_disbanded_successfully": "Votre équipe a été dissoute avec succès",
"your_team_disbanded_successfully": "Votre équipe a bien été dissoute",
"error_creating_team": "Erreur lors de la création de l'équipe",
"you": "Vous",
"send_email": "Envoyer un e-mail",
"member_already_invited": "Le membre a déjà été invité",
"enter_email_or_username": "Saisissez une adresse e-mail ou un nom d'utilisateur",
"team_name_taken": "Ce nom est déjà pris",
"must_enter_team_name": "Vous devez entrer un nom d'équipe",
"team_url_required": "Vous devez entrer une URL d'équipe",
"must_enter_team_name": "Vous devez saisir un nom d'équipe",
"team_url_required": "Vous devez saisir une URL d'équipe",
"team_url_taken": "Cette URL est déjà prise",
"team_publish": "Publier l'équipe",
"number_sms_notifications": "Numéro de téléphone (notifications SMS)",
"attendee_email_workflow": "E-mail du participant",
"attendee_email_info": "E-mail de la personne ayant réservé",
"invalid_credential": "Oh non ! Il semble que l’autorisation ait expiré ou a été révoquée. Veuillez la réinstaller.",
"kbar_search_placeholder": "Saisissez une commande ou une recherche...",
"invalid_credential": "Oh non ! L'autorisation semble avoir expiré ou avoir été révoquée. Veuillez la réinstaller.",
"choose_common_schedule_team_event": "Choisissez un horaire commun",
"choose_common_schedule_team_event_description": "Activez cette option si vous souhaitez utiliser un horaire commun entre les hôtes. Si désactivée, chaque hôte sera réservé en fonction de son planning par défaut.",
"reason": "Raison",
@@ -1370,5 +1397,8 @@
"test_preview": "Tester l'aperçu",
"route_to": "Router vers",
"test_preview_description": "Tester votre formulaire de routage sans envoyer de données",
"test_routing": "Tester le routage"
"test_routing": "Tester le routage",
"payment_app_disabled": "Un administrateur a désactivé une application de paiement",
"edit_event_type": "Modifier le type d'événement",
"admin_apps_description": "Activer les applications pour votre instance de Cal"
}
@@ -383,7 +383,6 @@
"go_to_billing_portal": "מעבר אל פורטל החיובים",
"need_anything_else": "צריך משהו נוסף?",
"further_billing_help": "אם דרוש לך סיוע נוסף בענייני חיוב, צוות התמיכה שלנו כאן כדי לעזור.",
"contact_our_support_team": "יצירת קשר עם צוות התמיכה",
"uh_oh": "אוי, לא!",
"no_event_types_have_been_setup": "משתמש זה עדיין לא הגדיר סוג אירוע.",
"edit_logo": "עריכת לוגו",
+80 -12
View File
@@ -16,8 +16,8 @@
"event_request_cancelled": "Il tuo evento programmato è stato cancellato",
"organizer": "Organizzatore",
"need_to_reschedule_or_cancel": "È necessario riprogrammare o annullare?",
"cancellation_reason": "Motivo dell'annullamento",
"cancellation_reason_placeholder": "Perché stai annullando? (facoltativo)",
"cancellation_reason": "Motivo dell'annullamento (facoltativo)",
"cancellation_reason_placeholder": "Perché stai annullando?",
"rejection_reason": "Motivo del rifiuto",
"rejection_reason_title": "Rifiutare la richiesta di prenotazione?",
"rejection_reason_description": "Sei sicuro di voler rifiutare la prenotazione? Informeremo la persona che ha cercato di prenotare. Puoi indicare il motivo qui sotto.",
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "Un evento è ancora in attesa di approvazione",
"event_is_still_waiting": "La richiesta d'evento è ancora in attesa: {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "Nessun altro risultato",
"no_results": "Nessun risultato",
"load_more_results": "Carica più risultati",
"integration_meeting_id": "{{integrationName}} meeting ID: {{meetingId}}",
"confirmed_event_type_subject": "Confermato: {{eventType}} con {{name}} il {{date}}",
@@ -248,6 +249,7 @@
"add_to_calendar": "Aggiungi al calendario",
"add_another_calendar": "Aggiungi un altro calendario",
"other": "Altro",
"email_sign_in_subject": "Link di accesso a {{appName}}",
"emailed_you_and_attendees": "Abbiamo inviato via email a te e agli altri partecipanti un invito al calendario con tutti i dettagli.",
"emailed_you_and_attendees_recurring": "Abbiamo inviato via email a te e agli altri partecipanti un invito al primo di questi eventi ricorrenti.",
"emailed_you_and_any_other_attendees": "Tu e tutti gli altri partecipanti siete stati inviati via email con queste informazioni.",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "Vai al portale di fatturazione",
"need_anything_else": "Hai bisogno di altro?",
"further_billing_help": "Se hai bisogno di ulteriore aiuto per la fatturazione, il nostro team di supporto è qui per aiutarti.",
"contact_our_support_team": "Contatta il nostro team di supporto",
"uh_oh": "Uh oh!",
"no_event_types_have_been_setup": "Questo utente non ha ancora impostato alcun tipo di evento.",
"edit_logo": "Modifica logo",
@@ -419,7 +420,8 @@
"forgotten_secret_description": "Nel caso la parola segreta venga persa o dimenticata, è possibile cambiarla, ma tieni presente che tutte le integrazioni che la utilizzano dovranno essere aggiornate",
"current_incorrect_password": "La password attuale non è corretta",
"password_hint_caplow": "Combinazione di caratteri maiuscoli e minuscoli",
"password_hint_min": "La lunghezza minima è di 7 caratteri",
"password_hint_min": "La lunghezza minima è di 8 caratteri",
"password_hint_admin_min": "La lunghezza minima è di 15 caratteri",
"password_hint_num": "Deve contenere almeno 1 numero",
"invalid_password_hint": "La password deve avere una lunghezza minima di 7 caratteri e deve contenere almeno 1 numero e una combinazione di caratteri maiuscoli e minuscoli",
"incorrect_password": "La password non è corretta.",
@@ -463,11 +465,14 @@
"booking_confirmation": "Conferma il tuo {{eventTypeTitle}} con {{profileName}}",
"booking_reschedule_confirmation": "Riprogramma il tuo {{eventTypeTitle}} con {{profileName}}",
"in_person_meeting": "Link o riunione di persona",
"attendeeInPerson": "Di persona (indirizzo del partecipante)",
"inPerson": "Di persona (indirizzo dell'organizzatore)",
"link_meeting": "Collegamento riunione",
"phone_call": "Numero di telefono del partecipante",
"your_number": "Il tuo numero di telefono",
"phone_number": "Numero Di Telefono",
"attendee_phone_number": "Numero di telefono del partecipante",
"organizer_phone_number": "Numero di telefono organizzatore",
"host_phone_number": "Il tuo numero di telefono",
"enter_phone_number": "Inserisci numero di telefono",
"reschedule": "Riprogramma",
@@ -504,7 +509,7 @@
"add_team_members_description": "Invita altri utenti a unirsi al tuo team",
"add_team_member": "Aggiungi un membro del team",
"invite_new_member": "Invita un nuovo membro",
"invite_new_member_description": "Nota: il <1>costo di un posto aggiuntivo (15$/m)</1> verrà addebitato sul tuo abbonamento.",
"invite_new_member_description": "Nota: Sul tuo abbonamento verrà addebitato <1>un posto aggiuntivo (15$/m)</1>.",
"invite_new_team_member": "Invita qualcuno nel tuo team.",
"change_member_role": "Cambia il ruolo del membro del team",
"disable_cal_branding": "Disabilita il branding {{appName}}",
@@ -554,6 +559,10 @@
"collective": "Collettivo",
"collective_description": "Pianifica le riunioni quando tutti i membri del team selezionati sono disponibili.",
"duration": "Durata",
"available_durations": "Durate disponibili",
"default_duration": "Durata predefinita",
"default_duration_no_options": "Scegliere prima delle durate disponibili",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "Minuti",
"round_robin": "Round Robin",
"round_robin_description": "Ciclo di riunioni tra più membri del team.",
@@ -601,7 +610,7 @@
"monthly_other": "mesi",
"yearly_one": "anno",
"yearly_other": "anni",
"plus_more": "altri {{count}}",
"plus_more": "Altri {{count}}",
"max": "Max",
"single_theme": "Tema Singolo",
"brand_color": "Colore del Marchio",
@@ -619,6 +628,7 @@
"teams": "Team",
"team": "Team",
"team_billing": "Fatturazione team",
"team_billing_description": "Gestisci la fatturazione per il tuo team",
"upgrade_to_flexible_pro_title": "Abbiamo cambiato la fatturazione per i team",
"upgrade_to_flexible_pro_message": "Alcuni membri del tuo team non hanno un posto. Aggiorna il tuo piano Pro per coprire i posti mancanti.",
"changed_team_billing_info": "A partire da gennaio 2022, applichiamo una tariffazione in base ai posti per i membri dei team. I membri del tuo team che utilizzavano Pro gratuitamente dispongono ora di un periodo di prova di 14 giorni. Terminata la prova, questi membri saranno nascosti dal tuo team, se non aggiornerai il piano.",
@@ -694,6 +704,7 @@
"hide_event_type": "Nascondi tipo evento",
"edit_location": "Aggiungi un luogo",
"into_the_future": "nel futuro",
"when_booked_with_less_than_notice": "Quando si prenota con un avviso inferiore a <time></time>",
"within_date_range": "All'interno di un intervallo di date",
"indefinitely_into_future": "Indefinitamente nel futuro",
"add_new_custom_input_field": "Aggiungi un nuovo campo di input personalizzato",
@@ -713,6 +724,8 @@
"delete_account_confirmation_message": "Sei sicuro di voler eliminare il tuo account {{appName}}? Chiunque abbia il link del tuo account non potrà più utilizzarlo per prenotare e le preferenze che hai salvato andranno perse.",
"integrations": "Integrazioni",
"apps": "App",
"apps_description": "Abilita le app per la tua istanza di Cal",
"apps_listing": "Elenco delle app",
"category_apps": "App {{category}}",
"app_store": "App Store",
"app_store_description": "Collegare persone, tecnologia e luogo di lavoro.",
@@ -736,6 +749,7 @@
"toggle_calendars_conflict": "Attiva/disattiva i calendari in cui desideri controllare i conflitti per evitare doppie prenotazioni.",
"select_destination_calendar": "Crea eventi su",
"connect_additional_calendar": "Collega calendario aggiuntivo",
"calendar_updated_successfully": "Calendario aggiornato correttamente",
"conferencing": "Conferenze",
"calendar": "Calendario",
"payments": "Pagamenti",
@@ -768,6 +782,7 @@
"trending_apps": "App di tendenza",
"explore_apps": "App {{category}}",
"installed_apps": "App installate",
"free_to_use_apps": "Gratuito",
"no_category_apps": "Nessuna app {{category}}",
"no_category_apps_description_calendar": "Aggiungi un'app di calendario per controllare i conflitti ed evitare doppie prenotazioni",
"no_category_apps_description_conferencing": "Prova ad aggiungere un'app di conferenza per integrare le videochiamate con i clienti",
@@ -784,7 +799,7 @@
"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.",
"empty_installed_apps_button": "Esplora l'App Store o installa dalle app sottostanti",
"empty_installed_apps_button": "Esplora l'App Store",
"manage_your_connected_apps": "Gestisci le app installate o modifica le impostazioni",
"browse_apps": "Sfoglia app",
"features": "Funzionalità",
@@ -806,6 +821,8 @@
"verify_wallet": "Verifica Wallet",
"connect_metamask": "Connetti Metamask",
"create_events_on": "Crea eventi su:",
"enterprise_license": "Questa è una funzione Enterprise",
"enterprise_license_description": "Per abilitare questa funzione, ottenere una chiave di distribuzione presso la console {{consoleUrl}} e aggiungerla al proprio .env come CALCOM_LICENSE_KEY. Nel caso il team possieda già una licenza, contattare {{supportMail}} per assistenza.",
"missing_license": "Licenza mancante",
"signup_requires": "È necessaria una licenza commerciale",
"signup_requires_description": "{{companyName}} attualmente non offre una versione open source gratuita della pagina di registrazione. Per avere accesso completo ai componenti di registrazione è necessario acquisire una licenza commerciale. Per un uso personale consigliamo Prisma Data Platform o qualsiasi altra interfaccia Postgres per creare account.",
@@ -897,6 +914,7 @@
"user_impersonation_heading": "Accesso con altra identità",
"user_impersonation_description": "Consente al nostro team di supporto di effettuare l'accesso temporaneo a tuo nome per risolvere rapidamente gli eventuali problemi da te segnalati.",
"team_impersonation_description": "Consente ai membri del tuo team di accedere temporaneamente con il tuo nome.",
"allow_booker_to_select_duration": "Consenti alla persona che prenota di selezionare la durata",
"impersonate_user_tip": "Ogni utilizzo di questa funzionalità viene controllato.",
"impersonating_user_warning": "Accesso con l'identità dell'utente \"{{user}}\".",
"impersonating_stop_instructions": "<0>Fai clic qui per interrompere</0>.",
@@ -1014,6 +1032,9 @@
"error_removing_app": "Errore durante la rimozione dell'app",
"web_conference": "Conferenza Web",
"requires_confirmation": "Richiede conferma",
"always_requires_confirmation": "Sempre",
"requires_confirmation_threshold": "Richiede conferma se si prenota con un avviso di < {{time}} $t({{unit}}_timeUnit)",
"may_require_confirmation": "Può richiedere conferma",
"nr_event_type_one": "{{count}} tipo di evento",
"nr_event_type_other": "{{count}} tipi di evento",
"add_action": "Aggiungi azione",
@@ -1101,6 +1122,9 @@
"event_limit_tab_description": "Con quale frequenza è possibile fare una prenotazione",
"event_advanced_tab_description": "Impostazioni calendario e altro...",
"event_advanced_tab_title": "Avanzate",
"event_setup_multiple_duration_error": "Impostazione evento: In caso di più durate è richiesta almeno 1 opzione.",
"event_setup_multiple_duration_default_error": "Impostazione evento: Selezionare una durata predefinita valida.",
"event_setup_booking_limits_error": "I limiti per le prenotazioni devono essere specificati in ordine crescente [giorno,settimana,mese,anno]",
"select_which_cal": "Seleziona il calendario al quale aggiungere prenotazioni",
"custom_event_name": "Nome evento personalizzato",
"custom_event_name_description": "Crea nomi di eventi personalizzati da visualizzare nell'evento del calendario",
@@ -1154,8 +1178,11 @@
"invoices": "Fatture",
"embeds": "Integrazioni",
"impersonation": "Accesso con altra identità",
"impersonation_description": "Impostazioni per gestire gli accessi con altra identità",
"users": "Utenti",
"profile_description": "Gestisci le impostazioni del tuo profilo {{appName}}",
"users_description": "Qui è possibile trovare l'elenco di tutti gli utenti",
"users_listing": "Elenco degli utenti",
"general_description": "Gestisci le impostazioni di lingua e fuso orario",
"calendars_description": "Configura come i tipi di eventi devono interagire con i tuoi calendari",
"appearance_description": "Gestisci le impostazioni di aspetto delle tue prenotazioni",
@@ -1345,21 +1372,22 @@
"billing_frequency": "Frequenza di fatturazione",
"monthly": "Mensile",
"yearly": "Annuale",
"checkout": "Checkout",
"checkout": "Pagamento",
"your_team_disbanded_successfully": "Il tuo team è stato sciolto correttamente",
"error_creating_team": "Errore durante la creazione del team",
"you": "Tu",
"send_email": "Invia e-mail",
"member_already_invited": "Membro già invitato",
"enter_email_or_username": "Immettere un indirizzo e-mail o un nome utente",
"team_name_taken": "Nome già occupato",
"must_enter_team_name": "Immettere un nome del team",
"team_url_required": "Immettere un URL del team",
"team_name_taken": "Nome già utilizzato",
"must_enter_team_name": "Necessario immettere un nome per il team",
"team_url_required": "Immettere un URL per il team",
"team_url_taken": "URL già occupato",
"team_publish": "Pubblica team",
"number_sms_notifications": "Numero di telefono (notifiche SMS)",
"attendee_email_workflow": "E-mail partecipante",
"attendee_email_info": "E-mail della persona che prenota",
"kbar_search_placeholder": "Digitare un comando o eseguire una ricerca...",
"invalid_credential": "Errore! Sembra che l'autorizzazione sia scaduta o che sia stata revocata. Prova a reinstallare.",
"choose_common_schedule_team_event": "Scegli un programma comune",
"choose_common_schedule_team_event_description": "Abilita questa opzione se desideri utilizzare un programma comune per tutti gli organizzatori. Quando l'opzione è disabilitata, ciascun organizzatore verrà prenotato in base al proprio programma predefinito.",
@@ -1370,5 +1398,45 @@
"test_preview": "Test anteprima",
"route_to": "Instrada a",
"test_preview_description": "Esegui un test del modulo di instradamento senza inviare alcun dato",
"test_routing": "Test instradamento"
"test_routing": "Test instradamento",
"payment_app_disabled": "Un amministratore ha disabilitato un'app di pagamento",
"edit_event_type": "Modifica tipo di evento",
"collective_scheduling": "Pianificazione collettiva",
"make_it_easy_to_book": "Facilita la prenotazione del tuo team quando tutti sono disponibili.",
"find_the_best_person": "Trova la persona più adatta disponibile e passa tra i membri del team.",
"fixed_round_robin": "Round robin fisso",
"add_one_fixed_attendee": "Aggiungi un partecipante fisso e fai intervenire a turno altri partecipanti.",
"calcom_is_better_with_team": "Cal.com funziona meglio in team",
"add_your_team_members": "Aggiungi i membri del tuo team ai tuoi tipi di eventi. Usa la pianificazione collettiva per includere tutti o trova la persona più adatta con la pianificazione round robin.",
"booking_limit_reached": "È stato raggiunto il limite di prenotazione per questo tipo di evento",
"admin_has_disabled": "Un amministratore ha disabilitato {{appName}}",
"disabled_app_affects_event_type": "Un amministratore ha disabilitato {{appName}}, il che influenza il tuo tipo di evento {{eventType}}",
"disable_payment_app": "L'amministratore ha disabilitato {{appName}}, il che influenza il tuo tipo di evento {{title}}. I partecipanti sono ancora in grado di prenotare questo tipo di evento, ma non gli verrà richiesto di pagare. Per evitarlo, è possibile nascondere questo tipo di evento fino a che l'amministratore non riattivi il tuo metodo di pagamento.",
"payment_disabled_still_able_to_book": "I partecipanti sono ancora in grado di prenotare questo tipo di evento, ma non gli verrà richiesto di pagare. Per evitarlo, è possibile nascondere questo tipo di evento fino a che l'amministratore non riattivi il tuo metodo di pagamento.",
"app_disabled_with_event_type": "L'amministratore ha disabilitato {{appName}}, il che influenza il tuo tipo di evento {{title}}.",
"app_disabled_video": "L'amministratore ha disabilitato {{appName}}, il che potrebbe influenzare i tuoi tipi di eventi. Se hai dei tipi di eventi che utilizzano {{appName}} come luogo, allora verrà usato Cal Video come luogo predefinito.",
"app_disabled_subject": "{{appName}} è stato disabilitato",
"navigate_installed_apps": "Vai alle app installate",
"disabled_calendar": "Se si dispone di un altro calendario installato, le nuove prenotazioni saranno aggiunte a quel calendario. Se così non fosse, connettere 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",
"app_is_enabled": "{{appName}} è abilitato",
"app_is_disabled": "{{appName}} è disabilitato",
"keys_have_been_saved": "Le chiavi sono state salvate",
"disable_app": "Disabilita app",
"disable_app_description": "La disattivazione di questa app potrebbe causare problemi con il modo in cui i tuoi utenti interagiscono con Cal",
"edit_keys": "Modifica chiavi",
"admin_apps_description": "Abilita le app per la tua istanza di Cal",
"no_available_apps": "Nessuna app disponibile",
"no_available_apps_description": "Assicurarsi che ci siano delle applicazioni nella propria distribuzione in 'packages/app-store'",
"no_apps": "Non ci sono applicazioni abilitate in questa istanza di Cal",
"apps_settings": "Impostazioni delle app",
"fill_this_field": "Compilare il campo",
"options": "Opzioni",
"enter_option": "Immetti opzione {{index}}",
"add_an_option": "Aggiungi un'opzione",
"radio": "Radio",
"event_type_duplicate_copy_text": "{{slug}}-copia",
"set_as_default": "Imposta come predefinito",
"hide_eventtype_details": "Nascondi dettagli tipo di evento"
}
+21 -22
View File
@@ -16,8 +16,8 @@
"event_request_cancelled": "スケジュールされていた予定がキャンセルされました",
"organizer": "主催者",
"need_to_reschedule_or_cancel": "再スケジュールまたはキャンセルが必要ですか?",
"cancellation_reason": "キャンセルの理由",
"cancellation_reason_placeholder": "キャンセルが必要な理由を教えてください (任意)",
"cancellation_reason": "キャンセルの理由 (オプション)",
"cancellation_reason_placeholder": "キャンセルが必要な理由をお教えください",
"rejection_reason": "拒否の理由",
"rejection_reason_title": "予約のリクエストを拒否しますか?",
"rejection_reason_description": "この予約を拒否してよろしいですか?予約を試みたユーザーに通知を送ります。拒否の理由は、下にご記入いただけます。",
@@ -108,7 +108,7 @@
"link_expires": "追記: 有効期限は {{expiresIn}} 時間後に切れます。",
"upgrade_to_per_seat": "座席ごとにアップグレードする",
"team_upgrade_seats_details": "あなたのチームに所属する {{memberCount}} 人のメンバーのうち、{{unpaidCount}} 名分のライセンスが未払いとなっています。ライセンスごとの料金は、${{seatPrice}} / 月ですので、あなたのメンバーシップの合計金額は、${{totalCost}} / 月になると予想されます。",
"team_upgrade_banner_description": "新しいチームプランをお試しいただきありがとうございます。あなたのチーム「{{teamName}}」はアップグレードが必要なようです。",
"team_upgrade_banner_description": "新しいチームプランをお試しいただき、ありがとうございます。あなたのチーム「{{teamName}}」には、アップグレードが必要なようです。",
"team_upgrade_banner_action": "アップグレードはこちら",
"team_upgraded_successfully": "チームのアップグレードが正常に完了しました!",
"use_link_to_reset_password": "以下のリンクを使用してパスワードをリセットしてください",
@@ -383,7 +383,6 @@
"go_to_billing_portal": "請求ポータルに移動",
"need_anything_else": "他に必要なものはありませんか?",
"further_billing_help": "請求に関するサポートが必要な場合は、サポートチームがお手伝いします。",
"contact_our_support_team": "サポートチームに問い合わせる",
"uh_oh": "おっと!",
"no_event_types_have_been_setup": "このユーザーはまだイベントタイプを設定していません。",
"edit_logo": "ロゴを編集",
@@ -419,7 +418,7 @@
"forgotten_secret_description": "このシークレットを紛失または忘れた場合は変更が可能です。しかしこのシークレットを使っている連携はすべて最新のものにする必要があります",
"current_incorrect_password": "現在のパスワードが正しくありません",
"password_hint_caplow": "アルファベットの大文字と小文字を両方使用してください",
"password_hint_min": "7 文字以上入力してください",
"password_hint_min": "8 文字以上入力してください",
"password_hint_num": "少なくとも 1 文字は数字を含めてください",
"invalid_password_hint": "パスワードには少なくとも 1 文字以上数字を含め、アルファベットの大文字と小文字を両方使用した上で 7 文字以上の長さに設定してください",
"incorrect_password": "パスワードが正しくありません。",
@@ -504,7 +503,7 @@
"add_team_members_description": "他のユーザーをチームに招待する",
"add_team_member": "チームメンバーを追加",
"invite_new_member": "新しいチームメンバーを招待",
"invite_new_member_description": "注意:これによりサブスクリプションに<1>追加座席分(月15ドル)の費用が加算されます。</1>",
"invite_new_member_description": "注: これにより、サブスクリプションに<1>追加の座席料金 (15 米ドル/メンバー) が加算されます。</1>",
"invite_new_team_member": "チームに誰かを招待する。",
"change_member_role": "チームメンバーの役割を変更する",
"disable_cal_branding": "{{appName}} のブランディングを無効化",
@@ -601,7 +600,7 @@
"monthly_other": "月",
"yearly_one": "年",
"yearly_other": "年",
"plus_more": "{{count}} 回以上",
"plus_more": "あと {{count}} 回",
"max": "最大",
"single_theme": "シングルテーマ",
"brand_color": "ブランドカラー",
@@ -784,7 +783,7 @@
"analytics": "分析",
"empty_installed_apps_headline": "アプリがインストールされていません",
"empty_installed_apps_description": "アプリを使えば、ワークフローの質を高め、スケジュールライフを大幅に改善することができます。",
"empty_installed_apps_button": "App Store で探す",
"empty_installed_apps_button": "App Store を見る",
"manage_your_connected_apps": "インストール済みアプリの管理や設定の変更を行う",
"browse_apps": "アプリを閲覧",
"features": "特徴",
@@ -1229,7 +1228,7 @@
"exchange_compression": "GZip 圧縮",
"routing_forms_description": "作成したすべてのフォームとルートをここで確認できます。",
"routing_forms_send_email_owner": "所有者にメールを送信",
"routing_forms_send_email_owner_description": "フォーム送信時に所有者にメールを送信します",
"routing_forms_send_email_owner_description": "フォームの送信時に所有者にメールを送信します",
"add_new_form": "新しいフォームを追加",
"form_description": "予約者をルーティングするフォームを作成します",
"copy_link_to_form": "フォームへのリンクをコピー",
@@ -1296,7 +1295,7 @@
"fetching_calendars_error": "カレンダーの取得中にエラーが発生しました。<1>もう一度お試しいただく</1>か、カスタマーサポートまでお問い合わせください。",
"calendar_connection_fail": "カレンダーの接続に失敗しました",
"booking_confirmation_success": "正常に予約を確認しました",
"booking_rejection_success": "正常に予約を拒否しました",
"booking_rejection_success": "予約を正常に拒否しました",
"booking_confirmation_fail": "予約の確認に失敗しました",
"we_wont_show_again": "次回から表示しない",
"couldnt_update_timezone": "タイムゾーンを更新できませんでした",
@@ -1342,33 +1341,33 @@
"limit_future_bookings_description": "今後いつまでこのイベントを予約できるかを設定します",
"no_event_types": "イベントタイプは設定されていません",
"no_event_types_description": "{{name}}はあなたが予約するイベントのタイプを設定していません。",
"billing_frequency": "請求頻度",
"monthly": "月ごと",
"yearly": "年ごと",
"checkout": "精算",
"your_team_disbanded_successfully": "チームは正常に解散しました",
"billing_frequency": "請求の頻度",
"monthly": "毎月",
"yearly": "毎年",
"checkout": "チェックアウト",
"your_team_disbanded_successfully": "チームは正常に解散されました",
"error_creating_team": "チームの作成中にエラーが発生しました",
"you": "あなた",
"send_email": "メールを送信",
"member_already_invited": "メンバーは既に招待されています",
"enter_email_or_username": "メールアドレスまたはユーザー名を入力してください",
"team_name_taken": "この名前は既に使用されています",
"must_enter_team_name": "チーム名を入力する必要があります",
"team_url_required": "チームの URL を入力する必要があります",
"must_enter_team_name": "チーム名の入力は必須です",
"team_url_required": "チームの URL の入力は必須です",
"team_url_taken": "この URL は既に使用されています",
"team_publish": "チームを公開",
"number_sms_notifications": "電話番号(SMS 通知)",
"attendee_email_workflow": "出席者のメールアドレス",
"attendee_email_info": "予約者のメールアドレス",
"invalid_credential": "おっと!許可が期限切れになったか、取り消されたようです。もう一度インストールし直してください。",
"invalid_credential": "アクセス許可の有効期限が切れたか、取り消されたようです。もう一度インストールしてください。",
"choose_common_schedule_team_event": "共通のスケジュールを選択",
"choose_common_schedule_team_event_description": "ホスト間で共通のスケジュールを使用する場合は、これを有効にします。無効にすると、各ホストはデフォルトのスケジュールに基づいて予約されます。",
"choose_common_schedule_team_event_description": "ホスト間で共通のスケジュールを使用する場合には、これを有効にしてください。無効にすると、各ホストに対してはデフォルトのスケジュールに基づいて予約が行われます。",
"reason": "理由",
"sender_id": "送信者 ID",
"sender_id_error_message": "文字、数字、スペースのみ使用できます(最大 11 文字)",
"sender_id_error_message": "文字、数字、スペースのみが使用可能です (最大 11 文字)",
"test_routing_form": "ルーティングフォームをテスト",
"test_preview": "テストプレビュー",
"route_to": "ルート先",
"test_preview": "プレビューをテスト",
"route_to": "ルーティング先",
"test_preview_description": "データを送信せずにルーティングフォームをテストします",
"test_routing": "ルーティングをテスト"
}
+83 -16
View File
@@ -16,8 +16,8 @@
"event_request_cancelled": "회의 일정이 취소되었습니다.",
"organizer": "주최자",
"need_to_reschedule_or_cancel": "다시 일정을 잡거나 취소하시겠습니까?",
"cancellation_reason": "취소 사유",
"cancellation_reason_placeholder": "왜 취소를 선택하셨나요? (선택사항)",
"cancellation_reason": "취소 사유(선택사항)",
"cancellation_reason_placeholder": "취소하는 이유가 무엇인가요?",
"rejection_reason": "거부 사유",
"rejection_reason_title": "예약 요청을 거부하시겠습니까?",
"rejection_reason_description": "예약을 거부하시겠습니까? 예약하려고 시도하셨던 다른 분께 알려드릴 것입니다. 해당 사유는 아래에서 제공하실 수 있습니다.",
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "이벤트가 아직 승인을 기다리고 있습니다.",
"event_is_still_waiting": "이벤트 요청이 아직 대기중입니다: {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "더 이상 결과 없음",
"no_results": "결과 없음",
"load_more_results": "더 많은 결과 보기",
"integration_meeting_id": "{{integrationName}} 회의 ID: {{meetingId}}",
"confirmed_event_type_subject": "확인됨: {{date}}, {{name}} 의 {{eventType}}",
@@ -108,8 +109,8 @@
"link_expires": "{{expiresIn}} 시간 안에 만료됩니다.",
"upgrade_to_per_seat": "시트 단위로 업그레이드",
"team_upgrade_seats_details": "귀하의 팀원 {{memberCount}}명 중 {{unpaidCount}}명의 시트 비용이 미지급되었습니다. 시트당 ${{seatPrice}}/m로 계산시, 예상 총 멤버십 비용은 ${{totalCost}}/m입니다.",
"team_upgrade_banner_description": "당사의 새로운 팀 플랜을 사용해 주셔서 감사합니다. \"{{teamName}}\" 팀을 업그레이드해야 합니다.",
"team_upgrade_banner_action": "여기에서 업그레이드",
"team_upgrade_banner_description": "당사의 새로운 팀 플랜을 사용해 주셔서 감사합니다. 귀하의 \"{{teamName}}\" 팀에 업그레이드가 필요한 것으로 확인되었습니다.",
"team_upgrade_banner_action": "여기서 업그레이드",
"team_upgraded_successfully": "팀이 성공적으로 업그레이드되었습니다!",
"use_link_to_reset_password": "비밀번호를 재설정하시려면 아래 링크를 사용하세요.",
"hey_there": "안녕하세요.",
@@ -248,6 +249,7 @@
"add_to_calendar": "캘린더에 추가하기",
"add_another_calendar": "다른 캘린더 추가",
"other": "더 보기",
"email_sign_in_subject": "{{appName}}의 로그인 링크",
"emailed_you_and_attendees": "당신과 다른 참여자들에게 상세 사항이 담긴 캘린더 초대 이메일을 보냈습니다.",
"emailed_you_and_attendees_recurring": "귀하와 다른 참석자들에게 이 되풀이 이벤트의 첫 이벤트에 대한 예약 초대장을 이메일로 발송했습니다.",
"emailed_you_and_any_other_attendees": "당신과 다른 참여자들이 이 정보가 담긴 이메일을 주고 받았습니다.",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "결제 포털로 이동",
"need_anything_else": "다른 것이 필요하십니까?",
"further_billing_help": "청구와 관련하여 추가 도움이 필요한 경우 지원 팀이 도와드리겠습니다.",
"contact_our_support_team": "지원팀에 문의하기",
"uh_oh": "오!",
"no_event_types_have_been_setup": "사용자가 이벤트 타입을 설정하지 않았습니다.",
"edit_logo": "로고 수정하기",
@@ -420,6 +421,7 @@
"current_incorrect_password": "현재 비밀번호가 잘못되었습니다.",
"password_hint_caplow": "대문자와 소문자의 혼합",
"password_hint_min": "최소 7자 길이",
"password_hint_admin_min": "최소 15자 길이",
"password_hint_num": "1개 이상 숫자 포함",
"invalid_password_hint": "비밀번호는 7자 이상이어야 하고 숫자가 하나 이상 포함되어야 하며 대문자와 소문자가 혼합되어야 합니다",
"incorrect_password": "비밀번호가 잘못되었습니다",
@@ -463,11 +465,14 @@
"booking_confirmation": "{{profileName}}로 {{eventTypeTitle}} 확인",
"booking_reschedule_confirmation": "{{profileName}}을 사용하여 {{eventTypeTitle}} 일정을 변경하세요.",
"in_person_meeting": "온라인 또는 오프라인 회의",
"attendeeInPerson": "직접 방문(참석자 주소)",
"inPerson": "방문(주최자 주소)",
"link_meeting": "회의 링크",
"phone_call": "참석자 전화 번호",
"your_number": "본인 전화 번호",
"phone_number": "전화 번호",
"attendee_phone_number": "참석자 전화 번호",
"organizer_phone_number": "주최자 전화번호",
"host_phone_number": "본인 전화 번호",
"enter_phone_number": "전화 번호 입력",
"reschedule": "일정 변경",
@@ -504,7 +509,7 @@
"add_team_members_description": "다른 사람을 팀에 초대",
"add_team_member": "팀원 추가",
"invite_new_member": "새 팀원 초대",
"invite_new_member_description": "참고: 이렇게 하면 해당 구독에 <1>추가 좌석 비용($15/m)</1>이 발생합니다.",
"invite_new_member_description": "참고: 이로 인해 해당 구독에 <1>추가 시트 비용($15/m)</1>이 발생합니다.",
"invite_new_team_member": "누군가를 팀에 초대하기",
"change_member_role": "팀원 역할 변경",
"disable_cal_branding": "{{appName}} 브랜딩 비활성화",
@@ -554,6 +559,10 @@
"collective": "집단",
"collective_description": "선택한 모든 팀원이 참석할 수 있을 때 회의를 예약합니다.",
"duration": "기간",
"available_durations": "사용 가능한 기간",
"default_duration": "기본 기간",
"default_duration_no_options": "사용 가능한 기간을 먼저 선택하세요",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "분",
"round_robin": "라운드 로빈",
"round_robin_description": "여러 팀 구성원 간의 주기적인 회의",
@@ -619,6 +628,7 @@
"teams": "팀",
"team": "팀",
"team_billing": "팀 비용 청구",
"team_billing_description": "팀의 청구 관리",
"upgrade_to_flexible_pro_title": "팀에 대한 청구를 변경했습니다",
"upgrade_to_flexible_pro_message": "팀에 시트가 없는 구성원이 있습니다. 누락된 시트를 보완하려면 Pro 플랜을 업그레이드하세요.",
"changed_team_billing_info": "2022년 1월부터 팀원에 대해 시트별로 요금을 부과합니다. 무료로 Pro를 사용하던 팀원은 이제 14일 평가판을 사용할 수 있습니다. 평가판 기한이 만료되고 업그레이드하지 않으면 이 팀원은 팀에서 숨겨집니다.",
@@ -694,6 +704,7 @@
"hide_event_type": "이벤트 타입 숨기기",
"edit_location": "위치 수정",
"into_the_future": "미래로",
"when_booked_with_less_than_notice": "<time></time>회 미만 통지로 예약한 경우",
"within_date_range": "기간 내",
"indefinitely_into_future": "무한히 미래로",
"add_new_custom_input_field": "새 사용자 정의 입력 필드 추가",
@@ -713,6 +724,8 @@
"delete_account_confirmation_message": "{{appName}} 계정을 삭제하시겠습니까? 계정 링크를 공유한 사람은 더 이상 이 링크를 사용하여 예약할 수 없으며 저장한 모든 기본 설정이 손실됩니다.",
"integrations": "통합",
"apps": "앱",
"apps_description": "Cal 인스턴스용 앱 활성화",
"apps_listing": "앱 목록화",
"category_apps": "{{category}} 앱",
"app_store": "앱 스토어",
"app_store_description": "사람, 기술, 직장을 연결합니다.",
@@ -736,6 +749,7 @@
"toggle_calendars_conflict": "중복 예약을 방지하기 위해 충돌을 확인하려는 캘린더를 전환합니다.",
"select_destination_calendar": "이벤트 작성일",
"connect_additional_calendar": "추가 캘린더 연결",
"calendar_updated_successfully": "캘린더가 업데이트되었습니다",
"conferencing": "회의",
"calendar": "달력",
"payments": "결제",
@@ -768,6 +782,7 @@
"trending_apps": "트랜드 앱",
"explore_apps": "{{category}} 앱",
"installed_apps": "설치된 앱",
"free_to_use_apps": "무료",
"no_category_apps": "{{category}} 앱 없음",
"no_category_apps_description_calendar": "중복 예약을 방지하기 위해 충돌을 확인하려는 캘린더 앱을 추가합니다",
"no_category_apps_description_conferencing": "고객과 화상 통화를 통합하려면 회의 앱을 추가해 보세요",
@@ -784,7 +799,7 @@
"analytics": "분석",
"empty_installed_apps_headline": "설치된 앱 없음",
"empty_installed_apps_description": "앱을 통해 워크플로를 강화하고 일정 관리 수명을 대폭 향상할 수 있습니다.",
"empty_installed_apps_button": "앱 스토어를 둘러보거나 아래 앱 중에서 설치",
"empty_installed_apps_button": "앱 스토어 둘러보기",
"manage_your_connected_apps": "설치된 앱 관리 또는 설정 변경",
"browse_apps": "앱 찾아보기",
"features": "기능",
@@ -806,6 +821,8 @@
"verify_wallet": "지갑 인증",
"connect_metamask": "메타마스크 연결",
"create_events_on": "이벤트 생성일:",
"enterprise_license": "엔터프라이즈 기능입니다",
"enterprise_license_description": "이 기능을 활성화하려면 {{consoleUrl}} 콘솔에서 배포 키를 가져와 .env에 CALCOM_LICENSE_KEY로 추가하세요. 팀에 이미 라이선스가 있는 경우 {{supportMail}}에 문의하여 도움을 받으세요.",
"missing_license": "라이선스 없음",
"signup_requires": "상용 라이선스 필요",
"signup_requires_description": "지금은 {{companyName}}에서 가입 페이지의 무료 오픈 소스 버전을 제공하지 않습니다. 가입 구성 요소에 대한 정식 액세스 권한을 받으려면 상용 라이선스를 취득해야 합니다. 개인 용도의 경우 Prisma Data Platform 또는 기타 Postgres 인터페이스를 사용하여 계정을 생성하는 것이 좋습니다.",
@@ -897,6 +914,7 @@
"user_impersonation_heading": "사용자 가장",
"user_impersonation_description": "신고하신 문제를 신속하게 해결할 수 있도록 지원 팀이 귀하의 계정에 일시적으로 로그인할 수 있게 허용합니다.",
"team_impersonation_description": "팀 구성원이 일시적으로 귀하의 자격으로 로그인할 수 있게 합니다.",
"allow_booker_to_select_duration": "예약자가 기간을 선택하도록 허용",
"impersonate_user_tip": "이 기능의 사용은 모두 감사됩니다.",
"impersonating_user_warning": "\"{{user}}\"의 사용자 이름을 가장하는 중입니다.",
"impersonating_stop_instructions": "<0>중지하려면 여기를 클릭하십시오</0>.",
@@ -1014,6 +1032,9 @@
"error_removing_app": "앱 제거 중 오류 발생",
"web_conference": "웹 화상 회의",
"requires_confirmation": "확인 필수",
"always_requires_confirmation": "항상",
"requires_confirmation_threshold": "< {{time}} $t({{unit}}_timeUnit) 알림으로 예약한 경우 확인이 필요합니다",
"may_require_confirmation": "확인이 필요할 수 있음",
"nr_event_type_one": "{{count}} 이벤트 타입",
"nr_event_type_other": "{{count}} 이벤트 타입",
"add_action": "작업 추가",
@@ -1101,6 +1122,9 @@
"event_limit_tab_description": "예약 가능 횟수",
"event_advanced_tab_description": "캘린더 설정 및 기타...",
"event_advanced_tab_title": "고급",
"event_setup_multiple_duration_error": "이벤트 설정: 복수 기간에는 최소 1개의 옵션이 필요합니다.",
"event_setup_multiple_duration_default_error": "이벤트 설정: 유효한 기본 기간을 선택하십시오.",
"event_setup_booking_limits_error": "예약 한도는 오름차순이어야 합니다. [일,주,월,년]",
"select_which_cal": "예약을 추가할 캘린더 선택",
"custom_event_name": "사용자 정의 이벤트 이름",
"custom_event_name_description": "캘린더 이벤트에 표시할 사용자 정의 이벤트 이름 만들기",
@@ -1154,8 +1178,11 @@
"invoices": "청구서",
"embeds": "내장",
"impersonation": "가장",
"impersonation_description": "사용자 가장을 관리하기 위한 설정",
"users": "사용자",
"profile_description": "사용자 {{appName}} 프로필 설정 관리",
"users_description": "여기에서 모든 사용자 목록을 찾을 수 있습니다",
"users_listing": "사용자 목록화",
"general_description": "언어 및 시간대 설정 관리",
"calendars_description": "이벤트 타입이 캘린더와 상호 작용하는 방식 구성",
"appearance_description": "예약 모양 설정 관리",
@@ -1296,7 +1323,7 @@
"fetching_calendars_error": "캘린더를 가져오는 중 문제가 발생했습니다. <1>다시 시도</1>하거나 고객 지원에 문의하십시오.",
"calendar_connection_fail": "캘린더 연결 실패",
"booking_confirmation_success": "예약 확인 성공",
"booking_rejection_success": "예약 거부 성공",
"booking_rejection_success": "예약이 거부됨",
"booking_confirmation_fail": "예약 확인 실패",
"we_wont_show_again": "다시 표시 안 함",
"couldnt_update_timezone": "시간대를 업데이트할 수 없습니다",
@@ -1347,28 +1374,68 @@
"yearly": "연간",
"checkout": "체크아웃",
"your_team_disbanded_successfully": "귀하의 팀이 성공적으로 해산되었습니다",
"error_creating_team": "팀을 만드는 중 오류가 발생했습니다",
"you": "나",
"error_creating_team": "팀을 만드는 중 오류 발생",
"you": "귀하",
"send_email": "이메일 보내기",
"member_already_invited": "구성원이 이미 초대되었습니다",
"enter_email_or_username": "이메일 또는 사용자 이름을 입력하세요",
"team_name_taken": "이미 사용 중인 이름입니다",
"must_enter_team_name": "팀 이름을 입력하세요",
"team_url_required": "팀 URL을 입력해야 합니다",
"team_url_required": "팀 URL을 입력하세요",
"team_url_taken": "이미 사용 중인 URL입니다",
"team_publish": "팀 게시",
"number_sms_notifications": "전화번호 (SMS notifications)",
"attendee_email_workflow": "참석자 이메일",
"attendee_email_info": "예약자 이메일",
"kbar_search_placeholder": "명령어를 입력하거나 검색하세요...",
"invalid_credential": "죄송합니다! 권한이 만료되었거나 취소된 것 같습니다. 다시 설치하십시오.",
"choose_common_schedule_team_event": "공통 일정을 선택하세요",
"choose_common_schedule_team_event_description": "호스트 간에 공통 일정을 사용하려면 이 옵션을 활성화하십시오. 비활성화되면 각 호스트는 기본 일정에 따라 예약됩니다.",
"reason": "이유",
"choose_common_schedule_team_event": "공통 일정 선택",
"choose_common_schedule_team_event_description": "호스트 간에 공통 일정을 사용하려면 이 옵션을 활성화하세요. 비활성화되면 각 호스트는 기본 일정에 따라 예약됩니다.",
"reason": "사유",
"sender_id": "발신자 ID",
"sender_id_error_message": "영문, 숫자, 공백만 허용(최대 11자)",
"test_routing_form": "라우팅 양식 테스트",
"test_preview": "테스트 미리보기",
"test_preview": "미리보기 테스트",
"route_to": "목표 경로",
"test_preview_description": "데이터를 제출하지 않고 라우팅 양식 테스트",
"test_routing": "라우팅 테스트"
"test_routing": "라우팅 테스트",
"payment_app_disabled": "관리자가 결제 앱을 비활성화했습니다",
"edit_event_type": "이벤트 타입 편집",
"collective_scheduling": "집단 일정 관리",
"make_it_easy_to_book": "모든 사람이 참석할 수 있을 때 팀을 쉽게 예약할 수 있게 합니다.",
"find_the_best_person": "가능한 최상의 인원을 찾고 팀원을 순환 배정하십시오.",
"fixed_round_robin": "고정 라운드 로빈",
"add_one_fixed_attendee": "한 명의 고정 참석자를 추가하고 여러 참석자를 통해 라운드 로빈을 추가합니다.",
"calcom_is_better_with_team": "Cal.com은 팀과 함께라면 더욱 좋습니다",
"add_your_team_members": "이벤트 타입에 팀원을 추가하세요. 집단 일정 관리를 사용해 모든 사람을 포함하거나 라운드 로빈 일정 관리로 가장 적합한 인원을 찾으십시오.",
"booking_limit_reached": "이 이벤트 타입에 대한 예약 한도에 도달했습니다",
"admin_has_disabled": "관리자가 {{appName}} 앱을 비활성화했습니다",
"disabled_app_affects_event_type": "관리자가 이벤트 타입 {{eventType}}에 영향을 미치는 {{appName}} 앱을 비활성화했습니다",
"disable_payment_app": "관리자가 이벤트 타입 {{title}}에 영향을 미치는 {{appName}} 앱을 비활성화했습니다. 참석자는 여전히 이러한 타입의 이벤트를 예약할 수 있지만 결제하라는 메시지는 표시되지 않습니다. 관리자가 결제 수단을 다시 활성화할 때까지 이를 방지하기 위해 이벤트 타입을 숨길 수 있습니다.",
"payment_disabled_still_able_to_book": "참석자는 여전히 이러한 타입의 이벤트를 예약할 수 있지만 결제하라는 메시지는 표시되지 않습니다. 관리자가 결제 수단을 다시 활성화할 때까지 이를 방지하기 위해 이벤트 타입을 숨길 수 있습니다.",
"app_disabled_with_event_type": "관리자가 이벤트 타입 {{title}}에 영향을 미치는 {{appName}} 앱을 비활성화했습니다.",
"app_disabled_video": "관리자가 이벤트 타입에 영향을 미칠 수 있는 {{appName}} 앱을 비활성화했습니다. 위치가 {{appName}} 앱에 대한 이벤트 타입이 있는 경우 기본적으로 Cal Video로 설정됩니다.",
"app_disabled_subject": "{{appName}} 앱이 비활성화되었습니다",
"navigate_installed_apps": "설치된 앱으로 이동",
"disabled_calendar": "다른 캘린더를 설치한 경우 새 예약이 추가됩니다. 그렇지 않은 경우 새로운 예약을 놓치지 않도록 새 캘린더를 연결하십시오.",
"enable_apps": "앱 활성화",
"enable_apps_description": "사용자가 Cal.com과 통합할 수 있는 앱 활성화",
"app_is_enabled": "{{appName}} 앱이 활성화되었습니다",
"app_is_disabled": "{{appName}} 앱이 비활성화되었습니다",
"keys_have_been_saved": "키가 저장되었습니다",
"disable_app": "앱 비활성화",
"disable_app_description": "이 앱을 비활성화하면 사용자가 Cal과 상호 작용하는 방식에 문제가 발생할 수 있습니다",
"edit_keys": "키 편집",
"admin_apps_description": "Cal 인스턴스용 앱 활성화",
"no_available_apps": "사용 가능한 앱이 없습니다",
"no_available_apps_description": "'packages/app-store' 아래 배포에 앱이 있는지 확인하세요",
"no_apps": "Cal의 이 인스턴스에서 활성화된 앱이 없습니다",
"apps_settings": "앱 설정",
"fill_this_field": "이 필드에 입력하세요",
"options": "선택사항",
"enter_option": "선택사항 {{index}} 입력",
"add_an_option": "선택사항 추가",
"radio": "라디오",
"event_type_duplicate_copy_text": "{{slug}}-카피",
"set_as_default": "기본값으로 설정"
}
@@ -16,8 +16,8 @@
"event_request_cancelled": "Uw geplande gebeurtenis is geannuleerd",
"organizer": "Organisator",
"need_to_reschedule_or_cancel": "Moet u verplaatsen of annuleren?",
"cancellation_reason": "Reden voor annulering",
"cancellation_reason_placeholder": "Waarom annuleert u? (optioneel)",
"cancellation_reason": "Reden voor annulering (optioneel)",
"cancellation_reason_placeholder": "Waarom annuleert u?",
"rejection_reason": "Reden voor weigering",
"rejection_reason_title": "Boekingsverzoek weigeren?",
"rejection_reason_description": "Weet u zeker dat u de boeking wilt weigeren? We informeren de persoon die probeerde te boeken. U kunt hieronder een reden opgeven.",
@@ -108,7 +108,7 @@
"link_expires": "p.s. De uitnodiging verloopt over {{expiresIn}} uur.",
"upgrade_to_per_seat": "Upgraden naar per plaats",
"team_upgrade_seats_details": "Van de {{memberCount}} leden in uw team zijn {{unpaidCount}} plaats(en) onbetaald. Voor $ {{seatPrice}}/maand per plaats bedragen de geschatte totale kosten van uw abonnement $ {{totalCost}}/maand.",
"team_upgrade_banner_description": "Bedankt voor het testen van ons nieuwe teamabonnement. We hebben gemerkt dat uw team \"{{teamName}}\" moet worden geüpgraded.",
"team_upgrade_banner_description": "Bedankt voor het testen van ons nieuwe teamabonnement. We hebben geconstateerd dat uw team \"{{teamName}}\" moet worden geüpgraded.",
"team_upgrade_banner_action": "Hier upgraden",
"team_upgraded_successfully": "Uw team is geüpgraded!",
"use_link_to_reset_password": "Gebruik de onderstaande link om uw wachtwoord te resetten",
@@ -383,7 +383,6 @@
"go_to_billing_portal": "Ga naar het facturatiepaneel",
"need_anything_else": "Nog iets nodig?",
"further_billing_help": "Als u verdere hulp nodig heeft bij facturering, is ons ondersteuningsteam er om u te helpen.",
"contact_our_support_team": "Neem contact op met onze klantenservice",
"uh_oh": "Oeps!",
"no_event_types_have_been_setup": "Deze gebruiker heeft nog geen evenementen.",
"edit_logo": "Bewerk logo",
@@ -419,7 +418,7 @@
"forgotten_secret_description": "Als u dit geheim vergeten of verloren bent, kunt u het wijzigen. Maar wees u ervan bewust dat alle integraties die dit geheim gebruiken bijgewerkt moeten worden",
"current_incorrect_password": "Uw wachtwoord is verkeerd",
"password_hint_caplow": "Mix van hoofd- en kleine letters",
"password_hint_min": "Minimaal 7 tekens lang",
"password_hint_min": "Minimaal 8 tekens lang",
"password_hint_num": "Minimaal 1 cijfer bevatten",
"invalid_password_hint": "Het wachtwoord moet minimaal 7 tekens lang zijn, minimaal 1 cijfer bevatten en bestaan uit een mix van hoofd- en kleine letters",
"incorrect_password": "Uw wachtwoord is incorrect.",
@@ -784,7 +783,7 @@
"analytics": "Analyse",
"empty_installed_apps_headline": "Geen apps geïnstalleerd",
"empty_installed_apps_description": "Met apps kunt u uw workflow en uw planning aanzienlijk verbeteren.",
"empty_installed_apps_button": "Ontdek de App Store",
"empty_installed_apps_button": "Bladeren in App Store",
"manage_your_connected_apps": "Beheer uw geïnstalleerde apps of wijzig instellingen",
"browse_apps": "Door apps bladeren",
"features": "Functies",
@@ -385,7 +385,6 @@
"go_to_billing_portal": "Gå til betalingsportalen",
"need_anything_else": "Trenger du noe annet?",
"further_billing_help": "Hvis du trenger ytterligere hjelp med betaling, er supportteamet vårt her for å hjelpe.",
"contact_our_support_team": "Kontakt supportteamet vårt",
"uh_oh": "Uh oh!",
"no_event_types_have_been_setup": "Denne brukeren har ikke konfigurert noen hendelsestyper ennå.",
"edit_logo": "Rediger logo",
@@ -383,7 +383,6 @@
"go_to_billing_portal": "Przejdź do portalu rozliczeniowego",
"need_anything_else": "Potrzebujesz czegoś innego?",
"further_billing_help": "Jeśli potrzebujesz dalszej pomocy przy rozliczaniu, nasz zespół wsparcia jest tutaj, aby pomóc.",
"contact_our_support_team": "Skontaktuj się z naszym zespołem wsparcia",
"uh_oh": "Uh oh!",
"no_event_types_have_been_setup": "Ten użytkownik nie ustawił jeszcze żadnych typów wydarzeń.",
"edit_logo": "Edytuj logo",
@@ -17,7 +17,7 @@
"organizer": "Organizador",
"need_to_reschedule_or_cancel": "Precisa reagendar ou cancelar?",
"cancellation_reason": "Motivo do cancelamento",
"cancellation_reason_placeholder": "Por que você está cancelando? (opcional)",
"cancellation_reason_placeholder": "Por que você está cancelando?",
"rejection_reason": "Motivo da recusa",
"rejection_reason_title": "Recusar a solicitação de reserva?",
"rejection_reason_description": "Tem certeza de que deseja recusar a reserva? Avisaremos a pessoa que tentou reservar. Se quiser, informe um motivo abaixo.",
@@ -383,7 +383,6 @@
"go_to_billing_portal": "Ir para o portal de pagamento",
"need_anything_else": "Precisa de algo mais?",
"further_billing_help": "Se precisar de mais ajuda com o faturamento, o nosso time de suporte está aqui para ajudar.",
"contact_our_support_team": "Fale com a nossa equipe de suporte",
"uh_oh": "Oh oh!",
"no_event_types_have_been_setup": "Este usuário ainda não definiu nenhum tipo de evento.",
"edit_logo": "Editar logotipo",
@@ -419,7 +418,7 @@
"forgotten_secret_description": "Caso tenha perdido ou esquecido o segredo, você poderá alterá-lo. Mas fique ciente de que todas as integrações com esse segredo precisarão ser atualizadas",
"current_incorrect_password": "Senha atual está incorreta",
"password_hint_caplow": "Mistura de letras maiúsculas e minúsculas",
"password_hint_min": "Mínimo de sete caracteres",
"password_hint_min": "Mínimo de oito caracteres",
"password_hint_admin_min": "No mínimo 15 caracteres",
"password_hint_num": "Contém pelo menos um número",
"invalid_password_hint": "A senha precisa ter pelo menos sete caracteres com pelo menos um número e ser uma combinação de letras maiúsculas e minúsculas",
@@ -1356,7 +1355,7 @@
"team_name_taken": "Este nome já está em uso",
"must_enter_team_name": "É preciso inserir um nome de equipe",
"team_url_required": "É preciso inserir uma URL de equipe",
"team_url_taken": "Esta URL já está em uso",
"team_url_taken": "Este URL já está em uso",
"team_publish": "Publicar equipe",
"number_sms_notifications": "Número de telefone (notificações de SMS)",
"attendee_email_workflow": "E-mail do participante",
@@ -1368,7 +1367,7 @@
"sender_id": "ID do remetente",
"sender_id_error_message": "Apenas letras, números e espaços permitidos (no máx. 11 caracteres)",
"test_routing_form": "Testar formulário de roteamento",
"test_preview": "Testar prévia",
"test_preview": "Visualizar teste",
"route_to": "Direcionar para",
"test_preview_description": "Teste seu formulário de roteamento sem enviar dados",
"test_routing": "Testar roteamento"
+69 -2
View File
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "Um evento ainda aguarda aprovação",
"event_is_still_waiting": "Pedido de evento pendente: {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "Não existem mais resultados",
"no_results": "Sem resultados",
"load_more_results": "Carregar mais resultados",
"integration_meeting_id": "ID da reunião de {{integrationName}}: {{meetingId}}",
"confirmed_event_type_subject": "Confirmado: {{eventType}} com {{name}} em {{date}}",
@@ -248,6 +249,7 @@
"add_to_calendar": "Adicionar ao calendário",
"add_another_calendar": "Adicionar outro calendário",
"other": "Outras",
"email_sign_in_subject": "A sua ligação de início de sessão para {{appName}}",
"emailed_you_and_attendees": "Enviámos um email para si e para os outros participantes com um convite de calendário com todos os detalhes.",
"emailed_you_and_attendees_recurring": "Enviámos um email para si e para os outros participantes com um convite de calendário para o primeiro destes eventos recorrentes.",
"emailed_you_and_any_other_attendees": "Esta informação foi enviada para si e para todos os outros participantes.",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "Ir para o portal de pagamento",
"need_anything_else": "Precisa de algo mais?",
"further_billing_help": "Se precisar de mais ajuda com a faturação, a nossa equipa de suporte está aqui para ajudar.",
"contact_our_support_team": "Contacte a nossa equipa de suporte",
"uh_oh": "Uh oh!",
"no_event_types_have_been_setup": "Este utilizador ainda não configurou nenhum tipo de evento.",
"edit_logo": "Editar logótipo",
@@ -420,6 +421,7 @@
"current_incorrect_password": "A senha atual está incorreta",
"password_hint_caplow": "Mistura de letras maiúsculas e minúsculas",
"password_hint_min": "Tamanho mínimo de 7 caracteres",
"password_hint_admin_min": "Tamanho mínimo de 15 caracteres",
"password_hint_num": "Contém pelo menos 1 número",
"invalid_password_hint": "A palavra-passe deve ter no mínimo 7 caracteres, com pelo menos um número e uma mistura de letras maiúsculas e minúsculas",
"incorrect_password": "Palavra-Passe incorreta",
@@ -463,11 +465,14 @@
"booking_confirmation": "Confirme o seu {{eventTypeTitle}} com {{profileName}}",
"booking_reschedule_confirmation": "Reagende o seu {{eventTypeTitle}} com {{profileName}}",
"in_person_meeting": "Ligação ou reunião presencial",
"attendeeInPerson": "Pessoalmente (endereço do participante)",
"inPerson": "Pessoalmente (endereço do organizador)",
"link_meeting": "Ligar reunião",
"phone_call": "Número de telefone do participante",
"your_number": "O seu número de telefone",
"phone_number": "Número de Telefone",
"attendee_phone_number": "Número de telefone do participante",
"organizer_phone_number": "Número de telefone do organizador",
"host_phone_number": "O seu número de telefone",
"enter_phone_number": "Inserir Número do Telefone",
"reschedule": "Reagendar",
@@ -554,6 +559,10 @@
"collective": "Coletivo",
"collective_description": "Agende reuniões quando todos os membros selecionados da equipa estiverem disponíveis.",
"duration": "Duração",
"available_durations": "Durações disponíveis",
"default_duration": "Duração predefinida",
"default_duration_no_options": "Por favor, selecione primeiro as durações disponíveis",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "Minutos",
"round_robin": "Round Robin",
"round_robin_description": "Reuniões de ciclo entre vários membros da equipa.",
@@ -619,6 +628,7 @@
"teams": "Equipas",
"team": "Equipa",
"team_billing": "Facturação da equipa",
"team_billing_description": "Gerir a faturação para a sua equipa",
"upgrade_to_flexible_pro_title": "Alterámos os termos de facturação",
"upgrade_to_flexible_pro_message": "Há membros sem lugar na sua equipa. Actualize para o plano Pro para cobrir os lugares em falta.",
"changed_team_billing_info": "Desde Janeiro de 2022 a cobrança é efectuada por cada lugar de membro da equipa. Os membros da sua equipa que tivessem a versão Pro gratuitamente, estão agora num período experimental de 14 dias. Assim que este período expirar, estes membros ficarão ocultos da sua equipa, a menos que actualize agora.",
@@ -694,6 +704,7 @@
"hide_event_type": "Ocultar Tipo de Evento",
"edit_location": "Editar Localização",
"into_the_future": "no futuro",
"when_booked_with_less_than_notice": "Quando reservado com aviso prévio inferior a <time></time>",
"within_date_range": "Dentro de um período",
"indefinitely_into_future": "Indefinidamente para o futuro",
"add_new_custom_input_field": "Adicionar novo campo personalizado",
@@ -713,6 +724,8 @@
"delete_account_confirmation_message": "De certeza que quer eliminar a sua conta {{appName}}? Qualquer pessoa com quem tenha partilhado esta ligação de equipa deixará de poder fazer reservas através da respetiva ligação, e quaisquer preferências que tenha guardado serão perdidas.",
"integrations": "Integrações",
"apps": "Aplicações",
"apps_description": "Ativar aplicações para a sua instância do Cal",
"apps_listing": "Lista de aplicações",
"category_apps": "Aplicações de {{category}}",
"app_store": "App Store",
"app_store_description": "Ligar pessoas, tecnologia e o local de trabalho.",
@@ -736,6 +749,7 @@
"toggle_calendars_conflict": "Escolha os calendários que deseja verificar para evitar conflitos e duplicação de reservas.",
"select_destination_calendar": "Criar eventos em",
"connect_additional_calendar": "Ligar um calendário adicional",
"calendar_updated_successfully": "Calendário atualizado com sucesso",
"conferencing": "Conferência",
"calendar": "Calendário",
"payments": "Pagamentos",
@@ -768,6 +782,7 @@
"trending_apps": "Aplicações em destaque",
"explore_apps": "Aplicações de {{category}}",
"installed_apps": "Aplicações instaladas",
"free_to_use_apps": "Grátis",
"no_category_apps": "Sem aplicações de {{category}}",
"no_category_apps_description_calendar": "Adicione uma aplicação de calendário para verificar se existem conflitos e assim evitar sobreposição de marcações",
"no_category_apps_description_conferencing": "Experimente adicionar uma aplicação de conferência para integrar chamadas de vídeo com os seus clientes",
@@ -806,6 +821,8 @@
"verify_wallet": "Verificar carteira",
"connect_metamask": "Ligar Metamask",
"create_events_on": "Criar eventos em:",
"enterprise_license": "Esta é uma funcionalidade empresarial",
"enterprise_license_description": "Para ativar esta funcionalidade, obtenha uma chave de instalação na consola {{consoleUrl}} e adicione-a ao seu .env como CALCOM_LICENSE_KEY. Se a sua equipa já tem uma licença, entre em contacto com {{supportMail}} para obter ajuda.",
"missing_license": "Licença em falta",
"signup_requires": "Necessita de licença comercial",
"signup_requires_description": "De momento a {{companyName}} não oferece uma versão gratuita de código aberto da página de registo. Para aceder completamente aos componentes de registo tem de adquirir uma licença comercial. Para uso pessoal, recomendamos a Prisma Data Platform ou qualquer outra interface Postgres para criar contas.",
@@ -897,6 +914,7 @@
"user_impersonation_heading": "Representação de utilizador",
"user_impersonation_description": "Permite à nossa equipa de suporte iniciar sessão temporariamente em seu nome para rapidamente resolver quaisquer erros que nos reporte.",
"team_impersonation_description": "Permite que os administradores da sua equipa iniciem a sessão temporariamente em seu nome.",
"allow_booker_to_select_duration": "Permitir que o responsável pela reserva selecione a duração",
"impersonate_user_tip": "Todas as utilizações desta funcionalidade são auditadas.",
"impersonating_user_warning": "Representar o nome de utilizador \"{{user}}\".",
"impersonating_stop_instructions": "<0>Clique aqui para parar</0>.",
@@ -1014,6 +1032,9 @@
"error_removing_app": "Erro ao remover a aplicação",
"web_conference": "Conferência web",
"requires_confirmation": "Requer confirmação",
"always_requires_confirmation": "Sempre",
"requires_confirmation_threshold": "Requer confirmação se reservado com um aviso prévio inferior a {{time}} $t({{unit}}_timeUnit)",
"may_require_confirmation": "Pode necessitar de confirmação",
"nr_event_type_one": "{{count}} tipo de evento",
"nr_event_type_other": "{{count}} tipos de evento",
"add_action": "Adicionar acção",
@@ -1101,6 +1122,9 @@
"event_limit_tab_description": "Com que frequência pode ser reservado",
"event_advanced_tab_description": "Definições do calendário e muito mais...",
"event_advanced_tab_title": "Avançado",
"event_setup_multiple_duration_error": "Configuração do evento: durações múltiplas requerem pelo menos 1 opção.",
"event_setup_multiple_duration_default_error": "Configuração do evento: por favor, selecione uma duração predefinida válida.",
"event_setup_booking_limits_error": "Os limites de reserva devem estar ordenados de forma ascendente. [dia,semana,mês,ano]",
"select_which_cal": "Selecione para que calendário devem ser adicionadas as marcações",
"custom_event_name": "Nome de evento personalizado",
"custom_event_name_description": "Crie nomes de eventos personalizados a mostrar no calendário de eventos",
@@ -1154,8 +1178,11 @@
"invoices": "Faturas",
"embeds": "Incorporações",
"impersonation": "Representação",
"impersonation_description": "Definições para gerir a personificação de utilizadores",
"users": "Utilizadores",
"profile_description": "Gerir as definições para o seu perfil {{appName}}",
"users_description": "Aqui pode encontrar uma lista de todos os utilizadores",
"users_listing": "Lista de utilizadores",
"general_description": "Faça a gestão das definições do seu idioma e fuso horário",
"calendars_description": "Configure como os tipos de evento devem interagir com os seus calendários",
"appearance_description": "Faça a gestão das definições de aparência da sua reserva",
@@ -1360,6 +1387,7 @@
"number_sms_notifications": "Número de telefone (notificações SMS)",
"attendee_email_workflow": "E-mail do participante",
"attendee_email_info": "O e-mail do responsável pela reserva",
"kbar_search_placeholder": "Digite um comando ou pesquise...",
"invalid_credential": "Oh não! Parece que a permissão expirou ou foi revogada. Por favor, reinstale novamente.",
"choose_common_schedule_team_event": "Escolha uma agenda comum",
"choose_common_schedule_team_event_description": "Ative esta opção se quiser utilizar uma agenda comum entre anfitriões. Quando desativada, cada anfitrião será reservado com base na respetiva agenda pré-definida.",
@@ -1370,5 +1398,44 @@
"test_preview": "Pré-visualização do teste",
"route_to": "Encaminhar para",
"test_preview_description": "Teste o encaminhamento do seu formulário sem submeter qualquer dado",
"test_routing": "Testar encaminhamento"
"test_routing": "Testar encaminhamento",
"payment_app_disabled": "Um administrador desativou uma aplicação de pagamentos",
"edit_event_type": "Editar o tipo de evento",
"collective_scheduling": "Agendamento coletivo",
"make_it_easy_to_book": "Faça com que seja simples reservar a sua equipa quando todos estiverem disponíveis.",
"find_the_best_person": "Encontre a melhor pessoa disponível e navegue na sua equipa.",
"fixed_round_robin": "Distribuição equilibrada fixa",
"add_one_fixed_attendee": "Adicione um participante fixo e distribua de forma equilibrada a partir de um número de participantes.",
"calcom_is_better_with_team": "O Cal.com é melhor com equipas",
"add_your_team_members": "Adicione os membros da sua equipa aos seus tipos de eventos. Utilize o agendamento coletivo para incluir todos os membros ou encontre a pessoa mais adequada com o agendamento baseado numa distribuição equilibrada.",
"booking_limit_reached": "O limite de reservas para este tipo de evento foi atingido",
"admin_has_disabled": "{{appName}} foi desativada por um administrador",
"disabled_app_affects_event_type": "{{appName}} foi desativada por um administrador o que afeta o seu tipo de evento {{eventType}}",
"disable_payment_app": "O administrador desativou {{appName}} o que afeta o seu tipo de evento {{title}}. Os participantes ainda poderão reservar este tipo de evento, mas não será solicitado que façam qualquer pagamento. Pode ocultar o tipo de evento para evitar esta situação, até que o seu administrador reative o seu método de pagamento.",
"payment_disabled_still_able_to_book": "Os participantes ainda poderão reservar este tipo de evento, mas não será solicitado que façam qualquer pagamento. Pode ocultar o tipo de evento para evitar esta situação, até que o seu administrador reative o seu método de pagamento.",
"app_disabled_with_event_type": "O administrador desativou {{appName}} o que afeta o seu tipo de evento {{title}}.",
"app_disabled_video": "O administrador desativou {{appName}} o que afeta os seus tipos de eventos. Se tiver tipos de eventos com {{appName}} como o sítio, então o mesmo será predefinido para o Cal Video.",
"app_disabled_subject": "{{appName}} foi desativada",
"navigate_installed_apps": "Ir para as aplicações instaladas",
"disabled_calendar": "Se tiver outro calendário instalado, os novos pedidos serão adicionados ao mesmo. Se não é este o caso, deve associar um novo calendário para garantir que não perde novas reservas.",
"enable_apps": "Ativar aplicações",
"enable_apps_description": "Ativar aplicações que os utilizadores podem integrar com o Cal.com",
"app_is_enabled": "{{appName}} foi ativada",
"app_is_disabled": "{{appName}} foi desativada",
"keys_have_been_saved": "As chaves foram guardadas",
"disable_app": "Desativar a aplicação",
"disable_app_description": "A desativação desta aplicação pode causar problemas na forma como os seus utilizadores interagem com o Cal",
"edit_keys": "Editar chaves",
"admin_apps_description": "Ativar aplicações para a sua instância do Cal",
"no_available_apps": "Não existem aplicações disponíveis",
"no_available_apps_description": "Por favor, certifique-se que existem aplicações na sua instalação em 'packages/app-store'",
"no_apps": "Não existem aplicações ativas nesta instância do Cal",
"apps_settings": "Definições de aplicações",
"fill_this_field": "Por favor, preencha este campo",
"options": "Opções",
"enter_option": "Insira a opção {{index}}",
"add_an_option": "Adicione uma opção",
"radio": "Radio",
"event_type_duplicate_copy_text": "{{slug}}-copia",
"set_as_default": "Predefinir"
}
@@ -383,7 +383,6 @@
"go_to_billing_portal": "Accesați portalul de facturare",
"need_anything_else": "Aveți nevoie de altceva?",
"further_billing_help": "Dacă ai nevoie de ajutor suplimentar pentru facturare, echipa noastră de asistență este aici pentru a te ajuta.",
"contact_our_support_team": "Contactează echipa noastră de suport",
"uh_oh": "Uh oh!",
"no_event_types_have_been_setup": "Acest utilizator nu a configurat încă niciun tip de eveniment.",
"edit_logo": "Editare logo",
+19 -20
View File
@@ -16,8 +16,8 @@
"event_request_cancelled": "Ваша запланированная встреча была отменена",
"organizer": "Организатор",
"need_to_reschedule_or_cancel": "Хотите перенести или отменить?",
"cancellation_reason": "Причина отмены",
"cancellation_reason_placeholder": "Укажите причину отмены (необязательное поле)",
"cancellation_reason": "Причина отмены (необязательно)",
"cancellation_reason_placeholder": "Укажите причину отмены подписки",
"rejection_reason": "Причина отказа",
"rejection_reason_title": "Отказаться от бронирования?",
"rejection_reason_description": "Отказаться от брони? Мы сообщим пользователю, который пытался оформить бронь. Вы можете указать причину отказа ниже.",
@@ -108,7 +108,7 @@
"link_expires": "p.s. Это истекает через {{expiresIn}} часов.",
"upgrade_to_per_seat": "Перейти на оплату из расчета за количество мест",
"team_upgrade_seats_details": "В вашей команде {{memberCount}} пользователей; из них не оплачено еще {{unpaidCount}} мест. При оплате ${{seatPrice}} в месяц за место общая сумма к оплате составляет ${{totalCost}} в месяц.",
"team_upgrade_banner_description": "Спасибо, что перешли на наш новый тариф team. Мы заметили, что вам нужно обновить \"{{teamName}}\" команды.",
"team_upgrade_banner_description": "Спасибо, что решили попробовать наш новый тариф Team. Необходимо обновить команду «{{teamName}}».",
"team_upgrade_banner_action": "Обновить",
"team_upgraded_successfully": "Вы успешно перешли на новый тариф для своей команды!",
"use_link_to_reset_password": "Используйте ссылку ниже, чтобы сбросить пароль",
@@ -383,7 +383,6 @@
"go_to_billing_portal": "Перейти на платёжный портал",
"need_anything_else": "Нужно что-нибудь еще?",
"further_billing_help": "Если вам нужна дальнейшая помощь с оплатой счета, наша служба поддержки готова помочь.",
"contact_our_support_team": "Свяжитесь с нашей службой поддержки",
"uh_oh": "О нет!",
"no_event_types_have_been_setup": "Этот пользователь еще не создал ни одного события.",
"edit_logo": "Изменить логотип",
@@ -420,7 +419,7 @@
"current_incorrect_password": "Неверный текущий пароль",
"password_hint_caplow": "Используйте смесь заглавных и строчных букв",
"password_hint_min": "Не короче 7 символов",
"password_hint_admin_min": "Минимум 15 символов",
"password_hint_admin_min": "Не короче 15 символов",
"password_hint_num": "Не менее 1 цифры",
"invalid_password_hint": "Пароль должен быть не короче 7 символов и содержать заглавные и строчные буквы, а также не менее одной цифры",
"incorrect_password": "Неверный пароль",
@@ -505,7 +504,7 @@
"add_team_members_description": "Пригласите других пользователей в свою команду",
"add_team_member": "Добавить участника команды",
"invite_new_member": "Пригласить нового участника команды",
"invite_new_member_description": "Примечание. Стоимость подписки увеличится на <1>$15/месяц за дополнительное место</1>.",
"invite_new_member_description": "Примечание: стоимость подписки увеличится на <1>15 $/месяц за дополнительное место</1>.",
"invite_new_team_member": "Пригласите кого-нибудь в вашу команду.",
"change_member_role": "Изменить роль члена команды",
"disable_cal_branding": "Отключить брендинг {{appName}}",
@@ -602,7 +601,7 @@
"monthly_other": "мес.",
"yearly_one": "год",
"yearly_other": "г.",
"plus_more": "еще {{count}}",
"plus_more": "Еще {{count}}",
"max": "Макс.",
"single_theme": "Тема оформления",
"brand_color": "Цвет бренда",
@@ -785,7 +784,7 @@
"analytics": "Аналитика",
"empty_installed_apps_headline": "Нет установленных приложений",
"empty_installed_apps_description": "Приложения позволяют значительно улучшить рабочий процесс и жить по расписанию.",
"empty_installed_apps_button": "Перейти в App Store или установить одно из приложений ниже",
"empty_installed_apps_button": "Перейти в App Store",
"manage_your_connected_apps": "Управление установленными приложениями и изменение настроек",
"browse_apps": "Обзор приложений",
"features": "Возможности",
@@ -1230,7 +1229,7 @@
"exchange_compression": "Сжатие GZip",
"routing_forms_description": "Здесь показываются все созданные вами формы и маршруты.",
"routing_forms_send_email_owner": "Отправить письмо владельцу",
"routing_forms_send_email_owner_description": "Отправляет сообщение владельцу при отправке формы",
"routing_forms_send_email_owner_description": "Отправляет владельцу электронное письмо при отправке формы",
"add_new_form": "Добавить новую форму",
"form_description": "Создайте форму для перенаправления пользователей при бронировании",
"copy_link_to_form": "Скопировать ссылку на форму",
@@ -1344,8 +1343,8 @@
"no_event_types": "Типы событий не установлены",
"no_event_types_description": "{{name}} не установил(а) для вас типы событий, которые вы можете бронировать.",
"billing_frequency": "Периодичность расчетов",
"monthly": "Ежемесячно",
"yearly": "Ежегодно",
"monthly": "Ежемесячная",
"yearly": "Ежегодная",
"checkout": "Оплатить заказ",
"your_team_disbanded_successfully": "Ваша команда расформирована",
"error_creating_team": "Ошибка при создании команды",
@@ -1353,24 +1352,24 @@
"send_email": "Отправить письмо",
"member_already_invited": "Участник уже приглашен",
"enter_email_or_username": "Введите адрес электронной почты или имя пользователя",
"team_name_taken": "Это имя уже занято",
"team_name_taken": "Это название уже занято",
"must_enter_team_name": "Необходимо ввести название команды",
"team_url_required": "Необходимо ввести URL команды",
"team_url_taken": "Этот URL уже занят",
"team_url_required": "Необходимо ввести URL-адрес команды",
"team_url_taken": "Этот URL-адрес уже занят",
"team_publish": "Опубликовать команду",
"number_sms_notifications": "Номер телефона (SMS-уведомления)",
"attendee_email_workflow": "Электронная почта участника",
"attendee_email_info": "Электронная почта участника, на которого оформляется бронирование",
"invalid_credential": "Похоже, разрешение истекло или отозвано. Возобновите его.",
"invalid_credential": "Похоже, разрешение истекло или было отозвано. Необходима переустановка.",
"choose_common_schedule_team_event": "Выберите общее расписание",
"choose_common_schedule_team_event_description": "Позволяет организаторам использовать общее расписание. Если общее расписание отключено, бронирование выполняется для каждого организатора в расписании по умолчанию.",
"choose_common_schedule_team_event_description": "Позволяет организаторам использовать общее расписание. Если общее расписание отключено, то для каждого организатора бронирование выполняется в соответствии с его индивидуальным расписанием по умолчанию.",
"reason": "Причина",
"sender_id": "ID отправителя",
"sender_id_error_message": "Только буквы, цифры и пробелы (макс. 11 символов)",
"test_routing_form": "Тестировать форму машрутизации",
"test_preview": "Тестировать предварительный просмотр",
"route_to": "Путь к",
"test_routing_form": "Проверить форму машрутизации",
"test_preview": "Проверить предварительный просмотр",
"route_to": "Цель маршрута",
"test_preview_description": "Проверить форму маршрутизации без отправки данных",
"test_routing": "Тестировать машрутизацию",
"test_routing": "Проверить машрутизацию",
"booking_limit_reached": "Достигнут лимит бронирования для этого типа события"
}
+79 -12
View File
@@ -16,8 +16,8 @@
"event_request_cancelled": "Vaš zakazan dogadjaj je odbijen",
"organizer": "Organizator",
"need_to_reschedule_or_cancel": "Morate da odložite ili otkažete?",
"cancellation_reason": "Razlog otkazivanja",
"cancellation_reason_placeholder": "Zašto otkazujete? (opciono)",
"cancellation_reason": "Razlog otkazivanja (opciono)",
"cancellation_reason_placeholder": "Zašto otkazujete?",
"rejection_reason": "Razlog odbijanja",
"rejection_reason_title": "Odbij zahtev za rezervaciju?",
"rejection_reason_description": "Da li ste sigurni da želite da odbijete rezervaciju? Obavestićemo osobu koja je pokušala da rezerviše. Možete navesti razlog ispod.",
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "Dogadjaj još čeka na odobrenje",
"event_is_still_waiting": "Zahtev na dogadjaj još čeka: {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "Nema više rezultata",
"no_results": "Nema rezultata",
"load_more_results": "Učitaj više rezultata",
"integration_meeting_id": "{{integrationName}} ID sastanka: {{meetingId}}",
"confirmed_event_type_subject": "Potvrdjeno: {{eventType}} sa {{name}} datuma {{date}}",
@@ -108,7 +109,7 @@
"link_expires": "p.s. Ističe za {{expiresIn}} sati.",
"upgrade_to_per_seat": "Pretplati se na Po-Mestu",
"team_upgrade_seats_details": "Od {{memberCount}} članova u vašem timu, {{unpaidCount}} mesta/o nisu plaćena. Za ${{seatPrice}}/mesečno po mestu procenjena celokupna cena vašeg članstva je ${{totalCost}}/mesečno.",
"team_upgrade_banner_description": "Hvala što probate naš novi plan tima. Primetili smo da vaš tim „{{teamName}}“ treba da se nadogradi.",
"team_upgrade_banner_description": "Hvala vam što isprobavate naš novi plan za timove. Primetili smo da vaš tim „{{teamName}}“ treba da se nadogradi.",
"team_upgrade_banner_action": "Nadogradite ovde",
"team_upgraded_successfully": "Vaš tim je uspešno pretplaćen!",
"use_link_to_reset_password": "Resetujte lozinku koristeći link ispod",
@@ -248,6 +249,7 @@
"add_to_calendar": "Dodaj u kalendar",
"add_another_calendar": "Dodaj još jedan kalendar",
"other": "Drugo",
"email_sign_in_subject": "Vaša veza za prijavu na {{appName}}",
"emailed_you_and_attendees": "Poslali smo vama i ostalim učesnicima kalendarski poziv sa svim detaljima.",
"emailed_you_and_attendees_recurring": "Poslali smo vama i ostalim učesnicima kalendarski poziv za prvi od ovih ponavljajućih događaja.",
"emailed_you_and_any_other_attendees": "Vi i ostali učesnici ste dobili e-poštu sa ovim informacijama.",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "Idite na portal naplate",
"need_anything_else": "Treba li vam još nešto?",
"further_billing_help": "Ako vam treba pomoć sa naplatom, naš tim podrške je tu da pomogne.",
"contact_our_support_team": "Kontaktirjate naš tim podrške",
"uh_oh": "O ne!",
"no_event_types_have_been_setup": "Ovaj korisnik još nije podesio tipove događaja.",
"edit_logo": "Uredi logo",
@@ -420,6 +421,7 @@
"current_incorrect_password": "Trenutna lozinka je netačna",
"password_hint_caplow": "Mešavina velikih i malih slova",
"password_hint_min": "Dugačka minimalno 7 znakova",
"password_hint_admin_min": "Dugačka minimalno 15 znakova",
"password_hint_num": "Sadrži barem 1 broj",
"invalid_password_hint": "Lozinka mora da bude dugačka najmanje 7 znakova, da sadrži najmanje jedan broj i da ima mešavinu velikih i malih slova",
"incorrect_password": "Lozinka je netačna.",
@@ -463,11 +465,14 @@
"booking_confirmation": "Potvrdite vaš {{eventTypeTitle}} sa {{profileName}}",
"booking_reschedule_confirmation": "Odložite vaš {{eventTypeTitle}} sa {{profileName}}",
"in_person_meeting": "Sastanak uživo ili preko online linka",
"attendeeInPerson": "Lično (adresa polaznika)",
"inPerson": "Lično (adresa organizatora)",
"link_meeting": "Link za sastanak",
"phone_call": "Broj telefona učesnika",
"your_number": "Vaš broj telefona",
"phone_number": "Broj Telefona",
"attendee_phone_number": "Broj telefona učesnika",
"organizer_phone_number": "Broj telefona organizatora",
"host_phone_number": "Vaš broj telefona",
"enter_phone_number": "Unesite broj telefona",
"reschedule": "Odloži",
@@ -504,7 +509,7 @@
"add_team_members_description": "Pozovite druge da se pridruže vašem timu",
"add_team_member": "Dodaj člana tima",
"invite_new_member": "Pozovite novog člana",
"invite_new_member_description": "Napomena: Ovim će <1>biti naplaćeno dodatno mesto (15 $/m)</1> putem vaše pretplate.",
"invite_new_member_description": "</1>Napomena: Ovo će <1>povećati vašu pretplatu za dodatno mesto (15 $/mesečno)</1>.",
"invite_new_team_member": "Pozovite nekog u vaš tim.",
"change_member_role": "Promenite ulogu članu tima",
"disable_cal_branding": "Onemogući brendiranje {{appName}}",
@@ -554,6 +559,10 @@
"collective": "Kolektiv",
"collective_description": "Rezerviši sastanke kad su izabrani članovi tima dostupni.",
"duration": "Trajanje",
"available_durations": "Dostupna trajanja",
"default_duration": "Podrazumevano trajanje",
"default_duration_no_options": "Najpre izaberite dostupna trajanja",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "Minuti",
"round_robin": "Round Robin",
"round_robin_description": "Ciklusirajte sastanke između više članova tima.",
@@ -619,6 +628,7 @@
"teams": "Timovi",
"team": "Tim",
"team_billing": "Naplata za timove",
"team_billing_description": "Upravljajte naplatom za vaš tim",
"upgrade_to_flexible_pro_title": "Promenili smo proces naplate za timove",
"upgrade_to_flexible_pro_message": "Imate članove u timu koji nemaju mesto. Nadogradite svoj pro plan da biste pokrili mesta koja nedostaju.",
"changed_team_billing_info": "Od Januara 2022 vršimo naplatu po mestu za članove tima. Članovi vašeg tima koji su imali Pro besplatno su sada na Pro probnom periodu od 14 dana. Kad probni period istekne ovi članovi biće sakriveni od vašeg tima ukoliko se ne pretplatite na ovu uslugu.",
@@ -694,6 +704,7 @@
"hide_event_type": "Sakrij tip događaja",
"edit_location": "Uredi lokaciju",
"into_the_future": "u budućnost",
"when_booked_with_less_than_notice": "Kada je rezervisano sa manje od <time></time> napomenom",
"within_date_range": "U okviru vremenskog perioda",
"indefinitely_into_future": "Na neodređeno u budućnost",
"add_new_custom_input_field": "Dodajte prilagodljivo polje unosa",
@@ -713,6 +724,8 @@
"delete_account_confirmation_message": "Da li ste sigurni da želite izbrisati svoj {{appName}} nalog? S kim god ste podelili link ovog naloga više neće moći da ga koristi za rezervacije i sva podešavanja koja imate neće biti sačuvana.",
"integrations": "Integracije",
"apps": "Aplikacije",
"apps_description": "Omogućite aplikacije za vašu instancu Cal-a",
"apps_listing": "Spisak aplikacija",
"category_apps": "{{category}} aplikacije",
"app_store": "App Store",
"app_store_description": "Povezujemo ljude, tehnologiju i radno mesto.",
@@ -736,6 +749,7 @@
"toggle_calendars_conflict": "Uključite kalendare koje želite da proverite da li imaju konflikta da biste sprečili dvostruka zakazivanja.",
"select_destination_calendar": "Napravi događaje na",
"connect_additional_calendar": "Poveži se sa dodatnim kalendarom",
"calendar_updated_successfully": "Kalendar je uspešno ažuriran",
"conferencing": "Konferencija",
"calendar": "Kalendar",
"payments": "Plaćanja",
@@ -768,6 +782,7 @@
"trending_apps": "Aplikacije u trendu",
"explore_apps": "{{category}} aplikacije",
"installed_apps": "Instalirane aplikacije",
"free_to_use_apps": "Besplatno",
"no_category_apps": "Nema {{category}} aplikacija",
"no_category_apps_description_calendar": "Dodajte kalendar aplikaciju da biste proverili da li postoje konflikti i time sprečili dvostruka zakazivanja",
"no_category_apps_description_conferencing": "Pokušajte da dodate aplikaciju za konferenciju da biste integrisali video poziv sa svojim klijentima",
@@ -784,7 +799,7 @@
"analytics": "Analitika",
"empty_installed_apps_headline": "Aplikacije nisu instalirane",
"empty_installed_apps_description": "Aplikacije omogućavaju da unapredite svoj rad i značajno poboljšate svoja zakazivanja.",
"empty_installed_apps_button": "Istražite App Store ili instalirajte neku od aplikacija u nastavku",
"empty_installed_apps_button": "Istražite App Store",
"manage_your_connected_apps": "Upravljajte instaliranim aplikacijama ili promenite postavke",
"browse_apps": "Pregledajte aplikacije",
"features": "Funkcije",
@@ -806,6 +821,8 @@
"verify_wallet": "Verifikuj Wallet",
"connect_metamask": "Poveži Metamask",
"create_events_on": "Kreiraj događaje na",
"enterprise_license": "Ovo je enterprise funkcija",
"enterprise_license_description": "Da biste omogućili ovu funkciju, nabavite ključ za primenu na {{consoleUrl}} konzoli i dodajte ga u svoj .env kao CALCOM_LICENCE_KEY. Ako vaš tim već ima licencu, obratite se na {{supportMail}} za pomoć.",
"missing_license": "Nedostaje Licenca",
"signup_requires": "Komercijalna dozvola je neophodna",
"signup_requires_description": "{{companyName}} trenutno ne nudi besplatnu verziju otvorenog koda od stranice za prijavu. Da bi dobili potpuni pristup kompenentama za prijavu, neophodno je da ste vlasnik komercijalne licence. Za ličnu upotrebu preporučujemo Prisma Data Platformu ili bilo koji drugi Postgres interfejs za pravljenje naloga.",
@@ -897,6 +914,7 @@
"user_impersonation_heading": "Predstavljanje kao korisnik",
"user_impersonation_description": "Dozvoljava našem timu za podršku da se privremeno prijavi umesto vas kako bismo vam pomogli da brzo rešite sve probleme koje ste nam prijavili.",
"team_impersonation_description": "Dozvolite administratorima svog tima da se privremeno prijave kao vi.",
"allow_booker_to_select_duration": "Dozvoli polazniku da izabere trajanje",
"impersonate_user_tip": "Sva korišćenja ove funkcije se nadgledaju.",
"impersonating_user_warning": "Predstavljanje kao korisnik „{{user}}“.",
"impersonating_stop_instructions": "<0>Kliknite ovde da zaustavite</0>.",
@@ -1014,6 +1032,9 @@
"error_removing_app": "Greška prilikom uklanjanja aplikacije",
"web_conference": "Veb konferencija",
"requires_confirmation": "Potrebna je potvrda",
"always_requires_confirmation": "Uvek",
"requires_confirmation_threshold": "Zahteva potvrdu ako je rezervisano sa < {{time}}$t({{unit}}_timeUnit) napomenom",
"may_require_confirmation": "Može da zahteva potvrdu",
"nr_event_type_one": "{{count}} tip događaja",
"nr_event_type_other": "{{count}} tipova događaja",
"add_action": "Dodaj radnju",
@@ -1101,6 +1122,9 @@
"event_limit_tab_description": "Koliko često možete da budete rezervisani",
"event_advanced_tab_description": "Podešavanja kalendara i više...",
"event_advanced_tab_title": "Napredno",
"event_setup_multiple_duration_error": "Podešavanje događaja: Višestruka trajanja zahteva najmanje 1 opciju.",
"event_setup_multiple_duration_default_error": "Podešavanje događaja: Izaberite važeće podrazumevano trajanje.",
"event_setup_booking_limits_error": "Ograničenja zakazivanja moraju da budu u rastućem nizu. [dan,nedelja,mesec,godina]",
"select_which_cal": "Izaberite u koji kalendar želite da dodate rezervacije",
"custom_event_name": "Prilagođeno ime događaja",
"custom_event_name_description": "Kreirajte prilagođena imena događaja za prikaz na kalendaru događaja",
@@ -1154,8 +1178,11 @@
"invoices": "Fakture",
"embeds": "Ugrađivanja",
"impersonation": "Predstavljanje kao",
"impersonation_description": "Podešavanja za upravljanje lažnim predstavljanjem korisnika",
"users": "Korisnici",
"profile_description": "Upravljajte podešavanjima vašeg {{appName}} profila",
"users_description": "Ovde možete da nađete spisak svih korisnika",
"users_listing": "Spisak korisnika",
"general_description": "Upravljajte podešavanjima za vaš jezik i vremensku zonu",
"calendars_description": "Konfigurišite način na koji tipovi događaja vrše interakciju sa vašim kalendarima",
"appearance_description": "Upravljajte podešavanjima za izgled vaše rezervacije",
@@ -1345,7 +1372,7 @@
"billing_frequency": "Učestalost naplate",
"monthly": "Mesečno",
"yearly": "Godišnje",
"checkout": "Odjava",
"checkout": "Kasa",
"your_team_disbanded_successfully": "Vaš tim je uspešno rasformiran",
"error_creating_team": "Greška prilikom kreiranja tima",
"you": "Vi",
@@ -1353,22 +1380,62 @@
"member_already_invited": "Član je već pozvan",
"enter_email_or_username": "Unesite imejl ili korisničko ime",
"team_name_taken": "Ime je zauzeto",
"must_enter_team_name": "Unesite ime tima",
"must_enter_team_name": "Morate uneti naziv tima",
"team_url_required": "Morate da unesete URL tima",
"team_url_taken": "Ovaj URL je zauzet",
"team_publish": "Objavite tim",
"number_sms_notifications": "Broj telefona (obaveštenja putem SMS-a)",
"attendee_email_workflow": "Imejl polaznika",
"attendee_email_info": "Imejl osobe koja rezerviše",
"invalid_credential": "Izgleda da je dozvola istekla ili je povučena. Instalirajte ponovo.",
"kbar_search_placeholder": "Unesite komandu ili pretražite...",
"invalid_credential": "O, ne! Izgleda da je dozvola istekla ili je povučena. Instalirajte ponovo.",
"choose_common_schedule_team_event": "Izaberite zajednički raspored",
"choose_common_schedule_team_event_description": "Omogućite to ako želite da koristite zajednički raspored između domaćina. Kada je onemogućen, svaki domaćin može rezervisati na osnovu svog podrazumevanog rasporeda.",
"reason": "Razlog",
"sender_id": "ID pošiljaoca",
"sender_id_error_message": "Dozvoljena su samo slova, brojevi i razmaci (maks. 11 karaktera)",
"test_routing_form": "Obrazac testiranja usmeravanja",
"sender_id_error_message": "Dozvoljena su samo slova, brojevi i razmaci (maks. 11 znakova)",
"test_routing_form": "Testiranje obrasca za usmeravanje",
"test_preview": "Pregled testa",
"route_to": "Usmeri na",
"test_preview_description": "Proverite obrazac za usmeravanje bez slanja bilo kog podatka",
"test_routing": "Testiranje usmeravanja"
"test_routing": "Testiranje usmeravanja",
"payment_app_disabled": "Administrator je onemogućio aplikaciju za plaćanje",
"edit_event_type": "Izmenite tip događaja",
"collective_scheduling": "Kolektivno zakazivanje",
"make_it_easy_to_book": "Učinite lakim da zakažete svoj tim kada je su svi dostupni.",
"find_the_best_person": "Nađite najbolju dostupnu osobu i pregledajte svoj tim.",
"fixed_round_robin": "Fiksno kružno dodeljivanje",
"add_one_fixed_attendee": "Dodajte jednog fiksnog polaznika i kružno dodelite velikom broju polaznika.",
"calcom_is_better_with_team": "Cal.com je bolji uz timove",
"add_your_team_members": "Dodajte članove svog tima vašem tipovima događaja. Koristite kolektivno zakazivanje da uključite svakoga ili pronađete osobu koja najviše odgovara pomoću zakazivanja kružnim dodeljivanjem.",
"booking_limit_reached": "Dostignuta je granica zakazivanja za ovaj tip događaja",
"admin_has_disabled": "Administrator je onemogućio {{appName}}",
"disabled_app_affects_event_type": "Administrator je onemogućio {{appName}} što utiče na vaš tip događaja {{eventType}}",
"disable_payment_app": "Administrator je onemogućio {{appName}} što utiče na vaš tip događaja {{title}}. Polaznici još uvek mogu da zakažu ovaj tip događaja, ali im neće biti zatraženo da plate. Možete da sakrijete ovaj tip događaja da biste to sprečili dok vaš administrator ponovo ne omogući vaš način plaćanja.",
"payment_disabled_still_able_to_book": "Polaznici još uvek mogu da zakažu ovaj tip događaja, ali im neće biti zatraženo da plate. Možete da sakrijete ovaj tip događaja da biste to sprečili dok vaš administrator ponovo ne omogući vaš način plaćanja.",
"app_disabled_with_event_type": "Administrator je onemogućio {{appName}} što utiče na vaš tip događaja {{title}}.",
"app_disabled_video": "Administrator je onemogućio {{appName}} što može da utiče na vaš tip događaja. Ako imate tipove događaja sa {{appName}} kao lokacijom, onda će biti podrazumevano usmereni na Cal Video.",
"app_disabled_subject": "Aplikacija {{appName}} je onemogućena",
"navigate_installed_apps": "Idi na instalirane aplikacije",
"disabled_calendar": "Ako imate instaliran drugi kalendar, nova zakazivanja će biti dodata na njega. Ako nemate, onda povežite novi kalendar da ne biste propustili nova zakazivanja.",
"enable_apps": "Omogući aplikacije",
"enable_apps_description": "Omogući aplikacije koje korisnik može da integriše sa Cal.com",
"app_is_enabled": "Aplikacija {{appName}} je omogućena",
"app_is_disabled": "Aplikacija {{appName}} je onemogućena",
"keys_have_been_saved": "Ključevi su sačuvani",
"disable_app": "Onemogući aplikaciju",
"disable_app_description": "Onemogućavanje ove aplikacije može da stvori probleme u komunikaciji vaših korisnika sa Cal-om",
"edit_keys": "Izmeni ključeve",
"admin_apps_description": "Omogućite aplikacije za vašu instancu Cal-a",
"no_available_apps": "Nema dostupnih aplikacija",
"no_available_apps_description": "Uverite se da ima aplikacija u vašoj fascikli za instalaciju pod „packages/app-store“",
"no_apps": "Nema omogućenih aplikacija u ovoj instanci Cal-a",
"apps_settings": "Podešavanja aplikacija",
"fill_this_field": "Popunite ovo polje",
"options": "Opcije",
"enter_option": "Unesite opciju {{index}}",
"add_an_option": "Dodajte opciju",
"radio": "Radio",
"event_type_duplicate_copy_text": "{{slug}}-kopija",
"set_as_default": "Postavi kao podrazumevano"
}
+69 -2
View File
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "En bokning väntar fortfarande på godkännande",
"event_is_still_waiting": "Bokningsförfrågan väntar fortfarande: {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "Inga fler resultat",
"no_results": "Inga resultat",
"load_more_results": "Hämta fler resultat",
"integration_meeting_id": "{{integrationName}} mötes-ID: {{meetingId}}",
"confirmed_event_type_subject": "Bekräftad: {{eventType}} med {{name}} vid {{date}}",
@@ -248,6 +249,7 @@
"add_to_calendar": "Lägg till i kalender",
"add_another_calendar": "Lägg till en annan kalender",
"other": "Annat",
"email_sign_in_subject": "Din inloggningslänk för {{appName}}",
"emailed_you_and_attendees": "Vi skickade dig och de andra deltagarna en kalenderinbjudan med alla detaljer.",
"emailed_you_and_attendees_recurring": "Vi skickade dig och de andra deltagarna en kalenderinbjudan via e-post för den första av dessa återkommande händelser.",
"emailed_you_and_any_other_attendees": "Du och alla andra deltagare har fått e-post med denna information.",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "Gå till faktureringsportalen",
"need_anything_else": "Behöver du något annat?",
"further_billing_help": "Om du behöver ytterligare hjälp med fakturering finns vårt supportteam här för att hjälpa till.",
"contact_our_support_team": "Kontakta vårt kundsupportteam",
"uh_oh": "Åh nej!",
"no_event_types_have_been_setup": "Den här användaren har inte skapat några händelsetyper ännu.",
"edit_logo": "Ändra logotyp",
@@ -420,6 +421,7 @@
"current_incorrect_password": "Nuvarande lösenord är felaktigt",
"password_hint_caplow": "Blandning av versaler och gemener",
"password_hint_min": "Minst 8 tecken",
"password_hint_admin_min": "Minst 15 tecken",
"password_hint_num": "Innehåller minst 1 siffra",
"invalid_password_hint": "Lösenordet måste innehålla minst 7 tecken och har minst en siffra samt en blandning av versaler och gemener",
"incorrect_password": "Lösenordet är felaktigt.",
@@ -463,11 +465,14 @@
"booking_confirmation": "Bekräfta {{eventTypeTitle}} med {{profileName}}",
"booking_reschedule_confirmation": "Omboka {{eventTypeTitle}} med {{profileName}}",
"in_person_meeting": "Länk eller fysiskt möte",
"attendeeInPerson": "Personligen (deltagandeadress)",
"inPerson": "Personligen (orgnanisatörsadress)",
"link_meeting": "Länka möte",
"phone_call": "Deltagarens telefonnummer",
"your_number": "Ditt telefonnummer",
"phone_number": "Telefonnummer",
"attendee_phone_number": "Deltagarens telefonnummer",
"organizer_phone_number": "Organisatörens telefonnummer",
"host_phone_number": "Ditt telefonnummer",
"enter_phone_number": "Fyll i telefonnummer",
"reschedule": "Omplanera",
@@ -554,6 +559,10 @@
"collective": "Kollektiv",
"collective_description": "Schemalägg möten när alla valda teammedlemmar är tillgängliga.",
"duration": "Längd",
"available_durations": "Tillgänglig varaktighet",
"default_duration": "Standardvaraktighet",
"default_duration_no_options": "Välj först tillgänglig varaktighet",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "Minuter",
"round_robin": "Tur och ordning",
"round_robin_description": "Variera möten mellan olika teammedlemmar.",
@@ -619,6 +628,7 @@
"teams": "Teams",
"team": "Team",
"team_billing": "Team-fakturering",
"team_billing_description": "Hantera fakturering för ditt team",
"upgrade_to_flexible_pro_title": "Vi har ändrat fakturering för team",
"upgrade_to_flexible_pro_message": "Det finns medlemmar i ditt team utan en plats. Uppgradera din pro plan för att täcka saknade platser.",
"changed_team_billing_info": "Från och med januari 2022 tar vi betalt på plats-basis för teammedlemmar. Medlemmar i ditt team som hade Pro gratis är nu på en 14 dagars provperiod. När deras provperiod löper ut kommer dessa medlemmar att döljas från ditt team om du inte uppgraderar nu.",
@@ -694,6 +704,7 @@
"hide_event_type": "Dölj händelsetyp",
"edit_location": "Redigera plats",
"into_the_future": "in i framtiden",
"when_booked_with_less_than_notice": "Vid bokning med mindre än <time></time> varsel",
"within_date_range": "Inom ett datumintervall",
"indefinitely_into_future": "På obestämd tid in i framtiden",
"add_new_custom_input_field": "Lägg till nytt anpassat inmatningsfält",
@@ -713,6 +724,8 @@
"delete_account_confirmation_message": "Är du säker på att du vill radera ditt {{appName}} konto? Alla som du har delat din kontolänk med kommer inte längre att kunna boka med hjälp av den och eventuella inställningar du har sparat kommer att gå förlorade.",
"integrations": "Integrationer",
"apps": "Appar",
"apps_description": "Aktivera appar för din Cal-version",
"apps_listing": "App-listning",
"category_apps": "{{category}}-appar",
"app_store": "App Store",
"app_store_description": "Kopplar samman människor, teknik och arbetsplats.",
@@ -736,6 +749,7 @@
"toggle_calendars_conflict": "Ändra de kalendrar du vill kontrollera för konflikter för att förhindra dubbelbokningar.",
"select_destination_calendar": "Skapa händelser i",
"connect_additional_calendar": "Anslut ytterligare kalender",
"calendar_updated_successfully": "Kalendern har uppdaterats",
"conferencing": "Konferenser",
"calendar": "Kalender",
"payments": "Betalningar",
@@ -768,6 +782,7 @@
"trending_apps": "Trendande appar",
"explore_apps": "{{category}}-appar",
"installed_apps": "Installerade appar",
"free_to_use_apps": "Gratis",
"no_category_apps": "Inga {{category}}-appar",
"no_category_apps_description_calendar": "Lägg till en kalenderapp för att kontrollera konflikter och förhindra dubbelbokningar",
"no_category_apps_description_conferencing": "Prova att lägga till en konferensapp för att integrera videosamtal med dina kunder",
@@ -806,6 +821,8 @@
"verify_wallet": "Verifiera plånbok",
"connect_metamask": "Anslut Metamask",
"create_events_on": "Skapa händelser i",
"enterprise_license": "Det här är en företagsfunktion",
"enterprise_license_description": "För att aktivera den här funktionen hämtar du en distributionsnyckel i konsolen {{consoleUrl}} och lägger till den i din .env som CALCOM_LICENSE_KEY. Om ditt team redan har en licens kan du kontakta {{supportMail}} för att få hjälp.",
"missing_license": "Licens saknas",
"signup_requires": "Kommersiell licens krävs",
"signup_requires_description": "{{companyName}} erbjuder för närvarande inte en gratis open source-version av registreringssidan. För att få full tillgång till de registreringskomponenter du behöver för att skaffa en kommersiell licens. För personligt bruk rekommenderar vi Prisma Data Platform eller något annat Postgres-gränssnitt för att skapa konton.",
@@ -897,6 +914,7 @@
"user_impersonation_heading": "Användarpersonifiering",
"user_impersonation_description": "Gör det möjligt för vårt supportteam att tillfälligt logga in som du så att vi snabbt kan lösa problem som du rapporterar till oss.",
"team_impersonation_description": "Tillåter dina teamadministratörer att tillfälligt logga in som dig.",
"allow_booker_to_select_duration": "Låt bokaren välja varaktighet",
"impersonate_user_tip": "All användning av denna funktion granskas.",
"impersonating_user_warning": "Imiterar användarnamn \"{{user}}\".",
"impersonating_stop_instructions": "<0>Klicka här för att sluta</0>.",
@@ -1014,6 +1032,9 @@
"error_removing_app": "Det gick inte att ta bort app",
"web_conference": "Webbkonferens",
"requires_confirmation": "Kräver bekräftelse",
"always_requires_confirmation": "Alltid",
"requires_confirmation_threshold": "Kräver bekräftelse om bokat med < {{time}} $t({{unit}}_timeUnit) varsel",
"may_require_confirmation": "Kan kräva bekräftelse",
"nr_event_type_one": "{{count}} händelsetyp",
"nr_event_type_other": "{{count}} händelsetyper",
"add_action": "Lägg till åtgärd",
@@ -1101,6 +1122,9 @@
"event_limit_tab_description": "Hur ofta du kan bokas",
"event_advanced_tab_description": "Kalenderinställningar och mer...",
"event_advanced_tab_title": "Avancerat",
"event_setup_multiple_duration_error": "Konfiguration av händelse: Flera varaktigheter kräver minst 1 alternativ.",
"event_setup_multiple_duration_default_error": "Konfiguration av händelse: Välj en giltig standardvaraktighet.",
"event_setup_booking_limits_error": "Bokningsgränserna ska vara i stigande ordning. [dag,vecka,månad,år]",
"select_which_cal": "Välj vilken kalender du vill lägga till bokningar till",
"custom_event_name": "Anpassat händelsenamn",
"custom_event_name_description": "Skapa anpassade händelsenamn som ska visas på kalenderhändelse",
@@ -1154,8 +1178,11 @@
"invoices": "Fakturor",
"embeds": "Inbäddningar",
"impersonation": "Imitering",
"impersonation_description": "Inställningar för att hantera användarens personifiering",
"users": "Användare",
"profile_description": "Hantera inställningar för din {{appName}}-profil",
"users_description": "Här hittar du en lista med alla användare",
"users_listing": "Användarlistning",
"general_description": "Hantera inställningar för språk och tidszon",
"calendars_description": "Ställ in hur dina händelsetyper interagerar med dina kalendrar",
"appearance_description": "Hantera inställningar för ditt bokningsutseende",
@@ -1360,6 +1387,7 @@
"number_sms_notifications": "Telefonnummer (SMS-notiser)",
"attendee_email_workflow": "Deltagarens e-postadress",
"attendee_email_info": "Personens e-postadress för bokning",
"kbar_search_placeholder": "Skriv ett kommando eller sök...",
"invalid_credential": "Åh nej! Det verkar som om behörigheten löpt ut eller återkallats. Installera om igen.",
"choose_common_schedule_team_event": "Välj ett gemensamt schema",
"choose_common_schedule_team_event_description": "Aktivera den här funktionen om du vill använda ett gemensamt schema mellan värdarna. När den är inaktiverad bokas varje värd enligt sitt standardschema.",
@@ -1370,5 +1398,44 @@
"test_preview": "Testa förhandsgranskning",
"route_to": "Dirigera till",
"test_preview_description": "Testa ditt routingformulär utan att skicka data",
"test_routing": "Testa routing"
"test_routing": "Testa routing",
"payment_app_disabled": "En administratör har inaktiverat en betalningsapp",
"edit_event_type": "Redigera händelsetyp",
"collective_scheduling": "Kollektiv schemaläggning",
"make_it_easy_to_book": "Gör det enkelt att boka ditt team när alla är tillgängliga.",
"find_the_best_person": "Hitta den bästa personen som finns och cykla genom ditt team.",
"fixed_round_robin": "Fast runda robin",
"add_one_fixed_attendee": "Lägg till en fast deltagare och rundgång för ett antal deltagare.",
"calcom_is_better_with_team": "Cal.com är bättre med teams",
"add_your_team_members": "Lägg till dina teammedlemmar i dina händelsetyper. Använd kollektiv schemaläggning för att inkludera alla eller hitta den lämpligaste personen med rundgångsschemaläggning.",
"booking_limit_reached": "Bokningsgränsen för denna händelsetyp har uppnåtts",
"admin_has_disabled": "En administratör har inaktiverat {{appName}}",
"disabled_app_affects_event_type": "En administratör har inaktiverat {{appName}} vilket påverkar din händelsetyp {{eventType}}",
"disable_payment_app": "Administratören har inaktiverat {{appName}} vilket påverkar din evenemangstyp {{title}}. Deltagarna kan fortfarande boka den här typen av händelse men kommer inte att uppmanas att betala. Du kan dölja händelsetypen för att förhindra detta tills din administratör återaktiverar din betalningsmetod.",
"payment_disabled_still_able_to_book": "Deltagarna kan fortfarande boka den här typen av händelse, men kommer inte att uppmanas att betala. Du kan dölja händelsetypen för att förhindra detta tills din administratör återaktiverar betalningsmetoden.",
"app_disabled_with_event_type": "Administratören har inaktiverat {{appName}} vilket påverkar din händelsetyp {{title}}.",
"app_disabled_video": "Administratören har inaktiverat {{appName}} vilket kan påverka dina händelsetyper. Om du har händelsetyper med {{appName}} som plats kommer den att vara standard för Cal Video.",
"app_disabled_subject": "{{appName}} har inaktiverats",
"navigate_installed_apps": "Gå till installerade appar",
"disabled_calendar": "Om du har en annan kalender installerad kommer nya bokningar att läggas till i den. Om du inte sedan ansluter en ny kalender så missar du inga nya bokningar.",
"enable_apps": "Aktivera appar",
"enable_apps_description": "Aktivera appar som användare kan integrera med Cal.com",
"app_is_enabled": "{{appName}} har aktiverats",
"app_is_disabled": "{{appName}} har inaktiverats",
"keys_have_been_saved": "Nycklar har sparats",
"disable_app": "Inaktivera app",
"disable_app_description": "Inaktivering av den här appen kan orsaka problem med hur dina användare interagerar med Cal",
"edit_keys": "Redigera nycklar",
"admin_apps_description": "Aktivera appar för din Cal-version",
"no_available_apps": "Det finns inga tillgängliga appar",
"no_available_apps_description": "Se till att det finns appar i din distribution under \"paket/app-store\"",
"no_apps": "Det finns inga appar aktiverade i denna instans av Cal",
"apps_settings": "App-inställningar",
"fill_this_field": "Fyll i det här fältet",
"options": "Alternativ",
"enter_option": "Ange alternativ {{index}}",
"add_an_option": "Lägg till ett alternativ",
"radio": "Radio",
"event_type_duplicate_copy_text": "{{slug}}-kopia",
"set_as_default": "Ange som standard"
}
+69 -2
View File
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "Bir etkinlik hâlâ onay bekliyor",
"event_is_still_waiting": "Etkinlik isteği hâlâ beklemede: {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "Başka sonuç yok",
"no_results": "Sonuç yok",
"load_more_results": "Daha fazla sonuç yükle",
"integration_meeting_id": "{{integrationName}} toplantı kimliği: {{meetingId}}",
"confirmed_event_type_subject": "Onaylandı: {{date}} tarihinde {{name}} ile {{eventType}}",
@@ -248,6 +249,7 @@
"add_to_calendar": "Takvime ekle",
"add_another_calendar": "Başka bir takvim ekle",
"other": "Diğer",
"email_sign_in_subject": "{{appName}} için giriş bağlantınız",
"emailed_you_and_attendees": "Size ve diğer katılımcılara tüm ayrıntıları içeren bir takvim daveti e-postası gönderdik.",
"emailed_you_and_attendees_recurring": "Size ve diğer katılımcılara bu yinelenen etkinliklerin ilki için bir takvim daveti e-postası gönderdik.",
"emailed_you_and_any_other_attendees": "Bu bilgiler size ve diğer katılımcılara e-posta ile gönderildi.",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "Fatura portalına git",
"need_anything_else": "Başka bir şeye ihtiyacınız var mı?",
"further_billing_help": "Faturalandırma konusunda daha fazla yardıma ihtiyacınız olursa destek ekibimiz size yardımcı olmaya hazır.",
"contact_our_support_team": "Destek ekibimizle iletişime geçin",
"uh_oh": "Hay aksi!",
"no_event_types_have_been_setup": "Bu kullanıcı henüz etkinlik türü ayarlamadı.",
"edit_logo": "Logoyu düzenle",
@@ -420,6 +421,7 @@
"current_incorrect_password": "Mevcut şifre hatalı",
"password_hint_caplow": "Büyük ve küçük harf karışımı",
"password_hint_min": "En az 8 karakter uzunluğunda",
"password_hint_admin_min": "Minimum uzunluk 15 karakter",
"password_hint_num": "En az 1 rakam içermelidir",
"invalid_password_hint": "Şifre en az 7 karakter uzunluğunda olmalı, en az bir rakam içermeli ve büyük ve küçük harflerin karışımından oluşmalıdır",
"incorrect_password": "Şifre hatalı.",
@@ -463,11 +465,14 @@
"booking_confirmation": "{{profileName}} ile {{eventTypeTitle}} etkinliğinizi onaylayın",
"booking_reschedule_confirmation": "{{profileName}} ile {{eventTypeTitle}} etkinliğinizi yeniden planlayın",
"in_person_meeting": "Yüz yüze görüşme",
"attendeeInPerson": "Şahsen (Katılımcı Adresi)",
"inPerson": "Şahsen (Organizatör Adresi)",
"link_meeting": "Bağlantılı toplantı",
"phone_call": "Katılımcı Telefon Numarası",
"your_number": "Telefon numaranız",
"phone_number": "Telefon Numarası",
"attendee_phone_number": "Katılımcı Telefon Numarası",
"organizer_phone_number": "Organizatör Telefon Numarası",
"host_phone_number": "Telefon Numaranız",
"enter_phone_number": "Telefon numarası girin",
"reschedule": "Yeniden planla",
@@ -554,6 +559,10 @@
"collective": "Toplu",
"collective_description": "Tüm seçili ekip üyeleri müsait olduğunda toplantıları planlayın.",
"duration": "Süre",
"available_durations": "Uygun süreler",
"default_duration": "Varsayılan süre",
"default_duration_no_options": "Lütfen önce uygun süreleri seçin",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "Dakika",
"round_robin": "Round Robin",
"round_robin_description": "Toplantıları birden fazla ekip üyesi arasında döndürün.",
@@ -619,6 +628,7 @@
"teams": "Ekipler",
"team": "Ekip",
"team_billing": "Ekip Faturası",
"team_billing_description": "Ekibiniz için faturalandırmayı yönetin",
"upgrade_to_flexible_pro_title": "Ekipler için faturayı değiştirdik",
"upgrade_to_flexible_pro_message": "Ekibinizde alanı olmayan üyeler var. Eksik alanları doldurmak için profesyonel plana yükseltin.",
"changed_team_billing_info": "Ocak 2022'den itibaren her ekip üyesi alanı için ücret alıyoruz. Ücretsiz PRO'ya sahip olan ekip üyeleriniz şu anda 14 günlük deneme süresinde. Deneme süreleri sona erdiğinde ve yükseltme yapmadığınızda bu üyeler ekibinizden gizlenecektir.",
@@ -694,6 +704,7 @@
"hide_event_type": "Etkinlik türünü gizle",
"edit_location": "Konumu düzenle",
"into_the_future": "gelecekte",
"when_booked_with_less_than_notice": "<time></time> bildiriminden daha az süre ile rezervasyon yapıldığında",
"within_date_range": "Bir tarih aralığında",
"indefinitely_into_future": "Süresiz olarak gelecekte",
"add_new_custom_input_field": "Yeni özel veri girdi alanı ekle",
@@ -713,6 +724,8 @@
"delete_account_confirmation_message": "{{appName}} hesabınızı silmek istediğinizden emin misiniz? Hesap bağlantısı paylaştığınız herkes artık bu bağlantıyı kullanarak rezervasyon yapamayacak ve kaydettiğiniz tüm tercihler kaybolacak.",
"integrations": "Entegrasyonlar",
"apps": "Uygulamalar",
"apps_description": "Cal örneğiniz için uygulamayı etkinleştirin",
"apps_listing": "Uygulama listesi",
"category_apps": "{{category}} uygulamaları",
"app_store": "App Store",
"app_store_description": "İnsanları, teknolojiyi ve çalışma ortamını birbirine bağlıyor.",
@@ -736,6 +749,7 @@
"toggle_calendars_conflict": "Çifte rezervasyonu önlemek için çakışmaları kontrol etmek istediğiniz takvimleri değiştirin.",
"select_destination_calendar": "Şurada etkinlik oluştur:",
"connect_additional_calendar": "Ek takvim bağlayın",
"calendar_updated_successfully": "Takvim başarıyla güncellendi",
"conferencing": "Konferans",
"calendar": "Takvim",
"payments": "Ödemeler",
@@ -768,6 +782,7 @@
"trending_apps": "Popüler Uygulamalar",
"explore_apps": "{{category}} uygulamaları",
"installed_apps": "Yüklü Uygulamalar",
"free_to_use_apps": "Ücretsiz",
"no_category_apps": "{{category}} uygulaması yok",
"no_category_apps_description_calendar": "Çifte rezervasyonları önlemek amacıyla çakışmaları kontrol etmek için bir takvim uygulaması ekleyin",
"no_category_apps_description_conferencing": "Müşterilerinizle görüntülü arama yapmak için bir konferans uygulaması eklemeyi deneyin",
@@ -806,6 +821,8 @@
"verify_wallet": "Cüzdanı Doğrula",
"connect_metamask": "MetaMask'i Bağla",
"create_events_on": "Şurada etkinlik oluşturun:",
"enterprise_license": "Bu bir kurumsal özelliktir",
"enterprise_license_description": "Bu özelliği etkinleştirmek için {{consoleUrl}} konsolundan bir dağıtım anahtarı alın ve bunu .env dosyanıza CALCOM_LICENSE_KEY olarak ekleyin. Ekibinizin zaten bir lisansı varsa yardım için lütfen {{supportMail}} adresinden iletişime geçin.",
"missing_license": "Eksik Lisans",
"signup_requires": "Ticari lisans gerekli",
"signup_requires_description": "{{companyName}} şu anda kayıt sayfasının ücretsiz, açık kaynaklı bir sürümünü sunmamaktadır. Kayıt bileşenlerine tam erişim elde etmek için ticari bir lisans almanız gerekiyor. Kişisel kullanım için hesap oluşturmak üzere Prisma Data veya başka bir Postgres arayüzü öneriyoruz.",
@@ -897,6 +914,7 @@
"user_impersonation_heading": "Kullanıcı Kimliğine Bürünme",
"user_impersonation_description": "Bize bildirdiğiniz hataları hızlı bir şekilde çözmek için destek ekibimizin sizin adınıza geçici olarak oturum açmasına izin verir.",
"team_impersonation_description": "Ekip üyelerinizin sizin adınıza geçici olarak oturum açmasına izin verir.",
"allow_booker_to_select_duration": "Rezervasyon yapan kişinin süreyi seçmesine izin ver",
"impersonate_user_tip": "Bu özelliğin tüm kullanımları denetlenir.",
"impersonating_user_warning": "\"{{user}}\" kullanıcı adı kimliğine bürünme.",
"impersonating_stop_instructions": "<0>Durdurmak için Buraya tıklayın</0>.",
@@ -1014,6 +1032,9 @@
"error_removing_app": "Uygulama kaldırılırken bir hata oluştu",
"web_conference": "Web konferansı",
"requires_confirmation": "Onay gerekli",
"always_requires_confirmation": "Her zaman",
"requires_confirmation_threshold": "{{time}} $t({{unit}}_timeUnit) bildiriminden daha az süre ile rezervasyon yapıldığında onay gerekir",
"may_require_confirmation": "Onay gerekebilir",
"nr_event_type_one": "{{count}} etkinlik türü",
"nr_event_type_other": "{{count}} etkinlik türü",
"add_action": "Eylem ekle",
@@ -1101,6 +1122,9 @@
"event_limit_tab_description": "Ne sıklıkla rezervasyon yapabilirsiniz",
"event_advanced_tab_description": "Takvim ayarları ve daha fazlası...",
"event_advanced_tab_title": "Gelişmiş",
"event_setup_multiple_duration_error": "Etkinlik Kurulumu: Birden çok süre için en az 1 seçenek gereklidir.",
"event_setup_multiple_duration_default_error": "Etkinlik Kurulumu: Lütfen geçerli bir varsayılan süre seçin.",
"event_setup_booking_limits_error": "Rezervasyon limitleri artan sırada olmalıdır. [gün,hafta,ay,yıl]",
"select_which_cal": "Rezervasyonların hangi takvime ekleneceğini seçin",
"custom_event_name": "Özel etkinlik adı",
"custom_event_name_description": "Takvim etkinliğinde görüntülenecek özelleştirilmiş etkinlik adları oluşturun",
@@ -1154,8 +1178,11 @@
"invoices": "Faturalar",
"embeds": "Gömülüler",
"impersonation": "Kimliğe bürünme",
"impersonation_description": "Kullanıcı kimliğine bürünmeyi yönetmek için ayarlar",
"users": "Kullanıcılar",
"profile_description": "{{appName}} profiliniz için ayarları yönetin",
"users_description": "Burada tüm kullanıcıların bir listesini bulabilirsiniz",
"users_listing": "Kullanıcı listesi",
"general_description": "Diliniz ve saat diliminiz için ayarları yönetin",
"calendars_description": "Etkinlik türlerinizin takvimlerinizle nasıl etkileşimde bulunacağını yapılandırın",
"appearance_description": "Rezervasyon görünümünüz için ayarları yönetin",
@@ -1360,6 +1387,7 @@
"number_sms_notifications": "Telefon numarası (SMS bildirimleri)",
"attendee_email_workflow": "Katılımcı e-postası",
"attendee_email_info": "Rezervasyon yaptıran kişinin e-postası",
"kbar_search_placeholder": "Bir komut girin veya arayın...",
"invalid_credential": "Hata! İznin süresi dolmuş veya iptal edilmiş gibi görünüyor. Lütfen yeniden yükleyin.",
"choose_common_schedule_team_event": "Ortak bir program seçin",
"choose_common_schedule_team_event_description": "Toplantı sahipleri arasında ortak bir plan kullanmak istiyorsanız bunu etkinleştirin. Devre dışı bırakıldığında, her toplantı sahibi varsayılan programına göre rezerve edilir.",
@@ -1370,5 +1398,44 @@
"test_preview": "Ön İzlemeyi Test Et",
"route_to": "Şuraya yönlendir:",
"test_preview_description": "Herhangi bir veri göndermeden yönlendirme formunuzu test edin",
"test_routing": "Yönlendirmeyi Test Et"
"test_routing": "Yönlendirmeyi Test Et",
"payment_app_disabled": "Bir yönetici, ödeme uygulamasını devre dışı bıraktı",
"edit_event_type": "Etkinlik türünü düzenle",
"collective_scheduling": "Toplu Planlama",
"make_it_easy_to_book": "Herkes müsait olduğunda ekibinizin rezervasyon yapmasını kolaylaştırın.",
"find_the_best_person": "Mevcut en iyi kişiyi bulun ve ekip üyeleriniz arasında rotasyon yapın.",
"fixed_round_robin": "Sabit döngü",
"add_one_fixed_attendee": "Tek bir sabit katılımcı ve birden fazla katılımcının olduğu bir döngü ekleyin.",
"calcom_is_better_with_team": "Cal.com ekiplerle daha iyidir",
"add_your_team_members": "Ekip üyelerinizi etkinlik türlerinize ekleyin. Herkesi eklemek için toplu planlamayı kullanın veya döngüsel planlama ile en uygun kişiyi bulun.",
"booking_limit_reached": "Bu etkinlik türü için Rezervasyon Sınırına ulaşıldı",
"admin_has_disabled": "Bir yönetici {{appName}} uygulamasını devre dışı bıraktı",
"disabled_app_affects_event_type": "Bir yönetici {{eventType}} etkinlik türünüzü etkileyen {{appName}} uygulamasını devre dışı bıraktı",
"disable_payment_app": "Yönetici, {{title}} etkinlik türünüzü etkileyebilecek {{appName}} uygulamasını devre dışı bıraktı. Katılımcılar yine de bu tür bir etkinlik için rezervasyon yaptırabilirler ancak herhangi bir ödeme yapmalarına gerek yoktur. Bu durumu önlemek için yöneticiniz ödeme yönteminizi yeniden etkinleştirene kadar etkinlik türünü gizleyebilirsiniz.",
"payment_disabled_still_able_to_book": "Katılımcılar yine de bu tür bir etkinlik için rezervasyon yaptırabilirler ancak herhangi bir ödeme yapmalarına gerek yoktur. Bu durumu önlemek için yöneticiniz ödeme yönteminizi yeniden etkinleştirene kadar etkinlik türünü gizleyebilirsiniz.",
"app_disabled_with_event_type": "Yönetici, etkinlik {{title}} türünüzü etkileyen {{appName}} uygulamasını devre dışı bıraktı.",
"app_disabled_video": "Yönetici, etkinlik türlerinizi etkileyebilecek {{appName}} uygulamasını devre dışı bıraktı. Konum olarak {{appName}} olan etkinlik türleriniz varsa varsayılan uygulama Cal Video olacaktır.",
"app_disabled_subject": "{{appName}} devre dışı bırakıldı",
"navigate_installed_apps": "Yüklü uygulamalara git",
"disabled_calendar": "Yüklü başka bir takviminiz varsa yeni randevular bu takvime eklenecektir. Aksi takdirde yeni bir takvim bağlamazsanız yeni rezervasyonları kaçırabilirsiniz.",
"enable_apps": "Uygulamaları Etkinleştir",
"enable_apps_description": "Kullanıcıların Cal.com ile entegre edebileceği uygulamaları etkinleştirin",
"app_is_enabled": "{{appName}} etkinleştirildi",
"app_is_disabled": "{{appName}} devre dışı bırakıldı",
"keys_have_been_saved": "Anahtarlar kaydedildi",
"disable_app": "Uygulamaları Devre Dışı Bırak",
"disable_app_description": "Bu uygulamanın devre dışı bırakılması kullanıcılarınızın Cal ile etkileşimde bulunmaları konusunda sorunlara neden olabilir",
"edit_keys": "Anahtarları Düzenle",
"admin_apps_description": "Cal örneğiniz için uygulamayı etkinleştirin",
"no_available_apps": "Kullanılabilir uygulama yok",
"no_available_apps_description": "'Paketler/uygulama mağazası' altındaki dağıtım klasörünüzde uygulamaların olduğundan emin olun",
"no_apps": "Bu Cal örneğindeki hiçbir uygulama etkin değil",
"apps_settings": "Uygulama ayarları",
"fill_this_field": "Lütfen bu alanı doldurun",
"options": "Seçenekler",
"enter_option": "{{index}} Seçeneğini Girin",
"add_an_option": "Bir seçenek ekle",
"radio": "Radio",
"event_type_duplicate_copy_text": "{{slug}}-kopyala",
"set_as_default": "Varsayılan olarak ayarla"
}
+80 -15
View File
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "Захід досі чекає на схвалення",
"event_is_still_waiting": "Запит на захід досі в очікуванні: {{attendeeName}} – {{date}} – {{eventType}}",
"no_more_results": "Інших результатів немає",
"no_results": "Немає результатів",
"load_more_results": "Завантажити інші результати",
"integration_meeting_id": "Ідентифікатор наради «{{integrationName}}»: {{meetingId}}",
"confirmed_event_type_subject": "Підтверджено: {{eventType}} «{{name}}», {{date}}",
@@ -109,7 +110,7 @@
"upgrade_to_per_seat": "Перейдіть на версію з оплатою за місце",
"team_upgrade_seats_details": "У вас стільки неоплачених місць для учасників команди: {{unpaidCount}} із {{memberCount}}. Одне місце коштує {{seatPrice}} $ за місяць, тому загальна вартість вашого членства складатиме приблизно {{totalCost}} $ за місяць.",
"team_upgrade_banner_description": "Дякуємо, що спробували наш новий план для команд! Ми помітили, що вашу команду «{{teamName}}» потрібно перевести на план вищого рівня.",
"team_upgrade_banner_action": "Перейдіть на інший план тут",
"team_upgrade_banner_action": "Перейдіть на інший план",
"team_upgraded_successfully": "Версію для вашої команди оновлено!",
"use_link_to_reset_password": "Скористайтеся посиланням нижче, щоб скинути пароль",
"hey_there": "Привіт!",
@@ -248,6 +249,7 @@
"add_to_calendar": "Додати в календар",
"add_another_calendar": "Додати інший календар",
"other": "Інше",
"email_sign_in_subject": "Ваше посилання для входу в {{appName}}",
"emailed_you_and_attendees": "Ми надіслали вам та іншим учасникам запрошення до календаря з усіма деталями.",
"emailed_you_and_attendees_recurring": "Ми надіслали вам та іншим учасникам запрошення з календаря на перший із цих періодичних заходів.",
"emailed_you_and_any_other_attendees": "Цю інформацію надіслано вам і всім іншим учасникам.",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "Перейти на портал виставлення рахунків",
"need_anything_else": "Потрібно щось інше?",
"further_billing_help": "Якщо вам потрібна додаткова допомога з виставленням рахунків, наша служба підтримки готова її надати.",
"contact_our_support_team": "Зв’язатися зі службою підтримки",
"uh_oh": "Отакої!",
"no_event_types_have_been_setup": "Цей користувач ще не налаштував жодних типів заходів.",
"edit_logo": "Редагувати логотип",
@@ -419,7 +420,8 @@
"forgotten_secret_description": "Якщо ви загубили або забули цей таємний код, його можна змінити, але в такому випадку потрібно буде оновити всі інтеграції, що використовують його.",
"current_incorrect_password": "Поточний пароль неправильний",
"password_hint_caplow": "Комбінація великих і малих літер",
"password_hint_min": "Не менше ніж 7 символів завдовжки",
"password_hint_min": "Щонайменше 8 символів завдовжки",
"password_hint_admin_min": "Щонайменше 15 символів",
"password_hint_num": "Містить принаймні 1 цифру",
"invalid_password_hint": "Пароль має містити не менше ніж 7 символів: принаймні одну цифру та комбінацію великих і малих літер",
"incorrect_password": "Пароль неправильний.",
@@ -463,11 +465,14 @@
"booking_confirmation": "Підтвердьте свій захід ({{eventTypeTitle}}) із користувачем {{profileName}}",
"booking_reschedule_confirmation": "Перенесіть свій захід ({{eventTypeTitle}}) із користувачем {{profileName}}",
"in_person_meeting": "Посилання або особиста зустріч",
"attendeeInPerson": "Особисто (адреса відвідувача)",
"inPerson": "Особисто (адреса організатора)",
"link_meeting": "Нарада з посиланням",
"phone_call": "Номер телефону учасника",
"your_number": "Ваш номер телефону",
"phone_number": "Номер телефону",
"attendee_phone_number": "Номер телефону учасника",
"organizer_phone_number": "Телефон організатора",
"host_phone_number": "Ваш номер телефону",
"enter_phone_number": "Введіть номер телефону",
"reschedule": "Перенести",
@@ -554,6 +559,10 @@
"collective": "Групова",
"collective_description": "Плануйте наради, коли доступні всі вибрані учасники команди.",
"duration": "Тривалість",
"available_durations": "Доступні варіанти тривалості",
"default_duration": "Тривалість за замовчуванням",
"default_duration_no_options": "Спочатку виберіть доступні варіанти тривалості",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "Хвилини",
"round_robin": "Ротація",
"round_robin_description": "Кілька учасників команди призначаються для нарад циклічно й по черзі.",
@@ -619,6 +628,7 @@
"teams": "Команди",
"team": "Команда",
"team_billing": "Виставлення рахунків для команд",
"team_billing_description": "Керуйте виставленням рахунків у своїй команді",
"upgrade_to_flexible_pro_title": "Ми змінили умови оплати для команд",
"upgrade_to_flexible_pro_message": "У вашій команді є учасники без придбаних місць. Перейдіть на план Pro, щоб отримати всі потрібні місця.",
"changed_team_billing_info": "Станом на січень 2022 року оплата з учасників команди стягується за кількістю місць. Учасників вашої команди, які безкоштовно користувалися функціями Pro, тепер переведено на 14-денні пробні версії. Щойно пробний період завершиться, учасників вашої команди, для яких не придбано план Pro, буде приховано.",
@@ -694,6 +704,7 @@
"hide_event_type": "Приховати тип заходу",
"edit_location": "Змінити розташування",
"into_the_future": "у майбутньому",
"when_booked_with_less_than_notice": "Якщо бронювання створено менше ніж за <time></time> до заходу",
"within_date_range": "У діапазоні дат",
"indefinitely_into_future": "Колись у майбутньому",
"add_new_custom_input_field": "Додати нове власне поле введення",
@@ -713,6 +724,7 @@
"delete_account_confirmation_message": "Справді видалити обліковий запис {{appName}}? Усі, кому ви надавали посилання на свій обліковий запис, більше не зможуть бронювати ваш час за його допомогою. Усі збережені налаштування буде втрачено.",
"integrations": "Інтеграції",
"apps": "Додатки",
"apps_listing": "Список додатків",
"category_apps": "Додатки з категорії «{{category}}»",
"app_store": "App Store",
"app_store_description": "Спілкування та технології на робочому місці.",
@@ -736,6 +748,7 @@
"toggle_calendars_conflict": "Увімкніть ті календарі, які потрібно перевірити на наявність конфліктів, щоб уникнути подвійних бронювань.",
"select_destination_calendar": "Створюйте заходи в календарі",
"connect_additional_calendar": "Підключити додатковий календар",
"calendar_updated_successfully": "Календар оновлено",
"conferencing": "Відеоконференції",
"calendar": "Календар",
"payments": "Платежі",
@@ -768,6 +781,7 @@
"trending_apps": "Популярні додатки",
"explore_apps": "Додатки з категорії «{{category}}»",
"installed_apps": "Установлені додатки",
"free_to_use_apps": "Безкоштовні",
"no_category_apps": "{{category}} — немає додатків",
"no_category_apps_description_calendar": "Додайте додаток для календаря, щоб перевіряти, чи немає конфліктів у розкладі, і уникати подвійних бронювань",
"no_category_apps_description_conferencing": "Спробуйте додати додаток для конференцій, щоб інтегрувати можливість відеорозмов зі своїми клієнтами",
@@ -784,7 +798,7 @@
"analytics": "Аналітика",
"empty_installed_apps_headline": "Не встановлено жодних додатків",
"empty_installed_apps_description": "Додатки дають змогу оптимізувати робочий процес і спростити роботу з графіком.",
"empty_installed_apps_button": "Переглянути магазин додатків або встановити наведені нижче додатки",
"empty_installed_apps_button": "Переглянути магазин додатків",
"manage_your_connected_apps": "Керуйте встановленими додатками й налаштуваннями",
"browse_apps": "Огляд додатків",
"features": "Функції",
@@ -806,6 +820,8 @@
"verify_wallet": "Пройдіть перевірку гаманця",
"connect_metamask": "Підключіть Metamask",
"create_events_on": "Створюйте заходи в календарі",
"enterprise_license": "Це корпоративна функція",
"enterprise_license_description": "Щоб увімкнути цю функцію, отримайте ключ розгортання в консолі {{consoleUrl}} і додайте його у свій файл .env як CALCOM_LICENSE_KEY. Якщо у вашої команди вже є ліцензія, зверніться по допомогу за адресою {{supportMail}}.",
"missing_license": "Відсутня ліцензія",
"signup_requires": "Потрібна комерційна ліцензія",
"signup_requires_description": "{{companyName}} зараз не надає безкоштовну версію сторінки реєстрації з відкритим кодом. Щоб отримати повний доступ до складових функціоналу реєстрації, потрібно придбати комерційну ліцензію. Для особистого використання та створення облікових записів радимо Prisma Data Platform або будь-який інший інтерфейс Postgres.",
@@ -897,6 +913,7 @@
"user_impersonation_heading": "Виконання ролі користувача",
"user_impersonation_description": "Ви можете дозволити нашій команді підтримки тимчасово входити в систему під вашим іменем, щоб швидко вирішувати проблеми, про які ви повідомляєте.",
"team_impersonation_description": "Дозвольте адміністраторам своєї команди тимчасово входити під вашим іменем.",
"allow_booker_to_select_duration": "Дозволити автору бронювання вибирати тривалість",
"impersonate_user_tip": "Усі випадки використання цієї функції підпадають під аудит.",
"impersonating_user_warning": "Виконується роль користувача {{user}}.",
"impersonating_stop_instructions": "<0>Натисніть тут, щоб зупинити</0>.",
@@ -1014,6 +1031,9 @@
"error_removing_app": "Не вдалося вилучити додаток",
"web_conference": "Вебконференція",
"requires_confirmation": "Потрібне підтвердження",
"always_requires_confirmation": "Завжди",
"requires_confirmation_threshold": "Вимагати підтвердження, якщо бронювання створено менше ніж за {{time}} $t({{unit}}_timeUnit) до заходу",
"may_require_confirmation": "Може вимагати підтвердження",
"nr_event_type_one": "{{count}} тип заходу",
"nr_event_type_other": "Типів заходів: {{count}}",
"add_action": "Додати дію",
@@ -1101,6 +1121,9 @@
"event_limit_tab_description": "Як часто ваш час можуть бронювати",
"event_advanced_tab_description": "Налаштування календаря та інше…",
"event_advanced_tab_title": "Додатково",
"event_setup_multiple_duration_error": "Налаштування заходу: якщо мають бути доступні різні варіанти, потрібно вказати принаймні один.",
"event_setup_multiple_duration_default_error": "Налаштування заходу: виберіть припустиму тривалість за замовчуванням.",
"event_setup_booking_limits_error": "Ліміти на бронювання мають бути впорядковані за зростанням. [день,тиждень,місяць,рік]",
"select_which_cal": "Виберіть календар, у який додаватимуться бронювання",
"custom_event_name": "Користувацька назва події",
"custom_event_name_description": "Вибирайте для заходів власні назви, що показуватимуться в календарі",
@@ -1154,8 +1177,11 @@
"invoices": "Рахунки-фактури",
"embeds": "Вставки",
"impersonation": "Вхід під іншим іменем",
"impersonation_description": "Налаштування входу під іншим іменем",
"users": "Користувачі",
"profile_description": "Керуйте налаштуваннями свого профілю {{appName}}",
"users_description": "Тут наведено список усіх користувачів",
"users_listing": "Список користувачів",
"general_description": "Налаштуйте параметри мови й часового поясу",
"calendars_description": "Налаштуйте, як типи заходів мають взаємодіяти з вашими календарями",
"appearance_description": "Налаштуйте варіанти оформлення свого бронювання",
@@ -1228,7 +1254,7 @@
"exchange_authentication_ntlm": "Автентифікація NTLM",
"exchange_compression": "Стиснення GZip",
"routing_forms_description": "Тут можна переглянути всі створені вами форми й переспрямування.",
"routing_forms_send_email_owner": "Надсилати власнику електронний лист",
"routing_forms_send_email_owner": "Надіслати власнику електронний лист",
"routing_forms_send_email_owner_description": "Коли надсилається форма, власник отримує електронний лист",
"add_new_form": "Додати нову форму",
"form_description": "Створіть власну форму для переспрямування автора бронювання",
@@ -1345,30 +1371,69 @@
"billing_frequency": "Періодичність виставлення рахунків",
"monthly": "Щомісяця",
"yearly": "Щороку",
"checkout": "Оформлення замовлення",
"checkout": "Оплата",
"your_team_disbanded_successfully": "Вашу команду розформовано",
"error_creating_team": "Помилка створення команди",
"you": "Ви",
"send_email": "Надіслати ел. лист",
"send_email": "Надіслати лист",
"member_already_invited": "Учасника вже запрошено",
"enter_email_or_username": "Введіть адресу електронної пошти або ім’я користувача",
"enter_email_or_username": "Введіть електронну адресу або ім’я користувача",
"team_name_taken": "Це ім’я вже зайнято",
"must_enter_team_name": "Необхідно ввести назву команди",
"team_url_required": "Необхідно ввести URL-адресу команди",
"must_enter_team_name": "Потрібно ввести назву команди",
"team_url_required": "Потрібно ввести URL-адресу команди",
"team_url_taken": "Цю URL-адресу вже зайнято",
"team_publish": "Опублікувати команду",
"number_sms_notifications": "Номер телефону (SMS-сповіщення)",
"attendee_email_workflow": "Адреса ел. пошти учасника",
"attendee_email_info": "Адреса ел. пошти особи, яка бронює",
"invalid_credential": "Отакої! Схоже, дозвіл більше не дійсний або його відкликано. Повторіть інсталяцію.",
"kbar_search_placeholder": "Введіть команду або пошуковий запис…",
"invalid_credential": "Отакої! Схоже, дозвіл більше не дійсний або його відкликано. Перевстановіть додаток знову.",
"choose_common_schedule_team_event": "Виберіть спільний розклад",
"choose_common_schedule_team_event_description": "Увімкніть цей параметр, щоб використовувати спільний для двох ведучих розклад. Якщо цей параметр вимкнено, бронювання для кожного з ведучих відбуватиметься за їхніми власними графіками.",
"reason": "Причина",
"sender_id": "Ідентифікатор відправника",
"sender_id": "ID відправника",
"sender_id_error_message": "Дозволяються тільки літери, цифри та пробіли (макс. 11 символів)",
"test_routing_form": "Перевірка форми переспрямування",
"test_preview": "Перевірити попередній перегляд",
"route_to": "Куди переспрямувати",
"test_preview_description": "Перевірка вашої форми переспрямування без надсилання даних",
"test_routing": "Перевірка переспрямування"
"route_to": "Кінцева точка",
"test_preview_description": "Перевірте свою форму переспрямування без надсилання даних",
"test_routing": "Перевірка переспрямування",
"payment_app_disabled": "Додаток для оплати вимкнув адміністратор",
"edit_event_type": "Редагувати тип заходу",
"collective_scheduling": "Колективне планування",
"make_it_easy_to_book": "Просте бронювання у випадках, коли всі члени вашої команди доступні.",
"find_the_best_person": "Пошук найкращого доступного члена команди та циклічна ротація між ними.",
"fixed_round_robin": "Фіксована циклічна ротація",
"add_one_fixed_attendee": "Додавання одного фіксованого учасника та циклічна ротація між кількома учасниками.",
"calcom_is_better_with_team": "З Cal.com краще працювати в командах",
"add_your_team_members": "Додавайте членів своєї команди в типи заходів. Колективне планування дає змогу включати всіх або знаходити найвідповіднішого члена завдяки циклічній ротації.",
"booking_limit_reached": "Для цього типу заходу досягнуто ліміт бронювання",
"admin_has_disabled": "Адміністратор вимкнув {{appName}}",
"disabled_app_affects_event_type": "Адміністратор вимкнув {{appName}}, що впливає на ваш тип заходу «{{eventType}}»",
"disable_payment_app": "Адміністратор вимкнув {{appName}}, що впливає на ваш тип заходу «{{title}}». Учасники все одно можуть бронювати події такого типу, але від них не вимагатиметься оплата. Ви можете приховати цей тип заходу, щоб цього не ставалося, та дочекатися на активацію способу оплати з боку адміністратора.",
"payment_disabled_still_able_to_book": "Учасники все одно можуть бронювати події такого типу, але від них не вимагатиметься оплата. Ви можете приховати цей тип заходу, щоб цього не ставалося, та дочекатися на активацію способу оплати з боку адміністратора.",
"app_disabled_with_event_type": "Адміністратор вимкнув {{appName}}, що впливає на ваш тип заходу «{{title}}».",
"app_disabled_video": "Адміністратор вимкнув {{appName}}, що впливає на ваші типи заходів. Якщо у вас є типи заходів, де {{appName}} визначає розташування, за замовчуванням використовуватиметься Cal Video.",
"app_disabled_subject": "{{appName}} вимкнено",
"navigate_installed_apps": "Перейти до встановлених додатків",
"disabled_calendar": "Якщо ви встановите інший календар, у нього буде додано нові бронювання. Якщо цього не станеться, під’єднайте новий календар, щоб не пропускати нові бронювання.",
"enable_apps": "Увімкнення додатків",
"enable_apps_description": "Активуйте додатки, які користувачі зможуть інтегрувати з Cal.com",
"app_is_enabled": "{{appName}} увімкнено",
"app_is_disabled": "{{appName}} вимкнено",
"keys_have_been_saved": "Ключі збережено",
"disable_app": "Вимкнути додаток",
"disable_app_description": "Якщо вимкнути цей додаток, у користувачів можуть виникнути проблеми із роботою з Cal",
"edit_keys": "Редагувати ключі",
"no_available_apps": "Немає доступних додатків",
"no_available_apps_description": "Перевірте, чи в «packages/app-store» є додатки для розгортання",
"no_apps": "У цьому екземплярі Cal немає ввімкнених додатків",
"apps_settings": "Налаштування додатків",
"fill_this_field": "Заповніть це поле",
"options": "Варіанти",
"enter_option": "Введіть варіант {{index}}",
"add_an_option": "Додайте варіант",
"radio": "Радіо",
"event_type_duplicate_copy_text": "{{slug}}-копія",
"set_as_default": "Встановити за замовчуванням"
}
+42 -11
View File
@@ -16,8 +16,8 @@
"event_request_cancelled": "Sự kiện của bạn đã bị huỷ",
"organizer": "Tổ chức",
"need_to_reschedule_or_cancel": "Cần phải đổi lịch hẹn hoặc huỷ?",
"cancellation_reason": "Lý do huỷ",
"cancellation_reason_placeholder": "Tại sao bạn huỷ? (nếu có)",
"cancellation_reason": "Lý do hủy",
"cancellation_reason_placeholder": "Lý do bạn muốn hủy?",
"rejection_reason": "Lý do từ chối",
"rejection_reason_title": "Từ chối lịch hẹn?",
"rejection_reason_description": "Bạn có chắc bạn muốn từ chối đặt lịch hẹn? Chúng tôi sẽ cho người đặt biết. Bạn có thể cho người dùng biết lý do từ chối dưới đây.",
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "Một sự kiện vẫn đang chờ được chấp thuận",
"event_is_still_waiting": "Lời mời sự kiện vẫn đang chờ: {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "Không còn kết quả",
"no_results": "Không có kết quả",
"load_more_results": "Tải thêm kết quả",
"integration_meeting_id": "{{integrationName}} ID buổi hẹn: {{meetingId}}",
"confirmed_event_type_subject": "Đã chấp nhận: {{eventType}} với {{name}} tại {{date}}",
@@ -108,7 +109,7 @@
"link_expires": "p.s. Link hết hạn trong vòng {{expiresIn}} tiếng.",
"upgrade_to_per_seat": "Nâng cấp từng tài khoản",
"team_upgrade_seats_details": "Trong tổng {{memberCount}} thành viên của nhóm bạn, {{unpaidCount}} thành viên chưa được thanh toán. Với {{seatPrice}}$/tháng mỗi thành viên, tổng chi phí cho nhóm bạn là {{totalCost}}$/tháng.",
"team_upgrade_banner_description": "Cám ơn bạn đã dùng thử gói mới dành cho nhóm. Chúng tôi để ý thấy nhóm \"{{teamName}}\" của bạn cần được nâng cấp.",
"team_upgrade_banner_description": "Cám ơn bạn đã dùng thử gói kế hoạch mới dành cho nhóm. Chúng tôi để ý thấy nhóm \"{{teamName}}\" của bạn cần được nâng cấp.",
"team_upgrade_banner_action": "Nâng cấp tại đây",
"team_upgraded_successfully": "Nhóm bạn đã được nâng cấp thành công!",
"use_link_to_reset_password": "Sử dụng đường link dưới đây để thay đổi mật khẩu của bạn",
@@ -248,6 +249,7 @@
"add_to_calendar": "Thêm vào lịch",
"add_another_calendar": "Thêm lịch khác",
"other": "Khác",
"email_sign_in_subject": "Liên kết đăng nhập cho {{appName}}",
"emailed_you_and_attendees": "Chúng tôi đã gửi email cho bạn và những người tham dự khác một lời mời theo lịch hẹn với tất cả các chi tiết đi kèm.",
"emailed_you_and_attendees_recurring": "Chúng tôi đã gửi email cho bạn và những người tham dự khác một lời mời theo lịch hẹn cho sự kiện định kỳ đầu tiên trong số những sự kiện định kỳ này.",
"emailed_you_and_any_other_attendees": "Bạn và mọi người tham dự khác đã được gửi email với thông tin này.",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "Đi tới cổng thanh toán",
"need_anything_else": "Cần gì nữa không?",
"further_billing_help": "Nếu bạn cần thêm bất kỳ trợ giúp nào về thanh toán, nhóm hỗ trợ của chúng tôi luôn sẵn sàng trợ giúp.",
"contact_our_support_team": "Liên hệ với nhóm hỗ trợ của chúng tôi",
"uh_oh": "\bÔi không!",
"no_event_types_have_been_setup": "Người dùng này chưa thiết lập bất kỳ loại sự kiện nào.",
"edit_logo": "Chỉnh sửa logo",
@@ -419,7 +420,8 @@
"forgotten_secret_description": "Nếu bạn mất hoặc quên từ bí mật này, bạn có thể đổi nó, nhưng hãy biết cho tất cả mọi tích hợp dùng từ bí mật này sẽ cần được cập nhật",
"current_incorrect_password": "Mật khẩu hiện tại không đúng",
"password_hint_caplow": "Kết hợp các chữ cái in hoa & in thường",
"password_hint_min": "Tối thiểu có 7 ký tự",
"password_hint_min": "Dài tối thiểu 8 ký tự",
"password_hint_admin_min": "Dài tối thiểu 15 ký tự",
"password_hint_num": "Chứa ít nhất 1 con số",
"invalid_password_hint": "Mật khẩu phải có tối thiểu 7 ký tự chứa ít nhất một con số và phải kết hợp chữ cái in hoa lẫn in thường",
"incorrect_password": "Mật khẩu không đúng.",
@@ -463,11 +465,14 @@
"booking_confirmation": "Xác nhận {{eventTypeTitle}} của bạn với {{profileName}}",
"booking_reschedule_confirmation": "Lên lịch lại {{eventTypeTitle}} của bạn với {{profileName}}",
"in_person_meeting": "Gặp mặt trực tiếp",
"attendeeInPerson": "Đích thân (địa chỉ người tham gia)",
"inPerson": "Đích thân (địa chỉ người tổ chức)",
"link_meeting": "Liên kết cuộc họp",
"phone_call": "Số điện thoại của người tham gia",
"your_number": "Số điện thoại của bạn",
"phone_number": "Số điện thoại",
"attendee_phone_number": "Số điện thoại của người tham gia",
"organizer_phone_number": "Số điện thoại người tổ chức",
"host_phone_number": "Số điện thoại của bạn",
"enter_phone_number": "Nhập số điện thoại",
"reschedule": "Thay đổi lịch hẹn",
@@ -504,7 +509,7 @@
"add_team_members_description": "Mời người khác tham gia nhóm của bạn",
"add_team_member": "Thêm thành viên nhóm",
"invite_new_member": "Mời một thành viên mới",
"invite_new_member_description": "Lưu ý: Cái này sẽ <1>tốn phí thêm thành viên (15$/tháng)</1> cho gói đăng ký của bạn.",
"invite_new_member_description": "Lưu ý: Việc này sẽ <1>mất phí bổ sung chỗ ngồi (15$/tháng)</1> cho gói đăng ký của bạn.",
"invite_new_team_member": "Mời ai đó vào nhóm của bạn.",
"change_member_role": "Thay đổi vai trò thành viên trong nhóm",
"disable_cal_branding": "Tắt thương hiệu {{appName}}",
@@ -554,6 +559,10 @@
"collective": "Tập thể",
"collective_description": "Lên lịch họp khi tất cả các thành viên trong nhóm đã chọn đều có mặt.",
"duration": "Khoảng thời gian",
"available_durations": "Khoảng thời gian khả dụng",
"default_duration": "Khoảng thời gian mặc định",
"default_duration_no_options": "Vui lòng chọn trước tiên những khoảng thời gian khả dụng",
"multiple_duration_mins": "{{count}}$t(minute_timeUnit)",
"minutes": "Phút",
"round_robin": "Round Robin",
"round_robin_description": "Luân phiên những cuộc họp giữa các thành viên trong nhóm.",
@@ -619,6 +628,7 @@
"teams": "Các nhóm",
"team": "Nhóm",
"team_billing": "Thanh toán nhóm",
"team_billing_description": "Quản lí thanh toán cho đội ngũ của bạn",
"upgrade_to_flexible_pro_title": "Chúng tôi đã thay đổi thanh toán cho các nhóm",
"upgrade_to_flexible_pro_message": "Có những thành viên trong nhóm của bạn không có gói. Nâng cấp gói PRO của bạn để trang trải những gói bị thiếu.",
"changed_team_billing_info": "Kể từ tháng 1 năm 2022, chúng tôi tính phí trên cơ sở từng người cho các thành viên trong nhóm. Các thành viên trong nhóm của bạn mà từng có PRO miễn phí nay sẽ chuyển sang dùng thử 14 ngày. Khi thời gian dùng thử hết hạn, các thành viên này sẽ bị ẩn khỏi nhóm của bạn trừ khi bạn nâng cấp.",
@@ -694,6 +704,7 @@
"hide_event_type": "Ẩn loại sự kiện",
"edit_location": "Chỉnh sửa vị trí",
"into_the_future": "trong tương lai",
"when_booked_with_less_than_notice": "Khi đặt hẹn với khoảng thời gian thông báo ít hơn <time></time>",
"within_date_range": "Trong phạm vi ngày",
"indefinitely_into_future": "Vô thời hạn trong tương lai",
"add_new_custom_input_field": "Thêm trường tùy chỉnh mới",
@@ -713,6 +724,7 @@
"delete_account_confirmation_message": "Bạn có chắc chắn muốn xóa tài khoản {{appName}} của mình không? Bất kỳ ai mà bạn đã chia sẻ liên kết tài khoản của mình sẽ không thể đặt trước bằng liên kết đó nữa và mọi tùy chọn bạn đã lưu sẽ bị mất.",
"integrations": "Tích hợp",
"apps": "Ứng dụng",
"apps_listing": "Danh sách ứng dụng",
"category_apps": "Ứng dụng {{category}}",
"app_store": "Cửa hàng ứng dụng",
"app_store_description": "Kết nối con người, công nghệ và nơi làm việc.",
@@ -736,6 +748,7 @@
"toggle_calendars_conflict": "Bật lịch mà bạn muốn kiểm tra trùng ngày để tránh đặt lịch hẹn trùng.",
"select_destination_calendar": "Tạo sự kiện trên",
"connect_additional_calendar": "Kết nối lịch bổ sung",
"calendar_updated_successfully": "Đã cập nhật lịch thành công",
"conferencing": "Hội nghị",
"calendar": "Lịch",
"payments": "Thanh toán",
@@ -768,6 +781,7 @@
"trending_apps": "Ứng dụng thịnh hành",
"explore_apps": "{{category}} ứng dụng",
"installed_apps": "Ứng dụng đã cài đặt",
"free_to_use_apps": "Miễn phí",
"no_category_apps": "Không có ứng dụng {{category}}",
"no_category_apps_description_calendar": "Thêm một ứng dụng lịch để kiểm tra xung đột nhằm tránh đặt lịch hẹn trùng",
"no_category_apps_description_conferencing": "Thử thêm vào một ứng dụng hội nghị để hợp nhất cuộc gọi video với khách hàng của bạn",
@@ -784,7 +798,7 @@
"analytics": "Phân tích",
"empty_installed_apps_headline": "Chưa cài ứng dụng nào",
"empty_installed_apps_description": "Ứng dụng cho phép bạn cải thiện tiến độ công việc và cải thiện đáng kể việc sắp xếp tổ chức.",
"empty_installed_apps_button": "Khám phá App Store hoặc Cài đặt các ứng dụng dưới đây",
"empty_installed_apps_button": "Duyệt xem App Store",
"manage_your_connected_apps": "Quản lý các ứng dụng đã cài đặt của bạn hoặc thay đổi cài đặt",
"browse_apps": "Xem ứng dụng",
"features": "Tính năng",
@@ -806,6 +820,8 @@
"verify_wallet": "Xác minh Ví",
"connect_metamask": "Kết nối Metamask",
"create_events_on": "Tạo sự kiện trên",
"enterprise_license": "Đây là tính năng doanh nghiệp",
"enterprise_license_description": "Để bật tính năng này, nhận khoá triển khai tại console {{consoleUrl}} và thêm nó vào .env của bạn ở dạng CALCOM_LICENSE_KEY. Nếu nhóm của bạn đã có giấy phép, vui lòng liên hệ {{supportMail}} để được trợ giúp.",
"missing_license": "Giấy phép bị thiếu",
"signup_requires": "Yêu cầu giấy phép thương mại",
"signup_requires_description": "{{companyName}} hiện không cung cấp phiên bản nguồn mở miễn phí của trang đăng ký. Để nhận toàn quyền truy cập vào các thành phần đăng ký, bạn cần có giấy phép thương mại. Đối với mục đích sử dụng cá nhân, chúng tôi khuyên bạn nên sử dụng Nền tảng dữ liệu Prisma hoặc bất kỳ giao diện Postgres nào khác để tạo tài khoản.",
@@ -897,6 +913,7 @@
"user_impersonation_heading": "Mạo danh người dùng",
"user_impersonation_description": "Cho phép đội ngũ hỗ trợ tạm thời đăng nhập với tư cách là bạn nhằm giúp chúng tôi nhanh chóng giải quyết mọi vấn đề mà bạn báo cáo cho chúng tôi.",
"team_impersonation_description": "Cho phép các quản trị viên của nhóm bạn tạm thời đăng nhập bằng danh tính của bạn.",
"allow_booker_to_select_duration": "Cho phép người đặt hẹn chọn khoảng thời gian",
"impersonate_user_tip": "Tất cả những lần dùng tính năng này đều bị kiểm toán.",
"impersonating_user_warning": "Đang mạo danh tên người dùng \"{{user}}\".",
"impersonating_stop_instructions": "<0>Nhấp vào đây để ngừng lại</0>.",
@@ -1014,6 +1031,9 @@
"error_removing_app": "Lỗi khi gỡ bỏ ứng dụng",
"web_conference": "Hội nghị web",
"requires_confirmation": "Yêu cầu xác nhận",
"always_requires_confirmation": "Luôn luôn",
"requires_confirmation_threshold": "Cần xác nhận nếu đặt hẹn với thông báo trước chưa đầy {{time}}$t({{unit}}_timeUnit)",
"may_require_confirmation": "Có thể yêu cầu xác nhận",
"nr_event_type_one": "{{count}} loại sự kiện",
"nr_event_type_other": "{{count}} loại sự kiện",
"add_action": "Thêm hoạt động",
@@ -1101,6 +1121,9 @@
"event_limit_tab_description": "Bạn có thể được đặt lịch bao lần",
"event_advanced_tab_description": "Cài đặt cho lịch & nhiều tính năng khác...",
"event_advanced_tab_title": "Nâng cao",
"event_setup_multiple_duration_error": "Thiết lập sự kiện: Nhiều khoảng thời gian cần ít nhất 1 tuỳ chọn.",
"event_setup_multiple_duration_default_error": "Thiết lập sự kiện: Vui lòng chọn một khoảng thời gian mặc định hợp lệ.",
"event_setup_booking_limits_error": "Giới hạn lịch hẹn phải theo thứ tự tăng dần. [day,week,month,year]",
"select_which_cal": "Chọn lịch nào cần thêm lịch hẹn vào",
"custom_event_name": "Tên sự kiện tuỳ chỉnh",
"custom_event_name_description": "Tạo những tên sự kiện tuỳ chỉnh để hiển thị ở phần sự kiện lịch",
@@ -1154,8 +1177,11 @@
"invoices": "Hoá đơn",
"embeds": "Nhúng",
"impersonation": "Mạo danh",
"impersonation_description": "Cài đặt để quản lý mạo danh người dùng",
"users": "Người dùng",
"profile_description": "Quản lí cài đặt cho hồ sơ {{appName}} của bạn",
"users_description": "Tại đây bạn có thể tìm thấy danh sách tất cả người dùng",
"users_listing": "Danh sách người dùng",
"general_description": "Quản lí cài đặt cho ngôn ngữ và múi giờ của bạn",
"calendars_description": "Cấu hình cách các loại sự kiện của bạn tương tác với lịch của bạn",
"appearance_description": "Quản lí cài đặt cho giao diện lịch hẹn của bạn",
@@ -1356,19 +1382,24 @@
"must_enter_team_name": "Cần phải nhập một tên nhóm",
"team_url_required": "Cần phải nhập một URL của nhóm",
"team_url_taken": "URL này đã có người lấy rồi",
"team_publish": "Công bố nhóm",
"team_publish": "Đăng nhóm",
"number_sms_notifications": "Số điện thoại (thông báo SMS)",
"attendee_email_workflow": "Email người tham dự",
"attendee_email_info": "Email người tham gia lịch hẹn",
"kbar_search_placeholder": "Nhập một lệnh hoặc tìm kiếm...",
"invalid_credential": "Ôi không! Có vẻ như quyền đã hết hạn hoặc đã bị thu hồi. Vui lòng cài đặt lại.",
"choose_common_schedule_team_event": "Chọn một lịch chung",
"choose_common_schedule_team_event_description": "Bật cái này nếu bạn muốn dùng lịch thông thường giữa các chủ sự kiện. Khi tắt đi, mỗi chủ sự kiện sẽ được đặt lịch theo lịch mặc định của họ.",
"choose_common_schedule_team_event_description": "Bật mục này nếu bạn muốn dùng lịch thông thường giữa các chủ sự kiện. Khi tắt đi, mỗi chủ sự kiện sẽ được đặt lịch theo lịch mặc định của họ.",
"reason": "Lý do",
"sender_id": "ID người gửi",
"sender_id_error_message": "Chỉ chữ cái, số và khoảng trắng là được phép (tối đa 11 ký tự)",
"sender_id_error_message": "Chỉ được phép sử dụng chữ cái, số và khoảng trắng (tối đa 11 ký tự)",
"test_routing_form": "Kiểm tra Biểu mẫu định hướng",
"test_preview": "Kiểm tra Xem trước",
"route_to": "Định hướng đến",
"test_preview_description": "Kiểm tra biểu mẫu định hướng mà không cần gửi bất kỳ dữ liệu nào",
"test_routing": "Kiểm tra định hướng"
"test_routing": "Kiểm tra định hướng",
"payment_app_disabled": "Một quản trị viên đã vô hiệu hoá một ứng dụng thanh toán",
"edit_event_type": "Sửa loại sự kiện",
"collective_scheduling": "Lên lịch tập thể",
"admin_apps_description": "Bật các ứng dụng cho thực thể Cal của bạn"
}
@@ -52,6 +52,7 @@
"still_waiting_for_approval": "有活动正在等待您的批准",
"event_is_still_waiting": "活动请求仍在等待中: {{attendeeName}} - {{date}} - {{eventType}}",
"no_more_results": "无更多结果",
"no_results": "无结果",
"load_more_results": "加载更多结果",
"integration_meeting_id": "{{integrationName}} 会议 ID: {{meetingId}}",
"confirmed_event_type_subject": "已确认: 和 {{name}} 在 {{date}} 的 {{eventType}}",
@@ -248,6 +249,7 @@
"add_to_calendar": "添加到日历",
"add_another_calendar": "添加另一个日历",
"other": "其他",
"email_sign_in_subject": "您的 {{appName}} 登录链接",
"emailed_you_and_attendees": "我们通过电子邮件给您和其他参与者发送了一份包含所有详细信息的日历邀请。",
"emailed_you_and_attendees_recurring": "我们通过电子邮件给您和其他参与者发送了第一次定期活动的日历邀请。",
"emailed_you_and_any_other_attendees": "已向您和其他参与者发送了关于此信息的电子邮件",
@@ -383,7 +385,6 @@
"go_to_billing_portal": "转到账单门户网站",
"need_anything_else": "还有其他需要?",
"further_billing_help": "如果您在支付帐单时需要任何进一步的帮助,我们的支持团队会给您提供帮助。",
"contact_our_support_team": "联系我们的支持团队",
"uh_oh": "糟糕!",
"no_event_types_have_been_setup": "此用户尚未设置任何活动类型。",
"edit_logo": "编辑标志",
@@ -420,6 +421,7 @@
"current_incorrect_password": "当前密码不正确",
"password_hint_caplow": "大写和小写字母混合",
"password_hint_min": "至少 8 个字符长",
"password_hint_admin_min": "至少 15 个字符长",
"password_hint_num": "包含至少 1 个数字",
"invalid_password_hint": "密码必须是至少 7 个字符长,包含至少一个数字,并且是大写和小写字母混合",
"incorrect_password": "密码不正确。",
@@ -463,11 +465,14 @@
"booking_confirmation": "确认您和 {{profileName}} 的 {{eventTypeTitle}}",
"booking_reschedule_confirmation": "重新安排您和 {{profileName}} 的 {{eventTypeTitle}}",
"in_person_meeting": "线上或线下会议",
"attendeeInPerson": "本人(参与者地址)",
"inPerson": "本人(组织者地址)",
"link_meeting": "线上会议",
"phone_call": "参与者电话号码",
"your_number": "您的电话号码",
"phone_number": "电话号码",
"attendee_phone_number": "参与者电话号码",
"organizer_phone_number": "组织者电话号码",
"host_phone_number": "您的电话号码",
"enter_phone_number": "输入电话号码",
"reschedule": "重新安排",
@@ -554,6 +559,10 @@
"collective": "集体模式",
"collective_description": "当所有选定的团队成员都可预约时安排会议。",
"duration": "时长",
"available_durations": "可用时长",
"default_duration": "默认时长",
"default_duration_no_options": "请先选择可用时长",
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
"minutes": "分钟",
"round_robin": "轮流模式",
"round_robin_description": "和多个团队成员之间轮流举行会议。",
@@ -619,6 +628,7 @@
"teams": "团队",
"team": "团队",
"team_billing": "团队计费",
"team_billing_description": "管理您团队的计费",
"upgrade_to_flexible_pro_title": "我们更改了团队的计费方式",
"upgrade_to_flexible_pro_message": "您的团队中有没有位置的成员。升级到专业版来获得更多位置。",
"changed_team_billing_info": "自2022年1月起,我们将按每个位置向团队成员收取费用。 您的团队中免费获得专业版的成员现在正在进行为期14天的试用。 试用期结束后,这些成员将在您的团队中隐藏,除非您现在升级到专业版。",
@@ -713,6 +723,8 @@
"delete_account_confirmation_message": "您确定要删除您的 {{appName}} 账户? 任何曾通过您的账户链接与您预约过的人都将无法再使用它和您预约,您保存的任何偏好设置都将丢失。",
"integrations": "集成",
"apps": "应用",
"apps_description": "为您的 Cal 实例启用应用",
"apps_listing": "应用列表",
"category_apps": "{{category}}应用",
"app_store": "应用商店",
"app_store_description": "连接人员、技术和工作场所。",
@@ -736,6 +748,7 @@
"toggle_calendars_conflict": "切换要检查冲突的日历,以防止重复预约。",
"select_destination_calendar": "创建活动于",
"connect_additional_calendar": "连接其他日历",
"calendar_updated_successfully": "日历已成功更新",
"conferencing": "会议",
"calendar": "日历",
"payments": "付款",
@@ -768,6 +781,7 @@
"trending_apps": "热门应用",
"explore_apps": "{{category}}应用",
"installed_apps": "已安装的应用",
"free_to_use_apps": "免费",
"no_category_apps": "无{{category}}应用",
"no_category_apps_description_calendar": "添加日历应用以检查冲突,防止重复预约",
"no_category_apps_description_conferencing": "尝试添加会议应用以整合与客户的视频通话",
@@ -806,6 +820,8 @@
"verify_wallet": "验证钱包",
"connect_metamask": "连接元掩码",
"create_events_on": "活动创建于:",
"enterprise_license": "这是企业版功能",
"enterprise_license_description": "要启用此功能,请在 {{consoleUrl}} 控制台获取一个部署密钥,并将其添加到您的 .env 中作为 CALCOM_LICENSE_KEY。如果您的团队已经有许可证,请联系 {{supportMail}} 获取帮助。",
"missing_license": "缺少许可证",
"signup_requires": "需要商业许可证",
"signup_requires_description": "{{companyName}} 免费开源版目前不提供的注册页面。 要获得对注册页面组件的完全访问权,您需要获得商业许可证。 对于个人使用用途,我们推荐使用Prisma 数据平台或任何其他Postgres接口创建账户。",
@@ -897,6 +913,7 @@
"user_impersonation_heading": "用户模拟",
"user_impersonation_description": "允许我们的支持团队临时以您的身份登录,帮助我们快速解决您报告给我们的问题。",
"team_impersonation_description": "允许您的团队成员以您的身份临时登录。",
"allow_booker_to_select_duration": "允许预约者选择时长",
"impersonate_user_tip": "此功能的所有使用都已审核。",
"impersonating_user_warning": "正在模拟用户名“{{user}}”。",
"impersonating_stop_instructions": "<0>点击此处停止</0>。",
@@ -1014,6 +1031,8 @@
"error_removing_app": "删除应用时出错",
"web_conference": "网络会议",
"requires_confirmation": "需要确认",
"always_requires_confirmation": "始终",
"may_require_confirmation": "可能需要确认",
"nr_event_type_one": "{{count}} 种活动类型",
"nr_event_type_other": "{{count}} 种活动类型",
"add_action": "添加操作",
@@ -1101,6 +1120,9 @@
"event_limit_tab_description": "您多久可被预约一次",
"event_advanced_tab_description": "日历设置及更多...",
"event_advanced_tab_title": "高级",
"event_setup_multiple_duration_error": "活动设置: 多个时长需要至少 1 个选项。",
"event_setup_multiple_duration_default_error": "活动设置: 请选择有效的默认时长。",
"event_setup_booking_limits_error": "预约限制必须按升序排列。[日,周,月,年]",
"select_which_cal": "选择要添加预约的日历",
"custom_event_name": "自定义活动名称",
"custom_event_name_description": "创建要在日历活动上显示的自定义活动名称",
@@ -1154,8 +1176,11 @@
"invoices": "发票",
"embeds": "嵌入项",
"impersonation": "模拟",
"impersonation_description": "用于管理用户模拟的设置",
"users": "用户",
"profile_description": "管理您的 {{appName}} 个人资料的设置",
"users_description": "在这里可以找到所有用户的列表",
"users_listing": "用户列表",
"general_description": "管理语言和时区的设置",
"calendars_description": "配置您的活动类型与日历的交互方式",
"appearance_description": "管理预约页面外观的设置",
@@ -1360,6 +1385,7 @@
"number_sms_notifications": "电话号码(短信通知)",
"attendee_email_workflow": "参与者电子邮件",
"attendee_email_info": "预约人的电子邮件",
"kbar_search_placeholder": "输入命令或搜索...",
"invalid_credential": "哦不!看起来权限已过期或被撤销。请重新安装。",
"choose_common_schedule_team_event": "选择通用时间表",
"choose_common_schedule_team_event_description": "如果您希望在主持人之间使用通用时间表,请启用此选项。如果禁用,将根据主持人的默认时间表预约每个主持人。",
@@ -1370,5 +1396,36 @@
"test_preview": "测试预览",
"route_to": "根据途径找到",
"test_preview_description": "测试途径表格而不提交任何数据",
"test_routing": "测试途径"
"test_routing": "测试途径",
"payment_app_disabled": "管理员已禁用支付应用",
"edit_event_type": "编辑活动类型",
"collective_scheduling": "集体日程安排",
"fixed_round_robin": "固定轮流模式",
"calcom_is_better_with_team": "Cal.com 更适合团队",
"admin_has_disabled": "管理员已禁用 {{appName}}",
"disabled_app_affects_event_type": "管理员已禁用会影响活动类型 {{eventType}} 的 {{appName}}",
"disable_payment_app": "管理员已禁用会影响活动类型 {{title}} 的 {{appName}}。参与者仍然能预约此类型的活动,但不会被提示付款。您可以隐藏该活动类型来防止出现这种情况,直到管理员重新启用您的支付方式。",
"payment_disabled_still_able_to_book": "参与者仍然能预约此类型的活动,但不会被提示付款。您可以隐藏该活动类型来防止出现这种情况,直到管理员重新启用您的支付方式。",
"app_disabled_with_event_type": "管理员已禁用会影响活动类型 {{title}} 的 {{appName}}。",
"app_disabled_video": "管理员已禁用可能会影响活动类型的 {{appName}}。如果您有将 {{appName}} 作为位置的活动类型,则默认为 Cal Video。",
"app_disabled_subject": "{{appName}} 已被禁用",
"navigate_installed_apps": "转到已安装的应用",
"enable_apps": "启用应用",
"enable_apps_description": "启用用户可以与 Cal.com 集成的应用",
"app_is_enabled": "{{appName}} 已启用",
"app_is_disabled": "{{appName}} 已禁用",
"keys_have_been_saved": "密钥已保存",
"disable_app": "禁用应用",
"disable_app_description": "禁用此应用可能会导致您的用户与 Cal 的交互方式出现问题",
"edit_keys": "编辑密钥",
"admin_apps_description": "为您的 Cal 实例启用应用",
"no_available_apps": "没有可用的应用",
"no_available_apps_description": "请确保您在“packages/app-store”下的部署中有应用",
"no_apps": "此 Cal 实例中未启用任何应用",
"apps_settings": "应用设置",
"fill_this_field": "请填写此字段",
"options": "选项",
"enter_option": "输入选项 {{index}}",
"add_an_option": "添加选项",
"set_as_default": "设置为默认"
}
@@ -16,8 +16,8 @@
"event_request_cancelled": "預定的活動取消了",
"organizer": "主辦",
"need_to_reschedule_or_cancel": "要重新規劃還是取消嗎?",
"cancellation_reason": "取消的原因",
"cancellation_reason_placeholder": "為什麼打算取消?(選填)",
"cancellation_reason": "取消原因(選填)",
"cancellation_reason_placeholder": "您為什麼要取消?",
"rejection_reason": "拒絕的原因",
"rejection_reason_title": "拒絕預約嗎?",
"rejection_reason_description": "確定要拒絕預約嗎?我們會讓想要進行預約的對方知道。可以在下方提供原因。",
@@ -109,7 +109,7 @@
"upgrade_to_per_seat": "升級為算座位數",
"team_upgrade_seats_details": "團隊裡一共有 {{memberCount}} 位成員,{{unpaidCount}} 個座位不是付費的。每個月每個座位的費用是 ${{seatPrice}},因此會員資格總共是每個月 ${{totalCost}}。",
"team_upgrade_banner_description": "謝謝您試用我們新的團隊方案。我們注意到您的團隊「{{teamName}}」需要升級。",
"team_upgrade_banner_action": "到這裡升級",
"team_upgrade_banner_action": "在此處升級",
"team_upgraded_successfully": "團隊成功升級!",
"use_link_to_reset_password": "使用以下連結重置密碼",
"hey_there": "哈囉,",
@@ -383,7 +383,6 @@
"go_to_billing_portal": "前往付費入口",
"need_anything_else": "還要什麼嗎?",
"further_billing_help": "如果有關於付費的進一步需求,我們的支援團隊隨時上前幫忙。",
"contact_our_support_team": "聯繫支援團隊",
"uh_oh": "哎呦喂呀!",
"no_event_types_have_been_setup": "使用者尚未設定任何活動類型。",
"edit_logo": "編輯標誌",
@@ -419,7 +418,7 @@
"forgotten_secret_description": "如果您已遺失或忘記此密碼,可以變更密碼,但請注意,所有使用此密碼的整合都需更新",
"current_incorrect_password": "現有密碼不正確",
"password_hint_caplow": "大小寫字母組合",
"password_hint_min": "至少 7 個字元長",
"password_hint_min": "至少 8 個字元長",
"password_hint_num": "至少包含 1 個數字",
"invalid_password_hint": "密碼長度至少要有 7 個字元,其中包含至少一個數字和大小寫字母組合",
"incorrect_password": "密碼不正確。",
@@ -601,7 +600,7 @@
"monthly_other": "月",
"yearly_one": "年",
"yearly_other": "年",
"plus_more": "{{count}} 個以上",
"plus_more": "多 {{count}} 個",
"max": "最大",
"single_theme": "主題",
"brand_color": "品牌顏色",
@@ -784,7 +783,7 @@
"analytics": "分析",
"empty_installed_apps_headline": "沒有已安裝的應用程式",
"empty_installed_apps_description": "應用程式使您能夠增強您的工作流程並顯著改善您的日程安排。",
"empty_installed_apps_button": "探索 App Store 或從下列應用程式中選擇安裝",
"empty_installed_apps_button": "瀏覽 App Store",
"manage_your_connected_apps": "管理已安裝的應用程式或是變更設定",
"browse_apps": "瀏覽應用程式",
"features": "功能",
@@ -1229,7 +1228,7 @@
"exchange_compression": "GZip 壓縮",
"routing_forms_description": "您可以在這裡查看您已建立的所有表單和引導路徑。",
"routing_forms_send_email_owner": "傳送電子郵件給擁有者",
"routing_forms_send_email_owner_description": "表單提交時傳送電子郵件給擁有者",
"routing_forms_send_email_owner_description": "表單提交後傳送電子郵件給擁有者",
"add_new_form": "新增新表單",
"form_description": "建立您的表單來引導預約者",
"copy_link_to_form": "複製連結至表單",
@@ -1343,10 +1342,10 @@
"no_event_types": "未設定活動類型",
"no_event_types_description": "{{name}} 尚未設定任何活動類型供您預約。",
"billing_frequency": "付費頻率",
"monthly": "月",
"yearly": "年",
"monthly": "每月",
"yearly": "每年",
"checkout": "結帳",
"your_team_disbanded_successfully": "解散團隊成功",
"your_team_disbanded_successfully": "已成功解散您的團隊",
"error_creating_team": "建立團隊時發生錯誤",
"you": "您",
"send_email": "傳送電子郵件",
@@ -1360,15 +1359,15 @@
"number_sms_notifications": "電話號碼 (簡訊通知)",
"attendee_email_workflow": "與會者電子郵件",
"attendee_email_info": "預約人電子郵件",
"invalid_credential": "糟糕!權限似乎過期或遭到撤銷了。請再次重新安裝。",
"choose_common_schedule_team_event": "選擇一般行程表",
"choose_common_schedule_team_event_description": "若要在多個主辦者之間使用一般行程表,請啟用此選項。停用時,會根據每個主辦者預設的行程表預約對方的時間。",
"invalid_credential": "糟糕!權限似乎過期或遭到撤銷。請重新安裝。",
"choose_common_schedule_team_event": "選擇公用行程表",
"choose_common_schedule_team_event_description": "若要在多個主辦者之間使用公用行程表,請啟用此選項。停用時,會根據每個主辦者預設的行程表預約對方的時間。",
"reason": "原因",
"sender_id": "傳送者 ID",
"sender_id_error_message": "只能使用字母、數字和空格 (最多 11 個字元)",
"test_routing_form": "測試引導表單",
"test_preview": "測試預覽",
"route_to": "引導至",
"test_preview_description": "不提交任何資料並測試引導表單",
"test_preview_description": "測試引導表單而不提交任何資料",
"test_routing": "測試引導"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 697 KiB

+5
View File
@@ -432,6 +432,11 @@ hr {
border: none !important;
}
.react-date-picker__inputGroup__input {
padding-top: 0;
padding-bottom: 0;
}
/* animations */
.slideInBottom {
animation-duration: 0.3s;
@@ -10,6 +10,7 @@ const HAWAII_AND_NEWYORK_TEAM = [
timeZone: "America/Detroit", // GMT -4 per 22th of Aug, 2022
workingHours: [{ days: [1, 2, 3, 4, 5], startTime: 780, endTime: 1260 }],
busy: [],
dateOverrides: [],
},
{
timeZone: "Pacific/Honolulu", // GMT -10 per 22th of Aug, 2022
@@ -20,6 +21,7 @@ const HAWAII_AND_NEWYORK_TEAM = [
{ days: [5], startTime: 780, endTime: 1439 },
],
busy: [],
dateOverrides: [],
},
];
+16
View File
@@ -62,6 +62,7 @@ function generateFiles() {
const browserOutput = [`import dynamic from "next/dynamic"`];
const metadataOutput = [];
const schemasOutput = [];
const appKeysSchemasOutput = [];
const serverOutput = [];
const appDirs: { name: string; path: string }[] = [];
@@ -190,6 +191,20 @@ function generateFiles() {
})
);
appKeysSchemasOutput.push(
...getObjectExporter("appKeysSchemas", {
fileToBeImported: "zod.ts",
// Import path must have / even for windows and not \
importBuilder: (app) =>
`import { appKeysSchema as ${getVariableName(app.name)}_keys_schema } from "./${app.path.replace(
/\\/g,
"/"
)}/zod";`,
// Key must be appId as this is used by eventType metadata and lookup is by appId
entryBuilder: (app) => ` "${getAppId(app)}":${getVariableName(app.name)}_keys_schema ,`,
})
);
browserOutput.push(
...getObjectExporter("InstallAppButtonMap", {
fileToBeImported: "components/InstallAppButton.tsx",
@@ -225,6 +240,7 @@ function generateFiles() {
["apps.server.generated.ts", serverOutput],
["apps.browser.generated.tsx", browserOutput],
["apps.schemas.generated.ts", schemasOutput],
["apps.keys-schemas.generated.ts", appKeysSchemasOutput],
];
filesToGenerate.forEach(([fileName, output]) => {
fs.writeFileSync(`${APP_STORE_PATH}/${fileName}`, formatOutput(`${banner}${output.join("\n")}`));
+8 -2
View File
@@ -26,7 +26,10 @@ export async function getAppWithMetadata(app: { dirName: string }) {
/** Mainly to use in listings for the frontend, use in getStaticProps or getServerSideProps */
export async function getAppRegistry() {
const dbApps = await prisma.app.findMany({ select: { dirName: true, slug: true, categories: true } });
const dbApps = await prisma.app.findMany({
where: { enabled: true },
select: { dirName: true, slug: true, categories: true, enabled: true },
});
const apps = [] as Omit<App, "key">[];
for await (const dbapp of dbApps) {
const app = await getAppWithMetadata(dbapp);
@@ -51,7 +54,10 @@ export async function getAppRegistry() {
}
export async function getAppRegistryWithCredentials(userId: number) {
const dbApps = await prisma.app.findMany({ include: { credentials: { where: { userId } } } });
const dbApps = await prisma.app.findMany({
where: { enabled: true },
include: { credentials: { where: { userId } } },
});
const apps = [] as (Omit<App, "key"> & {
credentials: Credential[];
})[];
+3 -2
View File
@@ -26,7 +26,7 @@ export default function AppCard({
const [animationRef] = useAutoAnimate<HTMLDivElement>();
return (
<div className="mb-4 mt-2 rounded-md border border-gray-200">
<div className={`mb-4 mt-2 rounded-md border border-gray-200 ${!app.enabled && "grayscale"}`}>
<div className="p-4 text-sm sm:p-8">
<div className="flex w-full flex-col gap-2 sm:flex-row sm:gap-0">
{/* Don't know why but w-[42px] isn't working, started happening when I started using next/dynamic */}
@@ -44,6 +44,7 @@ export default function AppCard({
{app?.isInstalled ? (
<div className="ml-auto flex items-center">
<Switch
disabled={!app.enabled}
onCheckedChange={(enabled) => {
if (switchOnClick) {
switchOnClick(enabled);
@@ -54,7 +55,7 @@ export default function AppCard({
/>
</div>
) : (
<OmniInstallAppButton className="ml-auto flex items-center" appId={app?.slug} />
<OmniInstallAppButton className="ml-auto flex items-center" appId={app.slug} />
)}
</div>
</div>
@@ -0,0 +1,38 @@
import { useAutoAnimate } from "@formkit/auto-animate/react";
import { useMemo } from "react";
import { classNames } from "@calcom/lib";
import { HorizontalTabs, VerticalTabs } from "@calcom/ui";
import getAppCategories from "../_utils/getAppCategories";
const AppCategoryNavigation = ({
baseURL,
children,
containerClassname,
className,
}: {
baseURL: string;
children: React.ReactNode;
containerClassname: string;
className?: string;
}) => {
const appCategories = useMemo(() => getAppCategories(baseURL), [baseURL]);
const [animationRef] = useAutoAnimate<HTMLDivElement>();
return (
<div className={classNames("flex flex-col p-2 md:p-0 xl:flex-row", className)}>
<div className="hidden xl:block">
<VerticalTabs tabs={appCategories} sticky linkProps={{ shallow: true }} />
</div>
<div className="mb-4 block overflow-x-scroll xl:hidden">
<HorizontalTabs tabs={appCategories} linkProps={{ shallow: true }} />
</div>
<main className={containerClassname} ref={animationRef}>
{children}
</main>
</div>
);
};
export default AppCategoryNavigation;
+2
View File
@@ -7,3 +7,5 @@ export const appDataSchema = eventTypeAppCardZod.merge(
isSunrise: z.boolean(),
})
);
export const appKeysSchema = z.object({});
@@ -0,0 +1,43 @@
import { Icon } from "@calcom/ui";
const getAppCategories = (baseURL: string) => {
return [
{
name: "calendar",
href: `${baseURL}/calendar`,
icon: Icon.FiCalendar,
},
{
name: "conferencing",
href: `${baseURL}/conferencing`,
icon: Icon.FiVideo,
},
{
name: "payment",
href: `${baseURL}/payment`,
icon: Icon.FiCreditCard,
},
{
name: "automation",
href: `${baseURL}/automation`,
icon: Icon.FiShare2,
},
{
name: "analytics",
href: `${baseURL}/analytics`,
icon: Icon.FiBarChart,
},
{
name: "web3",
href: `${baseURL}/web3`,
icon: Icon.FiBarChart,
},
{
name: "other",
href: `${baseURL}/other`,
icon: Icon.FiGrid,
},
];
};
export default getAppCategories;
@@ -20,6 +20,7 @@ export const metadata = {
url: "https://cal.com/",
verified: true,
email: "help@cal.com",
dirName: "applecalendar",
} as AppMeta;
export default metadata;
@@ -25,6 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
key: symmetricEncrypt(JSON.stringify({ username, password }), process.env.CALENDSO_ENCRYPTION_KEY!),
userId: user.id,
appId: "apple-calendar",
invalid: false,
};
try {

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