Compare commits

...
59 Commits
Author SHA1 Message Date
Alex van Andel b055672ea0 v2.7.13
release.yml / release (push) Canceled after 0s
2023-04-11 19:51:38 +02:00
Alex van Andel e4fbca388c Fix yarn.lock file 2023-04-11 19:50:36 +02:00
Joe Au-Yeung 2b8bba09c9 Prevent updating app to enabled on seed app store (#8092) 2023-04-11 21:51:21 +05:30
Alex van Andel 98cf29f655 Wrap useSuspense with NoSSR (#8079) 2023-04-11 21:49:27 +05:30
GitStart-Cal.com ae8706316f CALCOM-7895 - [CAL-1320] remove "application_fee_amount" for all regions (#7933) 2023-04-11 16:16:43 +00:00
Jeroen ReumkensandAlex van Andel ec8e08d442 Added serverside translations to teams page. (#7999)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-04-11 21:39:02 +05:30
sean-brydonandPeer Richelsen c33f424333 Fixes /more page + fixes multiple duration badge on BookingPage (#8200)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-11 15:21:24 +00:00
Hariom Balhara 6f2312d1e2 Fix app not visible in app-store (#8202) 2023-04-11 14:58:57 +00:00
Peer Richelsen c2b6070b7e Update package.json
release.yml / release (push) Canceled after 0s
2023-04-11 16:54:49 +02:00
Efraín Rochín a5bebe2b48 bugfix/sync-not-working-properly (#8168) 2023-04-11 16:51:27 +02:00
sean-brydon e3a1f20cc8 Darkmode Fixes (#8191) 2023-04-11 12:15:10 +00:00
sean-brydon ab639591da Fix forcing hard refresh across forced themes (#8188) 2023-04-11 17:20:25 +05:30
Hariom BalharaandPeer Richelsen 21a9b64eb0 Fix missing sylaps icon in admin/apps (#8190)
* Fi missing sylaps icon in admin/apps and remove usage of imageSrc

* Remove imageSrc from googleCalendar and verify that logo is still shown

* Use logo in video apps location select:

* Remove imageSrc from templates

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-11 11:38:37 +00:00
Omar López 636ee7b8a1 Prioritizes published teams over unpublished ones (#8177) 2023-04-11 06:16:57 +00:00
Crowdin Bot 543e526568 New Crowdin translations by Github Action 2023-04-11 02:21:03 +00:00
Hariom Balhara e465c1d71d Redirect on top iframe always - This is to make sure embed redirects on top frame (#8164) 2023-04-11 07:50:15 +05:30
Peer Richelsen cdab9037ce added dark mode upgrade banner (#8172) 2023-04-10 21:14:28 +00:00
Crowdin Bot 21e454d0e2 New Crowdin translations by Github Action 2023-04-10 20:27:50 +00:00
zomars 7384f29a1c Update crowdin.yml 2023-04-10 13:27:00 -07:00
Keith Williams 9016a854ca v2.7.11
release.yml / release (push) Canceled after 0s
2023-04-10 16:46:55 -03:00
Nafees Nazik 446c29dd9c fix: booker phone input style (#8165)
* fix: text area styles

* fix: phone input styles

* fix: use the text area component

* fix: remove focus style
2023-04-10 18:12:15 +02:00
Leandro Miguel NarcisoandPeer Richelsen 2cf1f07d8f Fix loading indicator alignment touching the select input value (#8083)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-09 11:16:33 +00:00
Nafees NazikandPeer Richelsen 4f03f8e66d feat: dynamically elapse time left timer (#7938)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-09 10:20:51 +00:00
e1096620e6 fix: replace ltr and rtl with new ms and me (#8138)
Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-09 11:13:57 +02:00
sean-brydon d416386d7b Fix loading state - force subtle token to exist (#8153)
* Fix loading state - force subtle token to exist

* Add opacity
2023-04-08 18:50:17 +02:00
Keith Williams 5b7c55b029 v2.7.10
release.yml / release (push) Canceled after 0s
2023-04-07 17:04:56 -03:00
sean-brydon 9b05311d7f Fixes team skeleton - force brand on onboarding (#8142) 2023-04-07 19:28:06 +00:00
sean-brydon 313d25d7d8 Force lightmode (#8141) 2023-04-07 19:17:22 +00:00
Keith Williams 5ec9c41f7c v2.7.9 2023-04-07 15:29:01 -03:00
Nafees Nazik d56386d016 fix: dark mode variant in team member card (#8137) 2023-04-07 15:52:21 +00:00
sydwardraeandJoe Au-Yeung 0968ede733 fixed open-source to open source (#8127)
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
2023-04-07 09:15:13 +02:00
alannnc 8e3ed28ba9 Switch order from user/slug (#8134) 2023-04-07 09:13:22 +02:00
Nafees NazikandAlex van Andel 82ea4e9fb7 fix: switch bg when default checked (#8129)
* fix: switch bg

* Update packages/ui/components/form/switch/Switch.tsx

Remove accidental space

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-04-06 20:37:05 +00:00
Nafees Nazik 8a440c24c0 fix: email line height (#8130) 2023-04-06 17:38:13 +00:00
Alex van Andel 88a8da3ef6 Update lockfile 2023-04-06 17:59:17 +02:00
Nafees Nazik 49290fba72 fix: flash of title in event types page (#8114) 2023-04-06 12:25:54 +02:00
Udit Takkar a363b1e903 fix: ui and ux improvements in booking questions (#7734) 2023-04-06 08:17:53 +00:00
Carina WollendorferandCarinaWolli 7b5e2c8bab Don't schedule cancelled workflow reminders (#8096)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-04-06 13:15:01 +05:30
sean-brydon 4b0d22655d Feat/tokens + darkmode + theme support (#7876) 2023-04-05 18:14:46 +00:00
Peer Richelsen 4f92f60d85 added illustrations to app store (#8104) 2023-04-05 19:05:44 +02:00
Nafees Nazik 6986b5af40 Fix: availability not deleteable (#8086)
* fix: make availability deletable

* test: add e2e

* chore: add test id

* test: add a new test

* fix: remove utility
2023-04-05 12:42:22 -03:00
Alex van Andel e043a5efd8 Async import of the appStore packages (#8087) 2023-04-05 11:55:57 -03:00
Peer Richelsen bff28e5d6f discord app (#8101) 2023-04-05 14:07:18 +02:00
GitStart-Cal.comandgitstart-calcom f10ff18d9f FIX-CALCOM-8009 (#8097)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
2023-04-05 11:54:25 +02:00
Nafees Nazik cefe4754c7 fix: add older variables to whitelist (#8095) 2023-04-05 10:35:32 +02:00
Omar López d7aa3c7163 v2.7.8
release.yml / release (push) Canceled after 0s
2023-04-04 16:58:13 -07:00
Peer Richelsen e899a92071 added turkish (#8091) 2023-04-05 00:13:00 +02:00
zomars ebdbe407cd Fixes crowding update 2023-04-04 12:43:00 -07:00
e7f917bab9 Feedback and improvements (#7903)
Co-authored-by: gitstart-calcom <gitstart@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2023-04-04 10:35:51 -07:00
Hariom Balhara ba9688a04a BugFix: Don't open location dialog on removing location (#8085) 2023-04-04 14:43:41 +00:00
Stefan Florea f0e15d77dc feat: Google Tag Manager (GTM) App (#7952) 2023-04-04 17:53:11 +05:30
30b72b687e Follow up insights - user metrics (#8016)
* init page

* init insights frontend

* nit

* nit

* nit

* merge

* fixed icons

* i18n, needs features

* Init insights trpc

* Using trpc on client

* Added events timeline

* Seed analytics script

* connect ui with trpc

* Added and fixed event time lines

* WIP popular days and avg time duration event type

* added new metric graphs

* improved upgrade tip

* always show upgrade screen

* upgrade tremor.so and select inputs for page

* Remove log

* Move everything to components and add context

* Fix select types using calcom ui one

* Adding translations

* Add missing translations

* Add more translations

* min fix

* Fixes for date select

* Prefer early return and mobile design fixes

* Fix style for mobile

* Fix data with userId filter from popular events

* add user id to average time duration

* fix types for select-react

* Removed submodules

* Delete website

* Update yarn.lock

* Code organization and type fixes

* trpc fixes

* Builds are now passing

* Relocates server code

* Add url state in insights

* Update FiltersProvider.tsx

* Cleanup

* Update embed-iframe.ts

* Update FilterType.tsx

* Update seed-app-store.config.json

* Update index.tsx

* Renamed seeder

* Update FiltersProvider.tsx

* Fix for query params

* no wrap on lg screen

* Fix shadow borders from tremor components, fix title font

* Add ring-gray to match filters

* add cursor pointer

* copy improvements

* blue to black

* fixed date select focus

* text right for table

* Adds missing translation strings

* Fix url state for filter type

* more layout improvements

* more padding improvements

* more UI fixes (padding, sizes) requested by ciaran

* Apply suggestions from code review

* WIP user without team metrics

* yarn

* Adds loading state for graphs and tables

* fix average render when no data

* Multiple fixes in insights

* Fix most and least booked members querys

* More fix for clear filters

---------

Co-authored-by: Peer Richelsen <peer@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
2023-04-04 13:58:19 +02:00
Peer Richelsenandsean-brydon bbf50dbc53 fixed links and more on signup 404 page that requires license (#7937)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2023-04-04 11:49:44 +02:00
Ted SpareandPeer Richelsen bb547a0111 Apply labels from linked issues to a PR (#8069)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2023-04-04 09:28:36 +00:00
Hariom BalharaandAlex van Andel 1758288948 Improvement: Do immediate redirect for "Redirect on Booking" (#7947)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2023-04-04 14:22:27 +05:30
Udit Takkar 11789c725a fix: remove box shadow none (#8077)
Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
2023-04-04 08:06:11 +00:00
Hariom Balhara 73bfe0aebe HotFix: Broken Email Field in Routing Form (#8074)
* Add darkmode styles and make it full width and other classes that are for TextWidget

* Handle email text change
2023-04-04 07:55:56 +00:00
Harold ThétiotandHariom Balhara 24140f0f29 feat: add sylaps into the app-store in video category (#6728)
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2023-04-04 13:12:07 +05:30
e504f5cd9e Fix/send responses everywhere customInputs were being sent. (#7898)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2023-04-04 10:29:09 +05:30
491 changed files with 6948 additions and 4181 deletions

No files matched your search

@@ -0,0 +1,27 @@
name: "Apply issue labels to PR"
on:
pull_request:
types: [opened]
jobs:
label_on_pr:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 16
- name: Apply labels from linked issue to PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_DATA: ${{ toJson(github.event.pull_request) }}
run: node ./.github/workflows/scripts/apply-issue-labels-to-pr.ts
+3 -1
View File
@@ -15,6 +15,8 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
token: ${{ secrets.GH_ACCESS_TOKEN }}
- name: crowdin action - name: crowdin action
uses: crowdin/github-action@1.5.1 uses: crowdin/github-action@1.5.1
@@ -26,6 +28,6 @@ jobs:
localization_branch_name: main localization_branch_name: main
create_pull_request: false create_pull_request: false
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
@@ -0,0 +1,149 @@
const https = require("https");
async function applyLabelFromLinkedIssueToPR(pr, token) {
// Get the labels from issues linked to the PR
const query = `
query GetLinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 10) {
nodes {
number
labels(first: 10) {
nodes {
name
}
}
}
}
}
}
}
`;
const graphqlData = JSON.stringify({
query,
variables: {
owner: pr.base.repo.owner.login,
repo: pr.base.repo.name,
prNumber: pr.number,
},
});
const requestOptions = {
hostname: "api.github.com",
path: "/graphql",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": graphqlData.length,
"Authorization": "Bearer " + token,
"User-Agent": "Node.js",
},
};
// Use the native Node.js request library to make the request
let linkedIssues;
linkedIssues = await new Promise((resolve, reject) => {
const request = https.request(requestOptions, (response) => {
let responseBody = "";
response.on("data", (chunk) => {
responseBody += chunk;
});
response.on("end", () => {
resolve(
JSON.parse(responseBody)
?.data
?.repository
?.pullRequest
?.closingIssuesReferences
?.nodes
);
});
});
request.on("error", (error) => {
reject(error);
});
request.write(graphqlData);
request.end();
});
if (!linkedIssues || linkedIssues.length === 0) {
console.log("No issue linked.");
return;
}
// Iterate over linked issues and apply labels to PR
for (const issue of linkedIssues) {
const labels = issue?.labels?.nodes?.map((label) => label.name);
if (!labels || labels.length === 0) {
console.log(`No labels found on the linked issue #${issue.number}.`);
continue;
}
const labelsData = JSON.stringify({
labels: labels,
});
const requestOptions = {
hostname: "api.github.com",
path: `/repos/${pr.base.repo.owner.login}/${pr.base.repo.name}/issues/${pr.number}/labels`,
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": labelsData.length,
"Authorization": "Bearer " + token,
"User-Agent": "Node.js",
},
};
let labelResult;
labelResult = await new Promise((resolve, reject) => {
const request = https.request(requestOptions, (response) => {
let responseBody = "";
response.on("data", (chunk) => {
responseBody += chunk;
});
response.on("data", () => {});
response.on("end", () => {
resolve(JSON.parse(responseBody));
});
});
request.on("error", (error) => {
reject(error);
});
request.write(labelsData);
request.end();
});
if (labelResult.message) {
console.log(`Error labelling PR: ${labelResult.message}`);
continue;
}
console.log(
`Applied labels: ${labels.join(", ")} to PR #${
pr.number
} from linked issue #${issue.number}`
);
}
}
// Pass the PR data and GitHub token to the script
(async () => {
if (!process.env.PR_DATA) {
console.log("No PR data found.");
return;
}
const prData = JSON.parse(process.env.PR_DATA);
const token = process.env.GITHUB_TOKEN;
await applyLabelFromLinkedIssueToPR(prData, token);
})();
+3 -2
View File
@@ -1,4 +1,5 @@
import React, { ReactElement, ReactNode } from "react"; import type { ReactElement, ReactNode } from "react";
import React from "react";
import { classNames } from "@calcom/lib"; import { classNames } from "@calcom/lib";
@@ -42,7 +43,7 @@ export function VariantsTable({
</table> </table>
</div> </div>
{!isDark && ( {!isDark && (
<div data-mode="dark"> <div data-mode="dark" className="dark">
<VariantsTable titles={titles} isDark columnMinWidth={columnMinWidth}> <VariantsTable titles={titles} isDark columnMinWidth={columnMinWidth}>
{children} {children}
</VariantsTable> </VariantsTable>
@@ -193,3 +193,88 @@
.docs-story { .docs-story {
padding: 24px !important; padding: 24px !important;
} }
@layer {
:root {
/* background */
--cal-bg-emphasis: #E5E7EB;
--cal-bg: white;
--cal-bg-subtle: #F3F4F6;
--cal-bg-muted: #F9FAFB;
--cal-bg-inverted: #111827;
/* background -> components*/
--cal-bg-info: #DEE9FC;
--cal-bg-success: #E2FBE8;
--cal-bg-attention: #FCEED8;
--cal-bg-error: #F9E3E2;
/* Borders */
--cal-border-emphasis:#9CA3AF;
--cal-border: #D1D5DB;
--cal-border-subtle:#E5E7EB;
--cal-border-muted:#F3F4F6;
/* Content/Text */
--cal-text-emphasis: #111827;
--cal-text:#374151;
--cal-text-subtle:#6B7280;
--cal-text-muted:#9CA3AF;
--cal-text-inverted:white;
/* Content/Text -> components */
--cal-text-info:#253985;
--cal-text-success:#285231;
--cal-text-attention:#73321B;
--cal-text-error:#752522;
/* Brand shinanigans
-> These will be computed for the users theme at runtime.
*/
--cal-brand:#111827;
--cal-brand-emphasis:#101010;
}
.dark {
/* background */
--cal-bg-emphasis: #2B2B2B;
--cal-bg: #101010;
--cal-bg-subtle: #2B2B2B;
--cal-bg-muted: #1C1C1C;
--cal-bg-inverted: #F3F4F6;
/* background -> components*/
--cal-bg-info: #DEE9FC;
--cal-bg-success: #E2FBE8;
--cal-bg-attention: #FCEED8;
--cal-bg-error: #F9E3E2;
/* Borders */
--cal-border-emphasis: #575757;
--cal-border: #444444;
--cal-border-subtle: #2B2B2B;
--cal-border-muted: #1C1C1C;
/* Content/Text */
--cal-text-emphasis: #F3F4F6;
--cal-text: #D6D6D6;
--cal-text-subtle: #757575;
--cal-text-muted: #575757;
--cal-text-inverted: #101010;
/* Content/Text -> components */
--cal-text-info: #253985;
--cal-text-success: #285231;
--cal-text-attention: #73321B;
--cal-text-error: #752522;
/* Brand shenanigans
-> These will be computed for the users theme at runtime.
*/
--cal-brand: #111827;
--cal-brand-emphasis: #101010;
}
}
+2 -2
View File
@@ -29,7 +29,7 @@ export default function AddToHomescreen() {
<path d="M35 40H15c-1.7 0-3-1.3-3-3V19c0-1.7 1.3-3 3-3h7v2h-7c-.6 0-1 .4-1 1v18c0 .6.4 1 1 1h20c.6 0 1-.4 1-1V19c0-.6-.4-1-1-1h-7v-2h7c1.7 0 3 1.3 3 3v18c0 1.7-1.3 3-3 3z" /> <path d="M35 40H15c-1.7 0-3-1.3-3-3V19c0-1.7 1.3-3 3-3h7v2h-7c-.6 0-1 .4-1 1v18c0 .6.4 1 1 1h20c.6 0 1-.4 1-1V19c0-.6-.4-1-1-1h-7v-2h7c1.7 0 3 1.3 3 3v18c0 1.7-1.3 3-3 3z" />
</svg> </svg>
</span> </span>
<p className="text-xs font-medium text-white ltr:ml-3 rtl:mr-3"> <p className="text-inverted ms-3 text-xs font-medium">
<span className="inline">{t("add_to_homescreen")}</span> <span className="inline">{t("add_to_homescreen")}</span>
</p> </p>
</div> </div>
@@ -40,7 +40,7 @@ export default function AddToHomescreen() {
type="button" type="button"
className="-mr-1 flex rounded-md p-2 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-white"> className="-mr-1 flex rounded-md p-2 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-white">
<span className="sr-only">{t("dismiss")}</span> <span className="sr-only">{t("dismiss")}</span>
<FiX className="h-6 w-6 text-white" aria-hidden="true" /> <FiX className="text-inverted h-6 w-6" aria-hidden="true" />
</button> </button>
</div> </div>
</div> </div>
+2 -2
View File
@@ -63,12 +63,12 @@ export default function AppListCard(props: AppListCardProps) {
}, []); }, []);
return ( return (
<div className={`${highlight ? "bg-yellow-100" : ""}`}> <div className={`${highlight ? "dark:bg-muted bg-yellow-100" : ""}`}>
<div className="flex gap-x-3 px-5 py-4"> <div className="flex gap-x-3 px-5 py-4">
{logo ? <img className="h-10 w-10" src={logo} alt={`${title} logo`} /> : null} {logo ? <img className="h-10 w-10" src={logo} alt={`${title} logo`} /> : null}
<div className="flex grow flex-col gap-y-1 truncate"> <div className="flex grow flex-col gap-y-1 truncate">
<div className="flex items-center gap-x-2"> <div className="flex items-center gap-x-2">
<h3 className="truncate text-sm font-semibold text-gray-900">{title}</h3> <h3 className="text-emphasis truncate text-sm font-semibold">{title}</h3>
<div className="flex items-center gap-x-2"> <div className="flex items-center gap-x-2">
{isDefault && <Badge variant="green">{t("default")}</Badge>} {isDefault && <Badge variant="green">{t("default")}</Badge>}
{isTemplate && <Badge variant="red">Template</Badge>} {isTemplate && <Badge variant="red">Template</Badge>}
+34 -24
View File
@@ -509,7 +509,7 @@ const tabs = [
return ( return (
<> <>
<div> <div>
<small className="flex py-4 text-gray-500"> <small className="text-subtle flex py-4">
{t("place_where_cal_widget_appear", { appName: APP_NAME })} {t("place_where_cal_widget_appear", { appName: APP_NAME })}
</small> </small>
</div> </div>
@@ -517,7 +517,7 @@ const tabs = [
data-testid="embed-code" data-testid="embed-code"
ref={ref as typeof ref & MutableRefObject<HTMLTextAreaElement>} ref={ref as typeof ref & MutableRefObject<HTMLTextAreaElement>}
name="embed-code" name="embed-code"
className="h-[calc(100%-50px)] font-mono" className="text-default bg-default selection:bg-subtle h-[calc(100%-50px)] font-mono"
style={{ resize: "none", overflow: "auto" }} style={{ resize: "none", overflow: "auto" }}
readOnly readOnly
value={ value={
@@ -534,7 +534,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
<!-- Cal ${embedType} embed code ends -->` <!-- Cal ${embedType} embed code ends -->`
} }
/> />
<p className="hidden text-sm text-gray-500"> <p className="text-subtle hidden text-sm">
{t( {t(
"Need help? See our guides for embedding Cal on Wix, Squarespace, or WordPress, check our common questions, or explore advanced embed options." "Need help? See our guides for embedding Cal on Wix, Squarespace, or WordPress, check our common questions, or explore advanced embed options."
)} )}
@@ -561,12 +561,12 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
} }
return ( return (
<> <>
<small className="flex py-4 text-gray-500">{t("create_update_react_component")}</small> <small className="text-subtle flex py-4">{t("create_update_react_component")}</small>
<TextArea <TextArea
data-testid="embed-react" data-testid="embed-react"
ref={ref as typeof ref & MutableRefObject<HTMLTextAreaElement>} ref={ref as typeof ref & MutableRefObject<HTMLTextAreaElement>}
name="embed-react" name="embed-react"
className="h-[calc(100%-50px)] font-mono" className="text-default bg-default selection:bg-subtle h-[calc(100%-50px)] font-mono"
readOnly readOnly
style={{ resize: "none", overflow: "auto" }} style={{ resize: "none", overflow: "auto" }}
value={`/* First make sure that you have installed the package */ value={`/* First make sure that you have installed the package */
@@ -622,7 +622,7 @@ Cal("init", {origin:"${WEBAPP_URL}"});
const ThemeSelectControl = ({ children, ...props }: ControlProps<{ value: Theme; label: string }, false>) => { const ThemeSelectControl = ({ children, ...props }: ControlProps<{ value: Theme; label: string }, false>) => {
return ( return (
<components.Control {...props}> <components.Control {...props}>
<FiSun className="ml-2 h-4 w-4 text-gray-500" /> <FiSun className="text-subtle ml-2 h-4 w-4" />
{children} {children}
</components.Control> </components.Control>
); );
@@ -634,17 +634,17 @@ const ChooseEmbedTypesDialogContent = () => {
return ( return (
<DialogContent className="rounded-lg p-10" type="creation" size="lg"> <DialogContent className="rounded-lg p-10" type="creation" size="lg">
<div className="mb-2"> <div className="mb-2">
<h3 className="mb-2 text-2xl font-bold leading-none text-gray-900" id="modal-title"> <h3 className="font-cal text-emphasis mb-2 text-2xl font-bold leading-none" id="modal-title">
{t("how_you_want_add_cal_site", { appName: APP_NAME })} {t("how_you_want_add_cal_site", { appName: APP_NAME })}
</h3> </h3>
<div> <div>
<p className="text-sm text-gray-500">{t("choose_ways_put_cal_site", { appName: APP_NAME })}</p> <p className="text-subtle text-sm">{t("choose_ways_put_cal_site", { appName: APP_NAME })}</p>
</div> </div>
</div> </div>
<div className="flex items-start"> <div className="items-start space-y-2 md:flex md:space-y-0">
{embeds.map((embed, index) => ( {embeds.map((embed, index) => (
<button <button
className="hover:border-primary-900 w-1/3 rounded-md border border-gray-200 bg-gray-50 p-6 text-left hover:rounded-md hover:bg-gray-100 ltr:mr-4 ltr:last:mr-0 rtl:ml-4 rtl:last:ml-0" className="hover:bg-subtle bg-muted w-full rounded-md border border-transparent p-6 text-left hover:rounded-md ltr:mr-4 ltr:last:mr-0 rtl:ml-4 rtl:last:ml-0 lg:w-1/3"
key={index} key={index}
data-testid={embed.type} data-testid={embed.type}
onClick={() => { onClick={() => {
@@ -652,11 +652,11 @@ const ChooseEmbedTypesDialogContent = () => {
embedType: embed.type, embedType: embed.type,
}); });
}}> }}>
<div className="order-none box-border flex-none rounded-md border border-solid bg-white"> <div className="bg-default order-none box-border flex-none rounded-md border border-solid dark:bg-transparent dark:invert">
{embed.illustration} {embed.illustration}
</div> </div>
<div className="mt-4 font-semibold leading-none text-gray-900">{embed.title}</div> <div className="text-emphasis mt-4 font-semibold">{embed.title}</div>
<p className="mt-2 text-sm text-gray-500">{embed.subtitle}</p> <p className="text-subtle mt-2 text-sm">{embed.subtitle}</p>
</button> </button>
))} ))}
</div> </div>
@@ -821,9 +821,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
enableOverflow enableOverflow
type="creation"> type="creation">
<div className="flex"> <div className="flex">
<div className="flex w-1/3 flex-col bg-gray-50 p-8"> <div className="bg-muted flex w-1/3 flex-col p-8">
<h3 <h3
className="mb-2.5 flex items-center text-xl font-semibold leading-5 text-gray-900" className="text-emphasis mb-2.5 flex items-center text-xl font-semibold leading-5"
id="modal-title"> id="modal-title">
<button <button
className="h-6 w-6" className="h-6 w-6"
@@ -834,7 +834,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</button> </button>
{embed.title} {embed.title}
</h3> </h3>
<h4 className="mb-6 text-sm font-normal text-gray-500">{embed.subtitle}</h4> <h4 className="text-emphasis mb-6 text-sm font-normal">{embed.subtitle}</h4>
<div className="flex flex-col"> <div className="flex flex-col">
<div className={classNames("font-medium", embedType === "element-click" ? "hidden" : "")}> <div className={classNames("font-medium", embedType === "element-click" ? "hidden" : "")}>
<Collapsible <Collapsible
@@ -843,7 +843,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
<CollapsibleContent className="text-sm"> <CollapsibleContent className="text-sm">
<div className={classNames(embedType === "inline" ? "block" : "hidden")}> <div className={classNames(embedType === "inline" ? "block" : "hidden")}>
{/*TODO: Add Auto/Fixed toggle from Figma */} {/*TODO: Add Auto/Fixed toggle from Figma */}
<div className="mb-[9px] text-sm">Window sizing</div> <div className="text-default mb-[9px] text-sm">Window sizing</div>
<div className="justify-left flex items-center !font-normal"> <div className="justify-left flex items-center !font-normal">
<div className="mr-[9px]"> <div className="mr-[9px]">
<TextField <TextField
@@ -893,7 +893,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
<div <div
className={classNames( className={classNames(
"mt-4 items-center justify-between", "mt-4 items-center justify-between",
embedType === "floating-popup" ? "" : "hidden" embedType === "floating-popup" ? "text-emphasis" : "hidden"
)}> )}>
<div className="mb-2 text-sm">Button Text</div> <div className="mb-2 text-sm">Button Text</div>
{/* Default Values should come from preview iframe */} {/* Default Values should come from preview iframe */}
@@ -917,7 +917,9 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
<div <div
className={classNames( className={classNames(
"mt-4 flex items-center justify-start", "mt-4 flex items-center justify-start",
embedType === "floating-popup" ? "space-x-2 rtl:space-x-reverse" : "hidden" embedType === "floating-popup"
? "text-emphasis space-x-2 rtl:space-x-reverse"
: "hidden"
)}> )}>
<Switch <Switch
defaultChecked={true} defaultChecked={true}
@@ -933,12 +935,12 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
}); });
}} }}
/> />
<div className="text-sm">Display Calendar Icon Button</div> <div className="text-default text-sm">Display Calendar Icon Button</div>
</div> </div>
<div <div
className={classNames( className={classNames(
"mt-4 items-center justify-between", "mt-4 items-center justify-between",
embedType === "floating-popup" ? "" : "hidden" embedType === "floating-popup" ? "text-emphasis" : "hidden"
)}> )}>
<div className="mb-2">Position of Button</div> <div className="mb-2">Position of Button</div>
<Select <Select
@@ -957,7 +959,11 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
options={FloatingPopupPositionOptions} options={FloatingPopupPositionOptions}
/> />
</div> </div>
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}> <div
className={classNames(
"mt-4",
embedType === "floating-popup" ? "text-emphasis" : "hidden"
)}>
<div>Button Color</div> <div>Button Color</div>
<div className="w-full"> <div className="w-full">
<ColorPicker <ColorPicker
@@ -978,7 +984,11 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
/> />
</div> </div>
</div> </div>
<div className={classNames("mt-4", embedType === "floating-popup" ? "" : "hidden")}> <div
className={classNames(
"mt-4",
embedType === "floating-popup" ? "text-emphasis" : "hidden"
)}>
<div>Text Color</div> <div>Text Color</div>
<div className="w-full"> <div className="w-full">
<ColorPicker <ColorPicker
@@ -1020,7 +1030,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
}); });
}} }}
/> />
<div className="text-sm">{t("hide_eventtype_details")}</div> <div className="text-default text-sm">{t("hide_eventtype_details")}</div>
</div> </div>
{[ {[
{ name: "brandColor", title: "Brand Color" }, { name: "brandColor", title: "Brand Color" },
+4 -4
View File
@@ -123,7 +123,7 @@ export default function ImageUploader({
<DialogContent> <DialogContent>
<div className="mb-4 sm:flex sm:items-start"> <div className="mb-4 sm:flex sm:items-start">
<div className="mt-3 text-center sm:mt-0 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:text-left">
<h3 className="font-cal text-lg leading-6 text-gray-900" id="modal-title"> <h3 className="font-cal text-emphasis text-lg leading-6" id="modal-title">
{t("upload_target", { target })} {t("upload_target", { target })}
</h3> </h3>
</div> </div>
@@ -131,9 +131,9 @@ export default function ImageUploader({
<div className="mb-4"> <div className="mb-4">
<div className="cropper mt-6 flex flex-col items-center justify-center p-8"> <div className="cropper mt-6 flex flex-col items-center justify-center p-8">
{!result && ( {!result && (
<div className="flex h-20 max-h-20 w-20 items-center justify-start rounded-full bg-gray-50"> <div className="bg-muted flex h-20 max-h-20 w-20 items-center justify-start rounded-full">
{!imageSrc && ( {!imageSrc && (
<p className="w-full text-center text-sm text-white sm:text-xs"> <p className="text-inverted w-full text-center text-sm sm:text-xs">
{t("no_target", { target })} {t("no_target", { target })}
</p> </p>
)} )}
@@ -144,7 +144,7 @@ export default function ImageUploader({
</div> </div>
)} )}
{result && <CropContainer imageSrc={result as string} onCropComplete={setCroppedAreaPixels} />} {result && <CropContainer imageSrc={result as string} onCropComplete={setCroppedAreaPixels} />}
<label className="mt-8 rounded-sm border border-gray-300 bg-white px-3 py-1 text-xs font-medium leading-4 text-gray-700 hover:bg-gray-50 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-gray-900 focus:ring-offset-1 dark:border-gray-800 dark:bg-transparent dark:text-white dark:hover:bg-gray-900"> <label className="border-default bg-default hover:bg-mutedover:text-emphasis dark:hover:bg-inverted text-default dark:text-inverted focus:ring-empthasis mt-8 rounded-sm border px-3 py-1 text-xs font-medium leading-4 focus:outline-none focus:ring-2 focus:ring-offset-1 dark:border-gray-800 dark:bg-transparent">
<input <input
onInput={onInputFile} onInput={onInputFile}
type="file" type="file"
+4 -4
View File
@@ -69,8 +69,8 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
onClick={onClick} onClick={onClick}
className={classNames( className={classNames(
isCurrent isCurrent
? "border-gray-900 text-gray-900" ? "text-emphasis border-gray-900"
: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700", : "hover:border-default hover:text-default text-subtle border-transparent",
"group inline-flex items-center border-b-2 py-4 px-1 text-sm font-medium", "group inline-flex items-center border-b-2 py-4 px-1 text-sm font-medium",
className className
)} )}
@@ -78,7 +78,7 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
{tab.icon && ( {tab.icon && (
<tab.icon <tab.icon
className={classNames( className={classNames(
isCurrent ? "text-gray-900" : "text-gray-400 group-hover:text-gray-500", isCurrent ? "text-emphasis" : "group-hover:text-subtle text-muted",
"-ml-0.5 hidden h-4 w-4 ltr:mr-2 rtl:ml-2 sm:inline-block" "-ml-0.5 hidden h-4 w-4 ltr:mr-2 rtl:ml-2 sm:inline-block"
)} )}
aria-hidden="true" aria-hidden="true"
@@ -91,7 +91,7 @@ const NavTabs: FC<NavTabProps> = ({ tabs, linkProps, ...props }) => {
); );
})} })}
</nav> </nav>
<hr /> <hr className="border-subtle" />
</> </>
); );
}; };
+27 -21
View File
@@ -96,7 +96,7 @@ const Component = ({
return ( return (
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0"> <div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
{hasDescriptionItems && ( {hasDescriptionItems && (
<div className="align-center mb-4 -ml-4 -mr-4 flex min-h-[450px] w-auto basis-3/5 snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap bg-gray-100 p-4 md:mb-8 md:-ml-8 md:-mr-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:justify-center lg:rounded-md"> <div className="align-center bg-subtle mb-4 -ml-4 -mr-4 flex min-h-[450px] w-auto basis-3/5 snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap p-4 md:mb-8 md:-ml-8 md:-mr-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:justify-center lg:rounded-md">
{descriptionItems ? ( {descriptionItems ? (
descriptionItems.map((descriptionItem, index) => descriptionItems.map((descriptionItem, index) =>
typeof descriptionItem === "object" ? ( typeof descriptionItem === "object" ? (
@@ -127,13 +127,17 @@ const Component = ({
<div className="mb-8 flex pt-4"> <div className="mb-8 flex pt-4">
<header> <header>
<div className="mb-4 flex items-center"> <div className="mb-4 flex items-center">
<img className="min-h-16 min-w-16 h-16 w-16" src={logo} alt={name} /> <img
<h1 className="font-cal ml-4 text-3xl text-gray-900">{name}</h1> className={classNames(logo.includes("-dark") && "dark:invert", "min-h-16 min-w-16 h-16 w-16")}
src={logo}
alt={name}
/>
<h1 className="font-cal text-emphasis ml-4 text-3xl">{name}</h1>
</div> </div>
<h2 className="text-sm font-medium text-gray-600"> <h2 className="text-default text-sm font-medium">
<Link <Link
href={`categories/${categories[0]}`} href={`categories/${categories[0]}`}
className="rounded-md bg-gray-100 p-1 text-xs capitalize text-gray-800"> className="bg-subtle text-emphasis rounded-md p-1 text-xs capitalize">
{categories[0]} {categories[0]}
</Link>{" "} </Link>{" "}
• {t("published_by", { author })} • {t("published_by", { author })}
@@ -243,9 +247,11 @@ const Component = ({
</span> </span>
)} )}
<div className="prose-sm prose mt-8">{body}</div> <div className="prose-sm prose prose-headings:text-emphasis prose-code:text-default prose-strong:text-default text-default mt-8">
<h4 className="mt-8 font-semibold text-gray-900 ">{t("pricing")}</h4> {body}
<span> </div>
<h4 className="text-emphasis mt-8 font-semibold ">{t("pricing")}</h4>
<span className="text-default">
{price === 0 ? ( {price === 0 ? (
t("free_to_use_apps") t("free_to_use_apps")
) : ( ) : (
@@ -260,16 +266,16 @@ const Component = ({
)} )}
</span> </span>
<h4 className="mt-8 mb-2 font-semibold text-gray-900 ">{t("learn_more")}</h4> <h4 className="text-emphasis mt-8 mb-2 font-semibold ">{t("learn_more")}</h4>
<ul className="prose-sm -ml-1 -mr-1 leading-5"> <ul className="prose-sm -ml-1 -mr-1 leading-5">
{docs && ( {docs && (
<li> <li>
<a <a
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="text-sm font-normal text-black no-underline hover:underline" className="text-emphasis text-sm font-normal no-underline hover:underline"
href={docs}> href={docs}>
<FiBookOpen className="mr-1 -mt-1 inline h-4 w-4 text-gray-500" /> <FiBookOpen className="text-subtle mr-1 -mt-1 inline h-4 w-4" />
{t("documentation")} {t("documentation")}
</a> </a>
</li> </li>
@@ -279,9 +285,9 @@ const Component = ({
<a <a
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="font-normal text-black no-underline hover:underline" className="text-emphasis font-normal no-underline hover:underline"
href={website}> href={website}>
<FiExternalLink className="mr-1 -mt-px inline h-4 w-4 text-gray-500" /> <FiExternalLink className="text-subtle mr-1 -mt-px inline h-4 w-4" />
{website.replace("https://", "")} {website.replace("https://", "")}
</a> </a>
</li> </li>
@@ -291,9 +297,9 @@ const Component = ({
<a <a
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="font-normal text-black no-underline hover:underline" className="text-emphasis font-normal no-underline hover:underline"
href={"mailto:" + email}> href={"mailto:" + email}>
<FiMail className="mr-1 -mt-px inline h-4 w-4 text-gray-500" /> <FiMail className="text-subtle mr-1 -mt-px inline h-4 w-4" />
{email} {email}
</a> </a>
@@ -304,9 +310,9 @@ const Component = ({
<a <a
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="font-normal text-black no-underline hover:underline" className="text-emphasis font-normal no-underline hover:underline"
href={tos}> href={tos}>
<FiFile className="mr-1 -mt-px inline h-4 w-4 text-gray-500" /> <FiFile className="text-subtle mr-1 -mt-px inline h-4 w-4" />
{t("terms_of_service")} {t("terms_of_service")}
</a> </a>
</li> </li>
@@ -316,16 +322,16 @@ const Component = ({
<a <a
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="font-normal text-black no-underline hover:underline" className="text-emphasis font-normal no-underline hover:underline"
href={privacy}> href={privacy}>
<FiShield className="mr-1 -mt-px inline h-4 w-4 text-gray-500" /> <FiShield className="text-subtle mr-1 -mt-px inline h-4 w-4" />
{t("privacy_policy")} {t("privacy_policy")}
</a> </a>
</li> </li>
)} )}
</ul> </ul>
<hr className="my-8" /> <hr className="border-subtle my-8 border" />
<span className="leading-1 block text-xs text-gray-500"> <span className="leading-1 text-subtle block text-xs">
{t("every_app_published", { appName: APP_NAME, companyName: COMPANY_NAME })} {t("every_app_published", { appName: APP_NAME, companyName: COMPANY_NAME })}
</span> </span>
<a className="mt-2 block text-xs text-red-500" href={`mailto:${SUPPORT_MAIL_ADDRESS}`}> <a className="mt-2 block text-xs text-red-500" href={`mailto:${SUPPORT_MAIL_ADDRESS}`}>
@@ -99,7 +99,7 @@ function CalendarSwitch(props: {
}} }}
/> />
{!!props.destination && ( {!!props.destination && (
<span className="ml-8 inline-flex items-center gap-1 rounded-md bg-gray-100 px-2 py-1 text-sm font-normal text-gray-800 sm:ml-4"> <span className="bg-subtle text-default ml-8 inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm font-normal sm:ml-4">
<FiArrowLeft className="h-4 w-4" /> <FiArrowLeft className="h-4 w-4" />
{t("adding_events_to")} {t("adding_events_to")}
</span> </span>
@@ -177,14 +177,14 @@ function ConnectedCalendarsList(props: Props) {
credentialId={item.credentialId} credentialId={item.credentialId}
trashIcon trashIcon
onSuccess={props.onChanged} onSuccess={props.onChanged}
buttonProps={{ className: "border border-gray-300" }} buttonProps={{ className: "border border-default" }}
/> />
</div> </div>
}> }>
<div className="border-t border-gray-200"> <div className="border-subtle border-t">
{!fromOnboarding && ( {!fromOnboarding && (
<> <>
<p className="px-5 pt-4 text-sm text-gray-500">{t("toggle_calendars_conflict")}</p> <p className="text-subtle px-5 pt-4 text-sm">{t("toggle_calendars_conflict")}</p>
<ul className="space-y-2 px-5 py-4"> <ul className="space-y-2 px-5 py-4">
{item.calendars.map((cal) => ( {item.calendars.map((cal) => (
<CalendarSwitch <CalendarSwitch
@@ -218,7 +218,7 @@ function ConnectedCalendarsList(props: Props) {
credentialId={item.credentialId} credentialId={item.credentialId}
trashIcon trashIcon
onSuccess={props.onChanged} onSuccess={props.onChanged}
buttonProps={{ className: "border border-gray-300" }} buttonProps={{ className: "border border-default" }}
/> />
</div> </div>
} }
@@ -279,18 +279,18 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
</div> </div>
} }
/> />
<div className="flex justify-between rounded-md border border-gray-200 bg-gray-50 p-4"> <div className="bg-muted border-subtle flex justify-between rounded-md border p-4">
<div className="flex w-full flex-col items-start gap-4 md:flex-row md:items-center"> <div className="flex w-full flex-col items-start gap-4 md:flex-row md:items-center">
<div className="relative rounded-md border border-gray-200 bg-white p-1.5"> <div className="bg-default border-subtle relative rounded-md border p-1.5">
<FiCalendar className="h-8 w-8" strokeWidth="1" /> <FiCalendar className="text-default h-8 w-8" strokeWidth="1" />
<FiPlus <FiPlus
className="absolute left-4 top-1/2 ml-0.5 mt-[1px] h-2 w-2 text-black" className="text-emphasis absolute left-4 top-1/2 ml-0.5 mt-[1px] h-2 w-2"
strokeWidth="4" strokeWidth="4"
/> />
</div> </div>
<div className="md:w-6/12"> <div className="md:w-6/12">
<h1 className="text-sm font-semibold">{t("create_events_on")}</h1> <h1 className="text-emphasis text-sm font-semibold">{t("create_events_on")}</h1>
<p className="text-sm font-normal">{t("set_calendar")}</p> <p className="text-default text-sm font-normal">{t("set_calendar")}</p>
</div> </div>
<div className="justify-end md:w-6/12"> <div className="justify-end md:w-6/12">
<DestinationCalendarSelector <DestinationCalendarSelector
+1 -1
View File
@@ -28,7 +28,7 @@ export default function TwoFactor({ center = true }) {
<div className={center ? "mx-auto !mt-0 max-w-sm" : "!mt-0 max-w-sm"}> <div className={center ? "mx-auto !mt-0 max-w-sm" : "!mt-0 max-w-sm"}>
<Label className="mt-4"> {t("2fa_code")}</Label> <Label className="mt-4"> {t("2fa_code")}</Label>
<p className="mb-4 text-sm text-gray-500">{t("2fa_enabled_instructions")}</p> <p className="text-subtle mb-4 text-sm">{t("2fa_enabled_instructions")}</p>
<input hidden type="hidden" value={value} {...methods.register("totpCode")} /> <input hidden type="hidden" value={value} {...methods.register("totpCode")} />
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
<Input <Input
@@ -26,7 +26,7 @@ export default function AdminLayout({
const isAppsPage = router.asPath.startsWith("/settings/admin/apps"); const isAppsPage = router.asPath.startsWith("/settings/admin/apps");
return ( return (
<SettingsLayout {...rest}> <SettingsLayout {...rest}>
<div className="mx-auto flex max-w-4xl flex-row divide-y divide-gray-200"> <div className="divide-subtle mx-auto flex max-w-4xl flex-row divide-y">
<div className={isAppsPage ? "min-w-0" : "flex flex-1 [&>*]:flex-1"}> <div className={isAppsPage ? "min-w-0" : "flex flex-1 [&>*]:flex-1"}>
<ErrorBoundary>{children}</ErrorBoundary> <ErrorBoundary>{children}</ErrorBoundary>
</div> </div>
@@ -7,7 +7,7 @@ import classNames from "@lib/classNames";
function SkeletonLoader() { function SkeletonLoader() {
return ( return (
<ul className="animate-pulse divide-y divide-gray-200 rounded-md border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden"> <ul className="divide-subtle border-subtle bg-default animate-pulse divide-y rounded-md border sm:mx-0 sm:overflow-hidden">
<SkeletonItem /> <SkeletonItem />
<SkeletonItem /> <SkeletonItem />
<SkeletonItem /> <SkeletonItem />
@@ -20,8 +20,8 @@ export default SkeletonLoader;
function SkeletonItem() { function SkeletonItem() {
return ( return (
<li> <li>
<div className="flex items-center justify-between py-5 hover:bg-neutral-50 ltr:pl-4 rtl:pr-4 sm:ltr:pl-0 sm:rtl:pr-0"> <div className="flex items-center justify-between py-5 ltr:pl-4 rtl:pr-4 sm:ltr:pl-0 sm:rtl:pr-0">
<div className="items-between flex w-full flex-col justify-center hover:bg-neutral-50 sm:px-6"> <div className="items-between flex w-full flex-col justify-center sm:px-6">
<SkeletonText className="my-1 h-4 w-32" /> <SkeletonText className="my-1 h-4 w-32" />
<SkeletonText className="my-1 h-2 w-24" /> <SkeletonText className="my-1 h-2 w-24" />
<SkeletonText className="my-1 h-2 w-40" /> <SkeletonText className="my-1 h-2 w-40" />
@@ -43,7 +43,7 @@ export const SelectSkeletonLoader = ({ className }: { className?: string }) => {
return ( return (
<li <li
className={classNames( className={classNames(
"group flex w-full items-center justify-between rounded-sm border border-gray-200 px-[10px] py-3", "border-subtle group flex w-full items-center justify-between rounded-sm border px-[10px] py-3",
className className
)}> )}>
<div className="flex-grow truncate text-sm"> <div className="flex-grow truncate text-sm">
@@ -41,7 +41,7 @@ export function AvailableEventLocations({ locations }: { locations: Props["event
return ( return (
<div key={`${location.type}-${index}`} className="flex flex-row items-center text-sm font-medium"> <div key={`${location.type}-${index}`} className="flex flex-row items-center text-sm font-medium">
{eventLocationType.iconUrl === "/link.svg" ? ( {eventLocationType.iconUrl === "/link.svg" ? (
<FiLink className="dark:text-darkgray-600 min-h-4 min-w-4 ml-[2px] opacity-70 ltr:mr-[10px] rtl:ml-[10px] dark:opacity-100 " /> <FiLink className="text-default min-h-4 min-w-4 ml-[2px] opacity-70 ltr:mr-[10px] rtl:ml-[10px] " />
) : ( ) : (
<img <img
src={eventLocationType.iconUrl} src={eventLocationType.iconUrl}
@@ -61,7 +61,7 @@ export function AvailableEventLocations({ locations }: { locations: Props["event
const filteredLocations = renderLocations.filter(excludeNullValues) as JSX.Element[]; const filteredLocations = renderLocations.filter(excludeNullValues) as JSX.Element[];
return filteredLocations.length ? ( return filteredLocations.length ? (
<div className="dark:text-darkgray-600 mr-6 flex w-full flex-col space-y-4 break-words text-sm text-gray-600"> <div className="text-default mr-6 flex w-full flex-col space-y-4 break-words text-sm">
{filteredLocations} {filteredLocations}
</div> </div>
) : null; ) : null;
@@ -66,13 +66,13 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
return ( return (
<div ref={slotPickerRef}> <div ref={slotPickerRef}>
{!!date ? ( {!!date ? (
<div className="dark:bg-darkgray-100 mt-8 flex h-full w-full flex-col rounded-md px-4 text-center sm:mt-0 sm:p-4 md:-mb-5 md:min-w-[200px] md:p-4 lg:min-w-[300px]"> <div className="mt-8 flex h-full w-full flex-col rounded-md px-4 text-center sm:mt-0 sm:p-4 md:-mb-5 md:min-w-[200px] md:p-4 lg:min-w-[300px]">
<div className="mb-4 flex items-center text-left text-base"> <div className="mb-4 flex items-center text-left text-base">
<div className="mr-4"> <div className="mr-4">
<span className="text-bookingdarker dark:text-darkgray-800 font-semibold text-gray-900"> <span className="text-emphasis font-semibold">
{nameOfDay(i18n.language, Number(date.format("d")), "short")} {nameOfDay(i18n.language, Number(date.format("d")), "short")}
</span> </span>
<span className="text-bookinglight"> <span className="text-subtle">
, {date.toDate().toLocaleString(i18n.language, { month: "short" })} {date.format(" D ")} , {date.toDate().toLocaleString(i18n.language, { month: "short" })} {date.format(" D ")}
</span> </span>
</div> </div>
@@ -132,7 +132,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
{seatsPerTimeSlot && slot.attendees && (slotFull || notEnoughSeats) ? ( {seatsPerTimeSlot && slot.attendees && (slotFull || notEnoughSeats) ? (
<div <div
className={classNames( className={classNames(
"text-primary-500 dark:bg-darkgray-200 dark:text-darkgray-900 mb-2 block rounded-sm border bg-white py-2 font-medium opacity-25 dark:border-transparent ", "text-default bg-default border-subtle mb-2 block rounded-sm border py-2 font-medium opacity-25",
brand === "#fff" || brand === "#ffffff" ? "" : "" brand === "#fff" || brand === "#ffffff" ? "" : ""
)}> )}>
{dayjs(slot.time).tz(timeZone()).format(timeFormat)} {dayjs(slot.time).tz(timeZone()).format(timeFormat)}
@@ -147,8 +147,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
href={bookingUrl} href={bookingUrl}
prefetch={false} prefetch={false}
className={classNames( className={classNames(
"hover:bg-brand hover:border-brand hover:text-brandcontrast dark:hover:text-darkmodebrandcontrast", " bg-default dark:bg-muted border-default hover:bg-subtle hover:border-brand-default text-emphasis mb-2 block rounded-md border py-2 text-sm font-medium",
"dark:bg-darkgray-200 dark:hover:bg-darkmodebrand dark:hover:border-darkmodebrand dark:text-darkgray-800 mb-2 block rounded-md border bg-white py-2 text-sm font-medium dark:border-transparent",
brand === "#fff" || brand === "#ffffff" ? "" : "" brand === "#fff" || brand === "#ffffff" ? "" : ""
)} )}
data-testid="time"> data-testid="time">
@@ -174,7 +173,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
{!isLoading && !slots.length && ( {!isLoading && !slots.length && (
<div className="-mt-4 flex h-full w-full flex-col content-center items-center justify-center"> <div className="-mt-4 flex h-full w-full flex-col content-center items-center justify-center">
<h1 className="my-6 text-xl text-black dark:text-white">{t("all_booked_today")}</h1> <h1 className="text-emphasis my-6 text-xl">{t("all_booked_today")}</h1>
</div> </div>
)} )}
@@ -83,30 +83,25 @@ const BookingDescription: FC<Props> = (props) => {
size="sm" size="sm"
truncateAfter={3} truncateAfter={3}
/> />
<h2 className="mt-1 mb-2 break-words text-sm font-medium text-gray-600 dark:text-gray-300"> <h2 className="text-default mt-1 mb-2 break-words text-sm font-medium ">{profile.name}</h2>
{profile.name} <h1 className="font-cal text-emphasis mb-6 break-words text-2xl font-semibold leading-none">
</h2>
<h1 className="font-cal dark:text-darkgray-900 mb-6 break-words text-2xl font-semibold leading-none text-gray-900">
{eventType.title} {eventType.title}
</h1> </h1>
<div className="dark:text-darkgray-600 flex flex-col space-y-4 text-sm font-medium text-gray-600"> <div className=" text-default flex flex-col space-y-4 text-sm font-medium">
{eventType?.description && ( {eventType?.description && (
<div <div
className={classNames( className={classNames(
"scroll-bar scrollbar-track-w-20 -mx-5 flex max-h-[180px] overflow-y-scroll px-5 ", "scroll-bar scrollbar-track-w-20 -mx-5 flex max-h-[180px] overflow-y-scroll px-5 ",
isBookingPage && "dark:text-darkgray-600 text-sm font-medium text-gray-600" isBookingPage && "text-default text-sm font-medium"
)}> )}>
{/* TODO: Fix colors when token is introdcued to DS */}
<div className="max-w-full flex-shrink break-words [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"> <div className="max-w-full flex-shrink break-words [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600">
<EventTypeDescriptionSafeHTML eventType={eventType} /> <EventTypeDescriptionSafeHTML eventType={eventType} />
</div> </div>
</div> </div>
)} )}
{requiresConfirmation && ( {requiresConfirmation && (
<div <div className={classNames("items-top flex", isBookingPage && "text-default text-sm font-medium")}>
className={classNames(
"items-top flex",
isBookingPage && "dark:text-darkgray-600 text-sm font-medium text-gray-600"
)}>
<div> <div>
<FiCheckSquare className="ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px] " /> <FiCheckSquare className="ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px] " />
</div> </div>
@@ -119,7 +114,7 @@ const BookingDescription: FC<Props> = (props) => {
<div <div
className={classNames( className={classNames(
"flex flex-nowrap text-sm font-medium", "flex flex-nowrap text-sm font-medium",
isBookingPage && "dark:text-darkgray-600 text-gray-600", isBookingPage && "text-default",
!eventType.metadata?.multipleDuration && "items-center" !eventType.metadata?.multipleDuration && "items-center"
)}> )}>
<FiClock <FiClock
@@ -137,9 +132,9 @@ const BookingDescription: FC<Props> = (props) => {
variant="gray" variant="gray"
className={classNames( className={classNames(
duration === dur.toString() duration === dur.toString()
? "bg-darkgray-200 text-darkgray-900 dark:bg-darkmodebrand dark:!text-darkmodebrandcontrast" ? "bg-emphasis border-emphasis text-emphasis "
: "hover:bg-darkgray-200 dark:hover:bg-darkmodebrand hover:text-darkgray-900 dark:hover:text-darkmodebrandcontrast dark:bg-darkgray-200 bg-gray-200 text-gray-900 dark:text-white", : "bg-subtle text-default border-transparent ",
"cursor-pointer" "cursor-pointer border"
)} )}
onClick={() => { onClick={() => {
setDuration(dur); setDuration(dur);
+16 -16
View File
@@ -271,7 +271,7 @@ function BookingListItem(booking: BookingItemProps) {
label={ label={
<> <>
{t("rejection_reason")} {t("rejection_reason")}
<span className="font-normal text-gray-500"> (Optional)</span> <span className="text-subtle font-normal"> (Optional)</span>
</> </>
} }
value={rejectionReason} value={rejectionReason}
@@ -292,13 +292,13 @@ function BookingListItem(booking: BookingItemProps) {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
<tr className="group flex flex-col hover:bg-gray-50 sm:flex-row"> <tr className="hover:bg-muted group flex flex-col sm:flex-row">
<td <td
className="hidden align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:min-w-[12rem]" className="hidden align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:min-w-[12rem]"
onClick={onClickTableData}> onClick={onClickTableData}>
<div className="cursor-pointer py-4"> <div className="cursor-pointer py-4">
<div className="text-sm leading-6 text-gray-900">{startTime}</div> <div className="text-emphasis text-sm leading-6">{startTime}</div>
<div className="text-sm text-gray-500"> <div className="text-subtle text-sm">
{formatTime(booking.startTime, user?.timeFormat, user?.timeZone)} -{" "} {formatTime(booking.startTime, user?.timeFormat, user?.timeZone)} -{" "}
{formatTime(booking.endTime, user?.timeFormat, user?.timeZone)} {formatTime(booking.endTime, user?.timeFormat, user?.timeZone)}
<MeetingTimeInTimezones <MeetingTimeInTimezones
@@ -325,7 +325,7 @@ function BookingListItem(booking: BookingItemProps) {
</Badge> </Badge>
)} )}
{recurringDates !== undefined && ( {recurringDates !== undefined && (
<div className="mt-2 text-sm text-gray-400"> <div className="text-muted mt-2 text-sm">
<RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} /> <RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} />
</div> </div>
)} )}
@@ -335,8 +335,8 @@ function BookingListItem(booking: BookingItemProps) {
{/* Time and Badges for mobile */} {/* Time and Badges for mobile */}
<div className="w-full pt-4 pb-2 sm:hidden"> <div className="w-full pt-4 pb-2 sm:hidden">
<div className="flex w-full items-center justify-between sm:hidden"> <div className="flex w-full items-center justify-between sm:hidden">
<div className="text-sm leading-6 text-gray-900">{startTime}</div> <div className="text-emphasis text-sm leading-6">{startTime}</div>
<div className="pr-2 text-sm text-gray-500"> <div className="text-subtle pr-2 text-sm">
{formatTime(booking.startTime, user?.timeFormat, user?.timeZone)} -{" "} {formatTime(booking.startTime, user?.timeFormat, user?.timeZone)} -{" "}
{formatTime(booking.endTime, user?.timeFormat, user?.timeZone)} {formatTime(booking.endTime, user?.timeFormat, user?.timeZone)}
<MeetingTimeInTimezones <MeetingTimeInTimezones
@@ -365,7 +365,7 @@ function BookingListItem(booking: BookingItemProps) {
</Badge> </Badge>
)} )}
{recurringDates !== undefined && ( {recurringDates !== undefined && (
<div className="text-sm text-gray-400 sm:hidden"> <div className="text-muted text-sm sm:hidden">
<RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} /> <RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} />
</div> </div>
)} )}
@@ -375,21 +375,21 @@ function BookingListItem(booking: BookingItemProps) {
<div <div
title={title} title={title}
className={classNames( className={classNames(
"max-w-10/12 sm:max-w-56 text-sm font-medium leading-6 text-gray-900 md:max-w-full", "max-w-10/12 sm:max-w-56 text-emphasis text-sm font-medium leading-6 md:max-w-full",
isCancelled ? "line-through" : "" isCancelled ? "line-through" : ""
)}> )}>
{title} {title}
<span> </span> <span> </span>
{!!booking?.eventType?.price && !booking.paid && ( {!!booking?.eventType?.price && !booking.paid && (
<Badge className="hidden ltr:ml-2 ltr:mr-2 rtl:ml-2 sm:inline-flex" variant="orange"> <Badge className="ms-2 me-2 hidden sm:inline-flex" variant="orange">
{t("pending_payment")} {t("pending_payment")}
</Badge> </Badge>
)} )}
</div> </div>
{booking.description && ( {booking.description && (
<div <div
className="max-w-10/12 sm:max-w-32 md:max-w-52 xl:max-w-80 truncate text-sm text-gray-600" className="max-w-10/12 sm:max-w-32 md:max-w-52 xl:max-w-80 text-default truncate text-sm"
title={booking.description}> title={booking.description}>
&quot;{booking.description}&quot; &quot;{booking.description}&quot;
</div> </div>
@@ -413,7 +413,7 @@ function BookingListItem(booking: BookingItemProps) {
<> <>
{isPending && user?.id === booking.user?.id && <TableActions actions={pendingActions} />} {isPending && user?.id === booking.user?.id && <TableActions actions={pendingActions} />}
{isConfirmed && <TableActions actions={bookedActions} />} {isConfirmed && <TableActions actions={bookedActions} />}
{isRejected && <div className="text-sm text-gray-500">{t("rejected")}</div>} {isRejected && <div className="text-subtle text-sm">{t("rejected")}</div>}
</> </>
) : null} ) : null}
{isPast && isPending && !isConfirmed ? <TableActions actions={bookedActions} /> : null} {isPast && isPending && !isConfirmed ? <TableActions actions={bookedActions} /> : null}
@@ -475,10 +475,10 @@ const RecurringBookingsTooltip = ({ booking, recurringDates }: RecurringBookings
</p> </p>
); );
})}> })}>
<div className="text-gray-600 dark:text-white"> <div className="text-default">
<FiRefreshCcw <FiRefreshCcw
strokeWidth="3" strokeWidth="3"
className="float-left mr-1 mt-1.5 inline-block h-3 w-3 text-gray-400" className="text-muted float-left mr-1 mt-1.5 inline-block h-3 w-3"
/> />
<p className="mt-1 pl-5 text-xs"> <p className="mt-1 pl-5 text-xs">
{booking.status === BookingStatus.ACCEPTED {booking.status === BookingStatus.ACCEPTED
@@ -551,13 +551,13 @@ const DisplayAttendees = ({
}) => { }) => {
const { t } = useLocale(); const { t } = useLocale();
return ( return (
<div className="text-sm text-gray-900"> <div className="text-emphasis text-sm">
{user && <FirstAttendee user={user} currentEmail={currentEmail} />} {user && <FirstAttendee user={user} currentEmail={currentEmail} />}
{attendees.length > 1 ? <span>,&nbsp;</span> : <span>&nbsp;{t("and")}&nbsp;</span>} {attendees.length > 1 ? <span>,&nbsp;</span> : <span>&nbsp;{t("and")}&nbsp;</span>}
<Attendee {...attendees[0]} /> <Attendee {...attendees[0]} />
{attendees.length > 1 && ( {attendees.length > 1 && (
<> <>
<div className="inline-block text-sm text-gray-900">&nbsp;{t("and")}&nbsp;</div> <div className="text-emphasis inline-block text-sm">&nbsp;{t("and")}&nbsp;</div>
{attendees.length > 2 ? ( {attendees.length > 2 ? (
<Tooltip <Tooltip
content={attendees.slice(1).map((attendee) => ( content={attendees.slice(1).map((attendee) => (
@@ -47,11 +47,11 @@ export default function CancelBooking(props: Props) {
<> <>
{error && ( {error && (
<div className="mt-8"> <div className="mt-8">
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100"> <div className="bg-error mx-auto flex h-12 w-12 items-center justify-center rounded-full">
<FiX className="h-6 w-6 text-red-600" /> <FiX className="h-6 w-6 text-red-600" />
</div> </div>
<div className="mt-3 text-center sm:mt-5"> <div className="mt-3 text-center sm:mt-5">
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title"> <h3 className="text-emphasis text-lg font-medium leading-6" id="modal-title">
{error} {error}
</h3> </h3>
</div> </div>
@@ -59,13 +59,13 @@ export default function CancelBooking(props: Props) {
)} )}
{!error && ( {!error && (
<div className="mt-5 sm:mt-6"> <div className="mt-5 sm:mt-6">
<label className="text-bookingdark font-medium dark:text-white">{t("cancellation_reason")}</label> <label className="text-default font-medium">{t("cancellation_reason")}</label>
<TextArea <TextArea
ref={cancelBookingRef} ref={cancelBookingRef}
placeholder={t("cancellation_reason_placeholder")} placeholder={t("cancellation_reason_placeholder")}
value={cancellationReason} value={cancellationReason}
onChange={(e) => setCancellationReason(e.target.value)} onChange={(e) => setCancellationReason(e.target.value)}
className="dark:bg-darkgray-100 dark:border-darkgray-400 mt-2 mb-4 w-full dark:text-white " className="mt-2 mb-4 w-full "
rows={3} rows={3}
/> />
<div className="flex flex-col-reverse rtl:space-x-reverse "> <div className="flex flex-col-reverse rtl:space-x-reverse ">
@@ -77,7 +77,6 @@ export default function CancelBooking(props: Props) {
{t("nevermind")} {t("nevermind")}
</Button> </Button>
<Button <Button
className="dark:bg-darkmodebrand dark:hover:border-darkmodebrand dark:text-darkmodebrandcontrast bg-brand hover:border-brand text-brandcontrast flex justify-center"
data-testid="cancel" data-testid="cancel"
onClick={async () => { onClick={async () => {
setLoading(true); setLoading(true);
@@ -4,7 +4,7 @@ import { SkeletonText } from "@calcom/ui";
function SkeletonLoader() { function SkeletonLoader() {
return ( return (
<ul className="animate-pulse divide-y divide-gray-200 rounded-md border border-gray-200 bg-white sm:overflow-hidden"> <ul className="divide-subtle border-subtle bg-default animate-pulse divide-y rounded-md border sm:overflow-hidden">
<SkeletonItem /> <SkeletonItem />
<SkeletonItem /> <SkeletonItem />
<SkeletonItem /> <SkeletonItem />
+1 -1
View File
@@ -164,7 +164,7 @@ export const SlotPicker = ({
isLoading={isLoading} isLoading={isLoading}
className={classNames( className={classNames(
"mt-8 px-4 pb-4 sm:mt-0 md:min-w-[300px] md:px-4 lg:min-w-[455px]", "mt-8 px-4 pb-4 sm:mt-0 md:min-w-[300px] md:px-4 lg:min-w-[455px]",
selectedDate ? "sm:dark:border-darkgray-200 border-gray-200 sm:border-r sm:p-4 sm:pr-6" : "sm:p-4" selectedDate ? " border-subtle sm:border-r sm:p-4 sm:pr-6" : "sm:p-4"
)} )}
includedDates={Object.keys(monthSlots).filter((k) => monthSlots[k].length > 0)} includedDates={Object.keys(monthSlots).filter((k) => monthSlots[k].length > 0)}
locale={isLocaleReady ? i18n.language : "en"} locale={isLocaleReady ? i18n.language : "en"}
+2 -1
View File
@@ -27,7 +27,8 @@ const TimeOptions: FC<Props> = ({ onSelectTimeZone }) => {
<TimezoneSelect <TimezoneSelect
id="timeZone" id="timeZone"
classNames={{ classNames={{
singleValue: () => "dark:text-darkgray-600 text-gray-600", singleValue: () => "text-default",
dropdownIndicator: () => "text-default",
menu: () => "!w-64 max-w-[90vw]", menu: () => "!w-64 max-w-[90vw]",
}} }}
variant="minimal" variant="minimal"
@@ -14,8 +14,8 @@ import {
useIsBackgroundTransparent, useIsBackgroundTransparent,
useIsEmbed, useIsEmbed,
} from "@calcom/embed-core/embed-iframe"; } from "@calcom/embed-core/embed-iframe";
import CustomBranding from "@calcom/lib/CustomBranding";
import classNames from "@calcom/lib/classNames"; import classNames from "@calcom/lib/classNames";
import useGetBrandingColours from "@calcom/lib/getBrandColours";
import getPaymentAppData from "@calcom/lib/getPaymentAppData"; import getPaymentAppData from "@calcom/lib/getPaymentAppData";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme"; import useTheme from "@calcom/lib/hooks/useTheme";
@@ -24,7 +24,7 @@ import { getRecurringFreq } from "@calcom/lib/recurringStrings";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { detectBrowserTimeFormat, setIs24hClockInLocalStorage, TimeFormat } from "@calcom/lib/timeFormat"; import { detectBrowserTimeFormat, setIs24hClockInLocalStorage, TimeFormat } from "@calcom/lib/timeFormat";
import { trpc } from "@calcom/trpc"; import { trpc } from "@calcom/trpc";
import { HeadSeo } from "@calcom/ui"; import { HeadSeo, useCalcomTheme } from "@calcom/ui";
import { FiCreditCard, FiUser, FiRefreshCcw } from "@calcom/ui/components/icon"; import { FiCreditCard, FiUser, FiRefreshCcw } from "@calcom/ui/components/icon";
import { timeZone as localStorageTimeZone } from "@lib/clock"; import { timeZone as localStorageTimeZone } from "@lib/clock";
@@ -58,12 +58,24 @@ const dateQuerySchema = z.object({
export type Props = AvailabilityTeamPageProps | AvailabilityPageProps | DynamicAvailabilityPageProps; export type Props = AvailabilityTeamPageProps | AvailabilityPageProps | DynamicAvailabilityPageProps;
const useBrandColors = ({ brandColor, darkBrandColor }: { brandColor: string; darkBrandColor: string }) => {
const brandTheme = useGetBrandingColours({
lightVal: brandColor,
darkVal: darkBrandColor,
});
useCalcomTheme(brandTheme);
};
const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => { const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
const router = useRouter(); const router = useRouter();
const isEmbed = useIsEmbed(restProps.isEmbed); const isEmbed = useIsEmbed(restProps.isEmbed);
const query = dateQuerySchema.parse(router.query); const query = dateQuerySchema.parse(router.query);
const { rescheduleUid } = query; const { rescheduleUid } = query;
useTheme(profile.theme); useTheme(profile.theme);
useBrandColors({
brandColor: profile.brandColor,
darkBrandColor: profile.darkBrandColor,
});
const { t } = useLocale(); const { t } = useLocale();
const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker"); const availabilityDatePickerEmbedStyles = useEmbedStyles("availabilityDatePicker");
//TODO: Plan to remove shouldAlignCentrallyInEmbed config //TODO: Plan to remove shouldAlignCentrallyInEmbed config
@@ -157,30 +169,27 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
isBrandingHidden={restProps.isBrandingHidden} isBrandingHidden={restProps.isBrandingHidden}
/> />
<BookingPageTagManager eventType={eventType} /> <BookingPageTagManager eventType={eventType} />
<CustomBranding lightVal={profile.brandColor} darkVal={profile.darkBrandColor} />
<div> <div>
<main <main
className={classNames( className={classNames(
"flex flex-col md:mx-4", "flex flex-col md:mx-4",
shouldAlignCentrally ? "items-center" : "items-start", shouldAlignCentrally ? "items-center" : "items-start",
!isEmbed && classNames("mx-auto my-0 ease-in-out md:my-24") !isEmbed && classNames("bg-subtle dark:bg-default mx-auto my-0 ease-in-out md:my-24")
)}> )}>
<div> <div>
<div <div
style={availabilityDatePickerEmbedStyles} style={availabilityDatePickerEmbedStyles}
className={classNames( className={classNames(
isBackgroundTransparent isBackgroundTransparent ? "" : "bg-default dark:bg-muted pb-4 md:pb-0",
? "" "border-subtle md:rounded-md md:border",
: "dark:bg-darkgray-100 sm:dark:border-darkgray-300 bg-white pb-4 md:pb-0",
"border-bookinglightest md:rounded-md md:border",
isEmbed && "mx-auto" isEmbed && "mx-auto"
)}> )}>
<div className="md:flex"> <div className="md:flex">
{showEventTypeDetails && ( {showEventTypeDetails && (
<div <div
className={classNames( className={classNames(
"sm:dark:border-darkgray-200 flex flex-col border-gray-200 p-5 sm:border-r", " border-subtle flex flex-col p-5 sm:border-r",
"min-w-full md:w-[280px] md:min-w-[280px]", "min-w-full md:w-[230px] md:min-w-[230px]",
recurringEventCount && "xl:w-[380px] xl:min-w-[380px]" recurringEventCount && "xl:w-[380px] xl:min-w-[380px]"
)}> )}>
<BookingDescription profile={profile} eventType={eventType} rescheduleUid={rescheduleUid}> <BookingDescription profile={profile} eventType={eventType} rescheduleUid={rescheduleUid}>
@@ -188,7 +197,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
<div <div
className={classNames( className={classNames(
"flex flex-nowrap items-center text-sm font-medium", "flex flex-nowrap items-center text-sm font-medium",
"dark:text-darkgray-600 text-gray-600", " text-default",
"ltr:mr-[10px] rtl:ml-[10px]" "ltr:mr-[10px] rtl:ml-[10px]"
)}> )}>
<FiUser <FiUser
@@ -211,7 +220,7 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
type="number" type="number"
min="1" min="1"
max={eventType.recurringEvent.count} max={eventType.recurringEvent.count}
className="w-15 dark:bg-darkgray-200 h-7 rounded-sm border-gray-300 bg-white text-sm font-medium [appearance:textfield] ltr:mr-2 rtl:ml-2 dark:border-gray-500" className="w-15 border-default bg-default dark:border-empthasis h-7 rounded-sm text-sm font-medium [appearance:textfield] ltr:mr-2 rtl:ml-2"
defaultValue={eventType.recurringEvent.count} defaultValue={eventType.recurringEvent.count}
onChange={(event) => { onChange={(event) => {
setRecurringEventCount(parseInt(event?.target.value)); setRecurringEventCount(parseInt(event?.target.value));
@@ -243,12 +252,12 @@ const AvailabilityPage = ({ profile, eventType, ...restProps }: Props) => {
{/* Temporarily disabled - booking?.startTime && rescheduleUid && ( {/* Temporarily disabled - booking?.startTime && rescheduleUid && (
<div> <div>
<p <p
className="mt-4 mb-3 text-gray-600 dark:text-darkgray-600" className="mt-4 mb-3 text-default"
data-testid="former_time_p_desktop"> data-testid="former_time_p_desktop">
{t("former_time")} {t("former_time")}
</p> </p>
<p className="text-gray-500 line-through dark:text-darkgray-600"> <p className="text-subtle line-through ">
<CalendarIcon className="ltr:mr-[10px] rtl:ml-[10px] -mt-1 inline-block h-4 w-4 text-gray-500" /> <CalendarIcon className="ltr:mr-[10px] rtl:ml-[10px] -mt-1 inline-block h-4 w-4 text-subtle" />
{typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)} {typeof booking.startTime === "string" && parseDate(dayjs(booking.startTime), i18n)}
</p> </p>
</div> </div>
@@ -11,7 +11,6 @@ import { z } from "zod";
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager"; import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
import type { EventLocationType } from "@calcom/app-store/locations"; import type { EventLocationType } from "@calcom/app-store/locations";
import { getEventLocationType, locationKeyToString } from "@calcom/app-store/locations";
import { createPaymentLink } from "@calcom/app-store/stripepayment/lib/client"; import { createPaymentLink } from "@calcom/app-store/stripepayment/lib/client";
import { getEventTypeAppData } from "@calcom/app-store/utils"; import { getEventTypeAppData } from "@calcom/app-store/utils";
import type { LocationObject } from "@calcom/core/location"; import type { LocationObject } from "@calcom/core/location";
@@ -29,10 +28,12 @@ import {
import getBookingResponsesSchema, { import getBookingResponsesSchema, {
getBookingResponsesPartialSchema, getBookingResponsesPartialSchema,
} from "@calcom/features/bookings/lib/getBookingResponsesSchema"; } from "@calcom/features/bookings/lib/getBookingResponsesSchema";
import getLocationOptionsForSelect from "@calcom/features/bookings/lib/getLocationOptionsForSelect";
import { FormBuilderField } from "@calcom/features/form-builder/FormBuilder"; import { FormBuilderField } from "@calcom/features/form-builder/FormBuilder";
import CustomBranding from "@calcom/lib/CustomBranding"; import { bookingSuccessRedirect } from "@calcom/lib/bookingSuccessRedirect";
import classNames from "@calcom/lib/classNames"; import classNames from "@calcom/lib/classNames";
import { APP_NAME } from "@calcom/lib/constants"; import { APP_NAME } from "@calcom/lib/constants";
import useGetBrandingColours from "@calcom/lib/getBrandColours";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import useTheme from "@calcom/lib/hooks/useTheme"; import useTheme from "@calcom/lib/hooks/useTheme";
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery"; import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
@@ -40,7 +41,7 @@ import { HttpError } from "@calcom/lib/http-error";
import { getEveryFreqFor } from "@calcom/lib/recurringStrings"; import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { TimeFormat } from "@calcom/lib/timeFormat"; import { TimeFormat } from "@calcom/lib/timeFormat";
import { Button, Form, Tooltip } from "@calcom/ui"; import { Button, Form, Tooltip, useCalcomTheme } from "@calcom/ui";
import { FiAlertTriangle, FiCalendar, FiRefreshCw, FiUser } from "@calcom/ui/components/icon"; import { FiAlertTriangle, FiCalendar, FiRefreshCw, FiUser } from "@calcom/ui/components/icon";
import { timeZone } from "@lib/clock"; import { timeZone } from "@lib/clock";
@@ -64,6 +65,14 @@ const BookingDescriptionPayment = dynamic(
() => import("@components/booking/BookingDescriptionPayment") () => import("@components/booking/BookingDescriptionPayment")
) as unknown as typeof import("@components/booking/BookingDescriptionPayment").default; ) as unknown as typeof import("@components/booking/BookingDescriptionPayment").default;
const useBrandColors = ({ brandColor, darkBrandColor }: { brandColor?: string; darkBrandColor?: string }) => {
const brandTheme = useGetBrandingColours({
lightVal: brandColor,
darkVal: darkBrandColor,
});
useCalcomTheme(brandTheme);
};
type BookingPageProps = BookPageProps | TeamBookingPageProps | HashLinkPageProps; type BookingPageProps = BookPageProps | TeamBookingPageProps | HashLinkPageProps;
const BookingFields = ({ const BookingFields = ({
fields, fields,
@@ -135,26 +144,14 @@ const BookingFields = ({
} }
const optionsInputs = field.optionsInputs; const optionsInputs = field.optionsInputs;
const options = locations.map((location) => { // TODO: Instead of `getLocationOptionsForSelect` options should be retrieved from dataStore[field.getOptionsAt]. It would make it agnostic of the `name` of the field.
const eventLocation = getEventLocationType(location.type); const options = getLocationOptionsForSelect(locations, t);
const locationString = locationKeyToString(location); options.forEach((option) => {
const optionInput = optionsInputs[option.value as keyof typeof optionsInputs];
if (typeof locationString !== "string" || !eventLocation) {
// It's possible that location app got uninstalled
return null;
}
const type = eventLocation.type;
const optionInput = optionsInputs[type as keyof typeof optionsInputs];
if (optionInput) { if (optionInput) {
optionInput.placeholder = t(eventLocation?.attendeeInputPlaceholder || ""); optionInput.placeholder = option.inputPlaceholder;
} }
return {
label: t(locationString),
value: type,
};
}); });
field.options = options.filter( field.options = options.filter(
(location): location is NonNullable<(typeof options)[number]> => !!location (location): location is NonNullable<(typeof options)[number]> => !!location
); );
@@ -268,15 +265,19 @@ const BookingPage = ({
); );
} }
return router.push({ const query = {
pathname: `/booking/${uid}`, isSuccessBookingPage: true,
query: { email: bookingForm.getValues("responses.email"),
isSuccessBookingPage: true, eventTypeSlug: eventType.slug,
email: bookingForm.getValues("responses.email"), seatReferenceUid: "seatReferenceUid" in responseData ? responseData.seatReferenceUid : null,
eventTypeSlug: eventType.slug, ...(rescheduleUid && booking?.startTime && { formerTime: booking.startTime.toString() }),
seatReferenceUid: "seatReferenceUid" in responseData ? responseData.seatReferenceUid : null, };
...(rescheduleUid && booking?.startTime && { formerTime: booking.startTime.toString() }),
}, return bookingSuccessRedirect({
router,
successRedirectUrl: eventType.successRedirectUrl,
query,
bookingUid: uid,
}); });
}, },
}); });
@@ -284,16 +285,18 @@ const BookingPage = ({
const recurringMutation = useMutation(createRecurringBooking, { const recurringMutation = useMutation(createRecurringBooking, {
onSuccess: async (responseData = []) => { onSuccess: async (responseData = []) => {
const { uid } = responseData[0] || {}; const { uid } = responseData[0] || {};
const query = {
return router.push({ isSuccessBookingPage: true,
pathname: `/booking/${uid}`, allRemainingBookings: true,
query: { email: bookingForm.getValues("responses.email"),
isSuccessBookingPage: true, eventTypeSlug: eventType.slug,
allRemainingBookings: true, formerTime: booking?.startTime.toString(),
email: bookingForm.getValues("responses.email"), };
eventTypeSlug: eventType.slug, return bookingSuccessRedirect({
formerTime: booking?.startTime.toString(), router,
}, successRedirectUrl: eventType.successRedirectUrl,
query,
bookingUid: uid,
}); });
}, },
}); });
@@ -303,6 +306,10 @@ const BookingPage = ({
} = useTypedQuery(routerQuerySchema); } = useTypedQuery(routerQuerySchema);
useTheme(profile.theme); useTheme(profile.theme);
useBrandColors({
brandColor: profile.brandColor,
darkBrandColor: profile.darkBrandColor,
});
const querySchema = getBookingResponsesPartialSchema({ const querySchema = getBookingResponsesPartialSchema({
eventType: { eventType: {
@@ -512,7 +519,6 @@ const BookingPage = ({
<link rel="icon" href="/favico.ico" /> <link rel="icon" href="/favico.ico" />
</Head> </Head>
<BookingPageTagManager eventType={eventType} /> <BookingPageTagManager eventType={eventType} />
<CustomBranding lightVal={profile.brandColor} darkVal={profile.darkBrandColor} />
<main <main
className={classNames( className={classNames(
shouldAlignCentrally ? "mx-auto" : "", shouldAlignCentrally ? "mx-auto" : "",
@@ -522,16 +528,16 @@ const BookingPage = ({
<div <div
className={classNames( className={classNames(
"main", "main",
isBackgroundTransparent ? "" : "dark:bg-darkgray-100 bg-white dark:border", isBackgroundTransparent ? "" : "dark:bg-darkgray-100 bg-default dark:border",
"dark:border-darkgray-300 rounded-md sm:border" "dark:border-darkgray-300 rounded-md sm:border"
)}> )}>
<div className="sm:flex"> <div className="sm:flex">
{showEventTypeDetails && ( {showEventTypeDetails && (
<div className="sm:dark:border-darkgray-300 dark:text-darkgray-600 flex flex-col px-6 pt-6 pb-0 text-gray-600 sm:w-1/2 sm:border-r sm:pb-6"> <div className="sm:dark:border-darkgray-300 text-default flex flex-col px-6 pt-6 pb-0 sm:w-1/2 sm:border-r sm:pb-6">
<BookingDescription isBookingPage profile={profile} eventType={eventType}> <BookingDescription isBookingPage profile={profile} eventType={eventType}>
<BookingDescriptionPayment eventType={eventType} /> <BookingDescriptionPayment eventType={eventType} />
{!rescheduleUid && eventType.recurringEvent?.freq && recurringEventCount && ( {!rescheduleUid && eventType.recurringEvent?.freq && recurringEventCount && (
<div className="items-start text-sm font-medium text-gray-600 dark:text-white"> <div className="dark:text-inverted text-default items-start text-sm font-medium">
<FiRefreshCw className="ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" /> <FiRefreshCw className="ml-[2px] inline-block h-4 w-4 ltr:mr-[10px] rtl:ml-[10px]" />
<p className="-ml-2 inline-block items-center px-2"> <p className="-ml-2 inline-block items-center px-2">
{getEveryFreqFor({ {getEveryFreqFor({
@@ -560,7 +566,7 @@ const BookingPage = ({
content={recurringStrings.slice(5).map((timeFormatted, key) => ( content={recurringStrings.slice(5).map((timeFormatted, key) => (
<p key={key}>{timeFormatted}</p> <p key={key}>{timeFormatted}</p>
))}> ))}>
<p className="dark:text-darkgray-600 text-sm"> <p className=" text-sm">
+ {t("plus_more", { count: recurringStrings.length - 5 })} + {t("plus_more", { count: recurringStrings.length - 5 })}
</p> </p>
</Tooltip> </Tooltip>
@@ -637,7 +643,6 @@ const BookingPage = ({
</Button> </Button>
<Button <Button
type="submit" type="submit"
className="dark:bg-darkmodebrand dark:hover:border-darkmodebrand dark:text-darkmodebrandcontrast bg-brand hover:border-brand text-brandcontrast"
data-testid={rescheduleUid ? "confirm-reschedule-button" : "confirm-book-button"} data-testid={rescheduleUid ? "confirm-reschedule-button" : "confirm-book-button"}
loading={mutation.isLoading || recurringMutation.isLoading}> loading={mutation.isLoading || recurringMutation.isLoading}>
{rescheduleUid ? t("reschedule") : t("confirm")} {rescheduleUid ? t("reschedule") : t("confirm")}
@@ -669,7 +674,7 @@ function ErrorMessage({ error }: { error: unknown }) {
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<FiAlertTriangle className="h-5 w-5 text-blue-400" aria-hidden="true" /> <FiAlertTriangle className="h-5 w-5 text-blue-400" aria-hidden="true" />
</div> </div>
<div className="ltr:ml-3 rtl:mr-3"> <div className="ms-3">
<p className="text-sm text-blue-700"> <p className="text-sm text-blue-700">
{rescheduleUid ? t("reschedule_fail") : t("booking_fail")}{" "} {rescheduleUid ? t("reschedule_fail") : t("booking_fail")}{" "}
{error instanceof HttpError || error instanceof Error ? ( {error instanceof HttpError || error instanceof Error ? (
@@ -164,7 +164,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
return ( return (
<div> <div>
<label htmlFor="locationInput" className="block text-sm font-medium text-gray-700"> <label htmlFor="locationInput" className="text-default block text-sm font-medium">
{t(eventLocationType.messageForOrganizer || "")} {t(eventLocationType.messageForOrganizer || "")}
</label> </label>
<div className="mt-1"> <div className="mt-1">
@@ -174,6 +174,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
id="locationInput" id="locationInput"
placeholder={t(eventLocationType.organizerInputPlaceholder || "")} placeholder={t(eventLocationType.organizerInputPlaceholder || "")}
required required
className="border-default block w-full rounded-sm text-sm"
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
defaultValue={ defaultValue={
defaultLocation ? defaultLocation[eventLocationType.defaultValueVariable] : undefined defaultLocation ? defaultLocation[eventLocationType.defaultValueVariable] : undefined
@@ -182,7 +183,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
<ErrorMessage <ErrorMessage
errors={locationFormMethods.formState.errors} errors={locationFormMethods.formState.errors}
name={eventLocationType.variable} name={eventLocationType.variable}
className="mt-1 text-sm text-red-500" className="text-error mt-1 text-sm"
as="p" as="p"
/> />
</div> </div>
@@ -208,7 +209,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
</div> </div>
); );
} else { } else {
return <p className="text-sm">{getMessageForOrganizer(selectedLocation, t)}</p>; return <p className="text-default text-sm">{getMessageForOrganizer(selectedLocation, t)}</p>;
} }
})(); })();
@@ -216,16 +217,16 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
<Dialog open={isOpenDialog} onOpenChange={(open) => setShowLocationModal(open)}> <Dialog open={isOpenDialog} onOpenChange={(open) => setShowLocationModal(open)}>
<DialogContent> <DialogContent>
<div className="flex flex-row space-x-3"> <div className="flex flex-row space-x-3">
<div className="bg-secondary-100 mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full sm:mx-0 sm:h-10 sm:w-10"> <div className="bg-subtle mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full sm:mx-0 sm:h-10 sm:w-10">
<FiMapPin className="text-primary-600 h-6 w-6" /> <FiMapPin className="text-emphasis h-6 w-6" />
</div> </div>
<div className="w-full"> <div className="w-full">
<div className="mt-3 text-center sm:mt-0 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:text-left">
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title"> <h3 className="text-emphasis text-lg font-medium leading-6" id="modal-title">
{t("edit_location")} {t("edit_location")}
</h3> </h3>
{!booking && ( {!booking && (
<p className="text-sm text-gray-400"> <p className="text-default text-sm">
<Trans i18nKey="cant_find_the_right_video_app_visit_our_app_store"> <Trans i18nKey="cant_find_the_right_video_app_visit_our_app_store">
Can&apos;t find the right video app? Visit our Can&apos;t find the right video app? Visit our
<Link className="cursor-pointer text-blue-500 underline" href="/apps/categories/video"> <Link className="cursor-pointer text-blue-500 underline" href="/apps/categories/video">
@@ -240,8 +241,8 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
{booking && ( {booking && (
<> <>
<p className="mt-6 mb-2 ml-1 text-sm font-bold text-black">{t("current_location")}:</p> <p className="text-emphasis mt-6 mb-2 ml-1 text-sm font-bold">{t("current_location")}:</p>
<p className="mb-2 ml-1 text-sm text-black"> <p className="text-emphasis mb-2 ml-1 text-sm">
{getHumanReadableLocationValue(booking.location, t)} {getHumanReadableLocationValue(booking.location, t)}
</p> </p>
</> </>
@@ -48,10 +48,10 @@ export const RescheduleDialog = (props: IRescheduleDialog) => {
</div> </div>
<div className="pt-1"> <div className="pt-1">
<DialogHeader title={t("send_reschedule_request")} /> <DialogHeader title={t("send_reschedule_request")} />
<p className="text-sm text-gray-500">{t("reschedule_modal_description")}</p> <p className="text-subtle text-sm">{t("reschedule_modal_description")}</p>
<p className="mt-6 mb-2 text-sm font-bold text-black"> <p className="text-emphasis mt-6 mb-2 text-sm font-bold">
{t("reason_for_reschedule_request")} {t("reason_for_reschedule_request")}
<span className="font-normal text-gray-500"> (Optional)</span> <span className="text-subtle font-normal"> (Optional)</span>
</p> </p>
<TextArea <TextArea
data-testid="reschedule_reason" data-testid="reschedule_reason"
+8 -8
View File
@@ -29,15 +29,15 @@ const ErrorDebugPanel: React.FC<{ error: Props["error"]; children?: never }> = (
]; ];
return ( return (
<div className="overflow-hidden bg-white shadow sm:rounded-lg"> <div className="bg-default overflow-hidden shadow sm:rounded-lg">
<div className="border-t border-gray-200 px-4 py-5 sm:p-0"> <div className="border-subtle border-t px-4 py-5 sm:p-0">
<dl className="sm:divide-y sm:divide-gray-200"> <dl className="sm:divide-subtle sm:divide-y">
{debugMap.map(([key, value]) => { {debugMap.map(([key, value]) => {
if (value !== undefined) { if (value !== undefined) {
return ( return (
<div key={key} className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6"> <div key={key} className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
<dt className="text-sm font-bold text-black">{key}</dt> <dt className="text-emphasis text-sm font-bold">{key}</dt>
<dd className="mt-1 text-sm text-black sm:col-span-2 sm:mt-0">{value}</dd> <dd className="text-emphasis mt-1 text-sm sm:col-span-2 sm:mt-0">{value}</dd>
</div> </div>
); );
} }
@@ -53,11 +53,11 @@ export const ErrorPage: React.FC<Props> = (props) => {
return ( return (
<> <>
<div className="min-h-screen bg-white px-4"> <div className="bg-default min-h-screen px-4">
<main className="mx-auto max-w-xl pb-6 pt-16 sm:pt-24"> <main className="mx-auto max-w-xl pb-6 pt-16 sm:pt-24">
<div className="text-center"> <div className="text-center">
<p className="text-sm font-semibold uppercase tracking-wide text-black">{statusCode}</p> <p className="text-emphasis text-sm font-semibold uppercase tracking-wide">{statusCode}</p>
<h1 className="mt-2 text-4xl font-extrabold tracking-tight text-gray-900 sm:text-5xl"> <h1 className="text-emphasis mt-2 text-4xl font-extrabold tracking-tight sm:text-5xl">
{message} {message}
</h1> </h1>
</div> </div>
@@ -120,7 +120,7 @@ const EventTypeScheduleDetails = () => {
schedule?.schedule.filter((item) => item.days.includes((dayNum + 1) % 7)) || []; schedule?.schedule.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
return ( return (
<div className="space-y-4 rounded border px-6 pb-4"> <div className="border-default space-y-4 rounded border px-6 pb-4">
<ol className="table border-collapse text-sm"> <ol className="table border-collapse text-sm">
{weekdayNames(i18n.language, 1, "long").map((day, index) => { {weekdayNames(i18n.language, 1, "long").map((day, index) => {
const isAvailable = !!filterDays(index).length; const isAvailable = !!filterDays(index).length;
@@ -128,8 +128,8 @@ const EventTypeScheduleDetails = () => {
<li key={day} className="my-6 flex border-transparent last:mb-2"> <li key={day} className="my-6 flex border-transparent last:mb-2">
<span <span
className={classNames( className={classNames(
"w-20 font-medium sm:w-32", "w-20 font-medium sm:w-32 ",
!isAvailable && "text-gray-500 opacity-50" !isAvailable ? "text-subtle line-through" : "text-default"
)}> )}>
{day} {day}
</span> </span>
@@ -138,25 +138,25 @@ const EventTypeScheduleDetails = () => {
) : isAvailable ? ( ) : isAvailable ? (
<div className="space-y-3 text-right"> <div className="space-y-3 text-right">
{filterDays(index).map((dayRange, i) => ( {filterDays(index).map((dayRange, i) => (
<div key={i} className="flex items-center leading-4"> <div key={i} className="text-default flex items-center leading-4">
<span className="w-16 sm:w-28 sm:text-left"> <span className="w-16 sm:w-28 sm:text-left">
{format(dayRange.startTime, timeFormat === 12)} {format(dayRange.startTime, timeFormat === 12)}
</span> </span>
<span className="ltr:ml-4 rtl:mr-4">-</span> <span className="ms-4">-</span>
<div className="ml-6">{format(dayRange.endTime, timeFormat === 12)}</div> <div className="ml-6">{format(dayRange.endTime, timeFormat === 12)}</div>
</div> </div>
))} ))}
</div> </div>
) : ( ) : (
<span className="ml-6 text-gray-500 opacity-50 sm:ml-0">{t("unavailable")}</span> <span className="text-subtle ml-6 sm:ml-0">{t("unavailable")}</span>
)} )}
</li> </li>
); );
})} })}
</ol> </ol>
<hr /> <hr className="border-subtle" />
<div className="flex flex-col justify-center gap-2 sm:flex-row sm:justify-between"> <div className="flex flex-col justify-center gap-2 sm:flex-row sm:justify-between">
<span className="flex items-center justify-center text-sm text-gray-600 sm:justify-start"> <span className="text-default flex items-center justify-center text-sm sm:justify-start">
<FiGlobe className="ltr:mr-2 rtl:ml-2" /> <FiGlobe className="ltr:mr-2 rtl:ml-2" />
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />} {schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
</span> </span>
@@ -194,7 +194,7 @@ const EventTypeSchedule = () => {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<label htmlFor="availability" className="mb-2 block text-sm font-medium leading-none text-gray-700"> <label htmlFor="availability" className="text-default mb-2 block text-sm font-medium leading-none">
{t("availability")} {t("availability")}
</label> </label>
<Controller <Controller
@@ -58,35 +58,35 @@ const CustomEventTypeModalForm: FC<CustomEventTypeModalFormProps> = (props) => {
className="mb-0" className="mb-0"
/> />
<div className="pt-6 text-sm"> <div className="pt-6 text-sm">
<div className="mb-6 rounded-md bg-gray-100 p-2"> <div className="bg-subtle mb-6 rounded-md p-2">
<h1 className="mb-2 ml-1 font-medium text-gray-900">{t("available_variables")}</h1> <h1 className="text-emphasis mb-2 ml-1 font-medium">{t("available_variables")}</h1>
<div className="mb-2.5 flex font-normal"> <div className="mb-2.5 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Event type title}`}</p> <p className="text-subtle ml-1 mr-5 w-28">{`{Event type title}`}</p>
<p className="text-gray-900">{t("event_name_info")}</p> <p className="text-emphasis">{t("event_name_info")}</p>
</div> </div>
<div className="mb-2.5 flex font-normal"> <div className="mb-2.5 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Organiser}`}</p> <p className="text-subtle ml-1 mr-5 w-28">{`{Organiser}`}</p>
<p className="text-gray-900">{t("your_full_name")}</p> <p className="text-emphasis">{t("your_full_name")}</p>
</div> </div>
<div className="mb-2.5 flex font-normal"> <div className="mb-2.5 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Scheduler}`}</p> <p className="text-subtle ml-1 mr-5 w-28">{`{Scheduler}`}</p>
<p className="text-gray-900">{t("scheduler_full_name")}</p> <p className="text-emphasis">{t("scheduler_full_name")}</p>
</div> </div>
<div className="mb-1 flex font-normal"> <div className="mb-1 flex font-normal">
<p className="ml-1 mr-5 w-28 text-gray-400">{`{Location}`}</p> <p className="text-subtle ml-1 mr-5 w-28">{`{Location}`}</p>
<p className="text-gray-900">{t("location_info")}</p> <p className="text-emphasis">{t("location_info")}</p>
</div> </div>
</div> </div>
<h1 className="mb-2 text-[14px] font-medium leading-4">{t("preview")}</h1> <h1 className="mb-2 text-[14px] font-medium leading-4">{t("preview")}</h1>
<div <div
className="flex h-[212px] w-full rounded-md border-y bg-cover bg-center" className="flex h-[212px] w-full rounded-md border-y bg-cover bg-center dark:invert"
style={{ style={{
backgroundImage: "url(/calendar-preview.svg)", backgroundImage: "url(/calendar-preview.svg)",
}}> }}>
<div className="m-auto flex items-center justify-center self-stretch"> <div className="m-auto flex items-center justify-center self-stretch">
<div className="mt-3 ml-11 box-border h-[110px] w-[120px] flex-col items-start gap-1 rounded-md border border-solid border-black bg-gray-100 text-[12px] leading-3"> <div className="bg-subtle mt-3 ml-11 box-border h-[110px] w-[120px] flex-col items-start gap-1 rounded-md border border-solid border-black text-[12px] leading-3">
<p className="overflow-hidden text-ellipsis p-1.5 font-medium text-gray-900">{previewText}</p> <p className="text-emphasis overflow-hidden text-ellipsis p-1.5 font-medium">{previewText}</p>
<p className="ml-1.5 text-[10px] font-normal text-gray-600">8 - 10 AM</p> <p className="text-default ml-1.5 text-[10px] font-normal">8 - 10 AM</p>
</div> </div>
</div> </div>
</div> </div>
@@ -8,13 +8,15 @@ import { v5 as uuidv5 } from "uuid";
import type { EventNameObjectType } from "@calcom/core/event"; import type { EventNameObjectType } from "@calcom/core/event";
import { getEventName } from "@calcom/core/event"; import { getEventName } from "@calcom/core/event";
import getLocationsOptionsForSelect from "@calcom/features/bookings/lib/getLocationOptionsForSelect";
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector"; import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
import { FormBuilder } from "@calcom/features/form-builder/FormBuilder"; import { FormBuilder } from "@calcom/features/form-builder/FormBuilder";
import { APP_NAME, CAL_URL, IS_SELF_HOSTED } from "@calcom/lib/constants"; import { classNames } from "@calcom/lib";
import { APP_NAME, CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { Prisma } from "@calcom/prisma/client"; import type { Prisma } from "@calcom/prisma/client";
import { trpc } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react";
import { Badge, Button, Checkbox, Label, SettingsToggle, showToast, TextField, Tooltip } from "@calcom/ui"; import { Button, Checkbox, Label, SettingsToggle, showToast, TextField, Tooltip } from "@calcom/ui";
import { FiEdit, FiCopy } from "@calcom/ui/components/icon"; import { FiEdit, FiCopy } from "@calcom/ui/components/icon";
import RequiresConfirmationController from "./RequiresConfirmationController"; import RequiresConfirmationController from "./RequiresConfirmationController";
@@ -99,7 +101,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
<Link <Link
href="/apps/categories/calendar" href="/apps/categories/calendar"
target="_blank" target="_blank"
className="text-sm text-gray-600 hover:text-gray-900"> className="hover:text-emphasis text-default text-sm">
{t("add_another_calendar")} {t("add_another_calendar")}
</Link> </Link>
</div> </div>
@@ -118,7 +120,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
)} )}
/> />
</div> </div>
<p className="text-sm text-gray-600">{t("select_which_cal")}</p> <p className="text-default text-sm">{t("select_which_cal")}</p>
</div> </div>
)} )}
<div className="w-full"> <div className="w-full">
@@ -134,28 +136,33 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
StartIcon={FiEdit} StartIcon={FiEdit}
variant="icon" variant="icon"
color="minimal" color="minimal"
className="hover:stroke-3 min-w-fit px-0 hover:bg-transparent hover:text-black" className="hover:stroke-3 hover:text-emphasis min-w-fit px-0 hover:bg-transparent"
onClick={() => setShowEventNameTip((old) => !old)} onClick={() => setShowEventNameTip((old) => !old)}
aria-label="edit custom name" aria-label="edit custom name"
/> />
} }
/> />
</div> </div>
<hr /> <hr className="border-subtle" />
<FormBuilder <FormBuilder
title={t("booking_questions_title")} title={t("booking_questions_title")}
description={t("booking_questions_description")} description={t("booking_questions_description")}
addFieldLabel={t("add_a_booking_question")} addFieldLabel={t("add_a_booking_question")}
formProp="bookingFields" formProp="bookingFields"
dataStore={{
options: {
locations: getLocationsOptionsForSelect(eventType?.locations ?? [], t),
},
}}
/> />
<hr /> <hr className="border-subtle" />
<RequiresConfirmationController <RequiresConfirmationController
seatsEnabled={seatsEnabled} seatsEnabled={seatsEnabled}
metadata={eventType.metadata} metadata={eventType.metadata}
requiresConfirmation={requiresConfirmation} requiresConfirmation={requiresConfirmation}
onRequiresConfirmation={setRequiresConfirmation} onRequiresConfirmation={setRequiresConfirmation}
/> />
<hr /> <hr className="border-subtle" />
<Controller <Controller
name="hideCalendarNotes" name="hideCalendarNotes"
control={formMethods.control} control={formMethods.control}
@@ -169,7 +176,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
/> />
)} )}
/> />
<hr /> <hr className="border-subtle" />
<Controller <Controller
name="successRedirectUrl" name="successRedirectUrl"
control={formMethods.control} control={formMethods.control}
@@ -183,9 +190,10 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
setRedirectUrlVisible(e); setRedirectUrlVisible(e);
onChange(e ? value : ""); onChange(e ? value : "");
}}> }}>
{/* Textfield has some margin by default we remove that so we can keep consitant aligment */} {/* Textfield has some margin by default we remove that so we can keep consistent alignment */}
<div className="lg:-ml-2"> <div className="lg:-ml-2 lg:-mb-2">
<TextField <TextField
className="w-full"
label={t("redirect_success_booking")} label={t("redirect_success_booking")}
labelSrOnly labelSrOnly
placeholder={t("external_redirect_url")} placeholder={t("external_redirect_url")}
@@ -194,28 +202,19 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
defaultValue={eventType.successRedirectUrl || ""} defaultValue={eventType.successRedirectUrl || ""}
{...formMethods.register("successRedirectUrl")} {...formMethods.register("successRedirectUrl")}
/> />
<div className="mt-2 flex"> <div
<Checkbox className={classNames(
description={t("disable_success_page")} "p-1 text-sm text-orange-600",
// Disable if it's not Self Hosted or if the redirect url is not set formMethods.getValues("successRedirectUrl") ? "block" : "hidden"
disabled={!IS_SELF_HOSTED || !formMethods.watch("successRedirectUrl")} )}>
{...formMethods.register("metadata.disableSuccessPage")} {t("redirect_url_warning")}
/>
{/*TODO: Extract it out into a component when used more than once*/}
{!IS_SELF_HOSTED && (
<Link href="https://cal.com/pricing" target="_blank">
<Badge variant="orange" className="ml-2">
Platform Only
</Badge>
</Link>
)}
</div> </div>
</div> </div>
</SettingsToggle> </SettingsToggle>
</> </>
)} )}
/> />
<hr /> <hr className="border-subtle" />
<SettingsToggle <SettingsToggle
data-testid="hashedLinkCheck" data-testid="hashedLinkCheck"
title={t("private_link")} title={t("private_link")}
@@ -248,7 +247,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
showToast(t("enabled_after_update_description"), "warning"); showToast(t("enabled_after_update_description"), "warning");
} }
}} }}
className="hover:stroke-3 hover:bg-transparent hover:text-black" className="hover:stroke-3 hover:text-emphasis hover:bg-transparent"
type="button"> type="button">
<FiCopy /> <FiCopy />
</Button> </Button>
@@ -257,7 +256,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
/> />
</div> </div>
</SettingsToggle> </SettingsToggle>
<hr /> <hr className="border-subtle" />
<Controller <Controller
name="seatsPerTimeSlotEnabled" name="seatsPerTimeSlotEnabled"
control={formMethods.control} control={formMethods.control}
@@ -84,7 +84,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
</div> </div>
<div> <div>
{!isLoading && notInstalledApps?.length ? ( {!isLoading && notInstalledApps?.length ? (
<h2 className="mt-0 mb-2 text-lg font-semibold text-gray-900">Available Apps</h2> <h2 className="text-emphasis mt-0 mb-2 text-lg font-semibold">Available Apps</h2>
) : null} ) : null}
<div className="before:border-0"> <div className="before:border-0">
{notInstalledApps?.map((app) => ( {notInstalledApps?.map((app) => (
@@ -254,7 +254,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
</div> </div>
</div> </div>
</div> </div>
<hr /> <hr className="border-subtle" />
<Controller <Controller
name="bookingLimits" name="bookingLimits"
control={formMethods.control} control={formMethods.control}
@@ -276,7 +276,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
</SettingsToggle> </SettingsToggle>
)} )}
/> />
<hr /> <hr className="border-subtle" />
<Controller <Controller
name="durationLimits" name="durationLimits"
control={formMethods.control} control={formMethods.control}
@@ -303,7 +303,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
</SettingsToggle> </SettingsToggle>
)} )}
/> />
<hr /> <hr className="border-subtle" />
<Controller <Controller
name="periodType" name="periodType"
control={formMethods.control} control={formMethods.control}
@@ -322,14 +322,14 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
return ( return (
<div <div
className={classNames( className={classNames(
"mb-2 flex flex-wrap items-center text-sm", "text-default mb-2 flex flex-wrap items-center text-sm",
watchPeriodType === "UNLIMITED" && "pointer-events-none opacity-30" watchPeriodType === "UNLIMITED" && "pointer-events-none opacity-30"
)} )}
key={period.type}> key={period.type}>
<RadioGroup.Item <RadioGroup.Item
id={period.type} id={period.type}
value={period.type} value={period.type}
className="min-w-4 flex h-4 w-4 cursor-pointer items-center rounded-full border border-black bg-white focus:border-2 focus:outline-none ltr:mr-2 rtl:ml-2"> className="min-w-4 bg-default flex h-4 w-4 cursor-pointer items-center rounded-full border border-black focus:border-2 focus:outline-none ltr:mr-2 rtl:ml-2">
<RadioGroup.Indicator className="relative flex h-4 w-4 items-center justify-center after:block after:h-2 after:w-2 after:rounded-full after:bg-black" /> <RadioGroup.Indicator className="relative flex h-4 w-4 items-center justify-center after:block after:h-2 after:w-2 after:rounded-full after:bg-black" />
</RadioGroup.Item> </RadioGroup.Item>
{period.prefix ? <span>{period.prefix}&nbsp;</span> : null} {period.prefix ? <span>{period.prefix}&nbsp;</span> : null}
@@ -337,14 +337,14 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
<div className="flex h-9"> <div className="flex h-9">
<Input <Input
type="number" type="number"
className="block w-16 rounded-md border-gray-300 py-3 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2" className="border-default block w-16 rounded-md py-3 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
placeholder="30" placeholder="30"
{...formMethods.register("periodDays", { valueAsNumber: true })} {...formMethods.register("periodDays", { valueAsNumber: true })}
defaultValue={eventType.periodDays || 30} defaultValue={eventType.periodDays || 30}
/> />
<select <select
id="" id=""
className="block h-9 w-full rounded-md border-gray-300 py-2 pl-3 pr-10 text-sm focus:outline-none" className="border-default bg-default text-default block h-9 w-full rounded-md py-2 pl-3 pr-10 text-sm focus:outline-none"
{...formMethods.register("periodCountCalendarDays")} {...formMethods.register("periodCountCalendarDays")}
defaultValue={eventType.periodCountCalendarDays ? "1" : "0"}> defaultValue={eventType.periodCountCalendarDays ? "1" : "0"}>
<option value="1">{t("calendar_days")}</option> <option value="1">{t("calendar_days")}</option>
@@ -353,7 +353,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
</div> </div>
)} )}
{period.type === "RANGE" && ( {period.type === "RANGE" && (
<div className="inline-flex space-x-2 ltr:ml-2 ltr:mr-2 rtl:ml-2 rtl:space-x-reverse"> <div className="ms-2 me-2 inline-flex space-x-2 rtl:space-x-reverse">
<Controller <Controller
name="periodDates" name="periodDates"
control={formMethods.control} control={formMethods.control}
@@ -373,9 +373,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
/> />
</div> </div>
)} )}
{period.suffix ? ( {period.suffix ? <span className="ms-2 me-2">&nbsp;{period.suffix}</span> : null}
<span className="ltr:ml-2 ltr:mr-2 rtl:ml-2">&nbsp;{period.suffix}</span>
) : null}
</div> </div>
); );
})} })}
@@ -229,16 +229,8 @@ export const EventSetupTab = (
return ( return (
<li <li
onClick={() => {
locationFormMethods.setValue("locationType", location.type);
locationFormMethods.unregister("locationLink");
locationFormMethods.unregister("locationAddress");
locationFormMethods.unregister("locationPhoneNumber");
setEditingLocationType(location.type);
openLocationModal(location.type);
}}
key={`${location.type}${index}`} key={`${location.type}${index}`}
className="mb-2 rounded-md border border-gray-300 py-1.5 px-2 hover:cursor-pointer"> className="border-default text-default mb-2 rounded-md border py-1.5 px-2 hover:cursor-pointer">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center"> <div className="flex items-center">
<img <img
@@ -246,7 +238,7 @@ export const EventSetupTab = (
className="h-4 w-4" className="h-4 w-4"
alt={`${eventLocationType.label} logo`} alt={`${eventLocationType.label} logo`}
/> />
<span className="line-clamp-1 text-sm ltr:ml-1 rtl:mr-1">{eventLabel}</span> <span className="line-clamp-1 ms-1 text-sm">{eventLabel}</span>
</div> </div>
<div className="flex"> <div className="flex">
<button <button
@@ -260,11 +252,11 @@ export const EventSetupTab = (
openLocationModal(location.type); openLocationModal(location.type);
}} }}
aria-label={t("edit")} aria-label={t("edit")}
className="mr-1 p-1 text-gray-500 hover:text-gray-900"> className="hover:text-emphasis text-subtle mr-1 p-1">
<FiEdit2 className="h-4 w-4" /> <FiEdit2 className="h-4 w-4" />
</button> </button>
<button type="button" onClick={() => removeLocation(location)} aria-label={t("remove")}> <button type="button" onClick={() => removeLocation(location)} aria-label={t("remove")}>
<FiX className="border-l-1 h-6 w-6 pl-1 text-gray-500 hover:text-gray-900 " /> <FiX className="border-l-1 hover:text-emphasis text-subtle h-6 w-6 pl-1 " />
</button> </button>
</div> </div>
</div> </div>
@@ -275,7 +267,7 @@ export const EventSetupTab = (
(location) => (location) =>
location.type === MeetLocationType && destinationCalendar?.integration !== "google_calendar" location.type === MeetLocationType && destinationCalendar?.integration !== "google_calendar"
) && ( ) && (
<div className="flex text-sm text-gray-600"> <div className="text-default flex text-sm">
<FiCheck className="mt-0.5 mr-1.5 h-2 w-2.5" /> <FiCheck className="mt-0.5 mr-1.5 h-2 w-2.5" />
<Trans i18nKey="event_type_requres_google_cal"> <Trans i18nKey="event_type_requres_google_cal">
<p> <p>
@@ -52,7 +52,7 @@ const CheckedHostField = ({
options?: Options<CheckedSelectOption>; options?: Options<CheckedSelectOption>;
} & Omit<Partial<ComponentProps<typeof CheckedTeamSelect>>, "onChange" | "value">) => { } & Omit<Partial<ComponentProps<typeof CheckedTeamSelect>>, "onChange" | "value">) => {
return ( return (
<div className="flex flex-col space-y-5 bg-gray-50 p-4"> <div className="bg-muted flex flex-col space-y-5 p-4">
<div> <div>
<Label>{labelText}</Label> <Label>{labelText}</Label>
<CheckedTeamSelect <CheckedTeamSelect
@@ -197,7 +197,7 @@ function EventTypeSingleLayout({
heading={eventType.title} heading={eventType.title}
CTA={ CTA={
<div className="flex items-center justify-end"> <div className="flex items-center justify-end">
<div className="hidden items-center rounded-md px-2 sm:hover:bg-gray-100 lg:flex"> <div className="sm:hover:bg-subtle hidden items-center rounded-md px-2 lg:flex">
<Skeleton <Skeleton
as={Label} as={Label}
htmlFor="hiddenSwitch" htmlFor="hiddenSwitch"
@@ -215,7 +215,7 @@ function EventTypeSingleLayout({
<VerticalDivider className="hidden lg:block" /> <VerticalDivider className="hidden lg:block" />
{/* TODO: Figure out why combined isnt working - works in storybook */} {/* TODO: Figure out why combined isnt working - works in storybook */}
<ButtonGroup combined containerProps={{ className: "border-gray-300 hidden lg:flex" }}> <ButtonGroup combined containerProps={{ className: "border-default hidden lg:flex" }}>
{/* We have to warp this in tooltip as it has a href which disabels the tooltip on buttons */} {/* We have to warp this in tooltip as it has a href which disabels the tooltip on buttons */}
<Tooltip content={t("preview")}> <Tooltip content={t("preview")}>
<Button <Button
@@ -263,7 +263,7 @@ function EventTypeSingleLayout({
<Button className="lg:hidden" StartIcon={FiMoreHorizontal} variant="icon" color="secondary" /> <Button className="lg:hidden" StartIcon={FiMoreHorizontal} variant="icon" color="secondary" />
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent style={{ minWidth: "200px" }}> <DropdownMenuContent style={{ minWidth: "200px" }}>
<DropdownMenuItem className="focus:ring-gray-100"> <DropdownMenuItem className="focus:ring-muted">
<DropdownItem <DropdownItem
target="_blank" target="_blank"
type="button" type="button"
@@ -273,7 +273,7 @@ function EventTypeSingleLayout({
{t("preview")} {t("preview")}
</DropdownItem> </DropdownItem>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem className="focus:ring-gray-100"> <DropdownMenuItem className="focus:ring-muted">
<DropdownItem <DropdownItem
type="button" type="button"
StartIcon={FiLink} StartIcon={FiLink}
@@ -284,7 +284,7 @@ function EventTypeSingleLayout({
{t("copy_link")} {t("copy_link")}
</DropdownItem> </DropdownItem>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem className="focus:ring-gray-100"> <DropdownMenuItem className="focus:ring-muted">
<DropdownItem <DropdownItem
type="button" type="button"
color="destructive" color="destructive"
@@ -295,7 +295,7 @@ function EventTypeSingleLayout({
</DropdownItem> </DropdownItem>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<div className="flex h-9 flex-row items-center justify-between py-2 px-4 sm:hover:bg-gray-100"> <div className="sm:hover:bg-subtle flex h-9 flex-row items-center justify-between py-2 px-4">
<Skeleton <Skeleton
as={Label} as={Label}
htmlFor="hiddenSwitch" htmlFor="hiddenSwitch"
@@ -312,7 +312,7 @@ function EventTypeSingleLayout({
</div> </div>
</DropdownMenuContent> </DropdownMenuContent>
</Dropdown> </Dropdown>
<div className="border-l-2 border-gray-300" /> <div className="border-default border-l-2" />
<Button <Button
className="ml-4 lg:ml-0" className="ml-4 lg:ml-0"
type="submit" type="submit"
@@ -339,7 +339,7 @@ function EventTypeSingleLayout({
<div className="w-full ltr:mr-2 rtl:ml-2"> <div className="w-full ltr:mr-2 rtl:ml-2">
<div <div
className={classNames( className={classNames(
"mt-4 rounded-md border-gray-200 bg-white sm:mx-0 xl:mt-0", "bg-default border-subtle mt-4 rounded-md sm:mx-0 xl:mt-0",
disableBorder ? "border-0 " : "p-2 md:border md:p-6" disableBorder ? "border-0 " : "p-2 md:border md:p-6"
)}> )}>
{children} {children}
@@ -57,12 +57,12 @@ export default function RecurringEventController({
{recurringEventState && ( {recurringEventState && (
<div data-testid="recurring-event-collapsible" className="text-sm"> <div data-testid="recurring-event-collapsible" className="text-sm">
<div className="flex items-center"> <div className="flex items-center">
<p className="text-gray-900 ltr:mr-2 rtl:ml-2">{t("repeats_every")}</p> <p className="text-emphasis ltr:mr-2 rtl:ml-2">{t("repeats_every")}</p>
<input <input
type="number" type="number"
min="1" min="1"
max="20" max="20"
className="block h-[36px] w-16 rounded-md border-gray-300 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2" className="border-default bg-default text-default block h-[36px] w-16 rounded-md text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
defaultValue={recurringEventState.interval} defaultValue={recurringEventState.interval}
onChange={(event) => { onChange={(event) => {
const newVal = { const newVal = {
@@ -89,12 +89,12 @@ export default function RecurringEventController({
/> />
</div> </div>
<div className="mt-4 flex items-center"> <div className="mt-4 flex items-center">
<p className="text-gray-900 ltr:mr-2 rtl:ml-2">{t("for_a_maximum_of")}</p> <p className="text-emphasis ltr:mr-2 rtl:ml-2">{t("for_a_maximum_of")}</p>
<input <input
type="number" type="number"
min="1" min="1"
max="20" max="20"
className="block h-[36px] w-16 rounded-md border-gray-300 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2" className="border-default bg-default text-default block h-[36px] w-16 rounded-md text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
defaultValue={recurringEventState.count} defaultValue={recurringEventState.count}
onChange={(event) => { onChange={(event) => {
const newVal = { const newVal = {
@@ -105,7 +105,7 @@ export default function RecurringEventController({
setRecurringEventState(newVal); setRecurringEventState(newVal);
}} }}
/> />
<p className="text-gray-900 ltr:mr-2 rtl:ml-2"> <p className="text-emphasis ltr:mr-2 rtl:ml-2">
{t("events", { {t("events", {
count: recurringEventState.count, count: recurringEventState.count,
})} })}
@@ -9,7 +9,7 @@ import type z from "zod";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { Input, Label, SettingsToggle } from "@calcom/ui"; import { Input, Label, SettingsToggle, RadioField } from "@calcom/ui";
type RequiresConfirmationControllerProps = { type RequiresConfirmationControllerProps = {
metadata: z.infer<typeof EventTypeMetaDataSchema>; metadata: z.infer<typeof EventTypeMetaDataSchema>;
@@ -77,72 +77,69 @@ export default function RequiresConfirmationController({
); );
} }
}}> }}>
<div className="-ml-2 flex flex-col flex-wrap justify-start gap-y-2 space-y-2"> <div className="flex flex-col flex-wrap justify-start gap-y-2 space-y-2">
<div className="flex items-center"> <RadioField label={t("always_requires_confirmation")} id="always" value="always" />
<RadioGroup.Item <RadioField
id="always" label={
value="always" <>
className="min-w-4 flex h-4 w-4 cursor-pointer items-center rounded-full border border-black bg-white focus:border-2 focus:outline-none ltr:mr-2 rtl:ml-2"> <Trans
<RadioGroup.Indicator className="relative flex h-4 w-4 items-center justify-center after:block after:h-2 after:w-2 after:rounded-full after:bg-black" /> i18nKey="when_booked_with_less_than_notice"
</RadioGroup.Item> defaults="When booked with less than <time></time> notice"
<Label htmlFor="always" className="!m-0 flex items-center"> components={{
{t("always_requires_confirmation")} time: (
</Label> <div className="mx-2 flex">
</div> <Input
type="number"
min={1}
onChange={(evt) => {
const val = Number(evt.target?.value);
setRequiresConfirmationSetup({
unit:
requiresConfirmationSetup?.unit ??
defaultRequiresConfirmationSetup.unit,
time: val,
});
formMethods.setValue("metadata.requiresConfirmationThreshold.time", val);
}}
className="border-default !m-0 block w-16 rounded-md text-sm [appearance:textfield]"
defaultValue={metadata?.requiresConfirmationThreshold?.time || 30}
/>
<select
onChange={(evt) => {
const val = evt.target.value as UnitTypeLongPlural;
setRequiresConfirmationSetup({
time:
requiresConfirmationSetup?.time ??
defaultRequiresConfirmationSetup.time,
unit: val,
});
formMethods.setValue("metadata.requiresConfirmationThreshold.unit", val);
}}
className="border-default text-default bg-default ml-2 block h-9 rounded-md py-2 pl-3 pr-10 text-sm focus:outline-none"
defaultValue={
metadata?.requiresConfirmationThreshold?.unit ||
defaultRequiresConfirmationSetup.unit
}>
<option value="minutes">{t("minute_timeUnit")}</option>
<option value="hours">{t("hour_timeUnit")}</option>
</select>
</div>
),
}}
/>
</>
}
id="notice"
value="notice"
/>
<div className="flex items-center"> <div className="flex items-center">
<RadioGroup.Item <RadioGroup.Item
id="notice" id="notice"
value="notice" value="notice"
className="min-w-4 flex h-4 w-4 cursor-pointer items-center rounded-full border border-black bg-white focus:border-2 focus:outline-none ltr:mr-2 rtl:ml-2"> className="min-w-4 bg-default flex h-4 w-4 cursor-pointer items-center rounded-full border border-black focus:border-2 focus:outline-none ltr:mr-2 rtl:ml-2">
<RadioGroup.Indicator className="relative flex h-4 w-4 items-center justify-center after:block after:h-2 after:w-2 after:rounded-full after:bg-black" /> <RadioGroup.Indicator className="relative flex h-4 w-4 items-center justify-center after:block after:h-2 after:w-2 after:rounded-full after:bg-black" />
</RadioGroup.Item> </RadioGroup.Item>
<Label htmlFor="notice" className="!m-0 flex items-center"> <Label htmlFor="notice" className="!m-0 flex items-center" />
<Trans
i18nKey="when_booked_with_less_than_notice"
defaults="When booked with less than <time></time> notice"
components={{
time: (
<div className="mx-2 flex">
<Input
type="number"
min={1}
onChange={(evt) => {
const val = Number(evt.target?.value);
setRequiresConfirmationSetup({
unit:
requiresConfirmationSetup?.unit ??
defaultRequiresConfirmationSetup.unit,
time: val,
});
formMethods.setValue("metadata.requiresConfirmationThreshold.time", val);
}}
className="!m-0 block w-16 rounded-md border-gray-300 text-sm [appearance:textfield]"
defaultValue={metadata?.requiresConfirmationThreshold?.time || 30}
/>
<select
onChange={(evt) => {
const val = evt.target.value as UnitTypeLongPlural;
setRequiresConfirmationSetup({
time:
requiresConfirmationSetup?.time ??
defaultRequiresConfirmationSetup.time,
unit: val,
});
formMethods.setValue("metadata.requiresConfirmationThreshold.unit", val);
}}
className="ml-2 block h-9 rounded-md border-gray-300 py-2 pl-3 pr-10 text-sm focus:outline-none"
defaultValue={
metadata?.requiresConfirmationThreshold?.unit ||
defaultRequiresConfirmationSetup.unit
}>
<option value="minutes">{t("minute_timeUnit")}</option>
<option value="hours">{t("hour_timeUnit")}</option>
</select>
</div>
),
}}
/>
</Label>
</div> </div>
</div> </div>
</RadioGroup.Root> </RadioGroup.Root>
@@ -11,7 +11,7 @@ function SkeletonLoader() {
<SkeletonText className="h-4 w-24" /> <SkeletonText className="h-4 w-24" />
</div> </div>
</div> </div>
<ul className="divide-y divide-gray-200 rounded-md border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden"> <ul className="border-subtle bg-default divide-subtle divide-y rounded-md border sm:mx-0 sm:overflow-hidden">
<SkeletonItem /> <SkeletonItem />
<SkeletonItem /> <SkeletonItem />
<SkeletonItem /> <SkeletonItem />
@@ -32,11 +32,11 @@ function SkeletonItem() {
<div className=""> <div className="">
<ul className="mt-2 flex space-x-4 rtl:space-x-reverse "> <ul className="mt-2 flex space-x-4 rtl:space-x-reverse ">
<li className="flex items-center whitespace-nowrap"> <li className="flex items-center whitespace-nowrap">
<FiClock className="mt-0.5 mr-1.5 inline h-4 w-4 text-gray-200" /> <FiClock className="text-subtle mt-0.5 mr-1.5 inline h-4 w-4" />
<SkeletonText className="h-4 w-12" /> <SkeletonText className="h-4 w-12" />
</li> </li>
<li className="flex items-center whitespace-nowrap"> <li className="flex items-center whitespace-nowrap">
<FiUser className="mt-0.5 mr-1.5 inline h-4 w-4 text-gray-200" /> <FiUser className="text-subtle mt-0.5 mr-1.5 inline h-4 w-4" />
<SkeletonText className="h-4 w-12" /> <SkeletonText className="h-4 w-12" />
</li> </li>
</ul> </ul>
@@ -6,17 +6,17 @@ import { Button } from "@calcom/ui";
interface ICalendarItem { interface ICalendarItem {
title: string; title: string;
description?: string; description?: string;
imageSrc: string; logo: string;
type: App["type"]; type: App["type"];
} }
const CalendarItem = (props: ICalendarItem) => { const CalendarItem = (props: ICalendarItem) => {
const { title, imageSrc, type } = props; const { title, logo, type } = props;
const { t } = useLocale(); const { t } = useLocale();
return ( return (
<div className="flex flex-row items-center justify-between p-5"> <div className="flex flex-row items-center justify-between p-5">
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<img src={imageSrc} alt={title} className="h-8 w-8" /> <img src={logo} alt={title} className="h-8 w-8" />
<p className="text-sm font-bold">{title}</p> <p className="text-sm font-bold">{title}</p>
</div> </div>
<InstallAppButtonWithoutPlanCheck <InstallAppButtonWithoutPlanCheck
@@ -28,14 +28,14 @@ const ConnectedCalendarItem = (prop: IConnectedCalendarItem) => {
<p className="font-sans text-sm font-bold leading-5"> <p className="font-sans text-sm font-bold leading-5">
{name} {name}
{/* Temporarily removed till we use it on another place */} {/* Temporarily removed till we use it on another place */}
{/* <span className="mx-1 rounded-[4px] bg-green-100 py-[2px] px-[6px] font-sans text-xs font-medium text-green-600"> {/* <span className="mx-1 rounded-[4px] bg-success py-[2px] px-[6px] font-sans text-xs font-medium text-green-600">
{t("default")} {t("default")}
</span> */} </span> */}
</p> </p>
<div className="fle-row flex"> <div className="fle-row flex">
<span <span
title={externalId} title={externalId}
className="max-w-44 mt-1 overflow-hidden text-ellipsis whitespace-nowrap font-sans text-sm text-gray-500"> className="max-w-44 text-subtle mt-1 overflow-hidden text-ellipsis whitespace-nowrap font-sans text-sm">
{externalId}{" "} {externalId}{" "}
</span> </span>
</div> </div>
@@ -44,11 +44,11 @@ const ConnectedCalendarItem = (prop: IConnectedCalendarItem) => {
{/* <Button {/* <Button
color="minimal" color="minimal"
type="button" type="button"
className="ml-auto flex rounded-md border border-gray-200 py-[10x] px-4 font-sans text-sm"> className="ml-auto flex rounded-md border border-subtle py-[10x] px-4 font-sans text-sm">
{t("edit")} {t("edit")}
</Button> */} </Button> */}
</div> </div>
<div className="h-[1px] w-full border-b border-gray-200" /> <div className="border-subtle h-[1px] w-full border-b" />
<div> <div>
<ul className="p-4"> <ul className="p-4">
{calendars?.map((calendar, i) => ( {calendars?.map((calendar, i) => (
@@ -16,7 +16,7 @@ const CreateEventsOnCalendarSelect = (props: ICreateEventsOnCalendarSelectProps)
<> <>
<div className="mt-6 flex flex-row"> <div className="mt-6 flex flex-row">
<div className="w-full"> <div className="w-full">
<label htmlFor="createEventsOn" className="flex text-sm font-medium text-gray-700"> <label htmlFor="createEventsOn" className="text-default flex text-sm font-medium">
{t("create_events_on")} {t("create_events_on")}
</label> </label>
<div className="mt-2"> <div className="mt-2">
@@ -31,13 +31,13 @@ const ConnectedCalendars = (props: IConnectCalendarsProps) => {
firstCalendar && firstCalendar &&
firstCalendar.integration && firstCalendar.integration &&
firstCalendar.integration.title && firstCalendar.integration.title &&
firstCalendar.integration.imageSrc && ( firstCalendar.integration.logo && (
<> <>
<List className="rounded-md border border-gray-200 bg-white p-0 dark:bg-black"> <List className="bg-default border-subtle rounded-md border p-0 dark:bg-black">
<ConnectedCalendarItem <ConnectedCalendarItem
key={firstCalendar.integration.title} key={firstCalendar.integration.title}
name={firstCalendar.integration.title} name={firstCalendar.integration.title}
logo={firstCalendar.integration.imageSrc} logo={firstCalendar.integration.logo}
externalId={ externalId={
firstCalendar && firstCalendar.calendars && firstCalendar.calendars.length > 0 firstCalendar && firstCalendar.calendars && firstCalendar.calendars.length > 0
? firstCalendar.calendars[0].externalId ? firstCalendar.calendars[0].externalId
@@ -49,22 +49,22 @@ const ConnectedCalendars = (props: IConnectCalendarsProps) => {
</List> </List>
{/* Create event on selected calendar */} {/* Create event on selected calendar */}
<CreateEventsOnCalendarSelect calendar={destinationCalendar} /> <CreateEventsOnCalendarSelect calendar={destinationCalendar} />
<p className="mt-4 text-sm text-gray-500">{t("connect_calendars_from_app_store")}</p> <p className="text-subtle mt-4 text-sm">{t("connect_calendars_from_app_store")}</p>
</> </>
)} )}
{/* Connect calendars list */} {/* Connect calendars list */}
{firstCalendar === undefined && queryIntegrations.data && queryIntegrations.data.items.length > 0 && ( {firstCalendar === undefined && queryIntegrations.data && queryIntegrations.data.items.length > 0 && (
<List className="mx-1 divide-y divide-gray-200 rounded-md border border-gray-200 bg-white p-0 dark:bg-black sm:mx-0"> <List className="bg-default divide-subtle border-subtle mx-1 divide-y rounded-md border p-0 dark:bg-black sm:mx-0">
{queryIntegrations.data && {queryIntegrations.data &&
queryIntegrations.data.items.map((item) => ( queryIntegrations.data.items.map((item) => (
<li key={item.title}> <li key={item.title}>
{item.title && item.imageSrc && ( {item.title && item.logo && (
<CalendarItem <CalendarItem
type={item.type} type={item.type}
title={item.title} title={item.title}
description={item.description} description={item.description}
imageSrc={item.imageSrc} logo={item.logo}
/> />
)} )}
</li> </li>
@@ -73,7 +73,7 @@ const ConnectedCalendars = (props: IConnectCalendarsProps) => {
)} )}
{queryIntegrations.isLoading && ( {queryIntegrations.isLoading && (
<ul className="divide-y divide-gray-200 rounded-md border border-gray-200 bg-white p-0 dark:bg-black"> <ul className="bg-default divide-subtle border-subtle divide-y rounded-md border p-0 dark:bg-black">
{[0, 0, 0, 0].map((_item, index) => { {[0, 0, 0, 0].map((_item, index) => {
return ( return (
<li className="flex w-full flex-row justify-center border-b-0 py-6" key={index}> <li className="flex w-full flex-row justify-center border-b-0 py-6" key={index}>
@@ -89,7 +89,7 @@ const ConnectedCalendars = (props: IConnectCalendarsProps) => {
type="button" type="button"
data-testid="save-calendar-button" data-testid="save-calendar-button"
className={classNames( className={classNames(
"mt-8 flex w-full flex-row justify-center rounded-md border border-black bg-black p-2 text-center text-sm text-white", "text-inverted mt-8 flex w-full flex-row justify-center rounded-md border border-black bg-black p-2 text-center text-sm",
disabledNextButton ? "cursor-not-allowed opacity-20" : "" disabledNextButton ? "cursor-not-allowed opacity-20" : ""
)} )}
onClick={() => nextStep()} onClick={() => nextStep()}
@@ -50,7 +50,7 @@ const SetupAvailability = (props: ISetupAvailabilityProps) => {
const updateSchedule = trpc.viewer.availability.schedule.update.useMutation(mutationOptions); const updateSchedule = trpc.viewer.availability.schedule.update.useMutation(mutationOptions);
return ( return (
<Form <Form
className="w-full bg-white text-black dark:bg-opacity-5 dark:text-white" className="bg-default dark:text-inverted text-emphasis w-full dark:bg-opacity-5"
form={availabilityForm} form={availabilityForm}
handleSubmit={async (values) => { handleSubmit={async (values) => {
try { try {
@@ -117,7 +117,7 @@ const UserProfile = (props: IUserProfileProps) => {
name="avatar" name="avatar"
id="avatar" id="avatar"
placeholder="URL" placeholder="URL"
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 text-sm focus:border-gray-800 focus:outline-none focus:ring-gray-800" className="border-default focus:ring-empthasis mt-1 block w-full rounded-sm border px-3 py-2 text-sm focus:border-gray-800 focus:outline-none"
defaultValue={imageSrc} defaultValue={imageSrc}
/> />
<div className="flex items-center px-4"> <div className="flex items-center px-4">
@@ -142,19 +142,19 @@ const UserProfile = (props: IUserProfileProps) => {
</div> </div>
</div> </div>
<fieldset className="mt-8"> <fieldset className="mt-8">
<Label className="mb-2 block text-sm font-medium text-gray-700">{t("about")}</Label> <Label className="text-default mb-2 block text-sm font-medium">{t("about")}</Label>
<Editor <Editor
getText={() => md.render(getValues("bio") || user?.bio || "")} getText={() => md.render(getValues("bio") || user?.bio || "")}
setText={(value: string) => setValue("bio", turndown(value))} setText={(value: string) => setValue("bio", turndown(value))}
excludedToolbarItems={["blockType"]} excludedToolbarItems={["blockType"]}
/> />
<p className="mt-2 font-sans text-sm font-normal text-gray-600 dark:text-white"> <p className="dark:text-inverted text-default mt-2 font-sans text-sm font-normal">
{t("few_sentences_about_yourself")} {t("few_sentences_about_yourself")}
</p> </p>
</fieldset> </fieldset>
<Button <Button
type="submit" type="submit"
className="mt-8 flex w-full flex-row justify-center rounded-md border border-black bg-black p-2 text-center text-sm text-white"> className="text-inverted mt-8 flex w-full flex-row justify-center rounded-md border border-black bg-black p-2 text-center text-sm">
{t("finish")} {t("finish")}
<ArrowRightIcon className="ml-2 h-4 w-4 self-center" aria-hidden="true" /> <ArrowRightIcon className="ml-2 h-4 w-4 self-center" aria-hidden="true" />
</Button> </Button>
@@ -73,7 +73,7 @@ const UserSettings = (props: IUserSettingsProps) => {
{/* Full name textfield */} {/* Full name textfield */}
<div className="w-full"> <div className="w-full">
<label htmlFor="name" className="mb-2 block text-sm font-medium text-gray-700"> <label htmlFor="name" className="text-default mb-2 block text-sm font-medium">
{t("full_name")} {t("full_name")}
</label> </label>
<input <input
@@ -85,7 +85,7 @@ const UserSettings = (props: IUserSettingsProps) => {
type="text" type="text"
autoComplete="off" autoComplete="off"
autoCorrect="off" autoCorrect="off"
className="w-full rounded-md border border-gray-300 text-sm" className="border-default w-full rounded-md border text-sm"
/> />
{errors.name && ( {errors.name && (
<p data-testid="required" className="py-2 text-xs text-red-500"> <p data-testid="required" className="py-2 text-xs text-red-500">
@@ -95,7 +95,7 @@ const UserSettings = (props: IUserSettingsProps) => {
</div> </div>
{/* Timezone select field */} {/* Timezone select field */}
<div className="w-full"> <div className="w-full">
<label htmlFor="timeZone" className="block text-sm font-medium text-gray-700"> <label htmlFor="timeZone" className="text-default block text-sm font-medium">
{t("timezone")} {t("timezone")}
</label> </label>
@@ -106,7 +106,7 @@ const UserSettings = (props: IUserSettingsProps) => {
className="mt-2 w-full rounded-md text-sm" className="mt-2 w-full rounded-md text-sm"
/> />
<p className="mt-3 flex flex-row font-sans text-xs leading-tight text-gray-500 dark:text-white"> <p className="text-subtle dark:text-inverted mt-3 flex flex-row font-sans text-xs leading-tight">
{t("current_time")} {dayjs().tz(selectedTimeZone).format("LT").toString().toLowerCase()} {t("current_time")} {dayjs().tz(selectedTimeZone).format("LT").toString().toLowerCase()}
</p> </p>
</div> </div>
+4 -2
View File
@@ -24,7 +24,9 @@ export default function WizardLayout({
}, [router.asPath]); }, [router.asPath]);
return ( return (
<div className="dark:bg-brand dark:text-brand-contrast min-h-screen text-black" data-testid="onboarding"> <div
className="dark:bg-brand dark:text-brand-contrast text-emphasis min-h-screen"
data-testid="onboarding">
<div> <div>
<Toaster position="bottom-right" /> <Toaster position="bottom-right" />
</div> </div>
@@ -34,7 +36,7 @@ export default function WizardLayout({
<div className="mx-auto sm:max-w-[520px]"> <div className="mx-auto sm:max-w-[520px]">
<header> <header>
<p className="font-cal mb-3 text-[28px] font-medium leading-7">{title}&nbsp;</p> <p className="font-cal mb-3 text-[28px] font-medium leading-7">{title}&nbsp;</p>
<p className="font-sans text-sm font-normal text-gray-500">{subtitle}&nbsp;</p> <p className="text-subtle font-sans text-sm font-normal">{subtitle}&nbsp;</p>
</header> </header>
<Steps maxSteps={maxSteps} currentStep={currentStep} navigateToStep={noop} /> <Steps maxSteps={maxSteps} currentStep={currentStep} navigateToStep={noop} />
</div> </div>
@@ -59,14 +59,14 @@ const ChangePasswordSection = () => {
return ( return (
<> <>
<form className="divide-y divide-gray-200 lg:col-span-9" onSubmit={changePasswordHandler}> <form className="divide-subtle divide-y lg:col-span-9" onSubmit={changePasswordHandler}>
<div className="py-6 lg:pb-5"> <div className="py-6 lg:pb-5">
<div className="my-3"> <div className="my-3">
<h2 className="font-cal text-lg font-medium leading-6 text-gray-900">{t("change_password")}</h2> <h2 className="font-cal text-emphasis text-lg font-medium leading-6">{t("change_password")}</h2>
</div> </div>
<div className="flex flex-col space-y-2 sm:flex-row sm:space-y-0"> <div className="flex flex-col space-y-2 sm:flex-row sm:space-y-0">
<div className="w-full ltr:mr-2 rtl:ml-2 sm:w-1/2"> <div className="w-full ltr:mr-2 rtl:ml-2 sm:w-1/2">
<label htmlFor="current_password" className="block text-sm font-medium text-gray-700"> <label htmlFor="current_password" className="text-default block text-sm font-medium">
{t("current_password")} {t("current_password")}
</label> </label>
<div className="mt-1"> <div className="mt-1">
@@ -77,13 +77,13 @@ const ChangePasswordSection = () => {
name="current_password" name="current_password"
id="current_password" id="current_password"
required required
className="block w-full rounded-sm border-gray-300 text-sm" className="border-default block w-full rounded-sm text-sm"
placeholder={t("your_old_password")} placeholder={t("your_old_password")}
/> />
</div> </div>
</div> </div>
<div className="w-full sm:w-1/2"> <div className="w-full sm:w-1/2">
<label htmlFor="new_password" className="block text-sm font-medium text-gray-700"> <label htmlFor="new_password" className="text-default block text-sm font-medium">
{t("new_password")} {t("new_password")}
</label> </label>
<div className="mt-1"> <div className="mt-1">
@@ -94,7 +94,7 @@ const ChangePasswordSection = () => {
value={newPassword} value={newPassword}
required required
onInput={(e) => setNewPassword(e.currentTarget.value)} onInput={(e) => setNewPassword(e.currentTarget.value)}
className="block w-full rounded-sm border-gray-300 text-sm" className="border-default block w-full rounded-sm text-sm"
placeholder={t("super_secure_new_password")} placeholder={t("super_secure_new_password")}
/> />
</div> </div>
@@ -70,10 +70,10 @@ const DisableTwoFactorAuthModal = ({ onDisable, onCancel }: DisableTwoFactorAuth
<div className="mb-4"> <div className="mb-4">
<PasswordField <PasswordField
labelProps={{ labelProps={{
className: "block text-sm font-medium text-gray-700", className: "block text-sm font-medium text-default",
}} }}
{...form.register("password")} {...form.register("password")}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-black focus:outline-none focus:ring-black" className="border-default mt-1 block w-full rounded-md border px-3 py-2 text-sm focus:border-black focus:outline-none focus:ring-black"
/> />
<Label className="mt-4"> {t("2fa_code")}</Label> <Label className="mt-4"> {t("2fa_code")}</Label>
@@ -82,7 +82,7 @@ const DisableTwoFactorAuthModal = ({ onDisable, onCancel }: DisableTwoFactorAuth
</div> </div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"> <div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button type="submit" className="ltr:ml-2 ltr:mr-2 rtl:ml-2" disabled={isDisabling}> <Button type="submit" className="ms-2 me-2" disabled={isDisabling}>
{t("disable")} {t("disable")}
</Button> </Button>
<Button color="secondary" onClick={onCancel}> <Button color="secondary" onClick={onCancel}>
@@ -22,14 +22,14 @@ const DisableUserImpersonation = ({ disableImpersonation }: { disableImpersonati
<div className="flex flex-col justify-between pt-9 pl-2 sm:flex-row"> <div className="flex flex-col justify-between pt-9 pl-2 sm:flex-row">
<div> <div>
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<h2 className="font-cal text-lg font-medium leading-6 text-gray-900"> <h2 className="font-cal text-emphasis text-lg font-medium leading-6">
{t("user_impersonation_heading")} {t("user_impersonation_heading")}
</h2> </h2>
<Badge className="ml-2 text-xs" variant={!disableImpersonation ? "success" : "gray"}> <Badge className="ml-2 text-xs" variant={!disableImpersonation ? "success" : "gray"}>
{!disableImpersonation ? t("enabled") : t("disabled")} {!disableImpersonation ? t("enabled") : t("disabled")}
</Badge> </Badge>
</div> </div>
<p className="mt-1 text-sm text-gray-500">{t("user_impersonation_description")}</p> <p className="text-subtle mt-1 text-sm">{t("user_impersonation_description")}</p>
</div> </div>
<div className="mt-5 sm:mt-0 sm:self-center"> <div className="mt-5 sm:mt-0 sm:self-center">
<Button <Button
@@ -135,7 +135,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
<WithStep step={SetupStep.ConfirmPassword} current={step}> <WithStep step={SetupStep.ConfirmPassword} current={step}>
<form onSubmit={handleSetup}> <form onSubmit={handleSetup}>
<div className="mb-4"> <div className="mb-4">
<label htmlFor="password" className="mt-4 block text-sm font-medium text-gray-700"> <label htmlFor="password" className="text-default mt-4 block text-sm font-medium">
{t("password")} {t("password")}
</label> </label>
<div className="mt-1"> <div className="mt-1">
@@ -146,7 +146,7 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
required required
value={password} value={password}
onInput={(e) => setPassword(e.currentTarget.value)} onInput={(e) => setPassword(e.currentTarget.value)}
className="block w-full rounded-sm border-gray-300 text-sm" className="border-default block w-full rounded-sm text-sm"
/> />
</div> </div>
@@ -178,22 +178,19 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps)
<WithStep step={SetupStep.ConfirmPassword} current={step}> <WithStep step={SetupStep.ConfirmPassword} current={step}>
<Button <Button
type="submit" type="submit"
className="ltr:ml-2 ltr:mr-2 rtl:ml-2" className="ms-2 me-2"
onClick={handleSetup} onClick={handleSetup}
disabled={password.length === 0 || isSubmitting}> disabled={password.length === 0 || isSubmitting}>
{t("continue")} {t("continue")}
</Button> </Button>
</WithStep> </WithStep>
<WithStep step={SetupStep.DisplayQrCode} current={step}> <WithStep step={SetupStep.DisplayQrCode} current={step}>
<Button <Button type="submit" className="ms-2 me-2" onClick={() => setStep(SetupStep.EnterTotpCode)}>
type="submit"
className="ltr:ml-2 ltr:mr-2 rtl:ml-2"
onClick={() => setStep(SetupStep.EnterTotpCode)}>
{t("continue")} {t("continue")}
</Button> </Button>
</WithStep> </WithStep>
<WithStep step={SetupStep.EnterTotpCode} current={step}> <WithStep step={SetupStep.EnterTotpCode} current={step}>
<Button type="submit" className="ltr:ml-2 ltr:mr-2 rtl:ml-2" disabled={isSubmitting}> <Button type="submit" className="ms-2 me-2" disabled={isSubmitting}>
{t("enable")} {t("enable")}
</Button> </Button>
</WithStep> </WithStep>
@@ -17,12 +17,12 @@ const TwoFactorAuthSection = ({ twoFactorEnabled }: { twoFactorEnabled: boolean
<div className="flex flex-col justify-between pt-9 pl-2 sm:flex-row"> <div className="flex flex-col justify-between pt-9 pl-2 sm:flex-row">
<div> <div>
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<h2 className="font-cal text-lg font-medium leading-6 text-gray-900">{t("2fa")}</h2> <h2 className="font-cal text-emphasis text-lg font-medium leading-6">{t("2fa")}</h2>
<Badge className="ml-2 text-xs" variant={enabled ? "success" : "gray"}> <Badge className="ml-2 text-xs" variant={enabled ? "success" : "gray"}>
{enabled ? t("enabled") : t("disabled")} {enabled ? t("enabled") : t("disabled")}
</Badge> </Badge>
</div> </div>
<p className="mt-1 text-sm text-gray-500">{t("add_an_extra_layer_of_security")}</p> <p className="text-subtle mt-1 text-sm">{t("add_an_extra_layer_of_security")}</p>
</div> </div>
<div className="mt-5 sm:mt-0 sm:self-center"> <div className="mt-5 sm:mt-0 sm:self-center">
<Button <Button
@@ -4,13 +4,13 @@ const TwoFactorModalHeader = ({ title, description }: { title: string; descripti
return ( return (
<div className="mb-4 sm:flex sm:items-start"> <div className="mb-4 sm:flex sm:items-start">
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10"> <div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
<FiShield className="h-6 w-6 text-white" /> <FiShield className="text-inverted h-6 w-6" />
</div> </div>
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 className="font-cal text-lg font-medium leading-6 text-gray-900" id="modal-title"> <h3 className="font-cal text-emphasis text-lg font-medium leading-6" id="modal-title">
{title} {title}
</h3> </h3>
<p className="text-sm text-gray-400">{description}</p> <p className="text-muted text-sm">{description}</p>
</div> </div>
</div> </div>
); );
@@ -76,7 +76,7 @@ export function CalendarSwitch(props: {
{props.defaultSelected && ( {props.defaultSelected && (
<Badge variant="gray"> <Badge variant="gray">
<div className="flex"> <div className="flex">
<FiArrowLeft className="mr-1" /> {t("adding_events_to")} <FiArrowLeft className="text-default mr-1" /> {t("adding_events_to")}
</div> </div>
</Badge> </Badge>
)} )}
@@ -77,10 +77,10 @@ const DisableTwoFactorAuthModal = ({
<div className="mb-4"> <div className="mb-4">
<PasswordField <PasswordField
labelProps={{ labelProps={{
className: "block text-sm font-medium text-gray-700", className: "block text-sm font-medium text-default",
}} }}
{...form.register("password")} {...form.register("password")}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-black focus:outline-none focus:ring-black" className="border-default mt-1 block w-full rounded-md border px-3 py-2 text-sm focus:border-black focus:outline-none focus:ring-black"
/> />
<Label className="mt-4"> {t("2fa_code")}</Label> <Label className="mt-4"> {t("2fa_code")}</Label>
@@ -90,7 +90,7 @@ const DisableTwoFactorAuthModal = ({
</div> </div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse"> <div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<Button type="submit" className="ltr:ml-2 ltr:mr-2 rtl:ml-2" disabled={isDisabling}> <Button type="submit" className="ms-2 me-2" disabled={isDisabling}>
{t("disable")} {t("disable")}
</Button> </Button>
<Button color="secondary" onClick={onCancel}> <Button color="secondary" onClick={onCancel}>
@@ -171,22 +171,19 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable
<WithStep step={SetupStep.ConfirmPassword} current={step}> <WithStep step={SetupStep.ConfirmPassword} current={step}>
<Button <Button
type="submit" type="submit"
className="ltr:ml-2 ltr:mr-2 rtl:ml-2" className="ms-2 me-2"
onClick={handleSetup} onClick={handleSetup}
disabled={password.length === 0 || isSubmitting}> disabled={password.length === 0 || isSubmitting}>
{t("continue")} {t("continue")}
</Button> </Button>
</WithStep> </WithStep>
<WithStep step={SetupStep.DisplayQrCode} current={step}> <WithStep step={SetupStep.DisplayQrCode} current={step}>
<Button <Button type="submit" className="ms-2 me-2" onClick={() => setStep(SetupStep.EnterTotpCode)}>
type="submit"
className="ltr:ml-2 ltr:mr-2 rtl:ml-2"
onClick={() => setStep(SetupStep.EnterTotpCode)}>
{t("continue")} {t("continue")}
</Button> </Button>
</WithStep> </WithStep>
<WithStep step={SetupStep.EnterTotpCode} current={step}> <WithStep step={SetupStep.EnterTotpCode} current={step}>
<Button type="submit" className="ltr:ml-2 ltr:mr-2 rtl:ml-2" disabled={isSubmitting}> <Button type="submit" className="ms-2 me-2" disabled={isSubmitting}>
{t("enable")} {t("enable")}
</Button> </Button>
</WithStep> </WithStep>
+2 -2
View File
@@ -111,7 +111,7 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
<Label htmlFor="username" className={classNames(longWebsiteUrl && "mb-0")}> <Label htmlFor="username" className={classNames(longWebsiteUrl && "mb-0")}>
<span className="block">{t("username")}</span> <span className="block">{t("username")}</span>
{longWebsiteUrl && ( {longWebsiteUrl && (
<small className="items-centerpx-3 mt-2 inline-flex rounded-t-md border border-b-0 border-gray-300 bg-gray-100 py-1 px-3 text-gray-500"> <small className="items-centerpx-3 bg-subtle border-default text-subtle mt-2 inline-flex rounded-t-md border border-b-0 py-1 px-3">
{process.env.NEXT_PUBLIC_WEBSITE_URL} {process.env.NEXT_PUBLIC_WEBSITE_URL}
</small> </small>
)} )}
@@ -119,7 +119,7 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
<TextField <TextField
addOnLeading={ addOnLeading={
!longWebsiteUrl && ( !longWebsiteUrl && (
<span className="inline-flex items-center rounded-none px-3 text-sm text-gray-500"> <span className="text-subtle inline-flex items-center rounded-none px-3 text-sm">
{process.env.NEXT_PUBLIC_WEBSITE_URL}/ {process.env.NEXT_PUBLIC_WEBSITE_URL}/
</span> </span>
) )
+8 -8
View File
@@ -37,13 +37,13 @@ const ChooseLicense = (
<RadioGroup.Item value="FREE"> <RadioGroup.Item value="FREE">
<div <div
className={classNames( className={classNames(
"cursor-pointer space-y-2 rounded-md border bg-white p-4 hover:border-black", "bg-default cursor-pointer space-y-2 rounded-md border p-4 hover:border-black",
value === "FREE" && "ring-2 ring-black" value === "FREE" && "ring-2 ring-black"
)}> )}>
<h2 className="font-cal text-xl text-black">{t("agplv3_license")}</h2> <h2 className="font-cal text-emphasis text-xl">{t("agplv3_license")}</h2>
<p className="font-medium text-green-800">{t("free_license_fee")}</p> <p className="font-medium text-green-800">{t("free_license_fee")}</p>
<p className="text-gray-500">{t("forever_open_and_free")}</p> <p className="text-subtle">{t("forever_open_and_free")}</p>
<ul className="ml-4 list-disc text-left text-xs text-gray-500"> <ul className="text-subtle ml-4 list-disc text-left text-xs">
<li>{t("required_to_keep_your_code_open_source")}</li> <li>{t("required_to_keep_your_code_open_source")}</li>
<li>{t("cannot_repackage_and_resell")}</li> <li>{t("cannot_repackage_and_resell")}</li>
<li>{t("no_enterprise_features")}</li> <li>{t("no_enterprise_features")}</li>
@@ -53,15 +53,15 @@ const ChooseLicense = (
<RadioGroup.Item value="EE"> <RadioGroup.Item value="EE">
<div <div
className={classNames( className={classNames(
"cursor-pointer space-y-2 rounded-md border bg-white p-4 hover:border-black", "bg-default cursor-pointer space-y-2 rounded-md border p-4 hover:border-black",
value === "EE" && "ring-2 ring-black" value === "EE" && "ring-2 ring-black"
)}> )}>
<h2 className="font-cal text-xl text-black">{t("ee_enterprise_license")}</h2> <h2 className="font-cal text-emphasis text-xl">{t("ee_enterprise_license")}</h2>
<p className="font-medium text-green-800"> <p className="font-medium text-green-800">
{t("enterprise_booking_fee", { enterprise_booking_fee: ENTERPRISE_BOOKING_FEE })} {t("enterprise_booking_fee", { enterprise_booking_fee: ENTERPRISE_BOOKING_FEE })}
</p> </p>
<p className="text-gray-500">{t("enterprise_license_includes")}</p> <p className="text-subtle">{t("enterprise_license_includes")}</p>
<ul className="ml-4 list-disc text-left text-xs text-gray-500"> <ul className="text-subtle ml-4 list-disc text-left text-xs">
<li>{t("no_need_to_keep_your_code_open_source")}</li> <li>{t("no_need_to_keep_your_code_open_source")}</li>
<li>{t("repackage_rebrand_resell")}</li> <li>{t("repackage_rebrand_resell")}</li>
<li>{t("a_vast_suite_of_enterprise_features")}</li> <li>{t("a_vast_suite_of_enterprise_features")}</li>
@@ -87,7 +87,7 @@ const EnterpriseLicense = (
return ( return (
<FormProvider {...formMethods}> <FormProvider {...formMethods}>
<form {...rest} className="space-y-4 rounded-md bg-white px-8 py-10" onSubmit={handleSubmit}> <form {...rest} className="bg-default space-y-4 rounded-md px-8 py-10" onSubmit={handleSubmit}>
<div> <div>
<Button <Button
className="w-full justify-center text-lg" className="w-full justify-center text-lg"
@@ -97,8 +97,8 @@ const EnterpriseLicense = (
{t("purchase_license")} {t("purchase_license")}
</Button> </Button>
<div className="relative flex justify-center"> <div className="relative flex justify-center">
<hr className="my-8 w-full border-[1.5px] border-gray-200" /> <hr className="border-subtle my-8 w-full border-[1.5px]" />
<span className="absolute mt-[22px] bg-white px-3.5 text-sm">OR</span> <span className="bg-default absolute mt-[22px] px-3.5 text-sm">OR</span>
</div> </div>
{t("already_have_key")} {t("already_have_key")}
<Controller <Controller
@@ -108,7 +108,7 @@ const EnterpriseLicense = (
<TextField <TextField
{...formMethods.register("licenseKey")} {...formMethods.register("licenseKey")}
className={classNames( className={classNames(
"mb-0 group-hover:border-gray-400", "group-hover:border-emphasis mb-0",
(checkLicenseLoading || (errors.licenseKey === undefined && isDirty)) && "border-r-0" (checkLicenseLoading || (errors.licenseKey === undefined && isDirty)) && "border-r-0"
)} )}
placeholder="xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx" placeholder="xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx"
@@ -116,8 +116,8 @@ const EnterpriseLicense = (
value={value} value={value}
addOnFilled={false} addOnFilled={false}
addOnClassname={classNames( addOnClassname={classNames(
"hover:border-gray-300", "hover:border-default",
errors.licenseKey === undefined && isDirty && "group-hover:border-gray-400" errors.licenseKey === undefined && isDirty && "group-hover:border-emphasis"
)} )}
addOnSuffix={ addOnSuffix={
checkLicenseLoading ? ( checkLicenseLoading ? (
+2 -2
View File
@@ -23,8 +23,8 @@ const StepDone = (props: {
router.replace(props.nextStepPath); router.replace(props.nextStepPath);
}}> }}>
<div className="min-h-36 my-6 flex flex-col items-center justify-center"> <div className="min-h-36 my-6 flex flex-col items-center justify-center">
<div className="flex h-[72px] w-[72px] items-center justify-center rounded-full bg-gray-600 dark:bg-white"> <div className="dark:bg-default flex h-[72px] w-[72px] items-center justify-center rounded-full bg-gray-600">
<FiCheck className="inline-block h-10 w-10 text-white dark:bg-white dark:text-gray-600" /> <FiCheck className="text-inverted dark:bg-default dark:text-default inline-block h-10 w-10" />
</div> </div>
<div className="max-w-[420px] text-center"> <div className="max-w-[420px] text-center">
<h2 className="mt-6 mb-1 text-lg font-medium dark:text-gray-300">{t("all_done")}</h2> <h2 className="mt-6 mb-1 text-lg font-medium dark:text-gray-300">{t("all_done")}</h2>
+6 -5
View File
@@ -2,6 +2,7 @@ import Link from "next/link";
import { WEBAPP_URL } from "@calcom/lib/constants"; import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import { md } from "@calcom/lib/markdownIt";
import type { TeamWithMembers } from "@calcom/lib/server/queries/teams"; import type { TeamWithMembers } from "@calcom/lib/server/queries/teams";
import { Avatar } from "@calcom/ui"; import { Avatar } from "@calcom/ui";
@@ -18,20 +19,20 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n
return ( return (
<Link key={member.id} href={`/${member.username}`}> <Link key={member.id} href={`/${member.username}`}>
<div className="sm:min-w-80 sm:max-w-80 dark:bg-darkgray-200 dark:hover:bg-darkgray-300 group flex min-h-full flex-col space-y-2 rounded-md bg-white p-4 hover:cursor-pointer hover:bg-gray-50 "> <div className="sm:min-w-80 sm:max-w-80 bg-default hover:bg-muted border-subtle group flex min-h-full flex-col space-y-2 rounded-md border p-4 hover:cursor-pointer">
<Avatar <Avatar
size="md" size="md"
alt={member.name || ""} alt={member.name || ""}
imageSrc={WEBAPP_URL + "/" + member.username + "/avatar.png"} imageSrc={WEBAPP_URL + "/" + member.username + "/avatar.png"}
/> />
<section className="line-clamp-4 mt-2 w-full space-y-1"> <section className="line-clamp-4 mt-2 w-full space-y-1">
<p className="font-medium text-gray-900 dark:text-white">{member.name}</p> <p className="text-default font-medium">{member.name}</p>
<div className="line-clamp-3 overflow-ellipsis text-sm font-normal text-gray-500 dark:text-white"> <div className="line-clamp-3 text-subtle overflow-ellipsis text-sm font-normal">
{!isBioEmpty ? ( {!isBioEmpty ? (
<> <>
<div <div
className="dark:text-darkgray-600 text-sm text-gray-500 [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600" className=" text-subtle text-sm [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"
dangerouslySetInnerHTML={{ __html: member.safeBio || "" }} dangerouslySetInnerHTML={{ __html: md.render(member.bio || "") }}
/> />
</> </>
) : ( ) : (
+4 -4
View File
@@ -24,18 +24,18 @@ export default function AuthContainer(props: React.PropsWithChildren<Props>) {
)} )}
<div className={classNames(props.showLogo ? "text-center" : "", "sm:mx-auto sm:w-full sm:max-w-md")}> <div className={classNames(props.showLogo ? "text-center" : "", "sm:mx-auto sm:w-full sm:max-w-md")}>
{props.heading && <h2 className="font-cal text-center text-3xl text-gray-900">{props.heading}</h2>} {props.heading && <h2 className="font-cal text-emphasis text-center text-3xl">{props.heading}</h2>}
</div> </div>
{props.loading && ( {props.loading && (
<div className="absolute z-50 flex h-screen w-full items-center bg-gray-50"> <div className="bg-muted absolute z-50 flex h-screen w-full items-center">
<Loader /> <Loader />
</div> </div>
)} )}
<div className="mb-auto mt-8 sm:mx-auto sm:w-full sm:max-w-md"> <div className="mb-auto mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="mx-2 rounded-md border border-gray-200 bg-white px-4 py-10 sm:px-10"> <div className="bg-default border-subtle mx-2 rounded-md border px-4 py-10 sm:px-10">
{props.children} {props.children}
</div> </div>
<div className="mt-8 text-center text-sm text-gray-600">{props.footerText}</div> <div className="text-default mt-8 text-center text-sm">{props.footerText}</div>
</div> </div>
</div> </div>
); );
+2 -2
View File
@@ -21,7 +21,7 @@ const EditableHeading = function EditableHeading({
<label className="min-w-8 relative inline-block"> <label className="min-w-8 relative inline-block">
<span className="whitespace-pre text-xl tracking-normal text-transparent">{value}&nbsp;</span> <span className="whitespace-pre text-xl tracking-normal text-transparent">{value}&nbsp;</span>
{!isEditing && isReady && ( {!isEditing && isReady && (
<FiEdit2 className=" ml-1 -mt-px inline h-3 w-3 text-gray-500 group-hover:text-gray-500" /> <FiEdit2 className=" text-subtle group-hover:text-subtle ml-1 -mt-px inline h-3 w-3" />
)} )}
<input <input
{...passThroughProps} {...passThroughProps}
@@ -29,7 +29,7 @@ const EditableHeading = function EditableHeading({
value={value} value={value}
required required
className={classNames( className={classNames(
"absolute top-0 left-0 w-full cursor-pointer border-none bg-transparent p-0 align-top text-xl text-gray-900 hover:text-gray-700 focus:text-black focus:outline-none focus:ring-0" "text-emphasis hover:text-default focus:text-emphasis absolute top-0 left-0 w-full cursor-pointer border-none bg-transparent p-0 align-top text-xl focus:outline-none focus:ring-0"
)} )}
onFocus={(e) => { onFocus={(e) => {
setIsEditing(true); setIsEditing(true);
+1 -1
View File
@@ -6,7 +6,7 @@ export default function InfoBadge({ content }: { content: string }) {
<> <>
<Tooltip side="top" content={content}> <Tooltip side="top" content={content}>
<span title={content}> <span title={content}>
<FiInfo className="relative top-px left-1 right-1 mt-px h-4 w-4 text-gray-500" /> <FiInfo className="text-subtle relative top-px left-1 right-1 mt-px h-4 w-4" />
</span> </span>
</Tooltip> </Tooltip>
</> </>
+2 -2
View File
@@ -12,8 +12,8 @@ export default function LinkIconButton(props: LinkIconButtonProps) {
<button <button
type="button" type="button"
{...props} {...props}
className="text-md flex items-center rounded-sm px-2 py-1 text-sm font-medium text-gray-700 hover:bg-gray-200 hover:text-gray-900"> className="text-md hover:bg-emphasis hover:text-emphasis text-default flex items-center rounded-sm px-2 py-1 text-sm font-medium">
<props.Icon className="h-4 w-4 text-gray-500 ltr:mr-2 rtl:ml-2" /> <props.Icon className="text-subtle h-4 w-4 ltr:mr-2 rtl:ml-2" />
{props.children} {props.children}
</button> </button>
</div> </div>
+1 -1
View File
@@ -19,7 +19,7 @@ export default function ModalContainer(
<DialogContent> <DialogContent>
<div <div
className={classNames( className={classNames(
"inline-block w-full transform bg-white text-left align-bottom transition-all sm:align-middle", "bg-default inline-block w-full transform text-left align-bottom transition-all sm:align-middle",
{ {
"sm:w-full sm:max-w-lg ": !props.wide, "sm:w-full sm:max-w-lg ": !props.wide,
"sm:w-4xl sm:max-w-4xl": props.wide, "sm:w-4xl sm:max-w-4xl": props.wide,
+1 -4
View File
@@ -9,10 +9,7 @@ const PoweredByCal = () => {
const isEmbed = useIsEmbed(); const isEmbed = useIsEmbed();
return ( return (
<div className={"p-2 text-center text-xs sm:text-right" + (isEmbed ? " max-w-3xl" : "")}> <div className={"p-2 text-center text-xs sm:text-right" + (isEmbed ? " max-w-3xl" : "")}>
<Link <Link href={POWERED_BY_URL} target="_blank" className="text-subtle opacity-50 hover:opacity-100">
href={POWERED_BY_URL}
target="_blank"
className="text-bookinglight opacity-50 hover:opacity-100 dark:text-white">
{t("powered_by")}{" "} {t("powered_by")}{" "}
<img <img
className="relative -mt-px inline h-[10px] w-auto dark:hidden" className="relative -mt-px inline h-[10px] w-auto dark:hidden"
@@ -13,8 +13,8 @@ export default function SettingInputContainer({
<div className="space-y-3"> <div className="space-y-3">
<div className="block sm:flex"> <div className="block sm:flex">
<div className="min-w-48 mb-4 sm:mb-0"> <div className="min-w-48 mb-4 sm:mb-0">
<label htmlFor={htmlFor} className="mt-1 flex text-sm font-medium text-gray-700"> <label htmlFor={htmlFor} className="text-default mt-1 flex text-sm font-medium">
<Icon className="mt-0.5 h-4 w-4 text-gray-500 ltr:mr-2 rtl:ml-2" /> <Icon className="text-subtle mt-0.5 h-4 w-4 ltr:mr-2 rtl:ml-2" />
{label} {label}
</label> </label>
</div> </div>
@@ -14,7 +14,7 @@ import type { TRPCClientErrorLike } from "@calcom/trpc/client";
import type { RouterOutputs } from "@calcom/trpc/react"; import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react";
import type { AppRouter } from "@calcom/trpc/server/routers/_app"; import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import { Button, Dialog, DialogClose, DialogContent, DialogHeader, Input, Label } from "@calcom/ui"; import { Button, Dialog, DialogClose, DialogContent, Input, Label } from "@calcom/ui";
import { FiCheck, FiEdit2, FiExternalLink } from "@calcom/ui/components/icon"; import { FiCheck, FiEdit2, FiExternalLink } from "@calcom/ui/components/icon";
export enum UsernameChangeStatusEnum { export enum UsernameChangeStatusEnum {
@@ -195,7 +195,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
<span <span
className={classNames( className={classNames(
isInputUsernamePremium ? "border border-orange-400 " : "", isInputUsernamePremium ? "border border-orange-400 " : "",
"hidden h-9 items-center rounded-l-md border border-r-0 border-gray-300 border-r-gray-300 bg-gray-50 px-3 text-sm text-gray-500 md:inline-flex" "border-default bg-muted text-subtle hidden h-9 items-center rounded-l-md border border-r-0 border-r-gray-300 px-3 text-sm md:inline-flex"
)}> )}>
{process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")}/ {process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")}/
</span> </span>
@@ -216,7 +216,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
markAsError markAsError
? "focus:shadow-0 focus:ring-shadow-0 border-red-500 focus:border-red-500 focus:outline-none" ? "focus:shadow-0 focus:ring-shadow-0 border-red-500 focus:border-red-500 focus:outline-none"
: "border-l-gray-300", : "border-l-gray-300",
disabled ? "bg-gray-100 text-gray-400 focus:border-0" : "" disabled ? "bg-subtle text-muted focus:border-0" : ""
)} )}
value={inputUsernameValue} value={inputUsernameValue}
onChange={(event) => { onChange={(event) => {
@@ -250,29 +250,30 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
{markAsError && <p className="mt-1 text-xs text-red-500">Username is already taken</p>} {markAsError && <p className="mt-1 text-xs text-red-500">Username is already taken</p>}
<Dialog open={openDialogSaveUsername}> <Dialog open={openDialogSaveUsername}>
<DialogContent> <DialogContent
<div className="flex flex-row"> Icon={FiEdit2}
<div className="xs:hidden flex h-10 w-10 flex-shrink-0 justify-center rounded-full bg-[#FAFAFA]"> title={t("confirm_username_change_dialog_title")}
<FiEdit2 className="m-auto h-6 w-6" /> description={
</div> <>
<div className="mb-4 w-full px-4 pt-1">
<DialogHeader title={t("confirm_username_change_dialog_title")} />
{usernameChangeCondition && usernameChangeCondition === UsernameChangeStatusEnum.UPGRADE && ( {usernameChangeCondition && usernameChangeCondition === UsernameChangeStatusEnum.UPGRADE && (
<p className="mb-4 text-sm text-gray-800">{t("change_username_standard_to_premium")}</p> <p className="text-default mb-4 text-sm">{t("change_username_standard_to_premium")}</p>
)} )}
</>
<div className="flex w-full flex-wrap rounded-sm bg-gray-100 py-3 text-sm"> }>
<div className="flex flex-row">
<div className="mb-4 w-full px-4 pt-1">
<div className="bg-subtle flex w-full flex-wrap rounded-sm py-3 text-sm">
<div className="flex-1 px-2"> <div className="flex-1 px-2">
<p className="text-gray-500">{t("current_username")}</p> <p className="text-subtle">{t("current_username")}</p>
<p className="mt-1" data-testid="current-username"> <p className="text-emphasis mt-1" data-testid="current-username">
{currentUsername} {currentUsername}
</p> </p>
</div> </div>
<div className="ml-6 flex-1"> <div className="ml-6 flex-1">
<p className="text-gray-500" data-testid="new-username"> <p className="text-subtle" data-testid="new-username">
{t("new_username")} {t("new_username")}
</p> </p>
<p>{inputUsernameValue}</p> <p className="text-emphasis">{inputUsernameValue}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -8,7 +8,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { TRPCClientErrorLike } from "@calcom/trpc/client"; import type { TRPCClientErrorLike } from "@calcom/trpc/client";
import { trpc } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react";
import type { AppRouter } from "@calcom/trpc/server/routers/_app"; import type { AppRouter } from "@calcom/trpc/server/routers/_app";
import { Button, Dialog, DialogClose, DialogContent, DialogHeader, TextField } from "@calcom/ui"; import { Button, Dialog, DialogClose, DialogContent, TextField } from "@calcom/ui";
import { FiCheck, FiEdit2 } from "@calcom/ui/components/icon"; import { FiCheck, FiEdit2 } from "@calcom/ui/components/icon";
interface ICustomUsernameProps { interface ICustomUsernameProps {
@@ -79,7 +79,7 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
const ActionButtons = () => { const ActionButtons = () => {
return usernameIsAvailable && currentUsername !== inputUsernameValue ? ( return usernameIsAvailable && currentUsername !== inputUsernameValue ? (
<div className="rlt:space-x-reverse mt-px flex flex-row space-x-2 ltr:ml-2 ltr:mr-2 rtl:ml-2 rtl:space-x-reverse"> <div className="rlt:space-x-reverse ms-2 me-2 mt-px flex flex-row space-x-2 rtl:space-x-reverse">
<Button <Button
type="button" type="button"
onClick={() => setOpenDialogSaveUsername(true)} onClick={() => setOpenDialogSaveUsername(true)}
@@ -154,31 +154,25 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
</div> </div>
)} )}
<Dialog open={openDialogSaveUsername}> <Dialog open={openDialogSaveUsername}>
<DialogContent> <DialogContent type="confirmation" Icon={FiEdit2} title={t("confirm_username_change_dialog_title")}>
<div style={{ display: "flex", flexDirection: "row" }}> <div className="flex flex-row">
<div className="xs:hidden flex h-10 w-10 flex-shrink-0 justify-center rounded-full bg-[#FAFAFA]"> <div className="mb-4 w-full pt-1">
<FiEdit2 className="m-auto h-6 w-6" /> <div className="bg-subtle flex w-full flex-wrap rounded-sm py-3 text-sm">
</div>
<div className="mb-4 w-full px-4 pt-1">
<DialogHeader title={t("confirm_username_change_dialog_title")} />
<div className="flex w-full flex-wrap rounded-sm bg-gray-100 py-3 text-sm">
<div className="flex-1 px-2"> <div className="flex-1 px-2">
<p className="text-gray-500">{t("current_username")}</p> <p className="text-subtle">{t("current_username")}</p>
<p className="mt-1" data-testid="current-username"> <p className="text-emphasis mt-1" data-testid="current-username">
{currentUsername} {currentUsername}
</p> </p>
</div> </div>
<div className="flex-1"> <div className="ml-6 flex-1">
<p className="text-gray-500" data-testid="new-username"> <p className="text-subtle" data-testid="new-username">
{t("new_username")} {t("new_username")}
</p> </p>
<p>{inputUsernameValue}</p> <p className="text-emphasis">{inputUsernameValue}</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="mt-4 flex flex-row-reverse gap-x-2"> <div className="mt-4 flex flex-row-reverse gap-x-2">
<Button <Button
type="button" type="button"
@@ -22,7 +22,7 @@ const CheckboxField = forwardRef<HTMLInputElement, Props>(
{React.createElement( {React.createElement(
descriptionAsLabel ? "div" : "label", descriptionAsLabel ? "div" : "label",
{ {
className: "flex text-sm font-medium text-gray-700", className: "flex text-sm font-medium text-default",
...(!descriptionAsLabel ...(!descriptionAsLabel
? { ? {
htmlFor: rest.id, htmlFor: rest.id,
@@ -40,7 +40,7 @@ const CheckboxField = forwardRef<HTMLInputElement, Props>(
{ {
className: classNames( className: classNames(
"relative flex items-start", "relative flex items-start",
descriptionAsLabel ? "text-gray-700" : "text-gray-900" descriptionAsLabel ? "text-default" : "text-emphasis"
), ),
}, },
<> <>
@@ -49,10 +49,10 @@ const CheckboxField = forwardRef<HTMLInputElement, Props>(
{...rest} {...rest}
ref={ref} ref={ref}
type="checkbox" type="checkbox"
className="text-primary-600 focus:ring-primary-500 h-4 w-4 rounded border-gray-300" className="text-primary-600 focus:ring-primary-500 border-default h-4 w-4 rounded"
/> />
</div> </div>
<span className="text-sm ltr:ml-3 rtl:mr-3">{description}</span> <span className="ms-3 text-sm">{description}</span>
</> </>
)} )}
{informationIconText && <InfoBadge content={informationIconText} />} {informationIconText && <InfoBadge content={informationIconText} />}
@@ -51,7 +51,7 @@ export const CheckedSelect = ({
{option.label} {option.label}
<FiX <FiX
onClick={() => props.onChange(value.filter((item) => item.value !== option.value))} onClick={() => props.onChange(value.filter((item) => item.value !== option.value))}
className="float-right mt-0.5 h-5 w-5 cursor-pointer text-gray-500" className="text-subtle float-right mt-0.5 h-5 w-5 cursor-pointer"
/> />
</div> </div>
))} ))}
+2 -2
View File
@@ -19,11 +19,11 @@ export const DatePicker = ({ minDate, disabled, date, onDatesChange, className }
return ( return (
<PrimitiveDatePicker <PrimitiveDatePicker
className={classNames( className={classNames(
"focus:ring-primary-500 focus:border-primary-500 rounded-sm border border-gray-300 p-1 pl-2 text-sm", "focus:ring-primary-500 focus:border-primary-500 border-default rounded-sm border p-1 pl-2 text-sm",
className className
)} )}
clearIcon={null} clearIcon={null}
calendarIcon={<FiCalendar className="h-5 w-5 text-gray-500" />} calendarIcon={<FiCalendar className="text-subtle h-5 w-5" />}
value={date} value={date}
minDate={minDate} minDate={minDate}
disabled={disabled} disabled={disabled}
@@ -19,7 +19,10 @@ export type GroupOptionType = GroupBase<LocationOption>;
const OptionWithIcon = ({ icon, label }: { icon?: string; label: string }) => { const OptionWithIcon = ({ icon, label }: { icon?: string; label: string }) => {
return ( return (
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{icon && <img src={icon} alt="cover" className="h-3.5 w-3.5" />} {/* TODO: figure out a way to invert icons when in dark mode. We can't just
dark:invert due to google meet cal etc all breaking when we do this
*/}
{icon && <img src={icon} alt="cover" className="h-3.5 w-3.5 dark:hidden" />}
<span className={classNames("text-sm font-medium")}>{label}</span> <span className={classNames("text-sm font-medium")}>{label}</span>
</div> </div>
); );
@@ -48,7 +51,7 @@ export default function LocationSelect(props: Props<LocationOption, false, Group
<span>{e.label}</span> <span>{e.label}</span>
</div> </div>
)} )}
formatGroupLabel={(e) => <p className="text-xs font-medium text-gray-600">{e.label}</p>} formatGroupLabel={(e) => <p className="text-default text-xs font-medium">{e.label}</p>}
{...props} {...props}
/> />
); );
+3 -3
View File
@@ -11,7 +11,7 @@ const MinutesField = forwardRef<HTMLInputElement, Props>(({ label, ...rest }, re
<div className="block sm:flex"> <div className="block sm:flex">
{!!label && ( {!!label && (
<div className="min-w-48 mb-4 sm:mb-0"> <div className="min-w-48 mb-4 sm:mb-0">
<label htmlFor={rest.id} className="flex h-full items-center text-sm font-medium text-gray-700"> <label htmlFor={rest.id} className="text-default flex h-full items-center text-sm font-medium">
{label} {label}
</label> </label>
</div> </div>
@@ -23,12 +23,12 @@ const MinutesField = forwardRef<HTMLInputElement, Props>(({ label, ...rest }, re
ref={ref} ref={ref}
type="number" type="number"
className={classNames( className={classNames(
"block w-full rounded-sm border-gray-300 pl-2 pr-12 text-sm", "border-default block w-full rounded-sm pl-2 pr-12 text-sm",
rest.className rest.className
)} )}
/> />
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3"> <div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<span className="text-sm text-gray-500" id="duration"> <span className="text-subtle text-sm" id="duration">
mins mins
</span> </span>
</div> </div>
+50 -17
View File
@@ -6,7 +6,8 @@ import { appWithTranslation } from "next-i18next";
import { ThemeProvider } from "next-themes"; import { ThemeProvider } from "next-themes";
import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app"; import type { AppProps as NextAppProps, AppProps as NextJsAppProps } from "next/app";
import type { NextRouter } from "next/router"; import type { NextRouter } from "next/router";
import type { ComponentProps, ReactNode } from "react"; import { useRouter } from "next/router";
import type { ComponentProps, PropsWithChildren, ReactNode } from "react";
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic"; import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic"; import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
@@ -60,6 +61,51 @@ const CustomI18nextProvider = (props: AppPropsWithChildren) => {
return <I18nextAdapter {...passedProps} />; return <I18nextAdapter {...passedProps} />;
}; };
const CalcomThemeProvider = (
props: PropsWithChildren<
WithNonceProps & { isThemeSupported?: boolean | ((arg: { router: NextRouter }) => boolean) }
>
) => {
// We now support the inverse of how we handled it in the past. Setting this to false will disable theme.
// undefined or true means we use system theme
const router = useRouter();
const isThemeSupported = (() => {
if (typeof props.isThemeSupported === "function") {
return props.isThemeSupported({ router: router });
}
if (typeof props.isThemeSupported === "undefined") {
return true;
}
return props.isThemeSupported;
})();
const forcedTheme = !isThemeSupported ? "light" : undefined;
// Use namespace of embed to ensure same namespaced embed are displayed with same theme. This allows different embeds on the same website to be themed differently
// One such example is our Embeds Demo and Testing page at http://localhost:3100
// Having `getEmbedNamespace` defined on window before react initializes the app, ensures that embedNamespace is available on the first mount and can be used as part of storageKey
const embedNamespace = typeof window !== "undefined" ? window.getEmbedNamespace() : null;
// If embedNamespace is not defined, we use the default storageKey -> The default storage key changs based on if we force light mode or not
// This is done to ensure that the default theme is light when we force light mode and as soon as you navigate to a page that is dark we dont need a hard refresh to change
const storageKey =
typeof embedNamespace === "string"
? `embed-theme-${embedNamespace}`
: !isThemeSupported
? "cal-light"
: "theme";
return (
<ThemeProvider
nonce={props.nonce}
enableColorScheme={false}
enableSystem={isThemeSupported}
forcedTheme={forcedTheme}
storageKey={storageKey}
attribute="class">
{props.children}
</ThemeProvider>
);
};
function FeatureFlagsProvider({ children }: { children: React.ReactNode }) { function FeatureFlagsProvider({ children }: { children: React.ReactNode }) {
const flags = useFlags(); const flags = useFlags();
return <FeatureProvider value={flags}>{children}</FeatureProvider>; return <FeatureProvider value={flags}>{children}</FeatureProvider>;
@@ -69,16 +115,6 @@ const AppProviders = (props: AppPropsWithChildren) => {
const session = trpc.viewer.public.session.useQuery().data; const session = trpc.viewer.public.session.useQuery().data;
// No need to have intercom on public pages - Good for Page Performance // No need to have intercom on public pages - Good for Page Performance
const isPublicPage = usePublicPage(); const isPublicPage = usePublicPage();
const isThemeSupported =
typeof props.Component.isThemeSupported === "function"
? props.Component.isThemeSupported({ router: props.router })
: props.Component.isThemeSupported;
const forcedTheme = isThemeSupported ? undefined : "light";
// Use namespace of embed to ensure same namespaced embed are displayed with same theme. This allows different embeds on the same website to be themed differently
// One such example is our Embeds Demo and Testing page at http://localhost:3100
// Having `getEmbedNamespace` defined on window before react initializes the app, ensures that embedNamespace is available on the first mount and can be used as part of storageKey
const embedNamespace = typeof window !== "undefined" ? window.getEmbedNamespace() : null;
const storageKey = typeof embedNamespace === "string" ? `embed-theme-${embedNamespace}` : "theme";
const RemainingProviders = ( const RemainingProviders = (
<EventCollectionProvider options={{ apiPath: "/api/collect-events" }}> <EventCollectionProvider options={{ apiPath: "/api/collect-events" }}>
@@ -86,16 +122,13 @@ const AppProviders = (props: AppPropsWithChildren) => {
<CustomI18nextProvider {...props}> <CustomI18nextProvider {...props}>
<TooltipProvider> <TooltipProvider>
{/* color-scheme makes background:transparent not work which is required by embed. We need to ensure next-theme adds color-scheme to `body` instead of `html`(https://github.com/pacocoursey/next-themes/blob/main/src/index.tsx#L74). Once that's done we can enable color-scheme support */} {/* color-scheme makes background:transparent not work which is required by embed. We need to ensure next-theme adds color-scheme to `body` instead of `html`(https://github.com/pacocoursey/next-themes/blob/main/src/index.tsx#L74). Once that's done we can enable color-scheme support */}
<ThemeProvider <CalcomThemeProvider
nonce={props.pageProps.nonce} nonce={props.pageProps.nonce}
enableColorScheme={false} isThemeSupported={props.Component.isThemeSupported}>
storageKey={storageKey}
forcedTheme={forcedTheme}
attribute="class">
<FeatureFlagsProvider> <FeatureFlagsProvider>
<MetaProvider>{props.children}</MetaProvider> <MetaProvider>{props.children}</MetaProvider>
</FeatureFlagsProvider> </FeatureFlagsProvider>
</ThemeProvider> </CalcomThemeProvider>
</TooltipProvider> </TooltipProvider>
</CustomI18nextProvider> </CustomI18nextProvider>
</SessionProvider> </SessionProvider>
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@calcom/web", "name": "@calcom/web",
"version": "2.7.7", "version": "2.7.13",
"private": true, "private": true,
"scripts": { "scripts": {
"analyze": "ANALYZE=true next build", "analyze": "ANALYZE=true next build",
@@ -170,7 +170,7 @@
"module-alias": "^2.2.2", "module-alias": "^2.2.2",
"msw": "^0.42.3", "msw": "^0.42.3",
"postcss": "^8.4.18", "postcss": "^8.4.18",
"tailwindcss": "^3.2.1", "tailwindcss": "^3.3.1",
"ts-jest": "^28.0.8", "ts-jest": "^28.0.8",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.9.4" "typescript": "^4.9.4"
+46 -44
View File
@@ -3,7 +3,7 @@ import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { COMPANY_NAME, DEVELOPER_DOCS, DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants"; import { DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HeadSeo } from "@calcom/ui"; import { HeadSeo } from "@calcom/ui";
import { FiBookOpen, FiCheck, FiChevronRight, FiFileText } from "@calcom/ui/components/icon"; import { FiBookOpen, FiCheck, FiChevronRight, FiFileText } from "@calcom/ui/components/icon";
@@ -88,21 +88,21 @@ export default function Custom404() {
noindex: true, noindex: true,
}} }}
/> />
<div className="min-h-screen bg-white px-4" data-testid="404-page"> <div className="bg-default min-h-screen px-4" data-testid="404-page">
<main className="mx-auto max-w-xl pt-16 pb-6 sm:pt-24"> <main className="mx-auto max-w-xl pt-16 pb-6 sm:pt-24">
{isSignup && process.env.NEXT_PUBLIC_WEBAPP_URL !== "https://app.cal.com" ? ( {isSignup && process.env.NEXT_PUBLIC_WEBAPP_URL !== "https://app.cal.com" ? (
<div> <div>
<div> <div>
<p className="text-sm font-semibold uppercase tracking-wide text-black"> <p className="text-emphasis text-sm font-semibold uppercase tracking-wide">
{t("missing_license")} {t("missing_license")}
</p> </p>
<h1 className="font-cal mt-2 text-3xl font-extrabold text-gray-900"> <h1 className="font-cal text-emphasis mt-2 text-3xl font-extrabold">
{t("signup_requires")} {t("signup_requires")}
</h1> </h1>
<p className="mt-4">{t("signup_requires_description", { companyName: COMPANY_NAME })}</p> <p className="mt-4">{t("signup_requires_description", { companyName: "Cal.com" })}</p>
</div> </div>
<div className="mt-12"> <div className="mt-12">
<h2 className="text-sm font-semibold uppercase tracking-wide text-gray-500"> <h2 className="text-subtle text-sm font-semibold uppercase tracking-wide">
{t("next_steps")} {t("next_steps")}
</h2> </h2>
<ul role="list" className="mt-4"> <ul role="list" className="mt-4">
@@ -116,44 +116,44 @@ export default function Custom404() {
</span> </span>
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-emphasis text-base font-medium">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2"> <span className="focus-within:ring-empthasis rounded-sm focus-within:ring-2 focus-within:ring-offset-2">
<span className="focus:outline-none"> <span className="focus:outline-none">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
{t("acquire_commercial_license")} {t("acquire_commercial_license")}
</span> </span>
</span> </span>
</h3> </h3>
<p className="text-base text-gray-500">{t("the_infrastructure_plan")}</p> <p className="text-subtle text-base">{t("the_infrastructure_plan")}</p>
</div> </div>
<div className="flex-shrink-0 self-center"> <div className="flex-shrink-0 self-center">
<FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" /> <FiChevronRight className="text-muted h-5 w-5" aria-hidden="true" />
</div> </div>
</a> </a>
</li> </li>
</ul> </ul>
<ul role="list" className="divide-y divide-gray-200 border-gray-200"> <ul role="list" className="border-subtle divide-subtle divide-y">
<li className="px-4 py-2"> <li className="px-4 py-2">
<Link <Link
href={`${DEVELOPER_DOCS}/self-hosting/installation`} href="https://cal.com/self-hosting/installation"
className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse"> className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="flex h-12 w-12 items-center justify-center rounded-lg bg-gray-50"> <span className="bg-muted flex h-12 w-12 items-center justify-center rounded-lg">
<FiFileText className="h-6 w-6 text-gray-700" aria-hidden="true" /> <FiFileText className="text-default h-6 w-6" aria-hidden="true" />
</span> </span>
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-emphasis text-base font-medium">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2"> <span className="focus-within:ring-empthasis rounded-sm focus-within:ring-2 focus-within:ring-offset-2">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
{t("prisma_studio_tip")} {t("prisma_studio_tip")}
</span> </span>
</h3> </h3>
<p className="text-base text-gray-500">{t("prisma_studio_tip_description")}</p> <p className="text-subtle text-base">{t("prisma_studio_tip_description")}</p>
</div> </div>
<div className="flex-shrink-0 self-center"> <div className="flex-shrink-0 self-center">
<FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" /> <FiChevronRight className="text-muted h-5 w-5" aria-hidden="true" />
</div> </div>
</Link> </Link>
</li> </li>
@@ -162,7 +162,7 @@ export default function Custom404() {
href={JOIN_SLACK} href={JOIN_SLACK}
className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse"> className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="flex h-12 w-12 items-center justify-center rounded-lg bg-gray-50"> <span className="bg-muted flex h-12 w-12 items-center justify-center rounded-lg">
<svg <svg
viewBox="0 0 2447.6 2452.5" viewBox="0 0 2447.6 2452.5"
className="h-6 w-6" className="h-6 w-6"
@@ -189,16 +189,16 @@ export default function Custom404() {
</span> </span>
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-emphasis text-base font-medium">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2"> <span className="focus-within:ring-empthasis rounded-sm focus-within:ring-2 focus-within:ring-offset-2">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
Slack Slack
</span> </span>
</h3> </h3>
<p className="text-base text-gray-500">{t("join_our_community")}</p> <p className="text-subtle text-base">{t("join_our_community")}</p>
</div> </div>
<div className="flex-shrink-0 self-center"> <div className="flex-shrink-0 self-center">
<FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" /> <FiChevronRight className="text-muted h-5 w-5" aria-hidden="true" />
</div> </div>
</a> </a>
</li> </li>
@@ -206,7 +206,7 @@ export default function Custom404() {
<div className="mt-8"> <div className="mt-8">
<Link <Link
href={`${WEBSITE_URL}/enterprise`} href={`${WEBSITE_URL}/enterprise`}
className="text-base font-medium text-black hover:text-gray-500"> className="hover:text-subtle text-emphasis text-base font-medium">
{t("contact_sales")} {t("contact_sales")}
<span aria-hidden="true"> &rarr;</span> <span aria-hidden="true"> &rarr;</span>
</Link> </Link>
@@ -216,8 +216,10 @@ export default function Custom404() {
) : ( ) : (
<> <>
<div className="text-center"> <div className="text-center">
<p className="text-sm font-semibold uppercase tracking-wide text-black">{t("error_404")}</p> <p className="text-emphasis text-sm font-semibold uppercase tracking-wide">
<h1 className="font-cal mt-2 text-4xl font-extrabold text-gray-900 sm:text-5xl"> {t("error_404")}
</p>
<h1 className="font-cal text-emphasis mt-2 text-4xl font-extrabold sm:text-5xl">
{isSuccessPage ? "Booking not found" : t("page_doesnt_exist")} {isSuccessPage ? "Booking not found" : t("page_doesnt_exist")}
</h1> </h1>
{isSubpage ? ( {isSubpage ? (
@@ -242,7 +244,7 @@ export default function Custom404() {
)} )}
</div> </div>
<div className="mt-12"> <div className="mt-12">
<h2 className="text-sm font-semibold uppercase tracking-wide text-gray-500"> <h2 className="text-subtle text-sm font-semibold uppercase tracking-wide">
{t("popular_pages")} {t("popular_pages")}
</h2> </h2>
{!isSubpage && isCalcom && ( {!isSubpage && isCalcom && (
@@ -259,46 +261,46 @@ export default function Custom404() {
</span> </span>
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-emphasis text-base font-medium">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2"> <span className="focus-within:ring-empthasis rounded-sm focus-within:ring-2 focus-within:ring-offset-2">
<span className="focus:outline-none"> <span className="focus:outline-none">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
{t("register")} <strong className="text-green-500">{username}</strong> {t("register")} <strong className="text-green-500">{username}</strong>
</span> </span>
</span> </span>
</h3> </h3>
<p className="text-base text-gray-500">{t("claim_username_and_schedule_events")}</p> <p className="text-subtle text-base">{t("claim_username_and_schedule_events")}</p>
</div> </div>
<div className="flex-shrink-0 self-center"> <div className="flex-shrink-0 self-center">
<FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" /> <FiChevronRight className="text-muted h-5 w-5" aria-hidden="true" />
</div> </div>
</a> </a>
</li> </li>
</ul> </ul>
)} )}
<ul role="list" className="mt-4 divide-y divide-gray-200 border-gray-200"> <ul role="list" className="border-subtle divide-subtle mt-4 divide-y">
{links.map((link, linkIdx) => ( {links.map((link, linkIdx) => (
<li key={linkIdx} className="px-4 py-2"> <li key={linkIdx} className="px-4 py-2">
<Link <Link
href={link.href} href={link.href}
className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse"> className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="flex h-12 w-12 items-center justify-center rounded-lg bg-gray-50"> <span className="bg-muted flex h-12 w-12 items-center justify-center rounded-lg">
<link.icon className="h-6 w-6 text-gray-700" aria-hidden="true" /> <link.icon className="text-default h-6 w-6" aria-hidden="true" />
</span> </span>
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-emphasis text-base font-medium">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2"> <span className="focus-within:ring-empthasis rounded-sm focus-within:ring-2 focus-within:ring-offset-2">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
{link.title} {link.title}
</span> </span>
</h3> </h3>
<p className="text-base text-gray-500">{link.description}</p> <p className="text-subtle text-base">{link.description}</p>
</div> </div>
<div className="flex-shrink-0 self-center"> <div className="flex-shrink-0 self-center">
<FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" /> <FiChevronRight className="text-muted h-5 w-5" aria-hidden="true" />
</div> </div>
</Link> </Link>
</li> </li>
@@ -308,7 +310,7 @@ export default function Custom404() {
href={JOIN_SLACK} href={JOIN_SLACK}
className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse"> className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="flex h-12 w-12 items-center justify-center rounded-lg bg-gray-50"> <span className="bg-muted flex h-12 w-12 items-center justify-center rounded-lg">
<svg <svg
viewBox="0 0 2447.6 2452.5" viewBox="0 0 2447.6 2452.5"
className="h-6 w-6" className="h-6 w-6"
@@ -335,22 +337,22 @@ export default function Custom404() {
</span> </span>
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<h3 className="text-base font-medium text-gray-900"> <h3 className="text-emphasis text-base font-medium">
<span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2"> <span className="focus-within:ring-empthasis rounded-sm focus-within:ring-2 focus-within:ring-offset-2">
<span className="absolute inset-0" aria-hidden="true" /> <span className="absolute inset-0" aria-hidden="true" />
Slack Slack
</span> </span>
</h3> </h3>
<p className="text-base text-gray-500">{t("join_our_community")}</p> <p className="text-subtle text-base">{t("join_our_community")}</p>
</div> </div>
<div className="flex-shrink-0 self-center"> <div className="flex-shrink-0 self-center">
<FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" /> <FiChevronRight className="text-muted h-5 w-5" aria-hidden="true" />
</div> </div>
</a> </a>
</li> </li>
</ul> </ul>
<div className="mt-8"> <div className="mt-8">
<Link href="/" className="text-base font-medium text-black hover:text-gray-500"> <Link href="/" className="hover:text-subtle text-emphasis text-base font-medium">
{t("or_go_back_home")} {t("or_go_back_home")}
<span aria-hidden="true"> &rarr;</span> <span aria-hidden="true"> &rarr;</span>
</Link> </Link>
+7 -7
View File
@@ -11,24 +11,24 @@ export default function Error500() {
const router = useRouter(); const router = useRouter();
return ( return (
<div className="flex h-screen bg-gray-100"> <div className="bg-subtle flex h-screen">
<Head> <Head>
<title>Something unexpected occurred | {APP_NAME}</title> <title>Something unexpected occurred | {APP_NAME}</title>
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
</Head> </Head>
<div className="rtl: m-auto rounded-md bg-white p-10 text-right ltr:text-left"> <div className="rtl: bg-default m-auto rounded-md p-10 text-right ltr:text-left">
<h1 className="font-cal text-6xl text-black">500</h1> <h1 className="font-cal text-emphasis text-6xl">500</h1>
<h2 className="mt-6 text-2xl font-medium text-black">It&apos;s not you, it&apos;s us.</h2> <h2 className="text-emphasis mt-6 text-2xl font-medium">It&apos;s not you, it&apos;s us.</h2>
<p className="mt-4 mb-6 max-w-2xl text-sm text-gray-600"> <p className="text-default mt-4 mb-6 max-w-2xl text-sm">
Something went wrong on our end. Get in touch with our support team, and we’ll get it fixed right Something went wrong on our end. Get in touch with our support team, and we’ll get it fixed right
away for you. away for you.
</p> </p>
{router.query.error && ( {router.query.error && (
<div className="mb-8 flex flex-col"> <div className="mb-8 flex flex-col">
<p className="mb-4 max-w-2xl text-sm text-gray-600"> <p className="text-default mb-4 max-w-2xl text-sm">
Please provide the following text when contacting support to better help you: Please provide the following text when contacting support to better help you:
</p> </p>
<pre className="w-full max-w-2xl whitespace-normal break-words rounded-md bg-gray-200 p-4 text-gray-900"> <pre className="bg-emphasis text-emphasis w-full max-w-2xl whitespace-normal break-words rounded-md p-4">
{router.query.error} {router.query.error}
<br /> <br />
<Button <Button
+20 -37
View File
@@ -14,7 +14,6 @@ import {
} from "@calcom/embed-core/embed-iframe"; } from "@calcom/embed-core/embed-iframe";
import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components"; import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components";
import EmptyPage from "@calcom/features/eventtypes/components/EmptyPage"; import EmptyPage from "@calcom/features/eventtypes/components/EmptyPage";
import CustomBranding from "@calcom/lib/CustomBranding";
import { WEBAPP_URL } from "@calcom/lib/constants"; import { WEBAPP_URL } from "@calcom/lib/constants";
import defaultEvents, { import defaultEvents, {
getDynamicEventDescription, getDynamicEventDescription,
@@ -48,9 +47,9 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
const groupEventTypes = props.users.some((user) => !user.allowDynamicBooking) ? ( const groupEventTypes = props.users.some((user) => !user.allowDynamicBooking) ? (
<div className="space-y-6" data-testid="event-types"> <div className="space-y-6" data-testid="event-types">
<div className="overflow-hidden rounded-sm border dark:border-gray-900"> <div className="overflow-hidden rounded-sm border ">
<div className="p-8 text-center text-gray-400 dark:text-white"> <div className="text-muted p-8 text-center">
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white">{" " + t("unavailable")}</h2> <h2 className="font-cal text-default mb-2 text-3xl">{" " + t("unavailable")}</h2>
<p className="mx-auto max-w-md">{t("user_dynamic_booking_disabled") as string}</p> <p className="mx-auto max-w-md">{t("user_dynamic_booking_disabled") as string}</p>
</div> </div>
</div> </div>
@@ -60,14 +59,14 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
{eventTypes.map((type, index) => ( {eventTypes.map((type, index) => (
<li <li
key={index} key={index}
className="dark:bg-darkgray-100 dark:border-darkgray-300 group relative border-b border-gray-200 bg-white first:rounded-t-md last:rounded-b-md last:border-b-0 hover:bg-gray-50"> className=" border-subtle bg-default hover:bg-muted group relative border-b first:rounded-t-md last:rounded-b-md last:border-b-0">
<FiArrowRight className="absolute right-3 top-3 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" /> <FiArrowRight className="text-emphasis absolute right-3 top-3 h-4 w-4 opacity-0 transition-opacity group-hover:opacity-100" />
<Link <Link
href={getUsernameSlugLink({ users: props.users, slug: type.slug })} href={getUsernameSlugLink({ users: props.users, slug: type.slug })}
className="flex justify-between px-6 py-4" className="flex justify-between px-6 py-4"
data-testid="event-type-link"> data-testid="event-type-link">
<div className="flex-shrink"> <div className="flex-shrink">
<p className="dark:text-darkgray-700 text-sm font-semibold text-gray-900">{type.title}</p> <p className=" text-emphasis text-sm font-semibold">{type.title}</p>
<EventTypeDescription className="text-sm" eventType={type} /> <EventTypeDescription className="text-sm" eventType={type} />
</div> </div>
<div className="mt-1 self-center"> <div className="mt-1 self-center">
@@ -118,53 +117,39 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
: [{ username: `${user.username}`, name: `${user.name}` }], : [{ username: `${user.username}`, name: `${user.name}` }],
}} }}
/> />
<CustomBranding lightVal={profile.brandColor} darkVal={profile.darkBrandColor} />
<div <div className={classNames(shouldAlignCentrally ? "mx-auto" : "", isEmbed ? "max-w-3xl" : "")}>
className={classNames(
shouldAlignCentrally ? "mx-auto" : "",
isEmbed ? "max-w-3xl" : "",
"dark:bg-darkgray-50"
)}>
<main <main
className={classNames( className={classNames(
shouldAlignCentrally ? "mx-auto" : "", shouldAlignCentrally ? "mx-auto" : "",
isEmbed isEmbed ? " border-bookinglightest bg-default rounded-md border" : "",
? " border-bookinglightest dark:bg-darkgray-50 rounded-md border bg-white sm:dark:border-gray-600"
: "",
"max-w-3xl py-24 px-4" "max-w-3xl py-24 px-4"
)}> )}>
{isSingleUser && ( // When we deal with a single user, not dynamic group {isSingleUser && ( // When we deal with a single user, not dynamic group
<div className="mb-8 text-center"> <div className="mb-8 text-center">
<Avatar imageSrc={user.avatar} size="xl" alt={nameOrUsername} /> <Avatar imageSrc={user.avatar} size="xl" alt={nameOrUsername} />
<h1 className="font-cal mb-1 text-3xl text-gray-900 dark:text-white"> <h1 className="font-cal text-emphasis mb-1 text-3xl">
{nameOrUsername} {nameOrUsername}
{user.verified && ( {user.verified && <BadgeCheckIcon className=" mx-1 -mt-1 inline h-6 w-6 text-blue-500" />}
<BadgeCheckIcon className="mx-1 -mt-1 inline h-6 w-6 text-blue-500 dark:text-white" />
)}
</h1> </h1>
{!isBioEmpty && ( {!isBioEmpty && (
<> <>
<div <div
className=" dark:text-darkgray-600 text-sm text-gray-500 [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600" className=" text-subtle text-sm [&_a]:text-blue-500 [&_a]:underline [&_a]:hover:text-blue-600"
dangerouslySetInnerHTML={{ __html: props.safeBio }} dangerouslySetInnerHTML={{ __html: props.safeBio }}
/> />
</> </>
)} )}
</div> </div>
)} )}
<div <div
className={classNames( className={classNames("rounded-md ", !isEventListEmpty && "border-subtle border")}
"rounded-md ",
!isEventListEmpty && "dark:border-darkgray-300 border border-gray-200"
)}
data-testid="event-types"> data-testid="event-types">
{user.away ? ( {user.away ? (
<div className="overflow-hidden rounded-sm border dark:border-gray-900"> <div className="overflow-hidden rounded-sm border ">
<div className="p-8 text-center text-gray-400 dark:text-white"> <div className="text-muted p-8 text-center">
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white"> <h2 className="font-cal text-default mb-2 text-3xl">😴{" " + t("user_away")}</h2>
😴{" " + t("user_away")}
</h2>
<p className="mx-auto max-w-md">{t("user_away_description") as string}</p> <p className="mx-auto max-w-md">{t("user_away_description") as string}</p>
</div> </div>
</div> </div>
@@ -175,8 +160,8 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
<div <div
key={type.id} key={type.id}
style={{ display: "flex", ...eventTypeListItemEmbedStyles }} style={{ display: "flex", ...eventTypeListItemEmbedStyles }}
className="dark:bg-darkgray-100 dark:hover:bg-darkgray-200 dark:border-darkgray-300 group relative border-b border-gray-200 bg-white first:rounded-t-md last:rounded-b-md last:border-b-0 hover:bg-gray-50"> className=" border-subtle bg-default hover:bg-muted group relative border-b first:rounded-t-md last:rounded-b-md last:border-b-0">
<FiArrowRight className="absolute right-4 top-4 h-4 w-4 text-black opacity-0 transition-opacity group-hover:opacity-100 dark:text-white" /> <FiArrowRight className="text-emphasis absolute right-4 top-4 h-4 w-4 opacity-0 transition-opacity group-hover:opacity-100" />
{/* Don't prefetch till the time we drop the amount of javascript in [user][type] page which is impacting score for [user] page */} {/* Don't prefetch till the time we drop the amount of javascript in [user][type] page which is impacting score for [user] page */}
<div className="block w-full p-5"> <div className="block w-full p-5">
<Link <Link
@@ -193,9 +178,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
}} }}
data-testid="event-type-link"> data-testid="event-type-link">
<div className="flex flex-wrap items-center"> <div className="flex flex-wrap items-center">
<h2 className="dark:text-darkgray-700 pr-2 text-sm font-semibold text-gray-700"> <h2 className=" text-default pr-2 text-sm font-semibold">{type.title}</h2>
{type.title}
</h2>
</div> </div>
<EventTypeDescription eventType={type} /> <EventTypeDescription eventType={type} />
</Link> </Link>
@@ -204,6 +187,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
)) ))
)} )}
</div> </div>
{isEventListEmpty && <EmptyPage name={user.name ?? "User"} />} {isEventListEmpty && <EmptyPage name={user.name ?? "User"} />}
</main> </main>
<Toaster position="bottom-right" /> <Toaster position="bottom-right" />
@@ -211,7 +195,6 @@ export default function User(props: inferSSRProps<typeof getServerSideProps> & E
</> </>
); );
} }
User.isThemeSupported = true;
const getEventTypesWithHiddenFromDB = async (userId: number) => { const getEventTypesWithHiddenFromDB = async (userId: number) => {
return ( return (
+5 -7
View File
@@ -20,12 +20,12 @@ export default function Type(props: AvailabilityPageProps) {
const { t } = useLocale(); const { t } = useLocale();
return props.away ? ( return props.away ? (
<div className="h-screen dark:bg-gray-900"> <div className="dark:bg-inverted h-screen">
<main className="mx-auto max-w-3xl px-4 py-24"> <main className="mx-auto max-w-3xl px-4 py-24">
<div className="space-y-6" data-testid="event-types"> <div className="space-y-6" data-testid="event-types">
<div className="overflow-hidden rounded-sm border dark:border-gray-900"> <div className="overflow-hidden rounded-sm border dark:border-gray-900">
<div className="p-8 text-center text-gray-400 dark:text-white"> <div className="text-muted dark:text-inverted p-8 text-center">
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white"> <h2 className="font-cal dark:text-inverted text-emphasis600 mb-2 text-3xl">
😴{" " + t("user_away")} 😴{" " + t("user_away")}
</h2> </h2>
<p className="mx-auto max-w-md">{t("user_away_description")}</p> <p className="mx-auto max-w-md">{t("user_away_description")}</p>
@@ -39,8 +39,8 @@ export default function Type(props: AvailabilityPageProps) {
<main className="mx-auto max-w-3xl px-4 py-24"> <main className="mx-auto max-w-3xl px-4 py-24">
<div className="space-y-6" data-testid="event-types"> <div className="space-y-6" data-testid="event-types">
<div className="overflow-hidden rounded-sm border dark:border-gray-900"> <div className="overflow-hidden rounded-sm border dark:border-gray-900">
<div className="p-8 text-center text-gray-400 dark:text-white"> <div className="text-muted dark:text-inverted p-8 text-center">
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white"> <h2 className="font-cal dark:text-inverted text-emphasis600 mb-2 text-3xl">
{" " + t("unavailable")} {" " + t("unavailable")}
</h2> </h2>
<p className="mx-auto max-w-md">{t("user_dynamic_booking_disabled")}</p> <p className="mx-auto max-w-md">{t("user_dynamic_booking_disabled")}</p>
@@ -54,8 +54,6 @@ export default function Type(props: AvailabilityPageProps) {
); );
} }
Type.isThemeSupported = true;
const paramsSchema = z.object({ type: z.string(), user: z.string() }); const paramsSchema = z.object({ type: z.string(), user: z.string() });
async function getUserPageProps(context: GetStaticPropsContext) { async function getUserPageProps(context: GetStaticPropsContext) {
// load server side dependencies // load server side dependencies
+8 -10
View File
@@ -13,6 +13,8 @@ import {
getGroupName, getGroupName,
getUsernameList, getUsernameList,
} from "@calcom/lib/defaultEvents"; } from "@calcom/lib/defaultEvents";
import getBooking from "@calcom/lib/getBooking";
import type { GetBookingType } from "@calcom/lib/getBooking";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import prisma, { bookEventTypeSelect } from "@calcom/prisma"; import prisma, { bookEventTypeSelect } from "@calcom/prisma";
@@ -22,8 +24,6 @@ import {
userMetadata as userMetadataSchema, userMetadata as userMetadataSchema,
} from "@calcom/prisma/zod-utils"; } from "@calcom/prisma/zod-utils";
import type { GetBookingType } from "@lib/getBooking";
import getBooking from "@lib/getBooking";
import type { inferSSRProps } from "@lib/types/inferSSRProps"; import type { inferSSRProps } from "@lib/types/inferSSRProps";
import BookingPage from "@components/booking/pages/BookingPage"; import BookingPage from "@components/booking/pages/BookingPage";
@@ -35,12 +35,12 @@ export type BookPageProps = inferSSRProps<typeof getServerSideProps>;
export default function Book(props: BookPageProps) { export default function Book(props: BookPageProps) {
const { t } = useLocale(); const { t } = useLocale();
return props.away ? ( return props.away ? (
<div className="h-screen dark:bg-gray-900"> <div className="dark:bg-inverted h-screen">
<main className="mx-auto max-w-3xl px-4 py-24"> <main className="mx-auto max-w-3xl px-4 py-24">
<div className="space-y-6" data-testid="event-types"> <div className="space-y-6" data-testid="event-types">
<div className="overflow-hidden rounded-sm border dark:border-gray-900"> <div className="overflow-hidden rounded-sm border dark:border-gray-900">
<div className="p-8 text-center text-gray-400 dark:text-white"> <div className="text-muted dark:text-inverted p-8 text-center">
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white"> <h2 className="font-cal dark:text-inverted text-default mb-2 text-3xl">
😴{" " + t("user_away")} 😴{" " + t("user_away")}
</h2> </h2>
<p className="mx-auto max-w-md">{t("user_away_description")}</p> <p className="mx-auto max-w-md">{t("user_away_description")}</p>
@@ -50,12 +50,12 @@ export default function Book(props: BookPageProps) {
</main> </main>
</div> </div>
) : props.isDynamicGroupBooking && !props.profile.allowDynamicBooking ? ( ) : props.isDynamicGroupBooking && !props.profile.allowDynamicBooking ? (
<div className="h-screen dark:bg-gray-900"> <div className="dark:bg-inverted h-screen">
<main className="mx-auto max-w-3xl px-4 py-24"> <main className="mx-auto max-w-3xl px-4 py-24">
<div className="space-y-6" data-testid="event-types"> <div className="space-y-6" data-testid="event-types">
<div className="overflow-hidden rounded-sm border dark:border-gray-900"> <div className="overflow-hidden rounded-sm border dark:border-gray-900">
<div className="p-8 text-center text-gray-400 dark:text-white"> <div className="text-muted dark:text-inverted p-8 text-center">
<h2 className="font-cal mb-2 text-3xl text-gray-600 dark:text-white"> <h2 className="font-cal dark:text-inverted text-default mb-2 text-3xl">
{" " + t("unavailable")} {" " + t("unavailable")}
</h2> </h2>
<p className="mx-auto max-w-md">{t("user_dynamic_booking_disabled")}</p> <p className="mx-auto max-w-md">{t("user_dynamic_booking_disabled")}</p>
@@ -69,8 +69,6 @@ export default function Book(props: BookPageProps) {
); );
} }
Book.isThemeSupported = true;
const querySchema = z.object({ const querySchema = z.object({
bookingUid: z.string().optional(), bookingUid: z.string().optional(),
count: z.coerce.number().optional(), count: z.coerce.number().optional(),
@@ -47,7 +47,7 @@ export const getStaticProps: GetStaticProps<
revalidate: 1, revalidate: 1,
}; };
} catch (error) { } catch (error) {
let message = "Unknown error while fetching calendarƒ"; let message = "Unknown error while fetching calendar";
if (error instanceof Error) message = error.message; if (error instanceof Error) message = error.message;
console.error(error, message); console.error(error, message);
return { return {
+2 -2
View File
@@ -41,12 +41,12 @@ class MyDocument extends Document<Props> {
<link rel="manifest" href="/site.webmanifest" /> <link rel="manifest" href="/site.webmanifest" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#000000" /> <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#000000" />
<meta name="msapplication-TileColor" content="#ff0000" /> <meta name="msapplication-TileColor" content="#ff0000" />
<meta name="theme-color" content="#ffffff" /> <meta name="theme-color" content="var(--cal-bg)" />
<Script src="/embed-init-iframe.js" strategy="beforeInteractive" /> <Script src="/embed-init-iframe.js" strategy="beforeInteractive" />
</Head> </Head>
<body <body
className="dark:bg-darkgray-50 desktop-transparent bg-gray-100 antialiased" className="dark:bg-darkgray-50 desktop-transparent bg-subtle antialiased"
style={ style={
isEmbed isEmbed
? { ? {
@@ -3,6 +3,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
import dayjs from "@calcom/dayjs"; import dayjs from "@calcom/dayjs";
import { sendOrganizerRequestReminderEmail } from "@calcom/emails"; import { sendOrganizerRequestReminderEmail } from "@calcom/emails";
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib"; import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
import { getTranslation } from "@calcom/lib/server/i18n"; import { getTranslation } from "@calcom/lib/server/i18n";
import prisma, { bookingMinimalSelect } from "@calcom/prisma"; import prisma, { bookingMinimalSelect } from "@calcom/prisma";
@@ -48,8 +49,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
eventType: { eventType: {
select: { select: {
recurringEvent: true, recurringEvent: true,
bookingFields: true,
}, },
}, },
responses: true,
uid: true, uid: true,
destinationCalendar: true, destinationCalendar: true,
}, },
@@ -96,6 +99,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
title: booking.title, title: booking.title,
description: booking.description || undefined, description: booking.description || undefined,
customInputs: isPrismaObjOrUndefined(booking.customInputs), customInputs: isPrismaObjOrUndefined(booking.customInputs),
...getCalEventResponses({
bookingFields: booking.eventType?.bookingFields ?? null,
booking,
}),
location: booking.location ?? "", location: booking.location ?? "",
startTime: booking.startTime.toISOString(), startTime: booking.startTime.toISOString(),
endTime: booking.endTime.toISOString(), endTime: booking.endTime.toISOString(),
+1 -1
View File
@@ -11,7 +11,7 @@ export default function SetupInformation(props: InferGetStaticPropsType<typeof g
const { status } = useSession(); const { status } = useSession();
if (status === "loading") { if (status === "loading") {
return <div className="absolute z-50 flex h-screen w-full items-center bg-gray-200" />; return <div className="bg-emphasis absolute z-50 flex h-screen w-full items-center" />;
} }
if (status === "unauthenticated") { if (status === "unauthenticated") {
@@ -24,11 +24,11 @@ export default function Apps({ apps }: InferGetStaticPropsType<typeof getStaticP
<> <>
<Link <Link
href="/apps" href="/apps"
className="inline-flex items-center justify-start gap-1 rounded-sm py-2 text-gray-900"> className="text-emphasis inline-flex items-center justify-start gap-1 rounded-sm py-2">
{isLocaleReady ? t("app_store") : <SkeletonText className="h-4 w-24" />}{" "} {isLocaleReady ? t("app_store") : <SkeletonText className="h-4 w-24" />}{" "}
</Link> </Link>
{category && ( {category && (
<span className="gap-1 text-gray-600"> <span className="text-default gap-1">
<span>&nbsp;/&nbsp;</span> <span>&nbsp;/&nbsp;</span>
{t("category_apps", { category: category[0].toUpperCase() + category?.slice(1) })} {t("category_apps", { category: category[0].toUpperCase() + category?.slice(1) })}
</span> </span>
+3 -3
View File
@@ -15,7 +15,7 @@ export default function Apps({ categories }: InferGetStaticPropsType<typeof getS
<div className="text-md flex items-center gap-1 px-4 pb-3 pt-3 font-normal md:px-8 lg:px-0 lg:pt-0"> <div className="text-md flex items-center gap-1 px-4 pb-3 pt-3 font-normal md:px-8 lg:px-0 lg:pt-0">
<Link <Link
href="/apps" href="/apps"
className="inline-flex items-center justify-start gap-1 rounded-sm py-2 text-gray-900"> className="text-emphasis inline-flex items-center justify-start gap-1 rounded-sm py-2">
<FiArrowLeft className="h-4 w-4" /> <FiArrowLeft className="h-4 w-4" />
{isLocaleReady ? t("app_store") : <SkeletonText className="h-6 w-24" />}{" "} {isLocaleReady ? t("app_store") : <SkeletonText className="h-6 w-24" />}{" "}
</Link> </Link>
@@ -27,10 +27,10 @@ export default function Apps({ categories }: InferGetStaticPropsType<typeof getS
key={category.name} key={category.name}
href={"/apps/categories/" + category.name} href={"/apps/categories/" + category.name}
data-testid={`app-store-category-${category.name}`} data-testid={`app-store-category-${category.name}`}
className="relative flex rounded-sm bg-gray-100 px-6 py-4 sm:block"> className="bg-subtle relative flex rounded-sm px-6 py-4 sm:block">
<div className="self-center"> <div className="self-center">
<h3 className="font-medium capitalize">{category.name}</h3> <h3 className="font-medium capitalize">{category.name}</h3>
<p className="text-sm text-gray-500"> <p className="text-subtle text-sm">
{t("number_apps", { count: category.count })}{" "} {t("number_apps", { count: category.count })}{" "}
<FiArrowRight className="inline-block h-4 w-4" /> <FiArrowRight className="inline-block h-4 w-4" />
</p> </p>
+3 -3
View File
@@ -36,9 +36,9 @@ function AppsSearch({
}) { }) {
return ( return (
<TextField <TextField
className="!border-gray-100 bg-gray-100 !pl-0 focus:!ring-offset-0" className="bg-subtle !border-muted !pl-0 focus:!ring-offset-0"
addOnLeading={<FiSearch className="h-4 w-4 text-gray-500" />} addOnLeading={<FiSearch className="text-subtle h-4 w-4" />}
addOnClassname="!border-gray-100" addOnClassname="!border-muted"
containerClassName={classNames("focus:!ring-offset-0", className)} containerClassName={classNames("focus:!ring-offset-0", className)}
type="search" type="search"
autoComplete="false" autoComplete="false"
+1 -1
View File
@@ -259,7 +259,7 @@ const IntegrationsContainer = ({
); );
} }
return ( return (
<div className="rounded-md border border-gray-200 p-7"> <div className="border-subtle rounded-md border p-7">
<ShellSubHeading <ShellSubHeading
title={t(variant || "other")} title={t(variant || "other")}
subtitle={t(`installed_app_${variant || "other"}_description`)} subtitle={t(`installed_app_${variant || "other"}_description`)}
+3 -3
View File
@@ -28,15 +28,15 @@ export default function Error() {
return ( return (
<AuthContainer title="" description=""> <AuthContainer title="" description="">
<div> <div>
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100"> <div className="bg-error mx-auto flex h-12 w-12 items-center justify-center rounded-full">
<FiX className="h-6 w-6 text-red-600" /> <FiX className="h-6 w-6 text-red-600" />
</div> </div>
<div className="mt-3 text-center sm:mt-5"> <div className="mt-3 text-center sm:mt-5">
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title"> <h3 className="text-emphasis text-lg font-medium leading-6" id="modal-title">
{error} {error}
</h3> </h3>
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-gray-500">{errorMsg}</p> <p className="text-subtle text-sm">{errorMsg}</p>
</div> </div>
</div> </div>
</div> </div>
+3 -3
View File
@@ -60,7 +60,7 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
<> <>
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h2 className="font-cal mt-6 text-center text-3xl font-extrabold text-gray-900"> <h2 className="font-cal text-emphasis mt-6 text-center text-3xl font-extrabold">
{t("password_updated")} {t("password_updated")}
</h2> </h2>
</div> </div>
@@ -77,8 +77,8 @@ export default function Page({ resetPasswordRequest, csrfToken }: Props) {
<> <>
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h2 className="font-cal mt-6 text-center text-3xl font-extrabold text-gray-900">{t("whoops")}</h2> <h2 className="font-cal text-emphasis mt-6 text-center text-3xl font-extrabold">{t("whoops")}</h2>
<h2 className="text-center text-3xl font-extrabold text-gray-900">{t("request_is_expired")}</h2> <h2 className="text-emphasis text-center text-3xl font-extrabold">{t("request_is_expired")}</h2>
</div> </div>
<p>{t("request_is_expired_instructions")}</p> <p>{t("request_is_expired_instructions")}</p>
<Link href="/auth/forgot-password" passHref legacyBehavior> <Link href="/auth/forgot-password" passHref legacyBehavior>
@@ -95,7 +95,7 @@ export default function ForgotPassword({ csrfToken }: { csrfToken: string }) {
footerText={ footerText={
!success && ( !success && (
<> <>
<Link href="/auth/login" className="font-medium text-gray-900"> <Link href="/auth/login" className="text-emphasis font-medium">
{t("back_to_signin")} {t("back_to_signin")}
</Link> </Link>
</> </>
+15 -4
View File
@@ -4,6 +4,7 @@ import type { GetServerSidePropsContext } from "next";
import { getCsrfToken, signIn } from "next-auth/react"; import { getCsrfToken, signIn } from "next-auth/react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import type { CSSProperties } from "react";
import { useState } from "react"; import { useState } from "react";
import { FormProvider, useForm } from "react-hook-form"; import { FormProvider, useForm } from "react-hook-form";
import { FaGoogle } from "react-icons/fa"; import { FaGoogle } from "react-icons/fa";
@@ -124,7 +125,15 @@ export default function Login({
}; };
return ( return (
<> <div
style={
{
"--cal-brand": "#111827",
"--cal-brand-emphasis": "#101010",
"--cal-brand-text": "white",
"--cal-brand-subtle": "#9CA3AF",
} as CSSProperties
}>
<AuthContainer <AuthContainer
title={t("login")} title={t("login")}
description={t("login")} description={t("login")}
@@ -159,7 +168,7 @@ export default function Login({
<Link <Link
href="/auth/forgot-password" href="/auth/forgot-password"
tabIndex={-1} tabIndex={-1}
className="text-sm font-medium text-gray-600"> className="text-default text-sm font-medium">
{t("forgot")} {t("forgot")}
</Link> </Link>
</div> </div>
@@ -187,7 +196,7 @@ export default function Login({
</form> </form>
{!twoFactorRequired && ( {!twoFactorRequired && (
<> <>
{(isGoogleLoginEnabled || isSAMLLoginEnabled) && <hr className="my-8" />} {(isGoogleLoginEnabled || isSAMLLoginEnabled) && <hr className="border-subtle my-8" />}
<div className="space-y-3"> <div className="space-y-3">
{isGoogleLoginEnabled && ( {isGoogleLoginEnabled && (
<Button <Button
@@ -215,7 +224,7 @@ export default function Login({
</FormProvider> </FormProvider>
</AuthContainer> </AuthContainer>
<AddToHomescreen /> <AddToHomescreen />
</> </div>
); );
} }
@@ -292,4 +301,6 @@ const _getServerSideProps = async function getServerSideProps(context: GetServer
}; };
}; };
Login.isThemeSupported = false;
export const getServerSideProps = withNonce(_getServerSideProps); export const getServerSideProps = withNonce(_getServerSideProps);
+8 -4
View File
@@ -16,7 +16,7 @@ import { ssrInit } from "@server/lib/ssr";
type Props = inferSSRProps<typeof getServerSideProps>; type Props = inferSSRProps<typeof getServerSideProps>;
export default function Logout(props: Props) { export function Logout(props: Props) {
const { status } = useSession(); const { status } = useSession();
if (status === "authenticated") signOut({ redirect: false }); if (status === "authenticated") signOut({ redirect: false });
const router = useRouter(); const router = useRouter();
@@ -31,15 +31,15 @@ export default function Logout(props: Props) {
return ( return (
<AuthContainer title={t("logged_out")} description={t("youve_been_logged_out")} showLogo> <AuthContainer title={t("logged_out")} description={t("youve_been_logged_out")} showLogo>
<div className="mb-4"> <div className="mb-4">
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100"> <div className="bg-success mx-auto flex h-12 w-12 items-center justify-center rounded-full">
<FiCheck className="h-6 w-6 text-green-600" /> <FiCheck className="h-6 w-6 text-green-600" />
</div> </div>
<div className="mt-3 text-center sm:mt-5"> <div className="mt-3 text-center sm:mt-5">
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-title"> <h3 className="text-emphasis text-lg font-medium leading-6" id="modal-title">
{t("youve_been_logged_out")} {t("youve_been_logged_out")}
</h3> </h3>
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-gray-500">{t("hope_to_see_you_soon")}</p> <p className="text-subtle text-sm">{t("hope_to_see_you_soon")}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -50,6 +50,10 @@ export default function Logout(props: Props) {
); );
} }
Logout.isThemeSupported = false;
export default Logout;
export async function getServerSideProps(context: GetServerSidePropsContext) { export async function getServerSideProps(context: GetServerSidePropsContext) {
const ssr = await ssrInit(context); const ssr = await ssrInit(context);
// Deleting old cookie manually, remove this code after all existing cookies have expired // Deleting old cookie manually, remove this code after all existing cookies have expired
+7 -3
View File
@@ -17,7 +17,7 @@ import EnterpriseLicense from "@components/setup/EnterpriseLicense";
import { ssrInit } from "@server/lib/ssr"; import { ssrInit } from "@server/lib/ssr";
export default function Setup(props: inferSSRProps<typeof getServerSideProps>) { export function Setup(props: inferSSRProps<typeof getServerSideProps>) {
const { t } = useLocale(); const { t } = useLocale();
const router = useRouter(); const router = useRouter();
const [value, setValue] = useState(props.isFreeLicense ? "FREE" : "EE"); const [value, setValue] = useState(props.isFreeLicense ? "FREE" : "EE");
@@ -103,7 +103,7 @@ export default function Setup(props: inferSSRProps<typeof getServerSideProps>) {
id={`wizard-step-${currentStep}`} id={`wizard-step-${currentStep}`}
name={`wizard-step-${currentStep}`} name={`wizard-step-${currentStep}`}
classNames={{ classNames={{
form: "mb-4 rounded-md bg-white px-0 pt-0 md:max-w-full", form: "mb-4 rounded-md bg-default px-0 pt-0 md:max-w-full",
appCategoryNavigationContainer: "max-h-[400px] overflow-y-auto md:p-4", appCategoryNavigationContainer: "max-h-[400px] overflow-y-auto md:p-4",
verticalTabsItem: "!w-48 md:p-4", verticalTabsItem: "!w-48 md:p-4",
}} }}
@@ -121,7 +121,7 @@ export default function Setup(props: inferSSRProps<typeof getServerSideProps>) {
return ( return (
<> <>
<Meta title={t("setup")} description={t("setup_description")} /> <Meta title={t("setup")} description={t("setup_description")} />
<main className="flex items-center bg-gray-100 print:h-full md:h-screen"> <main className="bg-subtle flex items-center print:h-full md:h-screen">
<WizardForm <WizardForm
href="/auth/setup" href="/auth/setup"
steps={steps} steps={steps}
@@ -135,6 +135,10 @@ export default function Setup(props: inferSSRProps<typeof getServerSideProps>) {
); );
} }
Setup.isThemeSupported = false;
export default Setup;
export const getServerSideProps = async (context: GetServerSidePropsContext) => { export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const { req, res } = context; const { req, res } = context;
+5 -5
View File
@@ -101,7 +101,7 @@ export default function Verify() {
} }
return ( return (
<div className="bg-black bg-opacity-90 text-white backdrop-blur-md backdrop-grayscale backdrop-filter"> <div className="text-inverted bg-black bg-opacity-90 backdrop-blur-md backdrop-grayscale backdrop-filter">
<Head> <Head>
<title> <title>
{/* @note: Ternary can look ugly ant his might be extracted later but I think at 3 it's not yet worth {/* @note: Ternary can look ugly ant his might be extracted later but I think at 3 it's not yet worth
@@ -117,11 +117,11 @@ export default function Verify() {
<div className="m-10 flex max-w-2xl flex-col items-start border border-white p-12 text-left"> <div className="m-10 flex max-w-2xl flex-col items-start border border-white p-12 text-left">
<div className="rounded-full border border-white p-3"> <div className="rounded-full border border-white p-3">
{hasPaymentFailed ? ( {hasPaymentFailed ? (
<ExclamationIcon className="h-12 w-12 flex-shrink-0 p-0.5 font-extralight text-white" /> <ExclamationIcon className="text-inverted h-12 w-12 flex-shrink-0 p-0.5 font-extralight" />
) : sessionId ? ( ) : sessionId ? (
<CheckIcon className="h-12 w-12 flex-shrink-0 p-0.5 font-extralight text-white" /> <CheckIcon className="text-inverted h-12 w-12 flex-shrink-0 p-0.5 font-extralight" />
) : ( ) : (
<MailOpenIcon className="h-12 w-12 flex-shrink-0 p-0.5 font-extralight text-white" /> <MailOpenIcon className="text-inverted h-12 w-12 flex-shrink-0 p-0.5 font-extralight" />
)} )}
</div> </div>
<h3 className="font-cal my-6 text-3xl font-normal"> <h3 className="font-cal my-6 text-3xl font-normal">
@@ -139,7 +139,7 @@ export default function Verify() {
{hasPaymentFailed && {hasPaymentFailed &&
"Once you activate your account you will be able to try purchase your premium username again or select a different one."} "Once you activate your account you will be able to try purchase your premium username again or select a different one."}
</p> </p>
<p className="mt-6 text-gray-400"> <p className="text-muted mt-6">
Don&apos;t see an email? Click the button below to send another email. Don&apos;t see an email? Click the button below to send another email.
</p> </p>
+10 -10
View File
@@ -53,7 +53,7 @@ const DateOverride = ({ workingHours }: { workingHours: WorkingHours[] }) => {
const { t } = useLocale(); const { t } = useLocale();
return ( return (
<div className="p-6"> <div className="p-6">
<h3 className="font-medium leading-6 text-gray-900"> <h3 className="text-emphasis font-medium leading-6">
{t("date_overrides")}{" "} {t("date_overrides")}{" "}
<Tooltip content={t("date_overrides_info")}> <Tooltip content={t("date_overrides_info")}>
<span className="inline-block"> <span className="inline-block">
@@ -61,7 +61,7 @@ const DateOverride = ({ workingHours }: { workingHours: WorkingHours[] }) => {
</span> </span>
</Tooltip> </Tooltip>
</h3> </h3>
<p className="mb-4 text-sm text-gray-500">{t("date_overrides_subtitle")}</p> <p className="text-subtle mb-4 text-sm">{t("date_overrides_subtitle")}</p>
<div className="space-y-2"> <div className="space-y-2">
<DateOverrideList <DateOverrideList
excludedDates={fields.map((field) => yyyymmdd(field.ranges[0].start))} excludedDates={fields.map((field) => yyyymmdd(field.ranges[0].start))}
@@ -181,7 +181,7 @@ export default function Availability() {
} }
CTA={ CTA={
<div className="flex items-center justify-end"> <div className="flex items-center justify-end">
<div className="flex items-center rounded-md px-2 sm:hover:bg-gray-100"> <div className="sm:hover:bg-subtle flex items-center rounded-md px-2">
<Skeleton <Skeleton
as={Label} as={Label}
htmlFor="hiddenSwitch" htmlFor="hiddenSwitch"
@@ -225,7 +225,7 @@ export default function Availability() {
<VerticalDivider /> <VerticalDivider />
<div className="border-l-2 border-gray-300" /> <div className="border-default border-l-2" />
<Button className="ml-4 lg:ml-0" type="submit" form="availability-form"> <Button className="ml-4 lg:ml-0" type="submit" form="availability-form">
{t("save")} {t("save")}
</Button> </Button>
@@ -245,7 +245,7 @@ export default function Availability() {
}} }}
className="flex flex-col sm:mx-0 xl:flex-row xl:space-x-6"> className="flex flex-col sm:mx-0 xl:flex-row xl:space-x-6">
<div className="flex-1 flex-row xl:mr-0"> <div className="flex-1 flex-row xl:mr-0">
<div className="mb-6 rounded-md border"> <div className="border-subtle mb-6 rounded-md border">
<div> <div>
{typeof me.data?.weekStart === "string" && ( {typeof me.data?.weekStart === "string" && (
<Schedule <Schedule
@@ -260,14 +260,14 @@ export default function Availability() {
)} )}
</div> </div>
</div> </div>
<div className="my-6 rounded-md border"> <div className="border-subtle my-6 rounded-md border">
{schedule?.workingHours && <DateOverride workingHours={schedule.workingHours} />} {schedule?.workingHours && <DateOverride workingHours={schedule.workingHours} />}
</div> </div>
</div> </div>
<div className="min-w-40 col-span-3 space-y-2 lg:col-span-1"> <div className="min-w-40 col-span-3 space-y-2 lg:col-span-1">
<div className="xl:max-w-80 w-full pr-4 sm:ml-0 sm:mr-36 sm:p-0"> <div className="xl:max-w-80 w-full pr-4 sm:ml-0 sm:mr-36 sm:p-0">
<div> <div>
<label htmlFor="timeZone" className="block text-sm font-medium text-gray-700"> <label htmlFor="timeZone" className="text-default block text-sm font-medium">
{t("timezone")} {t("timezone")}
</label> </label>
<Controller <Controller
@@ -276,7 +276,7 @@ export default function Availability() {
value ? ( value ? (
<TimezoneSelect <TimezoneSelect
value={value} value={value}
className="focus:border-brand mt-1 block w-72 rounded-md border-gray-300 text-sm" className="focus:border-brand-default border-default mt-1 block w-72 rounded-md text-sm"
onChange={(timezone) => onChange(timezone.value)} onChange={(timezone) => onChange(timezone.value)}
/> />
) : ( ) : (
@@ -285,9 +285,9 @@ export default function Availability() {
} }
/> />
</div> </div>
<hr className="my-6 mr-8" /> <hr className="border-subtle my-6 mr-8" />
<div className="hidden rounded-md md:block"> <div className="hidden rounded-md md:block">
<h3 className="text-sm font-medium text-gray-900">{t("something_doesnt_look_right")}</h3> <h3 className="text-emphasis text-sm font-medium">{t("something_doesnt_look_right")}</h3>
<div className="mt-3 flex"> <div className="mt-3 flex">
<Button href="/availability/troubleshoot" color="secondary"> <Button href="/availability/troubleshoot" color="secondary">
{t("launch_troubleshooter")} {t("launch_troubleshooter")}
+4 -9
View File
@@ -82,8 +82,8 @@ export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availab
/> />
</div> </div>
) : ( ) : (
<div className="mb-16 overflow-hidden rounded-md border border-gray-200 bg-white"> <div className="border-subtle bg-default mb-16 overflow-hidden rounded-md border">
<ul className="divide-y divide-gray-200" data-testid="schedules" ref={animationParentRef}> <ul className="divide-subtle divide-y" data-testid="schedules" ref={animationParentRef}>
{schedules.map((schedule) => ( {schedules.map((schedule) => (
<ScheduleListItem <ScheduleListItem
displayOptions={{ displayOptions={{
@@ -92,14 +92,9 @@ export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availab
}} }}
key={schedule.id} key={schedule.id}
schedule={schedule} schedule={schedule}
isDeletable={schedules.length !== 1}
updateDefault={updateMutation.mutate} updateDefault={updateMutation.mutate}
deleteFunction={() => { deleteFunction={deleteMutation.mutate}
if (schedules.length === 1) {
showToast(t("requires_at_least_one_schedule"), "error");
} else {
deleteMutation.mutate;
}
}}
/> />
))} ))}
</ul> </ul>
+8 -8
View File
@@ -46,7 +46,7 @@ const AvailabilityView = ({ user }: { user: User }) => {
}, [] as IBusySlot[]) || []; }, [] as IBusySlot[]) || [];
return ( return (
<div className="max-w-xl overflow-hidden rounded-md bg-white shadow"> <div className="bg-default max-w-xl overflow-hidden rounded-md shadow">
<div className="px-4 py-5 sm:p-6"> <div className="px-4 py-5 sm:p-6">
{t("overview_of_day")}{" "} {t("overview_of_day")}{" "}
<input <input
@@ -57,7 +57,7 @@ const AvailabilityView = ({ user }: { user: User }) => {
if (e.target.value) setSelectedDate(e.target.value); if (e.target.value) setSelectedDate(e.target.value);
}} }}
/> />
<small className="block text-gray-400">{t("hover_over_bold_times_tip")}</small> <small className="text-muted block">{t("hover_over_bold_times_tip")}</small>
<div className="mt-4 space-y-4"> <div className="mt-4 space-y-4">
<div className="bg-brand dark:bg-darkmodebrand overflow-hidden rounded-md"> <div className="bg-brand dark:bg-darkmodebrand overflow-hidden rounded-md">
<div className="text-brandcontrast dark:text-darkmodebrandcontrast px-4 py-2 sm:px-6"> <div className="text-brandcontrast dark:text-darkmodebrandcontrast px-4 py-2 sm:px-6">
@@ -79,15 +79,15 @@ const AvailabilityView = ({ user }: { user: User }) => {
.map((slot: IBusySlot) => ( .map((slot: IBusySlot) => (
<div <div
key={dayjs(slot.start).format("HH:mm")} key={dayjs(slot.start).format("HH:mm")}
className="overflow-hidden rounded-md bg-gray-100" className="bg-subtle overflow-hidden rounded-md"
data-testid="troubleshooter-busy-time"> data-testid="troubleshooter-busy-time">
<div className="px-4 py-5 text-black sm:p-6"> <div className="text-emphasis px-4 py-5 sm:p-6">
{t("calendar_shows_busy_between")}{" "} {t("calendar_shows_busy_between")}{" "}
<span className="font-medium text-gray-800" title={dayjs(slot.start).format("HH:mm")}> <span className="text-default font-medium" title={dayjs(slot.start).format("HH:mm")}>
{dayjs(slot.start).format("HH:mm")} {dayjs(slot.start).format("HH:mm")}
</span>{" "} </span>{" "}
{t("and")}{" "} {t("and")}{" "}
<span className="font-medium text-gray-800" title={dayjs(slot.end).format("HH:mm")}> <span className="text-default font-medium" title={dayjs(slot.end).format("HH:mm")}>
{dayjs(slot.end).format("HH:mm")} {dayjs(slot.end).format("HH:mm")}
</span>{" "} </span>{" "}
{t("on")} {dayjs(slot.start).format("D")}{" "} {t("on")} {dayjs(slot.start).format("D")}{" "}
@@ -98,8 +98,8 @@ const AvailabilityView = ({ user }: { user: User }) => {
</div> </div>
)); ));
return ( return (
<div className="overflow-hidden rounded-md bg-gray-100"> <div className="bg-subtle overflow-hidden rounded-md">
<div className="px-4 py-5 text-black sm:p-6">{t("calendar_no_busy_slots")}</div> <div className="text-emphasis px-4 py-5 sm:p-6">{t("calendar_no_busy_slots")}</div>
</div> </div>
); );
})()} })()}
Loaded 100 of 491 files, more files were not shown because too many files have changed in this diff. Show more