feat: v3 of UI components (#19136)
* ui playground * add types to ui * push avatar ignore ts and eslint errors * feat: V3 UI avatar (#18997) * avatar playground * add overflow-hidden back for loading indicator * update lock * feat: v3/badge (#18999) * badge intro * fix text on small badge * add dark mode warning * feat: v3/button (#19014) * TOC * wip buttons * shadows * secondary * semantic colours * minimal buttons * update disabled states * checkbox * dropdown v3 * load icons * fix design sections * fix icon gap * fix * remove client component from rsc * use peer as avatar * gif * fix button gap - dropdown iocn * support start icon in dropdowns * add alert demo + v3 component + darken semantic text * base inputs and text field * wip input * input field addon/suffix * toast page * wip select * match designs for input and select rounded * fix * fix select isloading transform * fix multi icons transition rotation * fix group heading * range slider demo + range slider * range slider popover design * v3 dialog * fix slider thumb size * wrap in suspense for useSearchParam difference in next14+ * base of tabs + horizontal tabs * fix select sizing to match input 36px * fix dark mode hover on button * target domState class for light/dark so we work on portal elements * fix default bg on popover * lightmode toasts + eleveation token * empty screen * intro to split button concept * mass colour updates * intro to themes * remove i18n from label with name * bouncy buttons * feat: small select * nav item dumb component * full demo * settings demo * fix tabs * fix mt * fix dropdown feedback from loom * alert fixes mentioned in loom * push font-cal * move to fumadocs * fix avatar * colors table * add typeography * icon + spacing guide * add border radius and shadow tables * fix colours and border radius * remove prose undline via fumas fork * fix icon buttons sizes * button group component * fix horizontal tabs * tab fixes * switches * pagination component * show toasts components raw * sync with airtable * add use client * 10px on input again * 10px in select also * fix input height * fix files merge * fix loading state for icon buttons * fix button fab * Enable addon to now overflow to next line in DS * use cal font for dialog text * add avatar square variant * fix select padding and menu size * fix type errors * fix type errors * add switch small size and fix type errors * fix ref types on input * update web styles * remove addonFilled to fix type errors * push dialog fixes * fix checkbox display * remove prose from alert examples * remove prose from all our component examples * badge rounded-lg on lg size * update colors * force w/h on icon buttons instead of inheriting * remove border-focus * remove border-focus from tailwind config * update tokens to match new colours * add new border subtle tokens and add to toasts * fix lg button padding * fix and drastically improve rtl rests * multi select use badges * use badge design for select pills * fix avatar alignment * fix tests and remove redudant ones * fix tests and remove redudant ones * update atoms globals css * extract tokens.css * extract app.css * force flex for atoms * add @calcom/ui/css * revert ui playground styles abstraction * fix input focus states * fix focus states for buttons * feat: v3/app work (#19233) * globals.css for app + event types listing search * few app wide NITS * fix select foucs within styling * fix formbuilder switch styles * fix disabled state of nested inputs * workflow fixes * webhook form fixes * i18n on tabs info * booking filters + tab * fix filters spacing * make first item so we get correct border radius if not default * round radio group in create event types + make button small * profile page * update editor br * button nits in existing places in the app * fix timezone select and multilist in guest form * fix text area on public booking page * size small event types create * margin on routing filter * filters improvments * routing insights fixes * fix date filter range * ooo fixes * fix teams/members bulk actions * create team UI * i18n in useTabs instead of in tab component now * Add event type setup border to availability * fix bulk actions on org members table * onboarding fixes * fix input types with ref stripe comparision * i18n fixes for platform * update globals of atoms * fix form builder firstname/lastname switch + correct footer useage * fix select * Revert "add @calcom/ui/css" This reverts commit 0dd8f9012907a278cfede4a98f289cb0744c9244. * revert tokens css * Revert "extract app.css" This reverts commit 10b998604816ad3310c24b36a02a638dabc5be24. * linear nits * workflow dropdown * fix page theme heading sizes to match cards * fix stepper for team creation * adjust margin to FilterSearchField * fix ooo icon * fix booker and dark mode due to darkgray 50 * more dark mode fixes * add size prop --------- Co-authored-by: Eunjae Lee <hey@eunjae.dev> Co-authored-by: Morgan Vernay <morgan@cal.com> * remove ui-playground * fix grow missing from merge * add search icon data-test-id back to search * fix FormAction style * fix datatestid for apps and tabs * change selector for team invitation e2e * fix use data-testid from item instaed of using names * rename upper case O * fix unit tests data-testid * fix unit tests data-testid * more data-testid changes * add data-testid to apps tab * data-testid to installed appPage * fix embed tests * fix filter-popover-trigger * use text-error and use dataId for locator * more e2e improvment * pass in single testId as button is now duplicate * show field-error instead * add test to eventtype tabs * Add css vars to embed too\ * add scrollbar and change dialog overlay colour * fix type * use attribute instead of class for tab detection * await promise * use new tokens for sprite generation * add correct testing Ids to e2e instead of button:text * use testIds for creation of orgs * Webhook receiver wait for 10 seconds --------- Co-authored-by: Eunjae Lee <hey@eunjae.dev> Co-authored-by: Morgan Vernay <morgan@cal.com> Co-authored-by: Hariom <hariombalhara@gmail.com> Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
This commit is contained in:
co-authored by
Eunjae Lee
Morgan Vernay
Hariom
Joe Au-Yeung
Joe Au-Yeung
parent
0f67faeef3
commit
5d709cf63c
@@ -230,7 +230,7 @@
|
||||
--cal-text-info: hsla(228, 56%, 33%, 1);
|
||||
--cal-text-success: hsla(133, 34%, 24%, 1);
|
||||
--cal-text-attention: hsla(16, 62%, 28%, 1);
|
||||
--cal-text-error: hsla(2, 55%, 30%, 1);
|
||||
--cal-text-error: hsla(0, 63%, 31%, 1);
|
||||
|
||||
/* Brand shinanigans
|
||||
-> These will be computed for the users theme at runtime.
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
@@ -1,36 +0,0 @@
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
@@ -1,485 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
/**
|
||||
* Add css variables here as well for light mode in addition to tailwind.config to avoid FOUC
|
||||
*/
|
||||
:root {
|
||||
/* background */
|
||||
|
||||
--cal-bg-emphasis: hsla(220,13%,91%,1);
|
||||
--cal-bg: hsla(0,0%,100%,1);
|
||||
--cal-bg-subtle: hsla(220, 14%, 96%,1);
|
||||
--cal-bg-muted: hsla(210,20%,98%,1);
|
||||
--cal-bg-inverted: hsla(0,0%,6%,1);
|
||||
|
||||
/* background -> components*/
|
||||
--cal-bg-info: hsla(218,83%,98%,1);
|
||||
--cal-bg-success: hsla(134,76%,94%,1);
|
||||
--cal-bg-attention: hsla(37, 86%, 92%, 1);
|
||||
--cal-bg-error: hsla(3,66%,93%,1);
|
||||
--cal-bg-dark-error: hsla(2, 55%, 30%, 1);
|
||||
|
||||
/* Borders */
|
||||
--cal-border-emphasis: hsla(218, 11%, 65%, 1);
|
||||
--cal-border: hsla(216, 12%, 84%, 1);
|
||||
--cal-border-subtle: hsla(220, 13%, 91%, 1);
|
||||
--cal-border-booker: #e5e7eb;
|
||||
--cal-border-muted: hsla(220, 14%, 96%, 1);
|
||||
--cal-border-error: hsla(4, 63%, 41%, 1);
|
||||
--cal-border-focus: hsla(0, 0%, 10%, 1);
|
||||
|
||||
/* Content/Text */
|
||||
--cal-text-emphasis: hsla(217, 19%, 27%, 1);
|
||||
--cal-text: hsla(217, 19%, 27%, 1);
|
||||
--cal-text-subtle: hsla(220, 9%, 46%, 1);
|
||||
--cal-text-muted: hsla(218, 11%, 65%, 1);
|
||||
--cal-text-inverted: hsla(0, 0%, 100%, 1);
|
||||
|
||||
/* Content/Text -> components */
|
||||
--cal-text-info: hsla(228, 56%, 33%, 1);
|
||||
--cal-text-success: hsla(133, 34%, 24%, 1);
|
||||
--cal-text-attention: hsla(16, 62%, 28%, 1);
|
||||
--cal-text-error: hsla(2, 55%, 30%, 1);
|
||||
|
||||
/* Brand shinanigans
|
||||
-> These will be computed for the users theme at runtime.
|
||||
*/
|
||||
--cal-brand: hsla(221, 39%, 11%, 1);
|
||||
--cal-brand-emphasis: hsla(0, 0%, 6%, 1);
|
||||
--cal-brand-text: hsla(0, 0%, 100%, 1);
|
||||
}
|
||||
.dark {
|
||||
/* background */
|
||||
|
||||
--cal-bg-emphasis: hsla(0, 0%, 25%, 1);
|
||||
--cal-bg: hsla(0, 0%, 10%, 1);
|
||||
--cal-bg-subtle: hsla(0, 0%, 18%, 1);
|
||||
--cal-bg-muted: hsla(0, 0%, 12%, 1);
|
||||
--cal-bg-inverted: hsla(220, 14%, 96%, 1);
|
||||
|
||||
/* background -> components*/
|
||||
--cal-bg-info: hsla(228, 56%, 33%, 1);
|
||||
--cal-bg-success: hsla(133, 34%, 24%, 1);
|
||||
--cal-bg-attention: hsla(16, 62%, 28%, 1);
|
||||
--cal-bg-error: hsla(2, 55%, 30%, 1);
|
||||
--cal-bg-dark-error: hsla(2, 55%, 30%, 1);
|
||||
|
||||
/* Borders */
|
||||
--cal-border-emphasis: hsla(0, 0%, 46%, 1);
|
||||
--cal-border: hsla(0, 0%, 34%, 1);
|
||||
--cal-border-subtle: hsla(0, 0%, 22%, 1);
|
||||
--cal-border-booker: hsla(0, 0%, 22%, 1);
|
||||
--cal-border-muted: hsla(0, 0%, 18%, 1);
|
||||
--cal-border-error: hsla(4, 63%, 41%, 1);
|
||||
--cal-border-focus: hsla(0, 0%, 100%, 1);
|
||||
|
||||
/* Content/Text */
|
||||
--cal-text-emphasis: hsla(240, 20%, 99%, 1);
|
||||
--cal-text: hsla(0, 0%, 84%, 1);
|
||||
--cal-text-subtle: hsla(0, 0%, 65%, 1);
|
||||
--cal-text-muted: hsla(0, 0%, 34%, 1);
|
||||
--cal-text-inverted: hsla(0, 0%, 10%, 1);
|
||||
|
||||
/* Content/Text -> components */
|
||||
--cal-text-info: hsla(218, 83%, 93%, 1);
|
||||
--cal-text-success: hsla(134, 76%, 94%, 1);
|
||||
--cal-text-attention: hsla(37, 86%, 92%, 1);
|
||||
--cal-text-error: hsla(3, 66%, 93%, 1);
|
||||
|
||||
/* Brand shenanigans
|
||||
-> These will be computed for the users theme at runtime.
|
||||
*/
|
||||
--cal-brand: hsla(0, 0%, 100%, 1);
|
||||
--cal-brand-emphasis: hsla(218, 11%, 65%, 1);
|
||||
--cal-brand-text: hsla(0, 0%, 0%,1);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-default
|
||||
}
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
color: var(--cal-brand-text);
|
||||
background: var(--cal-brand);
|
||||
}
|
||||
|
||||
::selection {
|
||||
color: var(--cal-brand-text);
|
||||
background: var(--cal-brand);
|
||||
}
|
||||
|
||||
body {
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/*
|
||||
Desktop App specific CSS
|
||||
https://docs.todesktop.com/
|
||||
*/
|
||||
|
||||
html.todesktop-platform-win32 .todesktop\:\!bg-transparent{
|
||||
background: inherit !important;
|
||||
}
|
||||
|
||||
/* disable user selection on buttons, links and images on desktop app */
|
||||
html.todesktop button,
|
||||
html.todesktop a,
|
||||
html.todesktop img,
|
||||
html.todesktop header {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
html.todesktop,
|
||||
html.todesktop div {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
/* make header draggable on desktop app */
|
||||
html.todesktop header {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
html.todesktop header button,
|
||||
html.todesktop header a {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
html.todesktop-platform-darwin body, html.todesktop-platform-darwin aside {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
html.todesktop-platform-darwin.dark main.bg-default {
|
||||
background: rgba(0, 0, 0, 0.6) !important;
|
||||
}
|
||||
|
||||
html.todesktop-platform-darwin.light main.bg-default {
|
||||
background: rgba(255, 255, 255, 0.8) !important;
|
||||
}
|
||||
|
||||
html.todesktop.light {
|
||||
--cal-bg-emphasis: hsla(0, 0%, 11%, 0.1);
|
||||
}
|
||||
|
||||
html.todesktop.dark {
|
||||
--cal-bg-emphasis: hsla(220, 2%, 26%, 0.3);
|
||||
}
|
||||
|
||||
/*
|
||||
Adds Utility to hide scrollbar to tailwind
|
||||
https://github.com/tailwindlabs/tailwindcss/discussions/2394
|
||||
https://github.com/tailwindlabs/tailwindcss/pull/5732
|
||||
*/
|
||||
@layer utilities {
|
||||
/* Chrome, Safari and Opera */
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Override the default tailwindcss-forms styling (default is: 'colors.blue.600')
|
||||
* @see: https://github.com/tailwindlabs/tailwindcss-forms/issues/14#issuecomment-1005376006
|
||||
*/
|
||||
[type="text"]:focus,
|
||||
[type="email"]:focus,
|
||||
[type="url"]:focus,
|
||||
[type="password"]:focus,
|
||||
[type="number"]:focus,
|
||||
[type="date"]:focus,
|
||||
[type="datetime-local"]:focus,
|
||||
[type="month"]:focus,
|
||||
[type="search"]:focus,
|
||||
[type="tel"]:focus,
|
||||
[type="checkbox"]:focus,
|
||||
[type="radio"]:focus,
|
||||
[type="time"]:focus,
|
||||
[type="week"]:focus,
|
||||
[multiple]:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
--tw-ring-color: var(--brand-color);
|
||||
border-color: var(--brand-color);
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.scroll-bar {
|
||||
@apply scrollbar-thin scrollbar-thumb-rounded-md dark:scrollbar-thumb-darkgray-300 scrollbar-thumb-gray-300 scrollbar-track-transparent;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: avoid global specific css */
|
||||
/* button[role="switch"][data-state="checked"] span {
|
||||
transform: translateX(16px);
|
||||
} */
|
||||
|
||||
@layer components {
|
||||
/* slider */
|
||||
.slider {
|
||||
@apply relative flex h-4 w-40 select-none items-center;
|
||||
}
|
||||
|
||||
.slider > .slider-track {
|
||||
@apply relative h-1 flex-grow rounded-md bg-gray-400;
|
||||
}
|
||||
|
||||
.slider .slider-range {
|
||||
@apply absolute h-full rounded-full bg-gray-700;
|
||||
}
|
||||
|
||||
.slider .slider-thumb {
|
||||
@apply block h-3 w-3 cursor-pointer rounded-full bg-gray-700 transition-all;
|
||||
}
|
||||
|
||||
.slider .slider-thumb:hover {
|
||||
@apply bg-gray-600;
|
||||
}
|
||||
|
||||
.slider .slider-thumb:focus {
|
||||
box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* hide chat bubble on mobile */
|
||||
@media only screen and (max-width: 768px) {
|
||||
/* Intercom FAB*/
|
||||
#launcher {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Zendesk FAB*/
|
||||
div[role="presentation"] > iframe {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Helpscout FAB*/
|
||||
.BeaconFabButtonFrame {
|
||||
margin-left: -30px;
|
||||
left: 50%;
|
||||
bottom: 28px !important;
|
||||
z-index: 1058 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: implement styling for react-multi-email */
|
||||
|
||||
/* !important to override react-dates */
|
||||
.DateRangePickerInput__withBorder {
|
||||
border: 0 !important;
|
||||
}
|
||||
.DateInput_input {
|
||||
border: 1px solid #d1d5db !important;
|
||||
border-radius: 2px !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
color: #000;
|
||||
padding: 11px 11px 9px !important;
|
||||
line-height: 16px !important;
|
||||
}
|
||||
|
||||
.DateInput_input__focused {
|
||||
border: 2px solid #000 !important;
|
||||
border-radius: 2px !important;
|
||||
box-shadow: none !important;
|
||||
padding: 10px 10px 9px !important;
|
||||
}
|
||||
|
||||
.DateRangePickerInput_arrow {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
.loader {
|
||||
display: block;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin: 60px auto;
|
||||
position: relative;
|
||||
border-width: 4px;
|
||||
border-style: solid;
|
||||
animation: loader 2s infinite ease;
|
||||
}
|
||||
|
||||
.loader-inner {
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
animation: loader-inner 2s infinite ease-in;
|
||||
}
|
||||
|
||||
.no-ring-inset {
|
||||
--tw-ring-inset: unset;
|
||||
}
|
||||
|
||||
@keyframes loader {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loader-inner {
|
||||
0% {
|
||||
height: 0%;
|
||||
}
|
||||
|
||||
25% {
|
||||
height: 0%;
|
||||
}
|
||||
|
||||
50% {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
75% {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
height: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
.text-inverted-important {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.transition-max-width {
|
||||
-webkit-transition-property: max-width;
|
||||
transition-property: max-width;
|
||||
}
|
||||
}
|
||||
|
||||
#timeZone input:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* react-date-picker forces a border upon us, cast it away */
|
||||
.react-date-picker__wrapper {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.react-date-picker__inputGroup__input {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* animations */
|
||||
.slideInBottom {
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
animation-name: slideInBottom;
|
||||
}
|
||||
|
||||
@keyframes slideInBottom {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30%);
|
||||
pointer-events: none;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* animations */
|
||||
.slideInTop {
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
animation-name: slideInTop;
|
||||
}
|
||||
|
||||
@keyframes slideInTop {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20%);
|
||||
pointer-events: none;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
transform: translateY(0%);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeIn {
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
animation-name: fadeIn;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure h-screen works on mobile Safari. By default h-screen
|
||||
* does not take into account the height of the address bar, causing
|
||||
* weird behaviour when scrolling — sometimes the height will be correct
|
||||
* and sometimes it won't, depending on whether the address bar is
|
||||
* in 'collapsed' state or not.
|
||||
* @see: https://benborgers.com/posts/tailwind-h-screen
|
||||
*/
|
||||
@supports (-webkit-touch-callout: none) {
|
||||
.h-screen {
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-search-cancel-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.react-tel-input .country-list .country:hover,
|
||||
.react-tel-input .country-list .country.highlight {
|
||||
@apply !bg-emphasis;
|
||||
}
|
||||
|
||||
.react-tel-input .flag-dropdown .selected-flag,
|
||||
.react-tel-input .flag-dropdown.open .selected-flag {
|
||||
@apply !bg-default;
|
||||
}
|
||||
|
||||
.react-tel-input .flag-dropdown {
|
||||
@apply !border-r-default left-0.5 !border-y-0 !border-l-0;
|
||||
}
|
||||
|
||||
.intercom-lightweight-app {
|
||||
@apply z-40 !important;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// import AvatarDemo from "./_components/AvatarDemo";
|
||||
|
||||
export default function Home() {
|
||||
return <div className="p-4 font-[family-name:var(--font-geist-sans)]">{/* <AvatarDemo /> */}</div>;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
});
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "ui-playground",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack --port=1337",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@calcom/config": "*",
|
||||
"@calcom/lib": "*",
|
||||
"@calcom/ui": "*",
|
||||
"next": "15.1.6",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.1.6",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,8 +0,0 @@
|
||||
import base from "@calcom/config/tailwind-preset";
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
...base,
|
||||
content: [...base.content],
|
||||
plugins: [...base.plugins, require("tailwindcss-animate")],
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -35,7 +35,7 @@ export default async function IconsPage() {
|
||||
<IconGrid
|
||||
title="Filled Icons"
|
||||
icons={icons}
|
||||
rootClassName="bg-darkgray-100 text-gray-50"
|
||||
rootClassName="bg-inverted text-inverted"
|
||||
iconClassName="fill-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -122,7 +122,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
`}</style>
|
||||
</head>
|
||||
<body
|
||||
className="dark:bg-darkgray-50 bg-subtle antialiased"
|
||||
className="dark:bg-default bg-subtle antialiased"
|
||||
style={
|
||||
isEmbed
|
||||
? {
|
||||
|
||||
@@ -18,7 +18,7 @@ const Page = async () => {
|
||||
<SettingsHeader
|
||||
title={t("out_of_office")}
|
||||
description={t("out_of_office_description")}
|
||||
CTA={<CreateNewOutOfOfficeEntryButton />}>
|
||||
CTA={<CreateNewOutOfOfficeEntryButton data-testid="add_entry_ooo" />}>
|
||||
<OutOfOfficeEntriesList />
|
||||
</SettingsHeader>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { TRPCClientErrorLike } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import { Button, Form, Icon } from "@calcom/ui";
|
||||
import { Button, Form } from "@calcom/ui";
|
||||
|
||||
interface ISetupAvailabilityProps {
|
||||
nextStep: () => void;
|
||||
@@ -73,12 +73,13 @@ const SetupAvailability = (props: ISetupAvailabilityProps) => {
|
||||
|
||||
<div>
|
||||
<Button
|
||||
EndIcon="arrow-right"
|
||||
data-testid="save-availability"
|
||||
type="submit"
|
||||
className="mt-2 w-full justify-center p-2 text-sm sm:mt-8"
|
||||
loading={availabilityForm.formState.isSubmitting}
|
||||
disabled={availabilityForm.formState.isSubmitting}>
|
||||
{t("next_step_text")} <Icon name="arrow-right" className="ml-2 h-4 w-4 self-center" />
|
||||
{t("next_step_text")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { FULL_NAME_LENGTH_MAX_LIMIT } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button, TimezoneSelect, Icon, Input } from "@calcom/ui";
|
||||
import { Button, TimezoneSelect, Input } from "@calcom/ui";
|
||||
|
||||
import { UsernameAvailabilityField } from "@components/ui/UsernameAvailability";
|
||||
|
||||
@@ -110,12 +110,12 @@ const UserSettings = (props: IUserSettingsProps) => {
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
EndIcon="arrow-right"
|
||||
type="submit"
|
||||
className="mt-8 flex w-full flex-row justify-center"
|
||||
loading={mutation.isPending}
|
||||
disabled={mutation.isPending}>
|
||||
{t("next_step_text")}
|
||||
<Icon name="arrow-right" className="ml-2 h-4 w-4 self-center" aria-hidden="true" />
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -45,7 +45,7 @@ const CustomEmailTextField = ({
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`border-default mt-2 flex w-full items-center rounded-md border ${
|
||||
className={`border-default mt-2 flex w-full items-center rounded-[10px] border ${
|
||||
inputFocus ? "ring-brand-default border-neutral-300 ring-2" : ""
|
||||
}`}>
|
||||
<TextField
|
||||
@@ -73,8 +73,8 @@ const CustomEmailTextField = ({
|
||||
<Button
|
||||
StartIcon="ellipsis"
|
||||
variant="icon"
|
||||
size="sm"
|
||||
color="secondary"
|
||||
size="xs"
|
||||
color="minimal"
|
||||
className="ml-2 rounded-md"
|
||||
data-testid="secondary-email-action-group-button"
|
||||
/>
|
||||
|
||||
@@ -115,7 +115,6 @@ const EnterpriseLicense = (
|
||||
placeholder="xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx"
|
||||
labelSrOnly={true}
|
||||
value={value}
|
||||
addOnFilled={false}
|
||||
addOnClassname={classNames(
|
||||
"hover:border-default",
|
||||
errors.licenseKey === undefined && isDirty && "group-hover:border-emphasis"
|
||||
|
||||
@@ -13,7 +13,7 @@ interface Props {
|
||||
|
||||
export default function AuthContainer(props: React.PropsWithChildren<Props>) {
|
||||
return (
|
||||
<div className="bg-subtle dark:bg-darkgray-50 flex min-h-screen flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="bg-subtle dark:bg-default flex min-h-screen flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
{props.showLogo && <Logo small inline={false} className="mx-auto mb-auto" />}
|
||||
|
||||
<div className={classNames(props.showLogo ? "text-center" : "", "sm:mx-auto sm:w-full sm:max-w-md")}>
|
||||
|
||||
@@ -42,7 +42,6 @@ function AppsSearch({
|
||||
const { t } = useLocale();
|
||||
return (
|
||||
<TextField
|
||||
className="bg-subtle !border-muted !pl-0 focus:!ring-offset-0"
|
||||
addOnLeading={<Icon name="search" className="text-subtle h-4 w-4" />}
|
||||
addOnClassname="!border-muted"
|
||||
containerClassName={classNames("focus:!ring-offset-0 m-1", className)}
|
||||
|
||||
@@ -44,22 +44,27 @@ const tabs: (VerticalTabItemProps | HorizontalTabItemProps)[] = [
|
||||
{
|
||||
name: "upcoming",
|
||||
href: "/bookings/upcoming",
|
||||
"data-testid": "upcoming",
|
||||
},
|
||||
{
|
||||
name: "unconfirmed",
|
||||
href: "/bookings/unconfirmed",
|
||||
"data-testid": "unconfirmed",
|
||||
},
|
||||
{
|
||||
name: "recurring",
|
||||
href: "/bookings/recurring",
|
||||
"data-testid": "recurring",
|
||||
},
|
||||
{
|
||||
name: "past",
|
||||
href: "/bookings/past",
|
||||
"data-testid": "past",
|
||||
},
|
||||
{
|
||||
name: "cancelled",
|
||||
href: "/bookings/cancelled",
|
||||
"data-testid": "cancelled",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -250,7 +255,12 @@ function BookingsContent({ status }: BookingsProps) {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="flex flex-row flex-wrap justify-between">
|
||||
<HorizontalTabs tabs={tabs} />
|
||||
<HorizontalTabs
|
||||
tabs={tabs.map((tab) => ({
|
||||
...tab,
|
||||
name: t(tab.name),
|
||||
}))}
|
||||
/>
|
||||
<FilterToggle setIsFiltersVisible={setIsFiltersVisible} />
|
||||
</div>
|
||||
<FiltersContainer isFiltersVisible={isFiltersVisible} />
|
||||
|
||||
@@ -983,7 +983,7 @@ export default function Success(props: PageProps) {
|
||||
className={classNames(
|
||||
"flex h-10 w-10 items-center justify-center rounded-full border text-2xl hover:opacity-100",
|
||||
rateValue === 1
|
||||
? "border-focus bg-emphasis"
|
||||
? "border-emphasis bg-emphasis"
|
||||
: "border-muted bg-default opacity-50"
|
||||
)}
|
||||
disabled={isFeedbackSubmitted}
|
||||
@@ -994,7 +994,7 @@ export default function Success(props: PageProps) {
|
||||
className={classNames(
|
||||
"flex h-10 w-10 items-center justify-center rounded-full border text-2xl hover:opacity-100",
|
||||
rateValue === 2
|
||||
? "border-focus bg-emphasis"
|
||||
? "border-emphasis bg-emphasis"
|
||||
: "border-muted bg-default opacity-50"
|
||||
)}
|
||||
disabled={isFeedbackSubmitted}
|
||||
@@ -1005,7 +1005,7 @@ export default function Success(props: PageProps) {
|
||||
className={classNames(
|
||||
"flex h-10 w-10 items-center justify-center rounded-full border text-2xl hover:opacity-100",
|
||||
rateValue === 3
|
||||
? "border-focus bg-emphasis"
|
||||
? "border-emphasis bg-emphasis"
|
||||
: " border-muted bg-default opacity-50"
|
||||
)}
|
||||
disabled={isFeedbackSubmitted}
|
||||
@@ -1016,7 +1016,7 @@ export default function Success(props: PageProps) {
|
||||
className={classNames(
|
||||
"flex h-10 w-10 items-center justify-center rounded-full border text-2xl hover:opacity-100",
|
||||
rateValue === 4
|
||||
? "border-focus bg-emphasis"
|
||||
? "border-emphasis bg-emphasis"
|
||||
: "border-muted bg-default opacity-50"
|
||||
)}
|
||||
disabled={isFeedbackSubmitted}
|
||||
@@ -1027,7 +1027,7 @@ export default function Success(props: PageProps) {
|
||||
className={classNames(
|
||||
"flex h-10 w-10 items-center justify-center rounded-full border text-2xl hover:opacity-100",
|
||||
rateValue === 5
|
||||
? "border-focus bg-emphasis"
|
||||
? "border-emphasis bg-emphasis"
|
||||
: "border-muted bg-default opacity-50"
|
||||
)}
|
||||
disabled={isFeedbackSubmitted}
|
||||
|
||||
@@ -120,9 +120,8 @@ const InfiniteTeamsTab: FC<InfiniteTeamsTabProps> = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<TextField
|
||||
className="max-w-64 bg-subtle !border-muted mb-4 mr-auto rounded-md !pl-0 focus:!ring-offset-0"
|
||||
className="max-w-64"
|
||||
addOnLeading={<Icon name="search" className="text-subtle h-4 w-4" />}
|
||||
addOnClassname="!border-muted"
|
||||
containerClassName="max-w-64 focus:!ring-offset-0 mb-4"
|
||||
type="search"
|
||||
value={searchTerm}
|
||||
@@ -600,7 +599,8 @@ export const InfiniteEventTypeList = ({
|
||||
variant="icon"
|
||||
color="secondary"
|
||||
StartIcon="ellipsis"
|
||||
className="ltr:radix-state-open:rounded-r-md rtl:radix-state-open:rounded-l-md"
|
||||
// Unsual practice to use radix state open but for some reason this dropdown and only thi dropdown clears the border radius of this button.
|
||||
className="ltr:radix-state-open:rounded-r-[--btn-group-radius] rtl:radix-state-open:rounded-l-[--btn-group-radius]"
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
@@ -915,6 +915,7 @@ const InfiniteScrollMain = ({
|
||||
name: item.profile.name ?? "",
|
||||
href: item.teamId ? `/event-types?teamId=${item.teamId}` : "/event-types?noTeam",
|
||||
avatar: item.profile.image,
|
||||
"data-testid": item.profile.name ?? "",
|
||||
}));
|
||||
|
||||
const activeEventTypeGroup =
|
||||
|
||||
@@ -250,7 +250,7 @@ const GeneralView = ({ localeProp, user, travelSchedules, revalidatePage }: Gene
|
||||
) : (
|
||||
<div className="bg-muted border-subtle mt-2 rounded-md border p-4">
|
||||
<Label>{t("travel_schedule")}</Label>
|
||||
<div className="dark:bg-darkgray-100 border-subtle mt-4 rounded-md border bg-white text-sm">
|
||||
<div className="border-subtle bg-default mt-4 rounded-md border text-sm">
|
||||
{watchedTzSchedules.map((schedule, index) => {
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -126,7 +126,6 @@ function UsernameField({
|
||||
{...props}
|
||||
{...register("username")}
|
||||
data-testid="signup-usernamefield"
|
||||
addOnFilled={false}
|
||||
/>
|
||||
{(!formState.isSubmitting || !formState.isSubmitted) && (
|
||||
<div className="text-gray text-default flex items-center text-sm">
|
||||
|
||||
@@ -74,7 +74,7 @@ function TeamPage({ team, considerUnpublished, isValidOrgDomain }: PageProps) {
|
||||
<li
|
||||
key={index}
|
||||
className={classNames(
|
||||
"dark:bg-darkgray-100 bg-default hover:bg-muted border-subtle group relative border-b transition first:rounded-t-md last:rounded-b-md last:border-b-0",
|
||||
"bg-default hover:bg-muted border-subtle group relative border-b transition first:rounded-t-md last:rounded-b-md last:border-b-0",
|
||||
!isEmbed && "bg-default"
|
||||
)}>
|
||||
<div className="px-6 py-4 ">
|
||||
@@ -157,7 +157,7 @@ function TeamPage({ team, considerUnpublished, isValidOrgDomain }: PageProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<main className="dark:bg-darkgray-50 bg-subtle mx-auto max-w-3xl rounded-md px-4 pb-12 pt-12">
|
||||
<main className="dark:bg-default bg-subtle mx-auto max-w-3xl rounded-md px-4 pb-12 pt-12">
|
||||
<div className="mx-auto mb-8 max-w-3xl text-center">
|
||||
<div className="relative">
|
||||
<Avatar alt={teamName} imageSrc={profileImageSrc} size="lg" />
|
||||
@@ -208,9 +208,7 @@ function TeamPage({ team, considerUnpublished, isValidOrgDomain }: PageProps) {
|
||||
<div className="border-subtle w-full border-t" />
|
||||
</div>
|
||||
<div className="relative flex justify-center">
|
||||
<span className="dark:bg-darkgray-50 bg-subtle text-subtle px-2 text-sm">
|
||||
{t("or")}
|
||||
</span>
|
||||
<span className="bg-subtle text-subtle px-2 text-sm">{t("or")}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ class MyDocument extends Document<Props> {
|
||||
</Head>
|
||||
|
||||
<body
|
||||
className="dark:bg-darkgray-50 bg-subtle antialiased"
|
||||
className="dark:bg-default bg-subtle antialiased"
|
||||
style={
|
||||
isEmbed
|
||||
? {
|
||||
|
||||
@@ -74,7 +74,7 @@ test.describe("apps/ A/B tests", () => {
|
||||
|
||||
const locator = page.getByTestId("horizontal-tab-upcoming");
|
||||
|
||||
await expect(locator).toHaveClass(/bg-emphasis/);
|
||||
await expect(locator).toBeDefined();
|
||||
});
|
||||
|
||||
test("should render the /getting-started", async ({ page, users }) => {
|
||||
|
||||
@@ -336,7 +336,7 @@ async function goToReactCodeTab(page: Page) {
|
||||
// To prevent early timeouts
|
||||
// eslint-disable-next-line playwright/no-wait-for-timeout
|
||||
await page.waitForTimeout(1000);
|
||||
await page.locator("[data-testid=horizontal-tab-React]").click();
|
||||
await page.locator("[data-testid=horizontal-tab-react]").click();
|
||||
}
|
||||
|
||||
async function clickEmbedButton(page: Page) {
|
||||
|
||||
@@ -66,9 +66,9 @@ export function createHttpServer(opts: { requestHandler?: RequestHandler } = {})
|
||||
eventEmitter.on("push", pushHandler);
|
||||
setTimeout(() => {
|
||||
if (resolved) return;
|
||||
// Timeout after 5 seconds
|
||||
// Timeout after 10 seconds
|
||||
reject(new Error("Timeout waiting for webhook"));
|
||||
}, 5000);
|
||||
}, 10000);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -258,7 +258,6 @@ test.describe("Manage Booking Questions", () => {
|
||||
await test.step("Go to First Team Event", async () => {
|
||||
const locator = page.getByTestId(`horizontal-tab-${team?.name}`);
|
||||
await locator.click();
|
||||
await expect(locator).toHaveClass(/bg-emphasis/);
|
||||
const $eventTypes = page.locator("[data-testid=event-types]").locator("li a");
|
||||
const firstEventTypeElement = $eventTypes.first();
|
||||
|
||||
|
||||
@@ -56,6 +56,6 @@ async function inviteAnEmail(page: Page, invitedUserEmail: string) {
|
||||
await page.getByTestId("new-organization-member-button").click();
|
||||
await page.locator('input[name="inviteUser"]').fill(invitedUserEmail);
|
||||
await submitAndWaitForResponse(page, "/api/trpc/teams/inviteMember?batch=1", {
|
||||
action: () => page.locator('button:text("Send invite")').click(),
|
||||
action: () => page.getByTestId("invite-new-member-button").click(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ test.describe("Organization", () => {
|
||||
await test.step("Basic info", async () => {
|
||||
// Check required fields
|
||||
await page.locator("button[type=submit]").click();
|
||||
await expect(page.locator(".text-red-700")).toHaveCount(3);
|
||||
await expect(page.getByTestId("field-error")).toHaveCount(3);
|
||||
|
||||
// Happy path
|
||||
await fillAndSubmitFirstStepAsAdmin(page, orgOwnerEmail, orgName, orgSlug);
|
||||
@@ -152,12 +152,12 @@ test.describe("Organization", () => {
|
||||
|
||||
await test.step("About the organization", async () => {
|
||||
// Choosing an avatar
|
||||
await page.locator('button:text("Upload")').click();
|
||||
await page.getByTestId("open-upload-avatar-dialog").click();
|
||||
const fileChooserPromise = page.waitForEvent("filechooser");
|
||||
await page.getByText("Choose a file...").click();
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(path.join(__dirname, "../../public/apple-touch-icon.png"));
|
||||
await page.locator('button:text("Save")').click();
|
||||
await page.getByTestId("upload-avatar").click();
|
||||
|
||||
// About text
|
||||
await page.locator('textarea[name="about"]').fill("This is a testing org");
|
||||
@@ -202,7 +202,7 @@ test.describe("Organization", () => {
|
||||
await page.locator('input[name="teams.0.name"]').fill("Marketing");
|
||||
|
||||
// Adding another team
|
||||
await page.locator('button:text("Add a team")').click();
|
||||
await page.getByTestId("add_a_team").click();
|
||||
await page.locator('input[name="teams.1.name"]').fill("Sales");
|
||||
|
||||
// Finishing the creation wizard
|
||||
@@ -269,7 +269,7 @@ test.describe("Organization", () => {
|
||||
await test.step("Basic info", async () => {
|
||||
// Check required fields
|
||||
await page.locator("button[type=submit]").click();
|
||||
await expect(page.locator(".text-red-700")).toHaveCount(3);
|
||||
await expect(page.getByTestId("field-error")).toHaveCount(3);
|
||||
|
||||
// Happy path
|
||||
await fillAndSubmitFirstStepAsAdmin(page, orgOwnerEmail, orgName, orgSlug);
|
||||
@@ -319,12 +319,12 @@ test.describe("Organization", () => {
|
||||
|
||||
await test.step("About the organization", async () => {
|
||||
// Choosing an avatar
|
||||
await page.locator('button:text("Upload")').click();
|
||||
await page.getByTestId("open-upload-avatar-dialog").click();
|
||||
const fileChooserPromise = page.waitForEvent("filechooser");
|
||||
await page.getByText("Choose a file...").click();
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(path.join(__dirname, "../../public/apple-touch-icon.png"));
|
||||
await page.locator('button:text("Save")').click();
|
||||
await page.getByTestId("upload-avatar").click();
|
||||
|
||||
// About text
|
||||
await page.locator('textarea[name="about"]').fill("This is a testing org");
|
||||
@@ -369,7 +369,7 @@ test.describe("Organization", () => {
|
||||
await page.locator('input[name="teams.0.name"]').fill("Marketing");
|
||||
|
||||
// Adding another team
|
||||
await page.locator('button:text("Add a team")').click();
|
||||
await page.getByTestId("add_a_team").click();
|
||||
await page.locator('input[name="teams.1.name"]').fill("Sales");
|
||||
|
||||
// Finishing the creation wizard
|
||||
@@ -451,12 +451,12 @@ test.describe("Organization", () => {
|
||||
|
||||
await test.step("About the organization", async () => {
|
||||
// Choosing an avatar
|
||||
await page.locator('button:text("Upload")').click();
|
||||
await page.getByTestId("open-upload-avatar-dialog").click();
|
||||
const fileChooserPromise = page.waitForEvent("filechooser");
|
||||
await page.getByText("Choose a file...").click();
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(path.join(__dirname, "../../public/apple-touch-icon.png"));
|
||||
await page.locator('button:text("Save")').click();
|
||||
await page.getByTestId("upload-avatar").click();
|
||||
|
||||
// About text
|
||||
await page.locator('textarea[name="about"]').fill("This is a testing org");
|
||||
@@ -511,7 +511,7 @@ test.describe("Organization", () => {
|
||||
await page.locator('input[name="teams.0.name"]').fill("Marketing");
|
||||
|
||||
// Adding another team
|
||||
await page.locator('button:text("Add a team")').click();
|
||||
await page.getByTestId("new-team-dialog-button").click();
|
||||
await page.locator('input[name="teams.1.name"]').fill("Sales");
|
||||
|
||||
// Finishing the creation wizard
|
||||
|
||||
@@ -546,7 +546,7 @@ async function inviteAnEmail(page: Page, invitedUserEmail: string, teamPage?: bo
|
||||
}
|
||||
await page.locator('input[name="inviteUser"]').fill(invitedUserEmail);
|
||||
const submitPromise = page.waitForResponse("/api/trpc/teams/inviteMember?batch=1");
|
||||
await page.locator('button:text("Send invite")').click();
|
||||
await page.getByTestId("invite-new-member-button").click();
|
||||
const response = await submitPromise;
|
||||
expect(response.status()).toBe(200);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ test.describe("Team", () => {
|
||||
});
|
||||
await page.getByTestId("new-member-button").click();
|
||||
await page.locator('input[name="inviteUser"]').fill(invitedUserEmail);
|
||||
await page.locator(`button:text("${t("send_invite")}")`).click();
|
||||
await page.getByText(t("send_invite")).click();
|
||||
const inviteLink = await expectInvitationEmailToBeReceived(
|
||||
page,
|
||||
emails,
|
||||
@@ -110,7 +110,7 @@ test.describe("Team", () => {
|
||||
});
|
||||
await page.getByTestId("new-member-button").click();
|
||||
await page.locator('input[name="inviteUser"]').fill(invitedUserEmail);
|
||||
await page.locator(`button:text("${t("send_invite")}")`).click();
|
||||
await page.getByText(t("send_invite")).click();
|
||||
await expectInvitationEmailToBeReceived(
|
||||
page,
|
||||
emails,
|
||||
@@ -155,7 +155,7 @@ test.describe("Team", () => {
|
||||
await page.goto(`/settings/teams/${team.id}/members`);
|
||||
await page.getByTestId("new-member-button").click();
|
||||
await page.locator('input[name="inviteUser"]').fill(invitedMember.email);
|
||||
await page.locator(`button:text("${t("send_invite")}")`).click();
|
||||
await page.getByText(t("send_invite")).click();
|
||||
|
||||
await invitedMember.apiLogin();
|
||||
await page.goto(`/teams`);
|
||||
|
||||
+230
-73
@@ -5,94 +5,248 @@
|
||||
* Add css variables here as well for light mode in addition to tailwind.config to avoid FOUC
|
||||
*/
|
||||
:root {
|
||||
/* background */
|
||||
/* Spacing Scale */
|
||||
--cal-spacing-0: 0px;
|
||||
--cal-spacing-px: 1px;
|
||||
--cal-spacing-0_5: 0.125rem; /* 2px */
|
||||
--cal-spacing-1: 0.25rem; /* 4px */
|
||||
--cal-spacing-1_5: 0.375rem; /* 6px */
|
||||
--cal-spacing-2: 0.5rem; /* 8px */
|
||||
--cal-spacing-2_5: 0.625rem; /* 10px */
|
||||
--cal-spacing-3: 0.75rem; /* 12px */
|
||||
--cal-spacing-3_5: 0.875rem; /* 14px */
|
||||
--cal-spacing-4: 1rem; /* 16px */
|
||||
--cal-spacing-5: 1.25rem; /* 20px */
|
||||
--cal-spacing-6: 1.5rem; /* 24px */
|
||||
--cal-spacing-7: 1.75rem; /* 28px */
|
||||
--cal-spacing-8: 2rem; /* 32px */
|
||||
--cal-spacing-9: 2.25rem; /* 36px */
|
||||
--cal-spacing-10: 2.5rem; /* 40px */
|
||||
--cal-spacing-11: 2.75rem; /* 44px */
|
||||
--cal-spacing-12: 3rem; /* 48px */
|
||||
--cal-spacing-14: 3.5rem; /* 56px */
|
||||
--cal-spacing-16: 4rem; /* 64px */
|
||||
--cal-spacing-20: 5rem; /* 80px */
|
||||
--cal-spacing-24: 6rem; /* 96px */
|
||||
--cal-spacing-28: 7rem; /* 112px */
|
||||
--cal-spacing-32: 8rem; /* 128px */
|
||||
--cal-spacing-36: 9rem; /* 144px */
|
||||
--cal-spacing-40: 10rem; /* 160px */
|
||||
--cal-spacing-44: 11rem; /* 176px */
|
||||
--cal-spacing-48: 12rem; /* 192px */
|
||||
--cal-spacing-52: 13rem; /* 208px */
|
||||
--cal-spacing-56: 14rem; /* 224px */
|
||||
--cal-spacing-60: 15rem; /* 240px */
|
||||
--cal-spacing-64: 16rem; /* 256px */
|
||||
--cal-spacing-72: 18rem; /* 288px */
|
||||
--cal-spacing-80: 20rem; /* 320px */
|
||||
--cal-spacing-96: 24rem; /* 384px */
|
||||
|
||||
--cal-bg-emphasis: hsla(220,13%,91%,1);
|
||||
--cal-bg: hsla(0,0%,100%,1);
|
||||
--cal-bg-subtle: hsla(220, 14%, 96%,1);
|
||||
--cal-bg-muted: hsla(210,20%,98%,1);
|
||||
--cal-bg-inverted: hsla(0,0%,6%,1);
|
||||
/* Border Radius */
|
||||
--cal-radius-none: 0px;
|
||||
--cal-radius-sm: 0.125rem; /* 2px */
|
||||
--cal-radius: 0.25rem; /* 4px */
|
||||
--cal-radius-md: 0.375rem; /* 6px */
|
||||
--cal-radius-lg: 0.5rem; /* 8px */
|
||||
--cal-radius-xl: 0.75rem; /* 12px */
|
||||
--cal-radius-2xl: 1rem; /* 16px */
|
||||
--cal-radius-3xl: 1.5rem; /* 24px */
|
||||
--cal-radius-full: 9999px;
|
||||
|
||||
/* background -> components*/
|
||||
--cal-bg-info: hsla(218,83%,98%,1);
|
||||
--cal-bg-success: hsla(134,76%,94%,1);
|
||||
--cal-bg-attention: hsla(37, 86%, 92%, 1);
|
||||
--cal-bg-error: hsla(3,66%,93%,1);
|
||||
--cal-bg-dark-error: hsla(2, 55%, 30%, 1);
|
||||
/* Background Standard */
|
||||
--cal-bg-emphasis: hsla(220, 13%, 91%, 1); /* gray.200 - active items and emphasising */
|
||||
--cal-bg: hsla(0, 0%, 100%, 1); /* white - default background */
|
||||
--cal-bg-subtle: hsla(220, 14%, 94%, 1); /* gray.100 - hovering on items, subtle raising */
|
||||
--cal-bg-muted: hsla(210, 20%, 97%, 1); /* gray.50 - large items, sidebar, sections */
|
||||
--cal-bg-inverted: hsla(210, 30%, 4%, 1); /* gray.900 - tooltips */
|
||||
|
||||
/* Background Primary */
|
||||
--cal-bg-primary: hsla(214, 30%, 16%, 1); /* stone-100 */
|
||||
--cal-bg-primary-emphasis: hsla(220, 6%, 25%, 1); /* stone-800 */
|
||||
--cal-bg-primary-muted: hsla(220, 14%, 94%, 1); /* stone-400 */
|
||||
|
||||
/* Background Brand */
|
||||
--cal-bg-brand: hsla(214, 30%, 16%, 1); /* gray-50 */
|
||||
--cal-bg-brand-emphasis: hsla(220, 6%, 25%, 1); /* stone-100 */
|
||||
--cal-bg-brand-muted: hsla(220, 14%, 94%, 1); /* stone-100 */
|
||||
|
||||
/* Background Semantic */
|
||||
--cal-bg-semantic-info-subtle: hsla(212, 88%, 97%, 1); /* blue-100 */
|
||||
--cal-bg-semantic-info-emphasis: hsla(236, 80%, 25%, 1); /* blue-500 */
|
||||
--cal-bg-semantic-success-subtle: hsla(167, 54%, 93%, 1); /* green-100 */
|
||||
--cal-bg-semantic-success-emphasis: hsla(158, 74%, 38%, 1); /* green-500 */
|
||||
--cal-bg-semantic-attention-subtle: hsla(34, 100%, 92%, 1); /* orange-100 */
|
||||
--cal-bg-semantic-attention-emphasis: hsla(15, 79%, 34%, 1); /* orange-500 */
|
||||
--cal-bg-semantic-error-subtle: hsla(0, 93%, 94%, 1); /* red-100 */
|
||||
--cal-bg-semantic-error-emphasis: hsla(0, 63%, 24%, 1); /* red-500 */
|
||||
|
||||
/* Background Visualization */
|
||||
--cal-bg-visualization-1-subtle: hsla(326, 78%, 95%, 1); /* pink-100 */
|
||||
--cal-bg-visualization-1-emphasis: hsla(330, 81%, 60%, 1); /* pink-500 */
|
||||
--cal-bg-visualization-2-subtle: hsla(256, 86%, 91%, 1); /* purple-100 */
|
||||
--cal-bg-visualization-2-emphasis: hsla(256, 85%, 57%, 1); /* purple-500 */
|
||||
--cal-bg-visualization-3-subtle: hsla(217, 87%, 91%, 1); /* blue-100 */
|
||||
--cal-bg-visualization-3-emphasis: hsla(235, 100%, 63%, 1); /* blue-500 */
|
||||
--cal-bg-visualization-4-subtle: hsla(167, 54%, 93%, 1); /* green-100 */
|
||||
--cal-bg-visualization-4-emphasis: hsla(158, 74%, 38%, 1); /* green-500 */
|
||||
--cal-bg-visualization-5-subtle: hsla(55, 97%, 88%, 1); /* yellow-100 */
|
||||
--cal-bg-visualization-5-emphasis: hsla(45, 93%, 47%, 1); /* yellow-500 */
|
||||
--cal-bg-visualization-6-subtle: hsla(34, 100%, 92%, 1); /* orange-100 */
|
||||
--cal-bg-visualization-6-emphasis: hsla(25, 95%, 53%, 1); /* orange-500 */
|
||||
--cal-bg-visualization-7-subtle: hsla(0, 96%, 89%, 1); /* red-100 */
|
||||
--cal-bg-visualization-7-emphasis: hsla(0, 84%, 60%, 1); /* red-500 */
|
||||
|
||||
/* Legacy Background Components - Consider deprecating */
|
||||
--cal-bg-info: hsla(221, 91%, 93%, 1); /* #dee9fc - info backgrounds */
|
||||
--cal-bg-success: hsla(142, 76%, 94%, 1); /* #e2fbe8 - success backgrounds */
|
||||
--cal-bg-attention: hsla(33, 100%, 92%, 1); /* #fceed8 - attention backgrounds */
|
||||
--cal-bg-error: hsla(3, 66%, 93%, 1); /* #f9e3e2 - error backgrounds */
|
||||
--cal-bg-dark-error: hsla(2, 55%, 30%, 1); /* Keeping existing dark error */
|
||||
|
||||
/* Borders */
|
||||
--cal-border-emphasis: hsla(218, 11%, 65%, 1);
|
||||
--cal-border: hsla(216, 12%, 84%, 1);
|
||||
--cal-border-subtle: hsla(220, 13%, 91%, 1);
|
||||
--cal-border-booker: #e5e7eb;
|
||||
--cal-border-muted: hsla(220, 14%, 96%, 1);
|
||||
--cal-border-error: hsla(4, 63%, 41%, 1);
|
||||
--cal-border-focus: hsla(0, 0%, 10%, 1);
|
||||
--cal-border-emphasis: hsla(218, 11%, 65%, 1); /* gray.400 - input:hover */
|
||||
--cal-border: hsla(216, 12%, 84%, 1); /* gray.300 - inputs */
|
||||
--cal-border-subtle: hsla(220, 13%, 91%, 1); /* gray.200 - container borders */
|
||||
--cal-border-muted: hsla(220, 14%, 94%, 1); /* gray.100 */
|
||||
--cal-border-error: hsla(0, 96%, 89%, 1); /* Keeping existing error border */
|
||||
--cal-border-semantic-error: hsla(0, 96%, 89%, 1);
|
||||
--cal-border-booker: var(--cal-border-subtle);
|
||||
|
||||
/* Content/Text */
|
||||
--cal-text-emphasis: hsla(217, 19%, 27%, 1);
|
||||
--cal-text: hsla(217, 19%, 27%, 1);
|
||||
--cal-text-subtle: hsla(220, 9%, 46%, 1);
|
||||
--cal-text-muted: hsla(218, 11%, 65%, 1);
|
||||
--cal-text-inverted: hsla(0, 0%, 100%, 1);
|
||||
/* Content/Text Standard */
|
||||
--cal-text-emphasis: hsla(210, 30%, 4%, 1); /* gray-900 */
|
||||
--cal-text: hsla(220, 6%, 25%, 1); /* gray-700 */
|
||||
--cal-text-subtle: hsla(220, 9%, 46%, 1); /* gray-500 */
|
||||
--cal-text-muted: hsla(218, 11%, 65%, 1); /* gray-400 */
|
||||
--cal-text-inverted: hsla(0, 0%, 100%, 1); /* white */
|
||||
|
||||
/* Content/Text -> components */
|
||||
--cal-text-info: hsla(228, 56%, 33%, 1);
|
||||
--cal-text-success: hsla(133, 34%, 24%, 1);
|
||||
--cal-text-attention: hsla(16, 62%, 28%, 1);
|
||||
--cal-text-error: hsla(2, 55%, 30%, 1);
|
||||
/* Content/Text Semantic */
|
||||
--cal-text-semantic-info: hsla(236, 80%, 25%, 1); /* blue-800 */
|
||||
--cal-text-semantic-success: hsla(150, 84%, 22%, 1); /* green-800 */
|
||||
--cal-text-semantic-attention: hsla(15, 79%, 34%, 1); /* orange-800 */
|
||||
--cal-text-semantic-error: hsla(0, 63%, 24%, 1); /* red-800 */
|
||||
|
||||
/* Brand shinanigans
|
||||
-> These will be computed for the users theme at runtime.
|
||||
*/
|
||||
--cal-brand: hsla(221, 39%, 11%, 1);
|
||||
--cal-brand-emphasis: hsla(0, 0%, 6%, 1);
|
||||
--cal-brand-text: hsla(0, 0%, 100%, 1);
|
||||
/* Content/Text Visualization */
|
||||
--cal-text-visualization-1: hsla(332, 79%, 25%, 1); /* pink-800 */
|
||||
--cal-text-visualization-2: hsla(270, 91%, 25%, 1); /* purple-800 */
|
||||
--cal-text-visualization-3: hsla(217, 91%, 25%, 1); /* blue-800 */
|
||||
--cal-text-visualization-4: hsla(142, 71%, 25%, 1); /* green-800 */
|
||||
--cal-text-visualization-5: hsla(45, 93%, 25%, 1); /* yellow-800 */
|
||||
--cal-text-visualization-6: hsla(24, 95%, 25%, 1); /* orange-800 */
|
||||
--cal-text-visualization-7: hsla(0, 84%, 25%, 1); /* red-800 */
|
||||
|
||||
/* Border/Semantic Subtle */
|
||||
--cal-border-semantic-attention-subtle: hsla(32, 98%, 83%, 1);
|
||||
--cal-border-semantic-error-subtle: hsla(0, 96%, 89%, 1);
|
||||
|
||||
/* Legacy Content/Text Components - Consider deprecating */
|
||||
--cal-text-info: hsla(225, 57%, 33%, 1); /* #253985 - interactive text/icons */
|
||||
--cal-text-success: hsla(144, 34%, 24%, 1); /* #285231 */
|
||||
--cal-text-attention: hsla(16, 62%, 28%, 1); /* Keeping existing attention text */
|
||||
--cal-text-error: hsla(0, 63%, 31%, 1); /* Keeping existing error text */
|
||||
|
||||
/* Brand */
|
||||
--cal-brand: hsla(221, 39%, 11%, 1); /* Keeping existing brand color */
|
||||
--cal-brand-emphasis: hsla(0, 0%, 6%, 1); /* Keeping existing brand emphasis */
|
||||
--cal-brand-text: hsla(0, 0%, 100%, 1); /* white */
|
||||
}
|
||||
.dark {
|
||||
/* background */
|
||||
/* Background Standard */
|
||||
--cal-bg-emphasis: hsla(0, 0%, 25%, 1); /* stone-700 */
|
||||
--cal-bg: hsla(0, 0%, 6%, 1); /* stone-950 */
|
||||
--cal-bg-subtle: hsla(0, 0%, 15%, 1); /* stone-800 */
|
||||
--cal-bg-muted: hsla(0, 0%, 9%, 1); /* stone-900 */
|
||||
--cal-bg-inverted: hsla(0, 0%, 98%, 1); /* stone-50 */
|
||||
|
||||
--cal-bg-emphasis: hsla(0, 0%, 25%, 1);
|
||||
--cal-bg: hsla(0, 0%, 10%, 1);
|
||||
--cal-bg-subtle: hsla(0, 0%, 18%, 1);
|
||||
--cal-bg-muted: hsla(0, 0%, 12%, 1);
|
||||
--cal-bg-inverted: hsla(220, 14%, 96%, 1);
|
||||
/* Background Primary */
|
||||
--cal-bg-primary: hsla(0, 0%, 96%, 1); /* stone-100 */
|
||||
--cal-bg-primary-emphasis: hsla(0, 0%, 64%, 1); /* stone-800 */
|
||||
--cal-bg-primary-muted: hsla(0, 0%, 15%, 1); /* stone-400 */
|
||||
|
||||
/* background -> components*/
|
||||
--cal-bg-info: hsla(228, 56%, 33%, 1);
|
||||
--cal-bg-success: hsla(133, 34%, 24%, 1);
|
||||
--cal-bg-attention: hsla(16, 62%, 28%, 1);
|
||||
--cal-bg-error: hsla(2, 55%, 30%, 1);
|
||||
--cal-bg-dark-error: hsla(2, 55%, 30%, 1);
|
||||
/* Background Brand */
|
||||
--cal-bg-brand: hsla(0, 0%, 98%, 1); /* gray-50 */
|
||||
--cal-bg-brand-emphasis: hsla(0, 0%, 96%, 1); /* stone-100 */
|
||||
--cal-bg-brand-muted: hsla(0, 0%, 96%, 1); /* stone-100 */
|
||||
|
||||
/* Borders */
|
||||
--cal-border-emphasis: hsla(0, 0%, 46%, 1);
|
||||
--cal-border: hsla(0, 0%, 34%, 1);
|
||||
--cal-border-subtle: hsla(0, 0%, 22%, 1);
|
||||
--cal-border-booker: hsla(0, 0%, 22%, 1);
|
||||
--cal-border-muted: hsla(0, 0%, 18%, 1);
|
||||
--cal-border-error: hsla(4, 63%, 41%, 1);
|
||||
--cal-border-focus: hsla(0, 0%, 100%, 1);
|
||||
/* Background Semantic */
|
||||
--cal-bg-semantic-info-subtle: hsla(236, 80%, 8%, 1); /* blue-800 */
|
||||
--cal-bg-semantic-info-emphasis: hsla(229, 90%, 74%, 1); /* blue-500 */
|
||||
--cal-bg-semantic-success-subtle: hsla(148, 88%, 16%, 1); /* green-800 */
|
||||
--cal-bg-semantic-success-emphasis: hsla(158, 74%, 38%, 1); /* green-500 */
|
||||
--cal-bg-semantic-attention-subtle: hsla(21, 86%, 8%, 1); /* orange-800 */
|
||||
--cal-bg-semantic-attention-emphasis: hsla(27, 96%, 61%, 1); /* orange-500 */
|
||||
--cal-bg-semantic-error-subtle: hsla(0, 70%, 8%, 1); /* red-800 */
|
||||
--cal-bg-semantic-error-emphasis: hsla(0, 91%, 71%, 1); /* red-500 */
|
||||
|
||||
/* Content/Text */
|
||||
--cal-text-emphasis: hsla(240, 20%, 99%, 1);
|
||||
--cal-text: hsla(0, 0%, 84%, 1);
|
||||
--cal-text-subtle: hsla(0, 0%, 65%, 1);
|
||||
--cal-text-muted: hsla(0, 0%, 34%, 1);
|
||||
--cal-text-inverted: hsla(0, 0%, 10%, 1);
|
||||
/* Background Visualization */
|
||||
--cal-bg-visualization-1-subtle: hsla(336, 74%, 35%, 1); /* pink-200 */
|
||||
--cal-bg-visualization-1-emphasis: hsla(330, 81%, 60%, 1); /* pink-500 */
|
||||
--cal-bg-visualization-2-subtle: hsla(252, 83%, 23%, 1); /* purple-200 */
|
||||
--cal-bg-visualization-2-emphasis: hsla(256, 85%, 57%, 1); /* purple-500 */
|
||||
--cal-bg-visualization-3-subtle: hsla(236, 74%, 35%, 1); /* blue-200 */
|
||||
--cal-bg-visualization-3-emphasis: hsla(235, 100%, 63%, 1); /* blue-500 */
|
||||
--cal-bg-visualization-4-subtle: hsla(150, 84%, 22%, 1); /* green-200 */
|
||||
--cal-bg-visualization-4-emphasis: hsla(158, 74%, 38%, 1); /* green-500 */
|
||||
--cal-bg-visualization-5-subtle: hsla(28, 73%, 26%, 1); /* yellow-200 */
|
||||
--cal-bg-visualization-5-emphasis: hsla(45, 93%, 47%, 1); /* yellow-500 */
|
||||
--cal-bg-visualization-6-subtle: hsla(15, 75%, 23%, 1); /* orange-200 */
|
||||
--cal-bg-visualization-6-emphasis: hsla(25, 95%, 53%, 1); /* orange-500 */
|
||||
--cal-bg-visualization-7-subtle: hsla(0, 70%, 35%, 1); /* red-200 */
|
||||
--cal-bg-visualization-7-emphasis: hsla(0, 84%, 60%, 1); /* red-500 */
|
||||
|
||||
/* Content/Text -> components */
|
||||
--cal-text-info: hsla(218, 83%, 93%, 1);
|
||||
--cal-text-success: hsla(134, 76%, 94%, 1);
|
||||
--cal-text-attention: hsla(37, 86%, 92%, 1);
|
||||
--cal-text-error: hsla(3, 66%, 93%, 1);
|
||||
/* Legacy Background Components - Consider deprecating */
|
||||
--cal-bg-info: hsla(228, 56%, 33%, 1); /* Keeping existing info background */
|
||||
--cal-bg-success: hsla(133, 34%, 24%, 1); /* Keeping existing success background */
|
||||
--cal-bg-attention: hsla(16, 62%, 28%, 1); /* Keeping existing attention background */
|
||||
--cal-bg-error: hsla(2, 55%, 30%, 1); /* Keeping existing error background */
|
||||
--cal-bg-dark-error: hsla(2, 55%, 30%, 1); /* Keeping existing dark error */
|
||||
|
||||
/* Brand shenanigans
|
||||
-> These will be computed for the users theme at runtime.
|
||||
*/
|
||||
--cal-brand: hsla(0, 0%, 100%, 1);
|
||||
--cal-brand-emphasis: hsla(218, 11%, 65%, 1);
|
||||
--cal-brand-text: hsla(0, 0%, 0%,1);
|
||||
/* Border Standard */
|
||||
--cal-border: hsla(0, 0%, 30%, 1); /* stone-600 */
|
||||
--cal-border-muted: hsla(0, 0%, 9%, 1); /* stone-500 */
|
||||
--cal-border-subtle: hsla(0, 0%, 15%, 1); /* stone-700 */
|
||||
--cal-border-emphasis: hsla(0, 0%, 45%, 1); /* stone-800 */
|
||||
--cal-border-booker: var(--cal-border-subtle);
|
||||
/* Border Semantic */
|
||||
--cal-border-semantic-error: hsla(0, 63%, 24%, 1); /* red-800 */
|
||||
|
||||
|
||||
/* Border/Semantic Subtle */
|
||||
--cal-border-semantic-attention-subtle: hsla(15, 75%, 23%, 1);
|
||||
--cal-border-semantic-error-subtle: hsla(0, 63%, 24%, 1);
|
||||
|
||||
|
||||
/* Legacy Border - Consider deprecating */
|
||||
--cal-border-error: hsla(0, 63%, 24%, 1); /* Keeping existing error border */
|
||||
|
||||
/* Content/Text Standard */
|
||||
--cal-text-emphasis: hsla(0, 0%, 98%, 1); /* stone-50 */
|
||||
--cal-text: hsla(0, 0%, 83%, 1); /* stone-300 */
|
||||
--cal-text-subtle: hsla(0, 0%, 64%, 1); /* stone-400 */
|
||||
--cal-text-muted: hsla(0, 0%, 64%, 1); /* stone-400 */
|
||||
--cal-text-inverted: hsla(0, 0%, 0%, 1); /* black */
|
||||
|
||||
/* Content/Text Semantic */
|
||||
--cal-text-semantic-info: hsla(229, 90%, 74%, 1); /* blue-100 */
|
||||
--cal-text-semantic-success: hsla(161, 49%, 54%, 1); /* green-100 */
|
||||
--cal-text-semantic-attention: hsla(27, 96%, 61%, 1); /* orange-100 */
|
||||
--cal-text-semantic-error: hsla(0, 91%, 71%, 1); /* red-100 */
|
||||
|
||||
/* Content/Text Semantic Emphasis */
|
||||
--cal-text-semantic-info-emphasis: hsla(217, 91%, 25%, 1); /* blue-800 */
|
||||
--cal-text-semantic-success-emphasis: hsla(142, 71%, 25%, 1); /* green-800 */
|
||||
--cal-text-semantic-attention-emphasis: hsla(24, 95%, 25%, 1); /* orange-800 */
|
||||
--cal-text-semantic-error-emphasis: hsla(0, 84%, 25%, 1); /* red-800 */
|
||||
|
||||
/* Legacy Content/Text Components - Consider deprecating */
|
||||
--cal-text-info: hsla(218, 83%, 93%, 1); /* Keeping existing info text */
|
||||
--cal-text-success: hsla(134, 76%, 94%, 1); /* Keeping existing success text */
|
||||
--cal-text-attention: hsla(37, 86%, 92%, 1); /* Keeping existing attention text */
|
||||
--cal-text-error: hsla(3, 66%, 93%, 1); /* Keeping existing error text */
|
||||
|
||||
/* Brand */
|
||||
--cal-brand: hsla(0, 0%, 100%, 1); /* white */
|
||||
--cal-brand-emphasis: hsla(218, 11%, 65%, 1); /* Keeping existing brand emphasis */
|
||||
--cal-brand-text: hsla(0, 0%, 0%, 1); /* black */
|
||||
}
|
||||
|
||||
@layer base {
|
||||
@@ -484,5 +638,8 @@ select:focus {
|
||||
@apply z-40 !important;
|
||||
}
|
||||
|
||||
[data-radix-popper-content-wrapper] {
|
||||
border: none;
|
||||
}
|
||||
|
||||
@import "./raqb.css";
|
||||
|
||||
@@ -12,6 +12,7 @@ type AppCategoryEntry = {
|
||||
name: AppCategories;
|
||||
href: string;
|
||||
icon: IconName;
|
||||
"data-testid": string;
|
||||
};
|
||||
|
||||
const getAppCategories = (baseURL: string, useQueryParam: boolean): AppCategoryEntry[] => {
|
||||
@@ -22,41 +23,49 @@ const getAppCategories = (baseURL: string, useQueryParam: boolean): AppCategoryE
|
||||
name: "analytics",
|
||||
href: getHref(baseURL, "analytics", useQueryParam),
|
||||
icon: "chart-bar",
|
||||
"data-testid": "analytics",
|
||||
},
|
||||
{
|
||||
name: "automation",
|
||||
href: getHref(baseURL, "automation", useQueryParam),
|
||||
icon: "share-2",
|
||||
"data-testid": "automation",
|
||||
},
|
||||
{
|
||||
name: "calendar",
|
||||
href: getHref(baseURL, "calendar", useQueryParam),
|
||||
icon: "calendar",
|
||||
"data-testid": "calendar",
|
||||
},
|
||||
{
|
||||
name: "conferencing",
|
||||
href: getHref(baseURL, "conferencing", useQueryParam),
|
||||
icon: "video",
|
||||
"data-testid": "conferencing",
|
||||
},
|
||||
{
|
||||
name: "crm",
|
||||
href: getHref(baseURL, "crm", useQueryParam),
|
||||
icon: "contact",
|
||||
"data-testid": "crm",
|
||||
},
|
||||
{
|
||||
name: "messaging",
|
||||
href: getHref(baseURL, "messaging", useQueryParam),
|
||||
icon: "mail",
|
||||
"data-testid": "messaging",
|
||||
},
|
||||
{
|
||||
name: "payment",
|
||||
href: getHref(baseURL, "payment", useQueryParam),
|
||||
icon: "credit-card",
|
||||
"data-testid": "payment",
|
||||
},
|
||||
{
|
||||
name: "other",
|
||||
href: getHref(baseURL, "other", useQueryParam),
|
||||
icon: "grid-3x3",
|
||||
"data-testid": "other",
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -10,17 +10,15 @@ import type { InputFieldProps } from "@calcom/ui/components/form/inputs/types";
|
||||
|
||||
type AddonProps = {
|
||||
children: React.ReactNode;
|
||||
isFilled?: boolean;
|
||||
className?: string;
|
||||
error?: boolean;
|
||||
onClickAddon?: () => void;
|
||||
};
|
||||
|
||||
const Addon = ({ isFilled, children, className, error }: AddonProps) => (
|
||||
const Addon = ({ children, className, error }: AddonProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
"addon-wrapper border-default [input:hover_+_&]:border-emphasis [input:hover_+_&]:border-l-default [&:has(+_input:hover)]:border-emphasis [&:has(+_input:hover)]:border-r-default h-9 border px-3 transition disabled:hover:cursor-not-allowed",
|
||||
isFilled && "bg-subtle",
|
||||
className
|
||||
)}>
|
||||
<div
|
||||
@@ -63,7 +61,6 @@ export const KeyField = forwardRef<HTMLInputElement, InputFieldProps & { default
|
||||
placeholder = isLocaleReady && i18n.exists(`${name}_placeholder`) ? t(`${name}_placeholder`) : "",
|
||||
className,
|
||||
addOnLeading,
|
||||
addOnFilled = true,
|
||||
addOnClassname,
|
||||
inputIsFullWidth,
|
||||
labelSrOnly,
|
||||
@@ -128,9 +125,7 @@ export const KeyField = forwardRef<HTMLInputElement, InputFieldProps & { default
|
||||
dir="ltr"
|
||||
className="focus-within:ring-brand-default group relative mb-1 flex items-center rounded-md transition focus-within:outline-none focus-within:ring-2">
|
||||
{addOnLeading && (
|
||||
<Addon
|
||||
isFilled={addOnFilled}
|
||||
className={classNames("ltr:rounded-l-md rtl:rounded-r-md", addOnClassname)}>
|
||||
<Addon className={classNames("ltr:rounded-l-md rtl:rounded-r-md", addOnClassname)}>
|
||||
{addOnLeading}
|
||||
</Addon>
|
||||
)}
|
||||
|
||||
@@ -525,7 +525,7 @@ export const FormAction = forwardRef(function FormAction<T extends typeof Button
|
||||
);
|
||||
}
|
||||
return (
|
||||
<DropdownMenuItem>
|
||||
<DropdownMenuItem className="hover:bg-[initial]">
|
||||
<Component
|
||||
ref={forwardedRef}
|
||||
{...actionProps}
|
||||
|
||||
@@ -178,7 +178,7 @@ export default function RoutingForms({
|
||||
newFormDialogState={newFormDialogState}
|
||||
setNewFormDialogState={setNewFormDialogState}>
|
||||
<div className="mb-10 w-full">
|
||||
<div className="flex">
|
||||
<div className="mb-2 flex">
|
||||
<TeamsFilter />
|
||||
</div>
|
||||
<FilterResults
|
||||
|
||||
@@ -198,7 +198,7 @@ function RoutingForm({ form, profile, ...restProps }: Props) {
|
||||
) : (
|
||||
<div className="mx-auto my-0 max-w-3xl md:my-24">
|
||||
<div className="w-full max-w-4xl ltr:mr-2 rtl:ml-2">
|
||||
<div className="main dark:bg-darkgray-100 sm:border-subtle bg-default -mx-4 rounded-md border border-neutral-200 p-4 py-6 sm:mx-0 sm:px-8">
|
||||
<div className="main sm:border-subtle bg-default -mx-4 rounded-md border border-neutral-200 p-4 py-6 sm:mx-0 sm:px-8">
|
||||
<div className="text-emphasis">{customPageMessage}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const plugin = require("tailwindcss/plugin");
|
||||
const { fontFamily } = require("tailwindcss/defaultTheme");
|
||||
const colors = require("tailwindcss/colors");
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
const subtleColor = "#E5E7EB";
|
||||
@@ -16,72 +17,191 @@ module.exports = {
|
||||
],
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
spacing: {
|
||||
0: "var(--cal-spacing-0)",
|
||||
px: "var(--cal-spacing-px)",
|
||||
0.5: "var(--cal-spacing-0_5)",
|
||||
1: "var(--cal-spacing-1)",
|
||||
1.5: "var(--cal-spacing-1_5)",
|
||||
2: "var(--cal-spacing-2)",
|
||||
2.5: "var(--cal-spacing-2_5)",
|
||||
3: "var(--cal-spacing-3)",
|
||||
3.5: "var(--cal-spacing-3_5)",
|
||||
4: "var(--cal-spacing-4)",
|
||||
5: "var(--cal-spacing-5)",
|
||||
6: "var(--cal-spacing-6)",
|
||||
7: "var(--cal-spacing-7)",
|
||||
8: "var(--cal-spacing-8)",
|
||||
9: "var(--cal-spacing-9)",
|
||||
10: "var(--cal-spacing-10)",
|
||||
11: "var(--cal-spacing-11)",
|
||||
12: "var(--cal-spacing-12)",
|
||||
14: "var(--cal-spacing-14)",
|
||||
16: "var(--cal-spacing-16)",
|
||||
20: "var(--cal-spacing-20)",
|
||||
24: "var(--cal-spacing-24)",
|
||||
28: "var(--cal-spacing-28)",
|
||||
32: "var(--cal-spacing-32)",
|
||||
36: "var(--cal-spacing-36)",
|
||||
40: "var(--cal-spacing-40)",
|
||||
44: "var(--cal-spacing-44)",
|
||||
48: "var(--cal-spacing-48)",
|
||||
52: "var(--cal-spacing-52)",
|
||||
56: "var(--cal-spacing-56)",
|
||||
60: "var(--cal-spacing-60)",
|
||||
64: "var(--cal-spacing-64)",
|
||||
72: "var(--cal-spacing-72)",
|
||||
80: "var(--cal-spacing-80)",
|
||||
96: "var(--cal-spacing-96)",
|
||||
},
|
||||
borderRadius: {
|
||||
none: "var(--cal-radius-none)",
|
||||
sm: "var(--cal-radius-sm)",
|
||||
DEFAULT: "var(--cal-radius)",
|
||||
md: "var(--cal-radius-md)",
|
||||
lg: "var(--cal-radius-lg)",
|
||||
xl: "var(--cal-radius-xl)",
|
||||
"2xl": "var(--cal-radius-2xl)",
|
||||
"3xl": "var(--cal-radius-3xl)",
|
||||
full: "var(--cal-radius-full)",
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
// Standard Background
|
||||
emphasis: "var(--cal-bg-emphasis)",
|
||||
default: "var(--cal-bg, white)",
|
||||
subtle: "var(--cal-bg-subtle)",
|
||||
muted: "var(--cal-bg-muted)",
|
||||
inverted: "var(--cal-bg-inverted)",
|
||||
info: "var(--cal-bg-info)",
|
||||
success: "var(--cal-bg-success)",
|
||||
attention: "var(--cal-bg-attention)",
|
||||
error: "var(--cal-bg-error)",
|
||||
darkerror: "var(--cal-bg-dark-error)",
|
||||
black: "#111111",
|
||||
|
||||
// Primary Background
|
||||
primary: {
|
||||
default: "var(--cal-bg-primary)",
|
||||
emphasis: "var(--cal-bg-primary-emphasis)",
|
||||
muted: "var(--cal-bg-primary-muted)",
|
||||
},
|
||||
|
||||
// Brand
|
||||
brand: {
|
||||
default: "var(--cal-brand,#111827)",
|
||||
emphasis: "var(--cal-brand-emphasis,#101010)",
|
||||
subtle: "var(--cal-brand-subtle,#9CA3AF)",
|
||||
accent: "var(--cal-brand-accent,white)",
|
||||
},
|
||||
gray: {
|
||||
50: "#F9FAFB",
|
||||
100: "#F3F4F6",
|
||||
200: "#E5E7EB",
|
||||
300: "#D1D5DB",
|
||||
400: "#9CA3AF",
|
||||
500: "#6B7280",
|
||||
600: "#4B5563",
|
||||
700: "#374151",
|
||||
800: "#1F2937",
|
||||
900: "#111827",
|
||||
|
||||
// Semantic Background
|
||||
semantic: {
|
||||
info: {
|
||||
subtle: "var(--cal-bg-semantic-info-subtle)",
|
||||
emphasis: "var(--cal-bg-semantic-info-emphasis)",
|
||||
},
|
||||
success: {
|
||||
subtle: "var(--cal-bg-semantic-success-subtle)",
|
||||
emphasis: "var(--cal-bg-semantic-success-emphasis)",
|
||||
},
|
||||
attention: {
|
||||
subtle: "var(--cal-bg-semantic-attention-subtle)",
|
||||
emphasis: "var(--cal-bg-semantic-attention-emphasis)",
|
||||
},
|
||||
error: {
|
||||
subtle: "var(--cal-bg-semantic-error-subtle)",
|
||||
emphasis: "var(--cal-bg-semantic-error-emphasis)",
|
||||
},
|
||||
},
|
||||
darkgray: {
|
||||
50: "#101010",
|
||||
100: "#1c1c1c",
|
||||
200: "#2b2b2b",
|
||||
300: "#444444",
|
||||
400: "#575757",
|
||||
500: "#767676",
|
||||
600: "#a5a5a5",
|
||||
700: "#d6d6d6",
|
||||
800: "#e8e8e8",
|
||||
900: "#f3f4f6",
|
||||
|
||||
// Visualization Background
|
||||
visualization: {
|
||||
1: {
|
||||
subtle: "var(--cal-bg-visualization-1-subtle)",
|
||||
emphasis: "var(--cal-bg-visualization-1-emphasis)",
|
||||
},
|
||||
2: {
|
||||
subtle: "var(--cal-bg-visualization-2-subtle)",
|
||||
emphasis: "var(--cal-bg-visualization-2-emphasis)",
|
||||
},
|
||||
3: {
|
||||
subtle: "var(--cal-bg-visualization-3-subtle)",
|
||||
emphasis: "var(--cal-bg-visualization-3-emphasis)",
|
||||
},
|
||||
4: {
|
||||
subtle: "var(--cal-bg-visualization-4-subtle)",
|
||||
emphasis: "var(--cal-bg-visualization-4-emphasis)",
|
||||
},
|
||||
5: {
|
||||
subtle: "var(--cal-bg-visualization-5-subtle)",
|
||||
emphasis: "var(--cal-bg-visualization-5-emphasis)",
|
||||
},
|
||||
6: {
|
||||
subtle: "var(--cal-bg-visualization-6-subtle)",
|
||||
emphasis: "var(--cal-bg-visualization-6-emphasis)",
|
||||
},
|
||||
7: {
|
||||
subtle: "var(--cal-bg-visualization-7-subtle)",
|
||||
emphasis: "var(--cal-bg-visualization-7-emphasis)",
|
||||
},
|
||||
},
|
||||
|
||||
// Legacy - Consider deprecating
|
||||
info: "var(--cal-bg-info)",
|
||||
success: "var(--cal-bg-success)",
|
||||
attention: "var(--cal-bg-attention)",
|
||||
error: "var(--cal-bg-error)",
|
||||
darkerror: "var(--cal-bg-dark-error)",
|
||||
|
||||
// Base colors
|
||||
black: "#111111",
|
||||
gray: colors.gray,
|
||||
darkgray: colors.slate,
|
||||
},
|
||||
borderColor: {
|
||||
emphasis: "var(--cal-border-emphasis, #9CA3AF)",
|
||||
default: "var(--cal-border, #D1D5DB)",
|
||||
subtle: `var(--cal-border-subtle, ${subtleColor})`,
|
||||
muted: "var(--cal-border-muted, #F3F4F6)",
|
||||
// Standard Borders
|
||||
emphasis: "var(--cal-border-emphasis)",
|
||||
default: "var(--cal-border)",
|
||||
subtle: "var(--cal-border-subtle)",
|
||||
muted: "var(--cal-border-muted)",
|
||||
|
||||
// Semantic Borders
|
||||
semantic: {
|
||||
error: "var(--cal-border-semantic-error)",
|
||||
"attention-subtle": "var(--cal-border-semantic-attention-subtle)",
|
||||
"error-subtle": "var(--cal-border-semantic-error-subtle)",
|
||||
},
|
||||
|
||||
// Used in booker embed customization
|
||||
booker: `var(--cal-border-booker, ${subtleColor})`,
|
||||
error: "var(--cal-border-error, #AA2E26)",
|
||||
focus: "var(--cal-border-focus, #1A1A1A)",
|
||||
"cal-bg": "var(--cal-bg, white)",
|
||||
// Legacy - Consider deprecating
|
||||
error: "var(--cal-border-error)",
|
||||
"cal-bg": "var(--cal-bg)",
|
||||
"cal-bg-muted": "var(--cal-bg-muted)",
|
||||
},
|
||||
textColor: {
|
||||
emphasis: "var(--cal-text-emphasis, #111827)",
|
||||
default: "var(--cal-text, #374151)",
|
||||
subtle: "var(--cal-text-subtle, #6B7280)",
|
||||
muted: "var(--cal-text-muted, #9CA3AF)",
|
||||
inverted: "var(--cal-text-inverted, white)",
|
||||
info: "var(--cal-text-info, #253985)",
|
||||
success: "var(--cal-text-success, #285231)",
|
||||
attention: "var(--cal-text-attention, #73321B)",
|
||||
error: "var(--cal-text-error, #752522)",
|
||||
brand: "var(--cal-brand-text,'white')",
|
||||
// Standard Text
|
||||
emphasis: "var(--cal-text-emphasis)",
|
||||
default: "var(--cal-text)",
|
||||
subtle: "var(--cal-text-subtle)",
|
||||
muted: "var(--cal-text-muted)",
|
||||
inverted: "var(--cal-text-inverted)",
|
||||
|
||||
// Semantic Text
|
||||
semantic: {
|
||||
info: "var(--cal-text-semantic-info)",
|
||||
success: "var(--cal-text-semantic-success)",
|
||||
attention: "var(--cal-text-semantic-attention)",
|
||||
error: "var(--cal-text-semantic-error)",
|
||||
},
|
||||
|
||||
// Semantic Text Emphasis
|
||||
"semantic-info-emphasis": "var(--cal-text-semantic-info-emphasis)",
|
||||
"semantic-success-emphasis": "var(--cal-text-semantic-success-emphasis)",
|
||||
"semantic-attention-emphasis": "var(--cal-text-semantic-attention-emphasis)",
|
||||
"semantic-error-emphasis": "var(--cal-text-semantic-error-emphasis)",
|
||||
|
||||
// Legacy - Consider deprecating
|
||||
info: "var(--cal-text-info)",
|
||||
success: "var(--cal-text-success)",
|
||||
attention: "var(--cal-text-attention)",
|
||||
error: "var(--cal-text-error)",
|
||||
brand: "var(--cal-brand-text)",
|
||||
},
|
||||
fill: {
|
||||
emphasis: "var(--cal-text-emphasis, #111827)",
|
||||
@@ -122,9 +242,6 @@ module.exports = {
|
||||
drawerSlideLeftAndFade: "drawerSlideLeftAndFade 150ms cubic-bezier(0.16, 1, 0.3, 1)",
|
||||
drawerSlideRightAndFade: "drawerSlideRightAndFade 150ms ease-in",
|
||||
},
|
||||
boxShadow: {
|
||||
dropdown: "0px 2px 6px -1px rgba(0, 0, 0, 0.08)",
|
||||
},
|
||||
borderWidth: {
|
||||
"booker-width": "var(--cal-border-booker-width, 1px)",
|
||||
},
|
||||
@@ -162,6 +279,39 @@ module.exports = {
|
||||
backgroundImage: {
|
||||
"gradient-primary": "radial-gradient(162.05% 170% at 109.58% 35%, #667593 0%, #E3E3E3 100%)",
|
||||
},
|
||||
boxShadow: {
|
||||
dropdown: "0px 5px 20px 0px rgba(0, 0, 0, 0.10), 0px 10px 40px 0px rgba(0, 0, 0, 0.03)",
|
||||
"switch-thumb": "0px 0.8px 0.8px 0px rgba(0, 0, 0, 0.10), 0px 0.8px 3.2px 0px rgba(0, 0, 0, 0.08)",
|
||||
"solid-gray-rested":
|
||||
"0px 2px 3px 0px rgba(0, 0, 0, 0.06), 0px 1px 1px 0px rgba(0, 0, 0, 0.08), 1px 4px 8px 0px rgba(0, 0, 0, 0.12), 0px 2px 0.4px 0px rgba(255, 255, 255, 0.16) inset, 0px -1.5px 2px 0px rgba(0, 0, 0, 0.40) inset",
|
||||
"solid-gray-hover":
|
||||
"0px 2px 3px 0px rgba(0, 0, 0, 0.06), 0px 1px 1px 0px rgba(0, 0, 0, 0.08), 1px 4px 8px 0px rgba(0, 0, 0, 0.12), 0px 2px 0.4px 0px rgba(255, 255, 255, 0.16) inset, 0px -2px 2px 0px rgba(0, 0, 0, 0.40) inset",
|
||||
"solid-gray-active":
|
||||
"0px 2px 3px 0px rgba(0, 0, 0, 0.40) inset, 0px 0px 2px 1px rgba(0, 0, 0, 0.40) inset",
|
||||
"outline-gray-rested": "0px 2px 3px 0px rgba(0, 0, 0, 0.03), 0px 2px 2px -1px rgba(0, 0, 0, 0.03)",
|
||||
"outline-gray-hover": "0px 2px 3px 0px rgba(0, 0, 0, 0.03), 0px 2px 2px -1px rgba(0, 0, 0, 0.03)",
|
||||
"outline-gray-active": "0px 2px 1px 0px rgba(0, 0, 0, 0.05) inset",
|
||||
"outline-gray-focused":
|
||||
"0px 0px 0px 1px rgba(255, 255, 255, 0.20), 0px 0px 0px 2px rgba(0, 0, 0, 0.10)",
|
||||
"outline-red-rested": "0px 2px 3px 0px rgba(0, 0, 0, 0.03), 0px 2px 2px -1px rgba(0, 0, 0, 0.03)",
|
||||
"outline-red-hover": "0px 1px 1px 0px rgba(0, 0, 0, 0.06), 0px 2px 3px 0px rgba(0, 0, 0, 0.08)",
|
||||
"outline-red-active":
|
||||
"0px 1px 1px 0px rgba(127, 29, 29, 0.06), 0px 0px 3px 0px rgba(127, 29, 29, 0.08), 0px 2px 2px 1px rgba(127, 29, 29, 0.06) inset",
|
||||
"elevation-low":
|
||||
"0px 1px 1px 0px rgba(0, 0, 0, 0.07), 0px 1px 2px 0px rgba(0, 0, 0, 0.08), 0px 2px 2px 0px rgba(0, 0, 0, 0.10), 0px 0px 8px 0px rgba(0, 0, 0, 0.05)",
|
||||
// Brand
|
||||
"button-solid-brand-default":
|
||||
" 0px 2px 3px 0px rgba(0, 0, 0, 0.06), 0px 1px 1px 0px rgba(0, 0, 0, 0.08), 1px 4px 8px 0px rgba(0, 0, 0, 0.12), 0px 2px 0.4px 0px rgba(255, 255, 255, 0.12) inset, 0px -3px 2px 0px rgba(0, 0, 0, 0.04) inset;",
|
||||
"button-solid-brand-hover":
|
||||
"0px 1px 1px 0px rgba(0, 0, 0, 0.10), 0px 2px 3px 0px rgba(0, 0, 0, 0.08), 1px 4px 8px 0px rgba(0, 0, 0, 0.12), 0px -3px 2px 0px rgba(0, 0, 0, 0.10) inset, 0px 2px 0.4px 0px rgba(255, 255, 255, 0.24) inset",
|
||||
"button-solid-brand-active":
|
||||
"0px 3px 1px 0px rgba(0, 0, 0, 0.10) inset, 0px 0px 2px 0px rgba(0, 0, 0, 0.10) inset",
|
||||
"button-solid-brand-focused":
|
||||
"0px 0px 0px 1px rgba(255, 255, 255, 0.40), 0px 0px 0px 2px rgba(0, 0, 0, 0.20), 0px 1px 1px 0px rgba(0, 0, 0, 0.10), 0px 2px 3px 0px rgba(0, 0, 0, 0.08), 1px 4px 8px 0px rgba(0, 0, 0, 0.12), 0px -3px 2px 0px rgba(0, 0, 0, 0.10) inset, 0px 2px 0.4px 0px rgba(255, 255, 255, 0.24) inset",
|
||||
// Outline - red
|
||||
"button-outline-red-focused":
|
||||
"0px 0px 0px 1px rgba(255, 255, 255, 0.32), 0px 0px 0px 2px rgba(220, 38, 38, 0.15)",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
@@ -183,6 +333,9 @@ module.exports = {
|
||||
},
|
||||
});
|
||||
}),
|
||||
plugin(function ({ addVariant }) {
|
||||
addVariant("enabled", "&:not(:disabled)");
|
||||
}),
|
||||
],
|
||||
variants: {
|
||||
scrollbar: ["dark"],
|
||||
|
||||
@@ -32,8 +32,7 @@
|
||||
--cal-text-info: #253985;
|
||||
--cal-text-success: #285231;
|
||||
--cal-text-attention: #73321b;
|
||||
--cal-text-error: #752522;
|
||||
|
||||
--cal-text-error: hsla(0, 63%, 31%, 1);
|
||||
/* Brand shenanigans
|
||||
-> These will be computed for the users theme at runtime.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
export const addAppCssVars = () => {
|
||||
try {
|
||||
const cssVarsStyle = `cal-floating-button, cal-modal-box, cal-inline {
|
||||
/* Spacing Scale */
|
||||
--cal-spacing-0: 0px;
|
||||
--cal-spacing-px: 1px;
|
||||
--cal-spacing-0_5: 0.125rem; /* 2px */
|
||||
--cal-spacing-1: 0.25rem; /* 4px */
|
||||
--cal-spacing-1_5: 0.375rem; /* 6px */
|
||||
--cal-spacing-2: 0.5rem; /* 8px */
|
||||
--cal-spacing-2_5: 0.625rem; /* 10px */
|
||||
--cal-spacing-3: 0.75rem; /* 12px */
|
||||
--cal-spacing-3_5: 0.875rem; /* 14px */
|
||||
--cal-spacing-4: 1rem; /* 16px */
|
||||
--cal-spacing-5: 1.25rem; /* 20px */
|
||||
--cal-spacing-6: 1.5rem; /* 24px */
|
||||
--cal-spacing-7: 1.75rem; /* 28px */
|
||||
--cal-spacing-8: 2rem; /* 32px */
|
||||
--cal-spacing-9: 2.25rem; /* 36px */
|
||||
--cal-spacing-10: 2.5rem; /* 40px */
|
||||
--cal-spacing-11: 2.75rem; /* 44px */
|
||||
--cal-spacing-12: 3rem; /* 48px */
|
||||
--cal-spacing-14: 3.5rem; /* 56px */
|
||||
--cal-spacing-16: 4rem; /* 64px */
|
||||
--cal-spacing-20: 5rem; /* 80px */
|
||||
--cal-spacing-24: 6rem; /* 96px */
|
||||
--cal-spacing-28: 7rem; /* 112px */
|
||||
--cal-spacing-32: 8rem; /* 128px */
|
||||
--cal-spacing-36: 9rem; /* 144px */
|
||||
--cal-spacing-40: 10rem; /* 160px */
|
||||
--cal-spacing-44: 11rem; /* 176px */
|
||||
--cal-spacing-48: 12rem; /* 192px */
|
||||
--cal-spacing-52: 13rem; /* 208px */
|
||||
--cal-spacing-56: 14rem; /* 224px */
|
||||
--cal-spacing-60: 15rem; /* 240px */
|
||||
--cal-spacing-64: 16rem; /* 256px */
|
||||
--cal-spacing-72: 18rem; /* 288px */
|
||||
--cal-spacing-80: 20rem; /* 320px */
|
||||
--cal-spacing-96: 24rem; /* 384px */
|
||||
|
||||
/* Border Radius */
|
||||
--cal-radius-none: 0px;
|
||||
--cal-radius-sm: 0.125rem; /* 2px */
|
||||
--cal-radius: 0.25rem; /* 4px */
|
||||
--cal-radius-md: 0.375rem; /* 6px */
|
||||
--cal-radius-lg: 0.5rem; /* 8px */
|
||||
--cal-radius-xl: 0.75rem; /* 12px */
|
||||
--cal-radius-2xl: 1rem; /* 16px */
|
||||
--cal-radius-3xl: 1.5rem; /* 24px */
|
||||
--cal-radius-full: 9999px;
|
||||
|
||||
/* Background Standard */
|
||||
--cal-bg-emphasis: hsla(220, 13%, 91%, 1); /* gray.200 - active items and emphasising */
|
||||
--cal-bg: hsla(0, 0%, 100%, 1); /* white - default background */
|
||||
--cal-bg-subtle: hsla(220, 14%, 94%, 1); /* gray.100 - hovering on items, subtle raising */
|
||||
--cal-bg-muted: hsla(210, 20%, 97%, 1); /* gray.50 - large items, sidebar, sections */
|
||||
--cal-bg-inverted: hsla(210, 30%, 4%, 1); /* gray.900 - tooltips */
|
||||
|
||||
/* Background Primary */
|
||||
--cal-bg-primary: hsla(214, 30%, 16%, 1); /* stone-100 */
|
||||
--cal-bg-primary-emphasis: hsla(220, 6%, 25%, 1); /* stone-800 */
|
||||
--cal-bg-primary-muted: hsla(220, 14%, 94%, 1); /* stone-400 */
|
||||
|
||||
/* Background Brand */
|
||||
--cal-bg-brand: hsla(214, 30%, 16%, 1); /* gray-50 */
|
||||
--cal-bg-brand-emphasis: hsla(220, 6%, 25%, 1); /* stone-100 */
|
||||
--cal-bg-brand-muted: hsla(220, 14%, 94%, 1); /* stone-100 */
|
||||
|
||||
/* Background Semantic */
|
||||
--cal-bg-semantic-info-subtle: hsla(212, 88%, 97%, 1); /* blue-100 */
|
||||
--cal-bg-semantic-info-emphasis: hsla(236, 80%, 25%, 1); /* blue-500 */
|
||||
--cal-bg-semantic-success-subtle: hsla(167, 54%, 93%, 1); /* green-100 */
|
||||
--cal-bg-semantic-success-emphasis: hsla(158, 74%, 38%, 1); /* green-500 */
|
||||
--cal-bg-semantic-attention-subtle: hsla(34, 100%, 92%, 1); /* orange-100 */
|
||||
--cal-bg-semantic-attention-emphasis: hsla(15, 79%, 34%, 1); /* orange-500 */
|
||||
--cal-bg-semantic-error-subtle: hsla(0, 93%, 94%, 1); /* red-100 */
|
||||
--cal-bg-semantic-error-emphasis: hsla(0, 63%, 24%, 1); /* red-500 */
|
||||
|
||||
/* Background Visualization */
|
||||
--cal-bg-visualization-1-subtle: hsla(326, 78%, 95%, 1); /* pink-100 */
|
||||
--cal-bg-visualization-1-emphasis: hsla(330, 81%, 60%, 1); /* pink-500 */
|
||||
--cal-bg-visualization-2-subtle: hsla(256, 86%, 91%, 1); /* purple-100 */
|
||||
--cal-bg-visualization-2-emphasis: hsla(256, 85%, 57%, 1); /* purple-500 */
|
||||
--cal-bg-visualization-3-subtle: hsla(217, 87%, 91%, 1); /* blue-100 */
|
||||
--cal-bg-visualization-3-emphasis: hsla(235, 100%, 63%, 1); /* blue-500 */
|
||||
--cal-bg-visualization-4-subtle: hsla(167, 54%, 93%, 1); /* green-100 */
|
||||
--cal-bg-visualization-4-emphasis: hsla(158, 74%, 38%, 1); /* green-500 */
|
||||
--cal-bg-visualization-5-subtle: hsla(55, 97%, 88%, 1); /* yellow-100 */
|
||||
--cal-bg-visualization-5-emphasis: hsla(45, 93%, 47%, 1); /* yellow-500 */
|
||||
--cal-bg-visualization-6-subtle: hsla(34, 100%, 92%, 1); /* orange-100 */
|
||||
--cal-bg-visualization-6-emphasis: hsla(25, 95%, 53%, 1); /* orange-500 */
|
||||
--cal-bg-visualization-7-subtle: hsla(0, 96%, 89%, 1); /* red-100 */
|
||||
--cal-bg-visualization-7-emphasis: hsla(0, 84%, 60%, 1); /* red-500 */
|
||||
|
||||
/* Legacy Background Components - Consider deprecating */
|
||||
--cal-bg-info: hsla(221, 91%, 93%, 1); /* #dee9fc - info backgrounds */
|
||||
--cal-bg-success: hsla(142, 76%, 94%, 1); /* #e2fbe8 - success backgrounds */
|
||||
--cal-bg-attention: hsla(33, 100%, 92%, 1); /* #fceed8 - attention backgrounds */
|
||||
--cal-bg-error: hsla(3, 66%, 93%, 1); /* #f9e3e2 - error backgrounds */
|
||||
--cal-bg-dark-error: hsla(2, 55%, 30%, 1); /* Keeping existing dark error */
|
||||
|
||||
/* Borders */
|
||||
--cal-border-emphasis: hsla(218, 11%, 65%, 1); /* gray.400 - input:hover */
|
||||
--cal-border: hsla(216, 12%, 84%, 1); /* gray.300 - inputs */
|
||||
--cal-border-subtle: hsla(220, 13%, 91%, 1); /* gray.200 - container borders */
|
||||
--cal-border-muted: hsla(220, 14%, 94%, 1); /* gray.100 */
|
||||
--cal-border-error: hsla(0, 96%, 89%, 1); /* Keeping existing error border */
|
||||
--cal-border-semantic-error: hsla(0, 96%, 89%, 1);
|
||||
--cal-border-booker: var(--cal-border-subtle);
|
||||
|
||||
/* Content/Text Standard */
|
||||
--cal-text-emphasis: hsla(210, 30%, 4%, 1); /* gray-900 */
|
||||
--cal-text: hsla(220, 6%, 25%, 1); /* gray-700 */
|
||||
--cal-text-subtle: hsla(220, 9%, 46%, 1); /* gray-500 */
|
||||
--cal-text-muted: hsla(218, 11%, 65%, 1); /* gray-400 */
|
||||
--cal-text-inverted: hsla(0, 0%, 100%, 1); /* white */
|
||||
|
||||
/* Content/Text Semantic */
|
||||
--cal-text-semantic-info: hsla(236, 80%, 25%, 1); /* blue-800 */
|
||||
--cal-text-semantic-success: hsla(150, 84%, 22%, 1); /* green-800 */
|
||||
--cal-text-semantic-attention: hsla(15, 79%, 34%, 1); /* orange-800 */
|
||||
--cal-text-semantic-error: hsla(0, 63%, 24%, 1); /* red-800 */
|
||||
|
||||
/* Content/Text Visualization */
|
||||
--cal-text-visualization-1: hsla(332, 79%, 25%, 1); /* pink-800 */
|
||||
--cal-text-visualization-2: hsla(270, 91%, 25%, 1); /* purple-800 */
|
||||
--cal-text-visualization-3: hsla(217, 91%, 25%, 1); /* blue-800 */
|
||||
--cal-text-visualization-4: hsla(142, 71%, 25%, 1); /* green-800 */
|
||||
--cal-text-visualization-5: hsla(45, 93%, 25%, 1); /* yellow-800 */
|
||||
--cal-text-visualization-6: hsla(24, 95%, 25%, 1); /* orange-800 */
|
||||
--cal-text-visualization-7: hsla(0, 84%, 25%, 1); /* red-800 */
|
||||
|
||||
/* Border/Semantic Subtle */
|
||||
--cal-border-semantic-attention-subtle: hsla(32, 98%, 83%, 1);
|
||||
--cal-border-semantic-error-subtle: hsla(0, 96%, 89%, 1);
|
||||
|
||||
/* Legacy Content/Text Components - Consider deprecating */
|
||||
--cal-text-info: hsla(225, 57%, 33%, 1); /* #253985 - interactive text/icons */
|
||||
--cal-text-success: hsla(144, 34%, 24%, 1); /* #285231 */
|
||||
--cal-text-attention: hsla(16, 62%, 28%, 1); /* Keeping existing attention text */
|
||||
--cal-text-error: hsla(0, 63%, 31%, 1); /* Keeping existing error text */
|
||||
|
||||
/* Brand */
|
||||
--cal-brand: hsla(221, 39%, 11%, 1); /* Keeping existing brand color */
|
||||
--cal-brand-emphasis: hsla(0, 0%, 6%, 1); /* Keeping existing brand emphasis */
|
||||
--cal-brand-text: hsla(0, 0%, 100%, 1); /* white */
|
||||
}`;
|
||||
|
||||
// Create a style rule for above css
|
||||
const style = document.createElement("style");
|
||||
style.id = "cal-css-vars";
|
||||
style.textContent = cssVarsStyle;
|
||||
document.head.appendChild(style);
|
||||
} catch (error) {
|
||||
console.error("Error adding app css vars - Report this issue to support", error);
|
||||
}
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
import { FloatingButton } from "./FloatingButton/FloatingButton";
|
||||
import { Inline } from "./Inline/inline";
|
||||
import { ModalBox } from "./ModalBox/ModalBox";
|
||||
import { addAppCssVars } from "./addAppCssVars";
|
||||
import type { InterfaceWithParent, interfaceWithParent, PrefillAndIframeAttrsConfig } from "./embed-iframe";
|
||||
import css from "./embed.css";
|
||||
import { SdkActionManager } from "./sdk-action-manager";
|
||||
@@ -1056,6 +1057,8 @@ function initializeGlobalCalProps() {
|
||||
globalCal.config.forwardQueryParams = globalCal.config.forwardQueryParams ?? false;
|
||||
}
|
||||
|
||||
addAppCssVars();
|
||||
|
||||
function log(...args: unknown[]) {
|
||||
console.log(...args);
|
||||
}
|
||||
|
||||
@@ -214,7 +214,8 @@ export const EventMeta = ({
|
||||
menuPosition="absolute"
|
||||
timezoneSelectCustomClassname={classNames?.eventMetaTimezoneSelect}
|
||||
classNames={{
|
||||
control: () => "!min-h-0 p-0 w-full border-0 bg-transparent focus-within:ring-0",
|
||||
control: () =>
|
||||
"!min-h-0 p-0 w-full border-0 bg-transparent focus-within:ring-0 shadow-none!",
|
||||
menu: () => "!w-64 max-w-[90vw] mb-1 ",
|
||||
singleValue: () => "text-text py-1",
|
||||
indicatorsContainer: () => "ml-auto",
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Dispatch, SetStateAction } from "react";
|
||||
|
||||
import { useFilterQuery } from "@calcom/features/bookings/lib/useFilterQuery";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Badge, Button, Icon, Tooltip } from "@calcom/ui";
|
||||
import { Badge, Button, Tooltip } from "@calcom/ui";
|
||||
|
||||
export interface FilterToggleProps {
|
||||
setIsFiltersVisible: Dispatch<SetStateAction<boolean>>;
|
||||
@@ -19,8 +19,7 @@ export function FilterToggle({ setIsFiltersVisible }: FilterToggleProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Button color="secondary" onClick={toggleFiltersVisibility} className="mb-4">
|
||||
<Icon name="filter" className="h-4 w-4" />
|
||||
<Button color="secondary" onClick={toggleFiltersVisibility} className="mb-4" StartIcon="filter">
|
||||
<Tooltip content={t("filters")}>
|
||||
<div className="mx-2">{t("filters")}</div>
|
||||
</Tooltip>
|
||||
|
||||
@@ -28,7 +28,7 @@ export function FiltersContainer({ isFiltersVisible }: FiltersContainerProps) {
|
||||
return (
|
||||
<div ref={animationParentRef}>
|
||||
{isFiltersVisible ? (
|
||||
<div className="no-scrollbar flex w-full space-x-2 overflow-x-scroll rtl:space-x-reverse">
|
||||
<div className="no-scrollbar mb-2 flex w-full space-x-2 overflow-x-scroll rtl:space-x-reverse">
|
||||
<PeopleFilter />
|
||||
<EventTypeFilter />
|
||||
<TeamsFilter />
|
||||
|
||||
@@ -5,7 +5,7 @@ import { forwardRef } from "react";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { Button, Icon } from "@calcom/ui";
|
||||
import type { IconName } from "@calcom/ui";
|
||||
import type { IconName, ButtonProps } from "@calcom/ui";
|
||||
|
||||
export type ActionItem<TData> =
|
||||
| {
|
||||
@@ -26,12 +26,12 @@ type ResponsiveButtonProps = {
|
||||
icon: IconName;
|
||||
onClick?: () => void;
|
||||
children: string;
|
||||
};
|
||||
} & ButtonProps;
|
||||
|
||||
const ResponsiveButton = forwardRef<HTMLButtonElement, ResponsiveButtonProps>(
|
||||
({ className, icon, onClick, children }, ref) => {
|
||||
({ icon, onClick, children, ...rest }, ref) => {
|
||||
return (
|
||||
<Button ref={ref} onClick={onClick} title={children}>
|
||||
<Button ref={ref} onClick={onClick} title={children} {...rest}>
|
||||
<Icon name={icon} size={18} className="sm:hidden" />
|
||||
<Icon name={icon} size={16} className="mr-2 hidden shrink-0 sm:inline" />
|
||||
<span className="sr-only shrink-0 md:not-sr-only">{children}</span>
|
||||
@@ -51,7 +51,7 @@ const Root = forwardRef<
|
||||
<div
|
||||
ref={ref}
|
||||
className={classNames(
|
||||
"bg-brand-default text-brand fixed bottom-0 left-0 z-40 flex w-full items-center space-x-1 overflow-x-auto px-2 py-2 sm:space-x-2 md:bottom-4 md:left-1/2 md:z-auto md:w-fit md:-translate-x-1/2 md:transform md:space-x-3 md:overflow-x-hidden md:rounded-lg md:px-4",
|
||||
"bg-default text-emphasis shadow-outline-gray-rested fixed bottom-0 left-0 z-40 flex w-full items-center space-x-1 overflow-x-auto border px-2 py-2 sm:space-x-2 md:bottom-4 md:left-1/2 md:z-auto md:w-fit md:-translate-x-1/2 md:transform md:space-x-3 md:overflow-x-hidden md:rounded-lg md:px-4",
|
||||
className
|
||||
)}
|
||||
style={{ ...style }}
|
||||
|
||||
@@ -47,8 +47,12 @@ function AddFilterButtonComponent<TData>(
|
||||
<div className="flex items-center space-x-2">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button ref={ref} color="secondary" data-testid="add-filter-button">
|
||||
<Icon name="sliders-horizontal" className="mr-2 h-4 w-4" />
|
||||
<Button
|
||||
ref={ref}
|
||||
color="secondary"
|
||||
data-testid="add-filter-button"
|
||||
StartIcon="sliders-horizontal"
|
||||
className="h-full">
|
||||
{t("filter")}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useDataTable } from "@calcom/features/data-table";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Icon, Button, Tooltip } from "@calcom/ui";
|
||||
import { Button, Tooltip } from "@calcom/ui";
|
||||
|
||||
export const ClearFiltersButton = ({ exclude }: { exclude?: string[] }) => {
|
||||
const { t } = useLocale();
|
||||
@@ -13,14 +13,13 @@ export const ClearFiltersButton = ({ exclude }: { exclude?: string[] }) => {
|
||||
return (
|
||||
<Tooltip content={t("clear_filters")}>
|
||||
<Button
|
||||
color="minimal"
|
||||
data-testid="clear-filters-button"
|
||||
variant="icon"
|
||||
color="secondary"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="min-w-24 h-[38px] border-0"
|
||||
className="min-w-24"
|
||||
StartIcon="x"
|
||||
onClick={() => clearAll(exclude)}>
|
||||
<Icon name="x" className="mr-1 h-4 w-4" />
|
||||
{t("clear")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -147,7 +147,7 @@ export const DateRangeFilter = ({ column }: DateRangeFilterProps) => {
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button color="secondary" className="items-center capitalize">
|
||||
<Button color="secondary" className="items-center capitalize" EndIcon="chevron-down">
|
||||
{!isCustomPreset && <span>{t(selectedPreset.labelKey, selectedPreset.i18nOptions)}</span>}
|
||||
{isCustomPreset &&
|
||||
(endDate ? (
|
||||
@@ -157,7 +157,6 @@ export const DateRangeFilter = ({ column }: DateRangeFilterProps) => {
|
||||
) : (
|
||||
<span>{format(startDate.toDate(), "LLL dd, y")} - End</span>
|
||||
))}
|
||||
<Icon name="chevron-down" className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="flex w-fit p-0" align="end">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import startCase from "lodash/startCase";
|
||||
|
||||
import type { IconName } from "@calcom/ui";
|
||||
import { Button, Icon, Popover, PopoverContent, PopoverTrigger, Badge } from "@calcom/ui";
|
||||
import { Button, Popover, PopoverContent, PopoverTrigger, Badge } from "@calcom/ui";
|
||||
|
||||
import { useFilterValue } from "../../hooks";
|
||||
import { type FilterableColumn, type FilterValue, ZFilterValue, ColumnFilterType } from "../../lib/types";
|
||||
@@ -137,11 +137,11 @@ export function FilterPopover({ column }: FilterPopoverProps) {
|
||||
<Button
|
||||
color="secondary"
|
||||
className="items-center"
|
||||
StartIcon={icon}
|
||||
EndIcon="chevron-down"
|
||||
data-testid={`filter-popover-trigger-${column.id}`}>
|
||||
<Icon name={icon} className="mr-2 h-4 w-4" />
|
||||
<span>{startCase(column.title)}</span>
|
||||
<AppliedFilterValue column={column} filterValue={filterValue} />
|
||||
<Icon name="chevron-down" className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-0" align="start">
|
||||
|
||||
@@ -31,7 +31,7 @@ export const LockedSwitch = (
|
||||
setUnlockedFields(fieldName, !enabled || undefined);
|
||||
}}
|
||||
checked={fieldState[fieldName]}
|
||||
small={!options.simple}
|
||||
size="sm"
|
||||
/>
|
||||
) : null;
|
||||
};
|
||||
@@ -58,7 +58,7 @@ export const LockedIndicator = (
|
||||
<Badge
|
||||
variant={isLocked ? "gray" : "green"}
|
||||
className={classNames(
|
||||
"ml-2 transform justify-between gap-1.5 p-1",
|
||||
"ml-2 transform justify-between p-1",
|
||||
isManagedEventType && !options.simple && "w-28"
|
||||
)}>
|
||||
{!options.simple && (
|
||||
@@ -78,7 +78,7 @@ export const LockedIndicator = (
|
||||
setUnlockedFields(fieldName, !enabled || undefined);
|
||||
}}
|
||||
checked={isLocked}
|
||||
small={!options.simple}
|
||||
size="sm"
|
||||
/>
|
||||
)}
|
||||
</Badge>
|
||||
|
||||
@@ -236,7 +236,8 @@ const AddNewTeamsFormChild = ({
|
||||
disabled={createTeamsMutation.isPending}
|
||||
onClick={handleCounterIncrease}
|
||||
aria-label={t("add_a_team")}
|
||||
className="my-1">
|
||||
className="my-1"
|
||||
data-testId="add_a_team">
|
||||
{t("add_a_team")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -120,7 +120,6 @@ export const CreateANewTeamForm = (props: CreateANewTeamFormProps) => {
|
||||
rules={{ required: t("team_url_required") }}
|
||||
render={({ field: { value } }) => (
|
||||
<TextField
|
||||
className="mt-2"
|
||||
name="slug"
|
||||
placeholder="acme"
|
||||
label={t("team_url")}
|
||||
|
||||
@@ -31,7 +31,9 @@ export default function DeleteBulkTeamMembers({ users, onRemove, isOrg, teamId }
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<DataTableSelectionBar.Button icon="ban">{t("Delete")}</DataTableSelectionBar.Button>
|
||||
<DataTableSelectionBar.Button icon="ban" color="destructive">
|
||||
{t("Delete")}
|
||||
</DataTableSelectionBar.Button>
|
||||
</DialogTrigger>
|
||||
<ConfirmationDialogContent
|
||||
variety="danger"
|
||||
|
||||
@@ -94,7 +94,9 @@ export function EventTypesList({ table, teamId }: Props) {
|
||||
<>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<DataTableSelectionBar.Button icon="link">{t("add_to_event_type")}</DataTableSelectionBar.Button>
|
||||
<DataTableSelectionBar.Button icon="link" color="secondary">
|
||||
{t("add_to_event_type")}
|
||||
</DataTableSelectionBar.Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[200px] p-0 shadow-md" align="start" sideOffset={12}>
|
||||
<Command>
|
||||
|
||||
@@ -433,8 +433,8 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps)
|
||||
}
|
||||
}}
|
||||
className={classNames("gap-2", props.token && "opacity-50")}
|
||||
StartIcon="link"
|
||||
data-testid="copy-invite-link-button">
|
||||
<Icon name="link" className="text-default h-4 w-4" aria-hidden="true" />
|
||||
<span className="hidden sm:inline">{t("copy_invite_link")}</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -674,7 +674,6 @@ function MemberListContent(props: Props) {
|
||||
type="button"
|
||||
color="primary"
|
||||
StartIcon="plus"
|
||||
className="rounded-md"
|
||||
onClick={() => props.setShowMemberInvitationModal(true)}
|
||||
data-testid="new-member-button">
|
||||
{t("add")}
|
||||
@@ -698,6 +697,7 @@ function MemberListContent(props: Props) {
|
||||
</p>
|
||||
{numberOfSelectedRows >= 2 && (
|
||||
<DataTableSelectionBar.Button
|
||||
color="secondary"
|
||||
onClick={() => setDynamicLinkVisible(!dynamicLinkVisible)}
|
||||
icon="handshake">
|
||||
{t("group_meeting")}
|
||||
|
||||
@@ -91,8 +91,8 @@ const ProfileView = ({ team }: ProfileViewProps) => {
|
||||
}}>
|
||||
<div className="border-subtle mt-6 flex items-center rounded-t-xl border p-6 text-sm">
|
||||
<div>
|
||||
<p className="font-semibold">{t("theme")}</p>
|
||||
<p className="text-default">{t("theme_applies_note")}</p>
|
||||
<p className="mt-0.5 text-base font-semibold leading-none">{t("theme")}</p>
|
||||
<p className="text-default text-sm leading-normal">{t("theme_applies_note")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-subtle flex flex-col justify-between border-x px-6 py-8 sm:flex-row">
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
||||
import { TeamsListing } from "../components";
|
||||
|
||||
const TeamListingView = () => {
|
||||
const { t } = useLocale();
|
||||
return <TeamsListing />;
|
||||
};
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ export const TimeTimeUnitInput = (props: Props) => {
|
||||
label=""
|
||||
disabled={props.disabled}
|
||||
defaultValue={form.getValues("time") ?? props.defaultTime ?? 24}
|
||||
className="-mt-2 rounded-r-none text-sm focus:ring-0"
|
||||
className="rounded-r-none text-sm focus:ring-0"
|
||||
{...form.register("time", { valueAsNumber: true })}
|
||||
addOnSuffix={
|
||||
<TimeUnitAddonSuffix
|
||||
|
||||
@@ -239,7 +239,7 @@ export default function WorkflowListPage({ workflows }: Props) {
|
||||
workflow?.team.logo,
|
||||
workflow.team?.name as string
|
||||
)}
|
||||
size="xxs"
|
||||
size="xs"
|
||||
className="mt-[3px] inline-flex justify-center"
|
||||
/>
|
||||
<div>{workflow.team.name}</div>
|
||||
|
||||
@@ -917,7 +917,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
|
||||
emailSubjectFormRef?.(e);
|
||||
refEmailSubject.current = e;
|
||||
}}
|
||||
rows={1}
|
||||
rows={2}
|
||||
disabled={props.readOnly || !hasActiveTeamPlan}
|
||||
className="my-0 focus:ring-transparent"
|
||||
required
|
||||
|
||||
@@ -21,6 +21,7 @@ export const tabs = [
|
||||
href: "embedTabName=embed-code",
|
||||
icon: "code" as const,
|
||||
type: "code",
|
||||
"data-testid": "HTML",
|
||||
Component: forwardRef<
|
||||
HTMLTextAreaElement | HTMLIFrameElement | null,
|
||||
{ embedType: EmbedType; calLink: string; previewState: PreviewState; namespace: string }
|
||||
@@ -75,6 +76,7 @@ export const tabs = [
|
||||
{
|
||||
name: "React",
|
||||
href: "embedTabName=embed-react",
|
||||
"data-testid": "react",
|
||||
icon: "code" as const,
|
||||
type: "code",
|
||||
Component: forwardRef<
|
||||
@@ -126,6 +128,7 @@ export const tabs = [
|
||||
href: "embedTabName=embed-preview",
|
||||
icon: "trello" as const,
|
||||
type: "iframe",
|
||||
"data-testid": "Preview",
|
||||
Component: forwardRef<
|
||||
HTMLIFrameElement | HTMLTextAreaElement | null,
|
||||
{ calLink: string; embedType: EmbedType; previewState: PreviewState; namespace: string }
|
||||
|
||||
@@ -2,20 +2,20 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
||||
const SkeletonEventType = () => {
|
||||
return (
|
||||
<div className="dark:bg-darkgray-100 bg-default h-24 w-full">
|
||||
<div className="bg-default h-24 w-full">
|
||||
<div className="p-5">
|
||||
<div className="flex space-x-2 rtl:space-x-reverse">
|
||||
<div className="dark:bg-darkgray-400 h-2 w-1/6 rounded-md bg-neutral-200" />
|
||||
<div className="dark:bg-darkgray-400 h-2 w-1/6 rounded-md bg-neutral-200" />
|
||||
<div className="bg-subtle h-2 w-1/6 rounded-md" />
|
||||
<div className="bg-subtle h-2 w-1/6 rounded-md" />
|
||||
</div>
|
||||
<div className="flex space-x-2 py-2 rtl:space-x-reverse">
|
||||
<div className="dark:bg-darkgray-400 h-2 w-1/12 rounded-md bg-neutral-200" />
|
||||
<div className="dark:bg-darkgray-400 h-2 w-1/6 rounded-md bg-neutral-200" />
|
||||
<div className="dark:bg-darkgray-400 h-2 w-1/12 rounded-md bg-neutral-200" />
|
||||
<div className="bg-subtle h-2 w-1/12 rounded-md" />
|
||||
<div className="bg-subtle h-2 w-1/6 rounded-md" />
|
||||
<div className="bg-subtle h-2 w-1/12 rounded-md" />
|
||||
</div>
|
||||
<div className="flex space-x-2 py-1 rtl:space-x-reverse">
|
||||
<div className="dark:bg-darkgray-200 h-6 w-1/6 rounded-md bg-neutral-300" />
|
||||
<div className="dark:bg-darkgray-200 h-6 w-1/6 rounded-md bg-neutral-300" />
|
||||
<div className="bg-subtle h-6 w-1/6 rounded-md" />
|
||||
<div className="bg-subtle h-6 w-1/6 rounded-md" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,7 +26,7 @@ function EmptyPage({ name }: { name: string }) {
|
||||
const { t } = useLocale();
|
||||
return (
|
||||
<div className="relative text-center">
|
||||
<div className="dark:divide-darkgray-100 flex flex-col divide-y-2 blur-[3px]">
|
||||
<div className="flex flex-col divide-y-2 blur-[3px]">
|
||||
<SkeletonEventType />
|
||||
<SkeletonEventType />
|
||||
<SkeletonEventType />
|
||||
|
||||
@@ -55,7 +55,6 @@ export const MultiplePrivateLinksController = ({
|
||||
color="minimal"
|
||||
size="sm"
|
||||
type="button"
|
||||
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
|
||||
aria-label="copy link"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(singleUseURL);
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ export default function RequiresConfirmationController({
|
||||
);
|
||||
}}
|
||||
className={classNames(
|
||||
"border-default !m-0 block w-16 rounded-r-none border-r-0 text-sm [appearance:textfield] focus:z-10 focus:border-r",
|
||||
"border-default h-8! !m-0 block w-16 rounded-r-none border-r-0 text-sm [appearance:textfield] focus:z-10 focus:border-r",
|
||||
customClassNames?.conditionalConfirmationRadio?.timeInput
|
||||
)}
|
||||
defaultValue={metadata?.requiresConfirmationThreshold?.time || 30}
|
||||
|
||||
@@ -92,7 +92,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
|
||||
<div className="before:border-0">
|
||||
{(isManagedEventType || isChildrenManagedEventType) && (
|
||||
<Alert
|
||||
severity={appsDisableProps.isLocked ? "neutral" : "green"}
|
||||
severity={appsDisableProps.isLocked ? "neutral" : "info"}
|
||||
className="mb-2"
|
||||
title={
|
||||
<Trans i18nKey={`${lockedText}_${isManagedEventType ? "for_members" : "by_team_admins"}`}>
|
||||
|
||||
@@ -576,7 +576,7 @@ const UseCommonScheduleSettingsToggle = ({
|
||||
const { t } = useLocale();
|
||||
const { useHostSchedulesForTeamEvent, toggleScheduleState } = useCommonScheduleState(eventType.schedule);
|
||||
return (
|
||||
<>
|
||||
<div className="border-subtle space-y-6 rounded-lg border p-6">
|
||||
<SettingsToggle
|
||||
checked={!useHostSchedulesForTeamEvent}
|
||||
onCheckedChange={toggleScheduleState}
|
||||
@@ -598,7 +598,7 @@ const UseCommonScheduleSettingsToggle = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -327,7 +327,7 @@ const MinimumBookingNoticeInput = React.forwardRef<
|
||||
label={t("minimum_booking_notice")}
|
||||
type="number"
|
||||
placeholder="0"
|
||||
className={classNames("mb-0 h-9 rounded-[4px] ltr:mr-2 rtl:ml-2", customClassNames?.input)}
|
||||
className={classNames("mb-0 h-9 ltr:mr-2 rtl:ml-2", customClassNames?.input)}
|
||||
min={0}
|
||||
/>
|
||||
<input type="hidden" ref={ref} {...passThroughProps} />
|
||||
|
||||
@@ -115,7 +115,7 @@ export const EventWebhooksTab = ({ eventType }: Pick<EventTypeSetupProps, "event
|
||||
<>
|
||||
{(isManagedEventType || isChildrenManagedEventType) && (
|
||||
<Alert
|
||||
severity={webhooksDisableProps.isLocked ? "neutral" : "green"}
|
||||
severity={webhooksDisableProps.isLocked ? "neutral" : "info"}
|
||||
className="mb-2"
|
||||
title={
|
||||
<Trans
|
||||
|
||||
@@ -132,9 +132,8 @@ const WorkflowListItem = (props: ItemProps) => {
|
||||
{!workflow.readOnly && (
|
||||
<div className="flex-none">
|
||||
<Link href={`/workflows/${workflow.id}`} passHref={true} target="_blank">
|
||||
<Button type="button" color="minimal" className="mr-4">
|
||||
<Button type="button" color="minimal" className="mr-4" EndIcon="external-link">
|
||||
<div className="hidden ltr:mr-2 rtl:ml-2 sm:block">{t("edit")}</div>
|
||||
<Icon name="external-link" className="text-default -mt-[2px] h-4 w-4 stroke-2" />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
@@ -244,7 +243,7 @@ function EventWorkflowsTab(props: Props) {
|
||||
<>
|
||||
{(isManagedEventType || isChildrenManagedEventType) && (
|
||||
<Alert
|
||||
severity={workflowsDisableProps.isLocked ? "neutral" : "green"}
|
||||
severity={workflowsDisableProps.isLocked ? "neutral" : "info"}
|
||||
className="mb-2"
|
||||
title={
|
||||
<Trans i18nKey={`${lockedText}_${isManagedEventType ? "for_members" : "by_team_admins"}`}>
|
||||
|
||||
@@ -236,8 +236,6 @@ export const Components: Record<FieldType, Component> = {
|
||||
const placeholder = props.placeholder;
|
||||
const { t } = useLocale();
|
||||
value = value || [];
|
||||
const inputClassName =
|
||||
"dark:placeholder:text-muted focus:border-emphasis border-subtle block w-full rounded-md border-default text-sm focus:ring-black disabled:bg-emphasis disabled:hover:cursor-not-allowed dark:selection:bg-green-500 disabled:dark:text-subtle bg-default";
|
||||
return (
|
||||
<>
|
||||
{value.length ? (
|
||||
@@ -252,7 +250,6 @@ export const Components: Record<FieldType, Component> = {
|
||||
id={`${props.name}.${index}`}
|
||||
disabled={readOnly}
|
||||
value={value[index]}
|
||||
className={inputClassName}
|
||||
onChange={(e) => {
|
||||
value[index] = e.target.value.toLowerCase();
|
||||
setValue(value);
|
||||
@@ -353,7 +350,7 @@ export const Components: Record<FieldType, Component> = {
|
||||
}
|
||||
setValue(newValue);
|
||||
}}
|
||||
className="border-default dark:border-default hover:bg-subtle checked:hover:bg-brand-default checked:bg-brand-default dark:checked:bg-brand-default dark:bg-darkgray-100 dark:hover:bg-subtle dark:checked:hover:bg-brand-default h-4 w-4 cursor-pointer rounded transition ltr:mr-2 rtl:ml-2"
|
||||
className="border-default dark:border-default hover:bg-subtle checked:hover:bg-brand-default checked:bg-brand-default dark:checked:bg-brand-default dark:hover:bg-subtle dark:checked:hover:bg-brand-default h-4 w-4 cursor-pointer rounded transition ltr:mr-2 rtl:ml-2"
|
||||
value={option.value}
|
||||
checked={value.includes(option.value)}
|
||||
/>
|
||||
|
||||
@@ -248,7 +248,6 @@ export const FormBuilder = function FormBuilder({
|
||||
onCheckedChange={(checked) => {
|
||||
update(index, { ...field, hidden: !checked });
|
||||
}}
|
||||
classNames={{ container: "p-2 hover:bg-subtle rounded transition" }}
|
||||
tooltip={t("show_on_booking_page")}
|
||||
/>
|
||||
)}
|
||||
@@ -487,12 +486,9 @@ function FieldEditDialog({
|
||||
|
||||
return (
|
||||
<Dialog open={dialog.isOpen} onOpenChange={onOpenChange} modal={false}>
|
||||
<DialogContent
|
||||
className="max-h-none p-0"
|
||||
data-testid="edit-field-dialog"
|
||||
forceOverlayWhenNoModal={true}>
|
||||
<DialogContent className="max-h-none" data-testid="edit-field-dialog" forceOverlayWhenNoModal={true}>
|
||||
<Form id="form-builder" form={fieldForm} handleSubmit={handleSubmit}>
|
||||
<div className="h-auto max-h-[85vh] overflow-auto px-8 pb-7 pt-8">
|
||||
<div className="h-auto max-h-[85vh] overflow-auto">
|
||||
<DialogHeader title={t("add_a_booking_question")} subtitle={t("booking_questions_description")} />
|
||||
<SelectField
|
||||
defaultValue={fieldTypesConfigMap.text}
|
||||
@@ -648,7 +644,7 @@ function FieldEditDialog({
|
||||
})()}
|
||||
</div>
|
||||
|
||||
<DialogFooter className="relative rounded px-8" showDivider>
|
||||
<DialogFooter className="relative">
|
||||
<DialogClose color="secondary">{t("cancel")}</DialogClose>
|
||||
<Button data-testid="field-add-save" type="submit">
|
||||
{isFieldEditMode ? t("save") : t("add")}
|
||||
@@ -805,7 +801,7 @@ function VariantFields({
|
||||
onCheckedChange={(checked) => {
|
||||
fieldForm.setValue("variant", checked ? otherVariant : defaultVariant);
|
||||
}}
|
||||
classNames={{ container: "p-2 mt-2 sm:hover:bg-muted rounded transition" }}
|
||||
classNames={{ container: "mt-2" }}
|
||||
tooltip={t("Toggle Variant")}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -249,6 +249,7 @@ export const ComponentForField = ({
|
||||
`Value ${value} is not valid for type ${componentConfig.propsType} for field ${field.name}`
|
||||
);
|
||||
}
|
||||
|
||||
if (componentConfig.propsType === "text") {
|
||||
return (
|
||||
<WithLabel field={field} htmlFor={field.name} readOnly={readOnly} noLabel={noLabel}>
|
||||
|
||||
@@ -44,7 +44,7 @@ const Download = () => {
|
||||
EndIcon="file-down"
|
||||
color="secondary"
|
||||
{...(isPending && { loading: isPending })}
|
||||
className="self-end sm:self-baseline">
|
||||
className="h-full self-end sm:self-baseline">
|
||||
{t("download")}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
@@ -73,7 +73,7 @@ export const OrgTeamsFilter = () => {
|
||||
<Avatar
|
||||
alt={`${placeholder} logo`}
|
||||
imageSrc={getPlaceholderAvatar(imageUrl, placeholder)}
|
||||
size="xsm"
|
||||
size="xs"
|
||||
className="mr-2"
|
||||
/>
|
||||
) : null;
|
||||
|
||||
@@ -43,8 +43,8 @@ export function ScheduleListItem({
|
||||
|
||||
return (
|
||||
<li key={schedule.id}>
|
||||
<div className="hover:bg-muted flex items-center justify-between py-5 transition ltr:pl-4 rtl:pr-4 sm:ltr:pl-0 sm:rtl:pr-0">
|
||||
<div className="group flex w-full items-center justify-between sm:px-6">
|
||||
<div className="hover:bg-muted flex items-center justify-between px-3 py-5 transition sm:px-4">
|
||||
<div className="group flex w-full items-center justify-between ">
|
||||
<Link
|
||||
href={`/availability/${schedule.id}`}
|
||||
className="flex-grow truncate text-sm"
|
||||
@@ -87,7 +87,6 @@ export function ScheduleListItem({
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
data-testid="schedule-more"
|
||||
className="mx-5"
|
||||
type="button"
|
||||
variant="icon"
|
||||
color="secondary"
|
||||
@@ -96,8 +95,8 @@ export function ScheduleListItem({
|
||||
</DropdownMenuTrigger>
|
||||
{!isPending && data && (
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem className="min-w-40 focus:ring-muted">
|
||||
{!schedule.isDefault && (
|
||||
{!schedule.isDefault && (
|
||||
<DropdownMenuItem className="min-w-40 focus:ring-muted">
|
||||
<DropdownItem
|
||||
type="button"
|
||||
StartIcon="star"
|
||||
@@ -109,8 +108,8 @@ export function ScheduleListItem({
|
||||
}}>
|
||||
{t("set_as_default")}
|
||||
</DropdownItem>
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<DropdownItem
|
||||
type="button"
|
||||
|
||||
@@ -4,11 +4,18 @@ import { useSearchParams } from "next/navigation";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Button, Icon } from "@calcom/ui";
|
||||
import type { ButtonProps } from "@calcom/ui";
|
||||
import { Button } from "@calcom/ui";
|
||||
|
||||
import { CreateOrEditOutOfOfficeEntryModal } from "./CreateOrEditOutOfOfficeModal";
|
||||
|
||||
const CreateNewOutOfOfficeEntry = () => {
|
||||
const CreateNewOutOfOfficeEntry = ({
|
||||
size,
|
||||
...rest
|
||||
}: {
|
||||
size?: ButtonProps["size"];
|
||||
"data-testid"?: string;
|
||||
}) => {
|
||||
const { t } = useLocale();
|
||||
|
||||
const params = useSearchParams();
|
||||
@@ -25,10 +32,12 @@ const CreateNewOutOfOfficeEntry = () => {
|
||||
<>
|
||||
<Button
|
||||
color="primary"
|
||||
size={size ?? "base"}
|
||||
className="flex items-center justify-between px-4"
|
||||
StartIcon="plus"
|
||||
onClick={() => setOpenModal(true)}
|
||||
data-testid="add_entry_ooo">
|
||||
<Icon name="plus" size={16} /> {t("add")}
|
||||
data-testid={rest["data-testid"]}>
|
||||
{t("add")}
|
||||
</Button>
|
||||
{openModal && (
|
||||
<CreateOrEditOutOfOfficeEntryModal
|
||||
|
||||
@@ -140,6 +140,7 @@ export const CreateOrEditOutOfOfficeEntryModal = ({
|
||||
}
|
||||
}}>
|
||||
<DialogContent
|
||||
enableOverflow
|
||||
onOpenAutoFocus={(event) => {
|
||||
event.preventDefault();
|
||||
}}>
|
||||
@@ -245,7 +246,7 @@ export const CreateOrEditOutOfOfficeEntryModal = ({
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
value={searchText}
|
||||
/>
|
||||
<div className="scroll-bar flex h-[150px] flex-col gap-0.5 overflow-y-scroll rounded-md border p-1">
|
||||
<div className="scroll-bar bg-default mt-2 flex h-[150px] flex-col gap-0.5 overflow-y-scroll rounded-[10px] border p-1">
|
||||
{memberListOptions.map((member) => (
|
||||
<label
|
||||
key={member.value}
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
Tooltip,
|
||||
} from "@calcom/ui";
|
||||
|
||||
import CreateNewOutOfOfficeEntryButton from "./CreateNewOutOfOfficeEntryButton";
|
||||
import { CreateOrEditOutOfOfficeEntryModal } from "./CreateOrEditOutOfOfficeModal";
|
||||
import type { BookingRedirectForm } from "./CreateOrEditOutOfOfficeModal";
|
||||
|
||||
@@ -53,6 +54,7 @@ export const OutOfOfficeEntriesList = () => {
|
||||
className="mt-6"
|
||||
headline={t("ooo_empty_title")}
|
||||
description={t("ooo_empty_description")}
|
||||
buttonRaw={<CreateNewOutOfOfficeEntryButton size="sm" />}
|
||||
customIcon={
|
||||
<div className="mt-4 h-[102px]">
|
||||
<div className="flex h-full flex-col items-center justify-center p-2 md:mt-0 md:p-0">
|
||||
@@ -63,7 +65,7 @@ export const OutOfOfficeEntriesList = () => {
|
||||
<div className="dark:bg-darkgray-50 absolute -top-3 left-3 -z-10 h-[70px] w-[70px] rotate-[24deg] rounded-3xl border-2 border-[#e5e7eb] p-8 opacity-60 dark:opacity-90">
|
||||
<div className="w-12" />
|
||||
</div>
|
||||
<div className="dark:bg-darkgray-50 relative z-0 flex h-[70px] w-[70px] items-center justify-center rounded-3xl border-2 border-[#e5e7eb] bg-white">
|
||||
<div className="dark:bg-darkgray-50 text-inverted relative z-0 flex h-[70px] w-[70px] items-center justify-center rounded-3xl border-2 border-[#e5e7eb] bg-white">
|
||||
<Icon name="clock" size={28} />
|
||||
<div className="dark:bg-darkgray-50 absolute right-4 top-5 h-[12px] w-[12px] rotate-[56deg] bg-white text-lg font-bold" />
|
||||
<span className="absolute right-4 top-3 font-sans text-sm font-extrabold">z</span>
|
||||
@@ -121,7 +123,7 @@ export const OutOfOfficeEntriesList = () => {
|
||||
<Button
|
||||
className="self-center rounded-lg border"
|
||||
type="button"
|
||||
color="minimal"
|
||||
color="secondary"
|
||||
variant="icon"
|
||||
data-testid={`ooo-edit-${item.toUser?.username || "n-a"}`}
|
||||
StartIcon="pencil"
|
||||
@@ -146,7 +148,7 @@ export const OutOfOfficeEntriesList = () => {
|
||||
<Button
|
||||
className="self-center rounded-lg border"
|
||||
type="button"
|
||||
color="minimal"
|
||||
color="destructive"
|
||||
variant="icon"
|
||||
disabled={deleteOutOfOfficeEntryMutation.isPending}
|
||||
StartIcon="trash-2"
|
||||
|
||||
@@ -39,7 +39,9 @@ export function DeleteBulkUsers({ users, onRemove }: Props) {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<DataTableSelectionBar.Button icon="ban">{t("Delete")}</DataTableSelectionBar.Button>
|
||||
<DataTableSelectionBar.Button icon="ban" color="destructive">
|
||||
{t("Delete")}
|
||||
</DataTableSelectionBar.Button>
|
||||
</DialogTrigger>
|
||||
<ConfirmationDialogContent
|
||||
variety="danger"
|
||||
|
||||
@@ -35,11 +35,17 @@ export function DynamicLink<T extends { username: string | null }>({
|
||||
<div className="items-center truncate p-2 md:max-w-[300px]">
|
||||
<p className="text-center md:text-left">{domainWithoutHttps}</p>
|
||||
</div>
|
||||
<div className="ml-auto flex items-center justify-center md:justify-start">
|
||||
<Button StartIcon="copy" size="sm" onClick={() => copyToClipboard(dynamicLinkOfSelectedUsers)}>
|
||||
{!isCopied ? t("copy") : t("copied")}
|
||||
<div className="ml-auto flex items-center justify-center gap-1 md:justify-start">
|
||||
<Button
|
||||
variant="icon"
|
||||
StartIcon={isCopied ? "check" : "copy"}
|
||||
size="sm"
|
||||
onClick={() => copyToClipboard(dynamicLinkOfSelectedUsers)}
|
||||
color="secondary">
|
||||
<span className="sr-only">{!isCopied ? t("copy") : t("copied")}</span>
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
EndIcon="external-link"
|
||||
size="sm"
|
||||
href={dynamicLinkOfSelectedUsers}
|
||||
|
||||
@@ -98,7 +98,9 @@ export function EventTypesList({ table, orgTeams }: Props) {
|
||||
<>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<DataTableSelectionBar.Button icon="link">{t("add_to_event_type")}</DataTableSelectionBar.Button>
|
||||
<DataTableSelectionBar.Button icon="link" color="secondary">
|
||||
{t("add_to_event_type")}
|
||||
</DataTableSelectionBar.Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[200px] p-0 shadow-md" align="start" sideOffset={12}>
|
||||
<Command>
|
||||
|
||||
@@ -332,7 +332,9 @@ function MassAssignAttributesBulkActionComponent({ table, filters }: Props) {
|
||||
}
|
||||
}}>
|
||||
<PopoverTrigger asChild>
|
||||
<DataTableSelectionBar.Button icon="tags">{t("add_attributes")}</DataTableSelectionBar.Button>
|
||||
<DataTableSelectionBar.Button icon="tags" color="secondary">
|
||||
{t("add_attributes")}
|
||||
</DataTableSelectionBar.Button>
|
||||
</PopoverTrigger>
|
||||
{/* We dont really use shadows much - but its needed here */}
|
||||
<PopoverContent className="p-0 shadow-md" align="start" sideOffset={12}>
|
||||
|
||||
@@ -87,7 +87,9 @@ export function TeamListBulkAction({ table }: Props) {
|
||||
<>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<DataTableSelectionBar.Button icon="users">{t("add_to_team")}</DataTableSelectionBar.Button>
|
||||
<DataTableSelectionBar.Button icon="users" color="secondary">
|
||||
{t("add_to_team")}
|
||||
</DataTableSelectionBar.Button>
|
||||
</PopoverTrigger>
|
||||
{/* We dont really use shadows much - but its needed here */}
|
||||
<PopoverContent className="w-[200px] p-0 shadow-md" align="start" sideOffset={12}>
|
||||
|
||||
@@ -579,7 +579,6 @@ function UserListTableContent() {
|
||||
type="button"
|
||||
color="primary"
|
||||
StartIcon="plus"
|
||||
className="rounded-md"
|
||||
onClick={() =>
|
||||
dispatch({
|
||||
type: "INVITE_MEMBER",
|
||||
@@ -604,8 +603,12 @@ function UserListTableContent() {
|
||||
</DataTableSelectionBar.Root>
|
||||
)}
|
||||
{numberOfSelectedRows > 0 && (
|
||||
<DataTableSelectionBar.Root className="justify-center">
|
||||
<p className="text-brand-subtle px-2 text-center text-xs leading-none sm:text-sm sm:font-medium">
|
||||
<DataTableSelectionBar.Root
|
||||
className="justify-center"
|
||||
style={{
|
||||
width: "max-content",
|
||||
}}>
|
||||
<p className="text-brand-subtle shrink-0 px-2 text-center text-xs leading-none sm:text-sm sm:font-medium">
|
||||
{t("number_selected", { count: numberOfSelectedRows })}
|
||||
</p>
|
||||
{!isPlatformUser ? (
|
||||
@@ -613,6 +616,7 @@ function UserListTableContent() {
|
||||
<TeamListBulkAction table={table} />
|
||||
{numberOfSelectedRows >= 2 && (
|
||||
<DataTableSelectionBar.Button
|
||||
color="secondary"
|
||||
onClick={() => setDynamicLinkVisible(!dynamicLinkVisible)}
|
||||
icon="handshake">
|
||||
{t("group_meeting")}
|
||||
|
||||
@@ -146,7 +146,7 @@ const WebhookForm = (props: {
|
||||
<Form
|
||||
form={formMethods}
|
||||
handleSubmit={(values) => props.onSubmit({ ...values, changeSecret, newSecret })}>
|
||||
<div className="border-subtle border-x p-6">
|
||||
<div className="border-subtle rounded-t-lg border p-6">
|
||||
<Controller
|
||||
name="subscriberUrl"
|
||||
control={formMethods.control}
|
||||
@@ -199,6 +199,7 @@ const WebhookForm = (props: {
|
||||
<>{t("event_triggers")}</>
|
||||
</Label>
|
||||
<Select
|
||||
grow
|
||||
options={translatedTriggerOptions}
|
||||
isMulti
|
||||
value={selectValue}
|
||||
@@ -353,7 +354,7 @@ const WebhookForm = (props: {
|
||||
</Button>
|
||||
</SectionBottomActions>
|
||||
|
||||
<div className="mt-6 rounded-md">
|
||||
<div className="mb-4 mt-6 rounded-md">
|
||||
<WebhookTestDisclosure />
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -65,18 +65,19 @@ export const usePlatformTabsNavigations = ({ formMethods, eventType, team, tabs
|
||||
|
||||
if (!requirePayment && tabs.includes("recurring")) {
|
||||
navigation.splice(3, 0, {
|
||||
name: "recurring",
|
||||
name: t("recurring"),
|
||||
onClick: () => setCurrentTab("recurring"),
|
||||
isActive: currentTab === "recurring",
|
||||
href: `${url}?tabName=recurring`,
|
||||
icon: "repeat",
|
||||
info: `recurring_event_tab_description`,
|
||||
info: t(`recurring_event_tab_description`),
|
||||
"data-testid": "recurring",
|
||||
});
|
||||
}
|
||||
|
||||
tabs.includes("availability") &&
|
||||
navigation.splice(1, 0, {
|
||||
name: "availability",
|
||||
name: t("availability"),
|
||||
onClick: () => setCurrentTab("availability"),
|
||||
isActive: currentTab === "availability",
|
||||
href: `${url}?tabName=availability`,
|
||||
@@ -93,12 +94,13 @@ export const usePlatformTabsNavigations = ({ formMethods, eventType, team, tabs
|
||||
}`
|
||||
: formMethods.getValues("scheduleName") ?? `default_schedule_name`
|
||||
: formMethods.getValues("scheduleName") ?? `default_schedule_name`,
|
||||
"data-testid": "availability",
|
||||
});
|
||||
|
||||
// If there is a team put this navigation item within the tabs
|
||||
if (team && tabs.includes("team")) {
|
||||
navigation.splice(2, 0, {
|
||||
name: "assignment",
|
||||
name: t("assignment"),
|
||||
onClick: () => setCurrentTab("team"),
|
||||
isActive: currentTab === "team",
|
||||
href: `${url}?tabName=team`,
|
||||
@@ -106,6 +108,7 @@ export const usePlatformTabsNavigations = ({ formMethods, eventType, team, tabs
|
||||
info: `${t(watchSchedulingType?.toLowerCase() ?? "")}${
|
||||
isManagedEventType ? ` - ${t("number_member", { count: watchChildrenCount || 0 })}` : ""
|
||||
}`,
|
||||
"data-testid": "assignment",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -146,40 +149,44 @@ function getNavigation({ length, multipleDuration, t, tabs, url, onClick, curren
|
||||
const tabsNavigation: VerticalTabItemProps[] = [];
|
||||
tabs.includes("setup") &&
|
||||
tabsNavigation.push({
|
||||
name: "event_setup_tab_title",
|
||||
name: t("event_setup_tab_title"),
|
||||
onClick: () => onClick("setup"),
|
||||
isActive: currentTab === "setup",
|
||||
href: `${url}?tabName=setup`,
|
||||
icon: "link",
|
||||
info: `${duration} ${t("minute_timeUnit")}`, // TODO: Get this from props
|
||||
"data-testid": `event_setup_tab_title`,
|
||||
});
|
||||
tabs.includes("limits") &&
|
||||
tabsNavigation.push({
|
||||
name: "event_limit_tab_title",
|
||||
name: t("event_limit_tab_title"),
|
||||
onClick: () => onClick("limits"),
|
||||
isActive: currentTab === "limits",
|
||||
href: `${url}?tabName=limits`,
|
||||
icon: "clock",
|
||||
info: `event_limit_tab_description`,
|
||||
info: t(`event_limit_tab_description`),
|
||||
"data-testid": "event_limit_tab_title",
|
||||
});
|
||||
|
||||
tabs.includes("advanced") &&
|
||||
tabsNavigation.push({
|
||||
name: "event_advanced_tab_title",
|
||||
name: t("event_advanced_tab_title"),
|
||||
onClick: () => onClick("advanced"),
|
||||
isActive: currentTab === "advanced",
|
||||
href: `${url}?tabName=advanced`,
|
||||
icon: "sliders-vertical",
|
||||
info: `event_advanced_tab_description`,
|
||||
info: t(`event_advanced_tab_description`),
|
||||
"data-testid": "event_advanced_tab_title",
|
||||
});
|
||||
tabs.includes("payments") &&
|
||||
tabsNavigation.push({
|
||||
name: "event_payments_tab_title",
|
||||
name: t("event_payments_tab_title"),
|
||||
onClick: () => onClick("payments"),
|
||||
isActive: currentTab === "payments",
|
||||
href: `${url}?tabName=payments`,
|
||||
icon: "credit-card",
|
||||
info: `event_payments_tab_description`,
|
||||
info: t(`event_payments_tab_description`),
|
||||
"data-testid": "event_payments_tab_title",
|
||||
});
|
||||
|
||||
return tabsNavigation;
|
||||
|
||||
@@ -83,56 +83,61 @@ export const useTabsNavigations = ({
|
||||
|
||||
if (!requirePayment) {
|
||||
navigation.splice(3, 0, {
|
||||
name: "recurring",
|
||||
name: t("recurring"),
|
||||
href: `/event-types/${formMethods.getValues("id")}?tabName=recurring`,
|
||||
icon: "repeat",
|
||||
info: `recurring_event_tab_description`,
|
||||
info: t(`recurring_event_tab_description`),
|
||||
"data-testid": "recurring",
|
||||
});
|
||||
}
|
||||
navigation.splice(1, 0, {
|
||||
name: "availability",
|
||||
name: t("availability"),
|
||||
href: `/event-types/${formMethods.getValues("id")}?tabName=availability`,
|
||||
icon: "calendar",
|
||||
info:
|
||||
isManagedEventType || isChildrenManagedEventType
|
||||
? formMethods.getValues("schedule") === null
|
||||
? "members_default_schedule"
|
||||
? t("members_default_schedule")
|
||||
: isChildrenManagedEventType
|
||||
? `${
|
||||
formMethods.getValues("scheduleName")
|
||||
? `${formMethods.getValues("scheduleName")} - ${t("managed")}`
|
||||
: `default_schedule_name`
|
||||
: t(`default_schedule_name`)
|
||||
}`
|
||||
: formMethods.getValues("scheduleName") ?? `default_schedule_name`
|
||||
: formMethods.getValues("scheduleName") ?? `default_schedule_name`,
|
||||
: formMethods.getValues("scheduleName") ?? t(`default_schedule_name`)
|
||||
: formMethods.getValues("scheduleName") ?? t(`default_schedule_name`),
|
||||
"data-testid": "availability",
|
||||
});
|
||||
// If there is a team put this navigation item within the tabs
|
||||
if (team) {
|
||||
navigation.splice(2, 0, {
|
||||
name: "assignment",
|
||||
name: t("assignment"),
|
||||
href: `/event-types/${formMethods.getValues("id")}?tabName=team`,
|
||||
icon: "users",
|
||||
info: `${t(watchSchedulingType?.toLowerCase() ?? "")}${
|
||||
isManagedEventType ? ` - ${t("number_member", { count: watchChildrenCount || 0 })}` : ""
|
||||
}`,
|
||||
"data-testid": "assignment",
|
||||
});
|
||||
}
|
||||
const showInstant = !(isManagedEventType || isChildrenManagedEventType);
|
||||
if (showInstant) {
|
||||
if (team) {
|
||||
navigation.push({
|
||||
name: "instant_tab_title",
|
||||
name: t("instant_tab_title"),
|
||||
href: `/event-types/${eventType.id}?tabName=instant`,
|
||||
icon: "phone-call",
|
||||
info: `instant_event_tab_description`,
|
||||
info: t(`instant_event_tab_description`),
|
||||
"data-testid": "instant_tab_title",
|
||||
});
|
||||
}
|
||||
}
|
||||
navigation.push({
|
||||
name: "webhooks",
|
||||
name: t("webhooks"),
|
||||
href: `/event-types/${formMethods.getValues("id")}?tabName=webhooks`,
|
||||
icon: "webhook",
|
||||
info: `${activeWebhooksNumber} ${t("active")}`,
|
||||
"data-testid": "webhooks",
|
||||
});
|
||||
const hidden = true; // hidden while in alpha trial. you can access it with tabName=ai
|
||||
if (team && hidden) {
|
||||
@@ -140,7 +145,8 @@ export const useTabsNavigations = ({
|
||||
name: "Cal.ai",
|
||||
href: `/event-types/${eventType.id}?tabName=ai`,
|
||||
icon: "sparkles",
|
||||
info: "cal_ai_event_tab_description", // todo `cal_ai_event_tab_description`,
|
||||
info: t("cal_ai_event_tab_description"), // todo `cal_ai_event_tab_description`,
|
||||
"data-testid": "Cal.ai",
|
||||
});
|
||||
}
|
||||
return navigation;
|
||||
@@ -189,35 +195,40 @@ function getNavigation({
|
||||
|
||||
return [
|
||||
{
|
||||
name: "event_setup_tab_title",
|
||||
name: t("event_setup_tab_title"),
|
||||
href: `/event-types/${id}?tabName=setup`,
|
||||
icon: "link",
|
||||
info: `${duration} ${t("minute_timeUnit")}`, // TODO: Get this from props
|
||||
"data-testid": `event_setup_tab_title`,
|
||||
},
|
||||
{
|
||||
name: "event_limit_tab_title",
|
||||
name: t("event_limit_tab_title"),
|
||||
href: `/event-types/${id}?tabName=limits`,
|
||||
icon: "clock",
|
||||
info: `event_limit_tab_description`,
|
||||
info: t(`event_limit_tab_description`),
|
||||
"data-testid": "event_limit_tab_title",
|
||||
},
|
||||
{
|
||||
name: "event_advanced_tab_title",
|
||||
name: t("event_advanced_tab_title"),
|
||||
href: `/event-types/${id}?tabName=advanced`,
|
||||
icon: "sliders-vertical",
|
||||
info: `event_advanced_tab_description`,
|
||||
info: t(`event_advanced_tab_description`),
|
||||
"data-testid": "event_advanced_tab_title",
|
||||
},
|
||||
{
|
||||
name: "apps",
|
||||
name: t("apps"),
|
||||
href: `/event-types/${id}?tabName=apps`,
|
||||
icon: "grid-3x3",
|
||||
//TODO: Handle proper translation with count handling
|
||||
info: `${installedAppsNumber} apps, ${enabledAppsNumber} ${t("active")}`,
|
||||
"data-testid": "apps",
|
||||
},
|
||||
{
|
||||
name: "workflows",
|
||||
name: t("workflows"),
|
||||
href: `/event-types/${id}?tabName=workflows`,
|
||||
icon: "zap",
|
||||
info: `${enabledWorkflowsNumber} ${t("active")}`,
|
||||
"data-testid": "workflows",
|
||||
},
|
||||
] satisfies VerticalTabItemProps[];
|
||||
}
|
||||
|
||||
+15110
-6840
File diff suppressed because one or more lines are too long
@@ -1,3 +1,4 @@
|
||||
import { cva } from "class-variance-authority";
|
||||
import classNames from "classnames";
|
||||
import type { ReactNode } from "react";
|
||||
import { forwardRef } from "react";
|
||||
@@ -5,36 +6,36 @@ import { forwardRef } from "react";
|
||||
import { Icon } from "../icon";
|
||||
import type { IconName } from "../icon";
|
||||
|
||||
export const alertStyles = cva("rounded-[10px] p-3", {
|
||||
variants: {
|
||||
severity: {
|
||||
neutral: "bg-default border-subtle border-[1px] text-default",
|
||||
info: "bg-semantic-info-subtle text-semantic-info",
|
||||
warning: "bg-semantic-attention-subtle text-semantic-attention",
|
||||
error: "bg-semantic-error-subtle text-semantic-error",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
severity: "neutral",
|
||||
},
|
||||
});
|
||||
|
||||
export interface AlertProps {
|
||||
title?: ReactNode;
|
||||
// @TODO: Message should be children, more flexible?
|
||||
message?: ReactNode;
|
||||
// @TODO: Provide action buttons so style is always the same.
|
||||
actions?: ReactNode;
|
||||
className?: string;
|
||||
iconClassName?: string;
|
||||
// @TODO: Success and info shouldn't exist as per design?
|
||||
severity: "success" | "warning" | "error" | "info" | "neutral" | "green";
|
||||
severity: "warning" | "error" | "info" | "neutral";
|
||||
CustomIcon?: IconName;
|
||||
customIconColor?: string;
|
||||
}
|
||||
|
||||
export const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
|
||||
const { severity, iconClassName, CustomIcon, customIconColor } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="alert"
|
||||
ref={ref}
|
||||
className={classNames(
|
||||
"rounded-md p-3",
|
||||
props.className,
|
||||
severity === "error" && "bg-red-100 text-red-900 dark:bg-red-900 dark:text-red-200",
|
||||
severity === "warning" && "text-attention bg-attention dark:bg-orange-900 dark:text-orange-200",
|
||||
severity === "info" && "bg-blue-100 text-blue-900 dark:bg-blue-900 dark:text-blue-200",
|
||||
severity === "green" && "bg-success text-success",
|
||||
severity === "success" && "bg-inverted text-inverted",
|
||||
severity === "neutral" && "bg-subtle text-default"
|
||||
)}>
|
||||
<div data-testid="alert" ref={ref} className={alertStyles({ severity, className: props.className })}>
|
||||
<div className="relative flex md:flex-row">
|
||||
{CustomIcon ? (
|
||||
<div className="flex-shrink-0">
|
||||
@@ -42,16 +43,16 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
|
||||
name={CustomIcon}
|
||||
data-testid="custom-icon"
|
||||
aria-hidden="true"
|
||||
className={classNames(`h-5 w-5`, iconClassName, customIconColor ?? "text-default")}
|
||||
className={classNames(`h4 mr-2 w-4`, iconClassName, customIconColor ?? "text-default")}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex-shrink-0">
|
||||
<div className={classNames("mr-2 flex-shrink-0", props.title ? "" : "mt-0.5")}>
|
||||
{severity === "error" && (
|
||||
<Icon
|
||||
name="circle-x"
|
||||
data-testid="circle-x"
|
||||
className={classNames("h-5 w-5 text-red-900 dark:text-red-200", iconClassName)}
|
||||
className={classNames("h4 w-4", iconClassName)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
@@ -59,7 +60,7 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
|
||||
<Icon
|
||||
name="triangle-alert"
|
||||
data-testid="alert-triangle"
|
||||
className={classNames("text-attention h-5 w-5 dark:text-orange-200", iconClassName)}
|
||||
className={classNames("h4 w-4", iconClassName)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
@@ -67,7 +68,7 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
|
||||
<Icon
|
||||
name="info"
|
||||
data-testid="info"
|
||||
className={classNames("h-5 w-5 text-blue-900 dark:text-blue-200", iconClassName)}
|
||||
className={classNames("h4 w-4", iconClassName)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
@@ -75,26 +76,20 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
|
||||
<Icon
|
||||
name="info"
|
||||
data-testid="neutral"
|
||||
className={classNames("text-default h-5 w-5 fill-transparent", iconClassName)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
{severity === "success" && (
|
||||
<Icon
|
||||
name="circle-check"
|
||||
data-testid="circle-check"
|
||||
className={classNames("fill-muted text-default h-5 w-5", iconClassName)}
|
||||
className={classNames("text-default h4 w-4 fill-transparent", iconClassName)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-grow flex-col sm:flex-row">
|
||||
<div className="ltr:ml-3 rtl:mr-3">
|
||||
<h3 className="text-sm font-medium">{props.title}</h3>
|
||||
<div className="text-sm">{props.message}</div>
|
||||
<div className="space-y-1 ltr:ml-3 rtl:mr-3">
|
||||
{props.title && <h3 className="text-sm font-medium leading-none">{props.title}</h3>}
|
||||
{props.message && <div className="text-sm leading-5">{props.message}</div>}
|
||||
</div>
|
||||
{props.actions && <div className="ml-auto mt-2 text-sm sm:mt-0 md:relative">{props.actions}</div>}
|
||||
{props.actions && (
|
||||
<div className="ml-auto mt-auto text-sm sm:mt-0 md:relative">{props.actions}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,9 +33,4 @@ describe("Tests for Alert component", () => {
|
||||
render(<Alert severity="neutral" />);
|
||||
expect(await screen.findByTestId("neutral")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("Should render corresponding icon: success", async () => {
|
||||
render(<Alert severity="success" />);
|
||||
expect(await screen.findByTestId("circle-check")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
||||
import { Provider as TooltipPrimitiveProvider } from "@radix-ui/react-tooltip";
|
||||
import { cva } from "class-variance-authority";
|
||||
import Link from "next/link";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
@@ -11,7 +12,8 @@ type Maybe<T> = T | null | undefined;
|
||||
|
||||
export type AvatarProps = {
|
||||
className?: string;
|
||||
size?: "xxs" | "xs" | "xsm" | "sm" | "md" | "mdLg" | "lg" | "xl";
|
||||
size?: "xs" | "xsm" | "sm" | "md" | "mdLg" | "lg" | "xl";
|
||||
shape?: "circle" | "square";
|
||||
imageSrc?: Maybe<string>;
|
||||
title?: string;
|
||||
alt: string;
|
||||
@@ -23,34 +25,65 @@ export type AvatarProps = {
|
||||
"data-testid"?: string;
|
||||
};
|
||||
|
||||
const sizesPropsBySize = {
|
||||
xxs: "w-3.5 h-3.5 min-w-3.5 min-h-3.5", // 14px
|
||||
xs: "w-4 h-4 min-w-4 min-h-4 max-h-4", // 16px
|
||||
xsm: "w-5 h-5 min-w-5 min-h-5", // 20px
|
||||
sm: "w-6 h-6 min-w-6 min-h-6", // 24px
|
||||
md: "w-8 h-8 min-w-8 min-h-8", // 32px
|
||||
mdLg: "w-10 h-10 min-w-10 min-h-10", //40px
|
||||
lg: "w-16 h-16 min-w-16 min-h-16", // 64px
|
||||
xl: "w-24 h-24 min-w-24 min-h-24", // 96px
|
||||
} as const;
|
||||
const avatarClasses = cva(
|
||||
"bg-emphasis border-default relative inline-flex aspect-square items-center justify-center border align-top",
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
xs: "w-4 h-4 min-w-4 min-h-4 max-h-4", // 16px
|
||||
xsm: "w-5 h-5 min-w-5 min-h-5", // 20px
|
||||
sm: "w-6 h-6 min-w-6 min-h-6", // 24px
|
||||
md: "w-8 h-8 min-w-8 min-h-8", // 32px
|
||||
mdLg: "w-10 h-10 min-w-10 min-h-10", // 40px
|
||||
lg: "w-16 h-16 min-w-16 min-h-16", // 64px
|
||||
xl: "w-24 h-24 min-w-24 min-h-24", // 96px
|
||||
},
|
||||
shape: {
|
||||
circle: "rounded-full",
|
||||
square: "",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: "md",
|
||||
shape: "circle",
|
||||
},
|
||||
compoundVariants: [
|
||||
{
|
||||
size: ["xs", "xsm", "sm"],
|
||||
shape: "square",
|
||||
className: "rounded",
|
||||
},
|
||||
{
|
||||
size: ["md"],
|
||||
shape: "square",
|
||||
className: "rounded-md",
|
||||
},
|
||||
{
|
||||
size: ["mdLg", "lg", "xl"],
|
||||
shape: "square",
|
||||
className: "rounded-[10px]",
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
export function Avatar(props: AvatarProps) {
|
||||
const { imageSrc, size = "md", alt, title, href, indicator } = props;
|
||||
const rootClass = classNames("aspect-square rounded-full", sizesPropsBySize[size]);
|
||||
const avatarClass = avatarClasses({ size, shape: props.shape });
|
||||
const rootClass = classNames("aspect-square rounded-full", avatarClass);
|
||||
let avatar = (
|
||||
<AvatarPrimitive.Root
|
||||
data-testid={props?.["data-testid"]}
|
||||
className={classNames(
|
||||
"bg-emphasis item-center relative inline-flex aspect-square justify-center rounded-full align-top",
|
||||
avatarClass,
|
||||
indicator ? "overflow-visible" : "overflow-hidden",
|
||||
props.className,
|
||||
sizesPropsBySize[size]
|
||||
props.className
|
||||
)}>
|
||||
<>
|
||||
<AvatarPrimitive.Image
|
||||
src={imageSrc ?? undefined}
|
||||
alt={alt}
|
||||
className={classNames("aspect-square rounded-full", sizesPropsBySize[size])}
|
||||
className={classNames("aspect-square", avatarClass)}
|
||||
/>
|
||||
<AvatarPrimitive.Fallback
|
||||
delayMs={600}
|
||||
|
||||
@@ -20,11 +20,12 @@ export const badgeStyles = cva("font-medium inline-flex items-center justify-cen
|
||||
red: "bg-error text-error",
|
||||
error: "bg-error text-error",
|
||||
grayWithoutHover: "bg-gray-100 text-gray-800 dark:bg-darkgray-200 dark:text-darkgray-800",
|
||||
purple: "bg-purple-50 text-purple-800",
|
||||
},
|
||||
size: {
|
||||
sm: "px-1 py-0.5 text-xs leading-3",
|
||||
md: "py-1 px-1.5 text-xs leading-3",
|
||||
lg: "py-1 px-2 text-sm leading-4",
|
||||
sm: "px-1 py-1 text-[10px] leading-none",
|
||||
md: "py-1 px-1.5 text-xs leading-none",
|
||||
lg: "py-1 px-1.5 text-sm leading-none rounded-lg",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
/* eslint-disable playwright/missing-playwright-await */
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { vi } from "vitest";
|
||||
|
||||
import { Icon } from "../icon";
|
||||
import { Badge, badgeStyles } from "./Badge";
|
||||
|
||||
describe("Tests for Badge component", () => {
|
||||
describe("Badge", () => {
|
||||
const variants = [
|
||||
"default",
|
||||
"warning",
|
||||
@@ -18,74 +16,71 @@ describe("Tests for Badge component", () => {
|
||||
"red",
|
||||
"error",
|
||||
"grayWithoutHover",
|
||||
];
|
||||
"purple",
|
||||
] as const;
|
||||
|
||||
const sizes = ["sm", "md", "lg"];
|
||||
const children = "Test Badge";
|
||||
|
||||
test.each(variants)("Should apply variant class", (variant) => {
|
||||
render(<Badge variant={variant as any}>{children}</Badge>);
|
||||
const badgeClass = screen.getByText(children).className;
|
||||
const badgeComponentClass = badgeStyles({ variant: variant as any });
|
||||
expect(badgeClass).toEqual(badgeComponentClass);
|
||||
});
|
||||
|
||||
test.each(sizes)("Should apply size class", (size) => {
|
||||
render(<Badge size={size as any}>{children}</Badge>);
|
||||
const badgeClass = screen.getByText(children).className;
|
||||
const badgeComponentClass = badgeStyles({ size: size as any });
|
||||
expect(badgeClass).toEqual(badgeComponentClass);
|
||||
});
|
||||
|
||||
test("Should render without errors", () => {
|
||||
render(<Badge>{children}</Badge>);
|
||||
expect(screen.getByText(children)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("Should render WithDot if the prop is true and shouldn't render if is false", async () => {
|
||||
const { rerender } = render(<Badge withDot>{children}</Badge>);
|
||||
expect(await screen.findByTestId("go-primitive-dot")).toBeInTheDocument();
|
||||
|
||||
rerender(<Badge>{children}</Badge>);
|
||||
expect(screen.queryByTestId("go-primitive-dot")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("Should render with a startIcon when startIcon prop is provided shouldn't render if is false", () => {
|
||||
const { rerender } = render(<Badge customStartIcon={<svg data-testid="start-icon" />}>{children}</Badge>);
|
||||
expect(screen.getByTestId("start-icon")).toBeInTheDocument();
|
||||
|
||||
rerender(<Badge>{children}</Badge>);
|
||||
expect(screen.queryByTestId("start-icon")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("Should render as a button when onClick prop is provided and shouldn't if is not", () => {
|
||||
const handleClick = vi.fn();
|
||||
const { rerender } = render(<Badge onClick={handleClick}>{children}</Badge>);
|
||||
test.each(variants)("renders %s variant correctly", (variant) => {
|
||||
render(<Badge variant={variant}>{children}</Badge>);
|
||||
const badge = screen.getByText(children);
|
||||
expect(badge.tagName).toBe("BUTTON");
|
||||
fireEvent.click(badge);
|
||||
expect(handleClick).toHaveBeenCalledTimes(1);
|
||||
|
||||
rerender(<Badge>{children}</Badge>);
|
||||
const updateBadge = screen.getByText(children);
|
||||
expect(updateBadge.tagName).not.toBe("BUTTON");
|
||||
expect(badge.className).toContain(badgeStyles({ variant }));
|
||||
});
|
||||
|
||||
test("Should render as a div when onClick prop is not provided", () => {
|
||||
test("renders with default props", () => {
|
||||
render(<Badge>{children}</Badge>);
|
||||
const badge = screen.getByText(children);
|
||||
expect(badge).toBeInTheDocument();
|
||||
expect(badge.tagName).toBe("DIV");
|
||||
});
|
||||
|
||||
test("Should render children when provided", () => {
|
||||
const { getByText } = render(
|
||||
test("renders with dot when withDot prop is true", () => {
|
||||
render(<Badge withDot>{children}</Badge>);
|
||||
expect(screen.getByTestId("go-primitive-dot")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders with startIcon when provided", () => {
|
||||
render(<Badge startIcon="info">{children}</Badge>);
|
||||
expect(screen.getByTestId("start-icon")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders with customStartIcon when provided", () => {
|
||||
render(<Badge customStartIcon={<Icon name="info" data-testid="custom-icon" />}>{children}</Badge>);
|
||||
expect(screen.getByTestId("custom-icon")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders as button with onClick handler", () => {
|
||||
const handleClick = vi.fn();
|
||||
render(<Badge onClick={handleClick}>{children}</Badge>);
|
||||
const badge = screen.getByText(children);
|
||||
|
||||
expect(badge.tagName).toBe("BUTTON");
|
||||
fireEvent.click(badge);
|
||||
expect(handleClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("renders with rounded style when rounded prop is true", () => {
|
||||
render(<Badge rounded>{children}</Badge>);
|
||||
const badge = screen.getByText(children);
|
||||
expect(badge.className).toContain("h-5 w-5 rounded-full p-0");
|
||||
});
|
||||
|
||||
test("renders with additional className when provided", () => {
|
||||
const customClass = "custom-class";
|
||||
render(<Badge className={customClass}>{children}</Badge>);
|
||||
const badge = screen.getByText(children);
|
||||
expect(badge.className).toContain(customClass);
|
||||
});
|
||||
|
||||
test("renders nested children correctly", () => {
|
||||
render(
|
||||
<Badge>
|
||||
<span>Child element 1</span>
|
||||
<span>Child element 2</span>
|
||||
<span data-testid="child-1">Child 1</span>
|
||||
<span data-testid="child-2">Child 2</span>
|
||||
</Badge>
|
||||
);
|
||||
|
||||
expect(getByText("Child element 1")).toBeInTheDocument();
|
||||
expect(getByText("Child element 2")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("child-1")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("child-2")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user