Merge branch 'main' into feat/sendgrid-app
commit
fa8a5d2577
|
@ -137,3 +137,10 @@ CLOSECOM_API_KEY=
|
|||
|
||||
# Sendgrid internal sync service
|
||||
SENDGRID_SYNC_API_KEY=
|
||||
|
||||
# Sendgrid internal email sender
|
||||
SENDGRID_API_KEY=
|
||||
|
||||
# Sentry
|
||||
NEXT_PUBLIC_SENTRY_DSN=
|
||||
SENTRY_IGNORE_API_RESOLUTION_ERROR=
|
||||
|
|
|
@ -5,7 +5,7 @@ name: 'Chromatic'
|
|||
|
||||
# Event for the workflow
|
||||
on:
|
||||
pull_request: # So we can test on forks
|
||||
pull_request_target: # So we can test on forks
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: Check types
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
|
|
|
@ -2,7 +2,7 @@ name: E2E App-Store Apps
|
|||
on:
|
||||
push:
|
||||
branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main
|
||||
pull_request: # So we can test on forks
|
||||
pull_request_target: # So we can test on forks
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: E2E Embed tests and booking flow(for non-embed as well)
|
||||
on:
|
||||
pull_request: # So we can test on forks
|
||||
pull_request_target: # So we can test on forks
|
||||
branches:
|
||||
- main
|
||||
# Embed e2e - tests verify booking flow which is applicable to non-embed case also. So, don't ignore apps/web changes.
|
||||
|
|
|
@ -2,7 +2,7 @@ name: E2E test
|
|||
on:
|
||||
push:
|
||||
branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main
|
||||
pull_request: # So we can test on forks
|
||||
pull_request_target: # So we can test on forks
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: "Pull Request Labeler"
|
||||
on:
|
||||
- pull_request
|
||||
- pull_request_target
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name: Lint
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
|
|
|
@ -2,7 +2,7 @@ name: Unit tests
|
|||
on:
|
||||
push:
|
||||
branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main
|
||||
pull_request: # So we can test on forks
|
||||
pull_request_target: # So we can test on forks
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
|
|
2
apps/api
2
apps/api
|
@ -1 +1 @@
|
|||
Subproject commit 18e96e2a47a48c5d79f98925f91f45a7cb7e2e11
|
||||
Subproject commit e0619d383ab976f0ddf8d92b97ade894f343ccce
|
|
@ -1 +1 @@
|
|||
Subproject commit 8313b3b4028fe7616641de95c7c3c5ae97d100aa
|
||||
Subproject commit 2219900e06c3a683c85ce066e6ea3eb2d6ae14e9
|
|
@ -17,7 +17,7 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"iframe-resizer-react": "^1.1.0",
|
||||
"next": "^12.2.5",
|
||||
"next": "^12.3.1",
|
||||
"nextra": "^1.1.0",
|
||||
"nextra-theme-docs": "^1.2.2",
|
||||
"react": "^18.2.0",
|
||||
|
|
|
@ -1,38 +1,24 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
next-env.d.ts
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
||||
storybook-static/
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
resources: [],
|
||||
debug: true,
|
||||
});
|
||||
|
||||
export default i18n;
|
|
@ -1,34 +1,29 @@
|
|||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
stories: ["../stories/**/*.stories.mdx", "../stories/**/*.stories.@(js|jsx|ts|tsx)"],
|
||||
stories: [
|
||||
"../intro.stories.mdx",
|
||||
"../../../packages/ui/components/**/*.stories.mdx",
|
||||
"../../../packages/ui/components/**/*.stories.@(js|jsx|ts|tsx)",
|
||||
],
|
||||
addons: [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-interactions",
|
||||
"storybook-addon-designs",
|
||||
"@storybook/addon-a11y",
|
||||
"storybook-addon-next",
|
||||
"storybook-react-i18next",
|
||||
{
|
||||
name: "storybook-addon-next",
|
||||
options: {
|
||||
nextConfigPath: path.resolve(__dirname, "../../web/next.config.js"),
|
||||
},
|
||||
},
|
||||
],
|
||||
framework: "@storybook/react",
|
||||
core: {
|
||||
builder: "webpack5",
|
||||
},
|
||||
webpackFinal: async (config) => {
|
||||
/**
|
||||
* Fixes font import with /
|
||||
* @see https://github.com/storybookjs/storybook/issues/12844#issuecomment-867544160
|
||||
*/
|
||||
config.resolve.roots = [path.resolve(__dirname, "../public"), "node_modules"];
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
"@/interfaces": path.resolve(__dirname, "../interfaces"),
|
||||
};
|
||||
|
||||
/**
|
||||
* Why webpack5... Just why?
|
||||
* @type {{console: boolean, process: boolean, timers: boolean, os: boolean, querystring: boolean, sys: boolean, fs: boolean, url: boolean, crypto: boolean, path: boolean, zlib: boolean, punycode: boolean, util: boolean, stream: boolean, assert: boolean, string_decoder: boolean, domain: boolean, vm: boolean, tty: boolean, http: boolean, buffer: boolean, constants: boolean, https: boolean, events: boolean}}
|
||||
*/
|
||||
staticDirs: ["../public"],
|
||||
webpackFinal: async (config, { configType }) => {
|
||||
config.resolve.fallback = {
|
||||
fs: false,
|
||||
assert: false,
|
||||
|
@ -55,7 +50,6 @@ module.exports = {
|
|||
vm: false,
|
||||
zlib: false,
|
||||
};
|
||||
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
window.global = window;
|
||||
window.isEmbed = ()=> {
|
||||
return location.search.includes("embed=")
|
||||
}
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import { RouterContext } from "next/dist/shared/lib/router-context";
|
||||
import * as NextImage from "next/image";
|
||||
import { addDecorator } from "@storybook/react";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
|
||||
import "../styles/globals.css";
|
||||
|
||||
const OriginalNextImage = NextImage.default;
|
||||
|
||||
Object.defineProperty(NextImage, "default", {
|
||||
configurable: true,
|
||||
value: (props) => <OriginalNextImage {...props} unoptimized />,
|
||||
});
|
||||
import "../styles/storybook-styles.css";
|
||||
import i18n from "./i18next";
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
|
@ -18,11 +13,10 @@ export const parameters = {
|
|||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
nextRouter: {
|
||||
Provider: RouterContext.Provider,
|
||||
},
|
||||
};
|
||||
|
||||
addDecorator((storyFn) => <I18nextProvider i18n={i18n}>{storyFn()}</I18nextProvider>);
|
||||
|
||||
window.getEmbedNamespace = () => {
|
||||
const url = new URL(document.URL);
|
||||
const namespace = url.searchParams.get("embed");
|
|
@ -1,21 +0,0 @@
|
|||
# Storybook & UI
|
||||
|
||||
Storybook is home to all of our commonly used components. We use this app to visually show all components that we have within the project + automatic type documentation generated for each component.
|
||||
|
||||
All changes to storybook/ui must require a visual/code review.
|
||||
|
||||
- Visual reviews happen on a tool called [Chormatic](http://chromatic.com/) and these reviews will happen by our product team.
|
||||
|
||||
- Code reviews happen as normal on any changes to storybook/ui designs
|
||||
|
||||
## Deployment
|
||||
|
||||
To deploy this project run
|
||||
|
||||
```bash
|
||||
cd apps/storybook
|
||||
```
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
|
@ -0,0 +1,23 @@
|
|||
import { ArgsTable } from "@storybook/addon-docs";
|
||||
import { SortType } from "@storybook/components";
|
||||
import { PropDescriptor } from "@storybook/store";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore storybook addon types component as any so we have to do
|
||||
type Component = any;
|
||||
type BaseProps = {
|
||||
include?: PropDescriptor;
|
||||
exclude?: PropDescriptor;
|
||||
sort?: SortType;
|
||||
};
|
||||
|
||||
type OfProps = BaseProps & {
|
||||
of: "." | "^" | Component;
|
||||
};
|
||||
|
||||
export function CustomArgsTable({ of, sort }: OfProps) {
|
||||
return (
|
||||
<div className="custom-args-wrapper">
|
||||
<ArgsTable of={of} sort={sort} />
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { classNames } from "@calcom/lib";
|
||||
|
||||
interface ExampleProps {
|
||||
children: React.ReactNode;
|
||||
title: string;
|
||||
}
|
||||
export const Example = ({ children, title }: ExampleProps) => {
|
||||
return (
|
||||
<div className="examples-item">
|
||||
<span className="examples-item-title">{title}</span>
|
||||
<div className="examples-item-content">{children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface ExamplesProps {
|
||||
children: React.ReactNode;
|
||||
title: string;
|
||||
footnote?: React.ReactNode;
|
||||
dark?: boolean;
|
||||
}
|
||||
|
||||
export const Examples = ({ children, title, footnote = null, dark }: ExamplesProps) => {
|
||||
return (
|
||||
<div className={classNames("examples", dark && "dark")}>
|
||||
<h2 className="examples-title">{title}</h2>
|
||||
<div className="examples-content">{children}</div>
|
||||
{!!footnote && <div className="examples-footnote">{footnote}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
export const Note = ({ children }: { children: React.ReactNode }) => (
|
||||
<div className="story-note">
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
);
|
|
@ -0,0 +1,21 @@
|
|||
export const Title = ({
|
||||
title,
|
||||
suffix,
|
||||
subtitle,
|
||||
offset,
|
||||
}: {
|
||||
title: string;
|
||||
suffix?: string;
|
||||
subtitle?: string;
|
||||
offset?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<div className={`story-title ${offset && "offset"}`}>
|
||||
<h1>
|
||||
{title}
|
||||
{suffix && <span>{suffix}</span>}
|
||||
</h1>
|
||||
{subtitle && <p>{subtitle}</p>}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,80 @@
|
|||
import React, { ReactElement, ReactNode } from "react";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
|
||||
export function VariantsTable({
|
||||
children,
|
||||
titles,
|
||||
isDark,
|
||||
columnMinWidth = 150,
|
||||
}: {
|
||||
children: ReactElement<RowProps> | ReactElement<RowProps>[];
|
||||
titles: string[];
|
||||
isDark?: boolean;
|
||||
// Mainly useful on mobile, so components don't get squeesed
|
||||
columnMinWidth?: number;
|
||||
}) {
|
||||
const columns = React.Children.toArray(children) as ReactElement<RowProps>[];
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
isDark &&
|
||||
"relative py-8 before:absolute before:left-0 before:top-0 before:block before:h-full before:w-screen before:bg-[#22252A]"
|
||||
)}>
|
||||
<div className="z-1 relative mx-auto w-full max-w-[1200px] overflow-auto pr-8 pt-6">
|
||||
<table>
|
||||
<RowTitles titles={titles} />
|
||||
{columns.map((column) => (
|
||||
<tr className="p-2 pr-6 pb-6" key={column.props.variant}>
|
||||
<th
|
||||
className="p-2 pr-6 pb-6 text-left text-sm font-normal text-[#8F8F8F]"
|
||||
key={column.props.variant}>
|
||||
{column.props.variant}
|
||||
</th>
|
||||
{React.Children.count(column.props.children) &&
|
||||
React.Children.map(column.props.children, (cell) => (
|
||||
<td className="p-2 pr-6 pb-6" style={{ minWidth: `${columnMinWidth}px` }}>
|
||||
{cell}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</table>
|
||||
</div>
|
||||
{!isDark && (
|
||||
<div data-mode="dark">
|
||||
<VariantsTable titles={titles} isDark columnMinWidth={columnMinWidth}>
|
||||
{children}
|
||||
</VariantsTable>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface RowProps {
|
||||
variant: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* There are two reasons we have this "empty" wrapper component:
|
||||
* 1. In order to have an isolate group per variant, which we iterate through in the table component.
|
||||
* 2. To have a way to pass the variant.
|
||||
*/
|
||||
export function VariantRow({ children }: RowProps) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
export function RowTitles({ titles }: { titles: string[] }) {
|
||||
return (
|
||||
<tr>
|
||||
<th className="p-2 pr-6 pb-6 text-left text-sm font-normal text-[#8F8F8F]" />
|
||||
{titles.map((title) => (
|
||||
<th className="p-2 pr-6 pb-6 text-left text-sm font-normal text-[#8F8F8F]" key={title}>
|
||||
{title}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export * from "./Examples";
|
||||
export * from "./Note";
|
||||
export * from "./VariantsTable";
|
||||
export * from "./Title";
|
||||
export * from "./CustomArgsTable";
|
|
@ -0,0 +1,11 @@
|
|||
import { Meta } from '@storybook/addon-docs';
|
||||
|
||||
<Meta title="Introduction" />
|
||||
|
||||
<div className="text-center flex flex-col items-center">
|
||||
<h1 style={{marginBottom: '24px', marginTop: '36px'}}>Welcome to Cal.com UI</h1>
|
||||
<p>This is the beginning of our storybook impovments to match Figma as close as possible. Like our Figma library, we will be adding more components as we go along.</p>
|
||||
<p>Our <a href="https://www.figma.com/file/9MOufQNLtdkpnDucmNX10R/%E2%9D%96-Cal-DS" target="_blank">Figma</a> library is avalible for anyone to view and use. If you have any questions or concerns, please reach out to the design team.</p>
|
||||
</div>
|
||||
|
||||
<img src="/sb-cover.jpg" />
|
|
@ -1,7 +0,0 @@
|
|||
const withTM = require("next-transpile-modules")(["@calcom/dayjs", "@calcom/ui"]);
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
};
|
||||
|
||||
module.exports = withTM(nextConfig);
|
|
@ -1,18 +1,13 @@
|
|||
{
|
||||
"name": "@calcom/storybook",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev:next": "next dev",
|
||||
"build:next": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint . --ignore-path .gitignore",
|
||||
"lint:report": "eslint . --format json --output-file ../../lint-results/storybook.json",
|
||||
"dev": "start-storybook -p 6006",
|
||||
"build": "build-storybook",
|
||||
"chromatic": "npx chromatic --project-token=56fadc9ab496"
|
||||
"build": "build-storybook"
|
||||
},
|
||||
"dependencies": {
|
||||
"@calcom/config": "*",
|
||||
"@calcom/dayjs": "*",
|
||||
"@calcom/ui": "*",
|
||||
"@radix-ui/react-avatar": "^1.0.0",
|
||||
|
@ -25,42 +20,35 @@
|
|||
"@radix-ui/react-slider": "^1.0.0",
|
||||
"@radix-ui/react-switch": "^1.0.0",
|
||||
"@radix-ui/react-tooltip": "^1.0.0",
|
||||
"next": "^12.2.5",
|
||||
"next-transpile-modules": "^9.0.0",
|
||||
"next": "^12.3.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-feather": "^2.0.10",
|
||||
"react-hot-toast": "^2.3.0"
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.10",
|
||||
"@calcom/config": "*",
|
||||
"@storybook/addon-a11y": "^6.5.10",
|
||||
"@storybook/addon-actions": "^6.5.10",
|
||||
"@storybook/addon-essentials": "^6.5.10",
|
||||
"@storybook/addon-interactions": "^6.5.10",
|
||||
"@storybook/addon-links": "^6.5.10",
|
||||
"@storybook/addon-postcss": "^2.0.0",
|
||||
"@storybook/builder-vite": "^0.2.2",
|
||||
"@storybook/builder-webpack5": "^6.5.10",
|
||||
"@storybook/manager-webpack5": "^6.5.10",
|
||||
"@storybook/react": "^6.5.10",
|
||||
"@tailwindcss/line-clamp": "^0.4.0",
|
||||
"@types/node": "16.9.1",
|
||||
"@babel/core": "^7.19.6",
|
||||
"@storybook/addon-actions": "^6.5.13",
|
||||
"@storybook/addon-essentials": "^6.5.13",
|
||||
"@storybook/addon-interactions": "^6.5.13",
|
||||
"@storybook/addon-links": "^6.5.13",
|
||||
"@storybook/builder-vite": "^0.2.4",
|
||||
"@storybook/builder-webpack5": "^6.5.13",
|
||||
"@storybook/manager-webpack5": "^6.5.13",
|
||||
"@storybook/react": "^6.5.13",
|
||||
"@storybook/testing-library": "^0.0.13",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "18.0.4",
|
||||
"autoprefixer": "^10.4.8",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@vitejs/plugin-react": "^2.1.0",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"babel-loader": "^8.2.5",
|
||||
"chromatic": "^6.7.4",
|
||||
"eslint": "^8.22.0",
|
||||
"postcss": "^8.4.16",
|
||||
"postcss-loader": "^7.0.0",
|
||||
"fs": "^0.0.1-security",
|
||||
"postcss": "^8.4.18",
|
||||
"postcss-pseudo-companion-classes": "^0.1.1",
|
||||
"rollup-plugin-polyfill-node": "^0.10.2",
|
||||
"storybook-addon-designs": "^6.3.1",
|
||||
"storybook-addon-next": "^1.6.7",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"_id": "@calcom/storybook@0.1.0"
|
||||
"storybook-addon-next": "^1.6.9",
|
||||
"storybook-react-i18next": "^1.1.2",
|
||||
"tailwindcss": "^3.2.1",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "^3.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import type { AppProps } from "next/app";
|
||||
|
||||
import "../styles/globals.css";
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />;
|
||||
}
|
||||
|
||||
export default MyApp;
|
|
@ -1,63 +0,0 @@
|
|||
import type { NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import Image from "next/image";
|
||||
|
||||
const Home: NextPage = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<meta name="description" content="Generated by create next app" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<main className={styles.main}>
|
||||
<h1 className={styles.title}>
|
||||
Welcome to <a href="https://nextjs.org">Next.js!</a>
|
||||
</h1>
|
||||
|
||||
<p className={styles.description}>
|
||||
Get started by editing <code className={styles.code}>pages/index.tsx</code>
|
||||
</p>
|
||||
|
||||
<div className={styles.grid}>
|
||||
<a href="https://nextjs.org/docs" className={styles.card}>
|
||||
<h2>Documentation →</h2>
|
||||
<p>Find in-depth information about Next.js features and API.</p>
|
||||
</a>
|
||||
|
||||
<a href="https://nextjs.org/learn" className={styles.card}>
|
||||
<h2>Learn →</h2>
|
||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
||||
</a>
|
||||
|
||||
<a href="https://github.com/vercel/next.js/tree/canary/examples" className={styles.card}>
|
||||
<h2>Examples →</h2>
|
||||
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}>
|
||||
<h2>Deploy →</h2>
|
||||
<p>Instantly deploy your Next.js site to a public URL with Vercel.</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer className={styles.footer}>
|
||||
<a
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
Powered by{" "}
|
||||
<span className={styles.logo}>
|
||||
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
|
||||
</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
|
@ -2,5 +2,21 @@ module.exports = {
|
|||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
/**
|
||||
* We use this postcss plugin to create custom css classes for
|
||||
* any pseudo classes (eg hover, focus) mentioned below.
|
||||
* This way you can eg show a hover state in Storybook by adding
|
||||
* this classname .sb-pseudo--hover to the item.
|
||||
*
|
||||
* These styles will only be added in storybook, and will NOT
|
||||
* end up in the final css bundle of apps using the components.
|
||||
*/
|
||||
"postcss-pseudo-companion-classes": {
|
||||
prefix: "sb-pseudo--",
|
||||
// We have to keep a restrictTo list here, because otherwise
|
||||
// this library will have issues processing tailwind's \: prefixed classes
|
||||
// and start adding dots everywhere, breaking this functionality.
|
||||
restrictTo: [":hover", ":focus"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 715 KiB |
|
@ -1,4 +0,0 @@
|
|||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.1 KiB |
|
@ -1,26 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
|
||||
import Avatar from "@calcom/ui/v2/core/Avatar";
|
||||
|
||||
export default {
|
||||
title: "Avatar",
|
||||
component: Avatar,
|
||||
} as ComponentMeta<typeof Avatar>;
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<>
|
||||
<Avatar size="sm" alt="Avatar Story" gravatarFallbackMd5="Ui@CAL.com" />
|
||||
<Avatar size="lg" alt="Avatar Story" gravatarFallbackMd5="Ui@CAL.com" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const Accepted = () => {
|
||||
return (
|
||||
<>
|
||||
<Avatar size="sm" alt="Avatar Story" gravatarFallbackMd5="Ui@CAL.com" accepted />
|
||||
<Avatar size="lg" alt="Avatar Story" gravatarFallbackMd5="Ui@CAL.com" accepted />
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,60 +0,0 @@
|
|||
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
||||
import { ComponentMeta } from "@storybook/react";
|
||||
|
||||
import AvatarGroup from "@calcom/ui/v2/core/AvatarGroup";
|
||||
|
||||
export default {
|
||||
title: "Avatar/Group",
|
||||
component: AvatarGroup,
|
||||
} as ComponentMeta<typeof AvatarGroup>;
|
||||
|
||||
const IMAGES = [
|
||||
{
|
||||
image: "https://cal.com/stakeholder/peer.jpg",
|
||||
alt: "Peer",
|
||||
title: "Peer Richelsen",
|
||||
},
|
||||
{
|
||||
image: "https://cal.com/stakeholder/bailey.jpg",
|
||||
alt: "Bailey",
|
||||
title: "Bailey Pumfleet",
|
||||
},
|
||||
{
|
||||
image: "https://cal.com/stakeholder/alex-van-andel.jpg",
|
||||
alt: "Alex",
|
||||
title: "Alex Van Andel",
|
||||
},
|
||||
{
|
||||
image: "https://cal.com/stakeholder/ciaran.jpg",
|
||||
alt: "Ciarán",
|
||||
title: "Ciarán Hanrahan",
|
||||
},
|
||||
{
|
||||
image: "https://cal.com/stakeholder/peer.jpg",
|
||||
alt: "Peer",
|
||||
title: "Peer Richelsen",
|
||||
},
|
||||
{
|
||||
image: "https://cal.com/stakeholder/bailey.jpg",
|
||||
alt: "Bailey",
|
||||
title: "Bailey Pumfleet",
|
||||
},
|
||||
{
|
||||
image: "https://cal.com/stakeholder/alex-van-andel.jpg",
|
||||
alt: "Alex",
|
||||
title: "Alex Van Andel",
|
||||
},
|
||||
{
|
||||
image: "https://cal.com/stakeholder/ciaran.jpg",
|
||||
alt: "Ciarán",
|
||||
title: "Ciarán Hanrahan",
|
||||
},
|
||||
];
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<AvatarGroup size="lg" items={IMAGES} />
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
|
@ -1,58 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
import { Bell } from "react-feather";
|
||||
|
||||
import Badge from "@calcom/ui/v2/core/Badge";
|
||||
|
||||
export default {
|
||||
title: "Badge",
|
||||
component: Badge,
|
||||
} as ComponentMeta<typeof Badge>;
|
||||
|
||||
export const All = () => (
|
||||
<div className="">
|
||||
<h1>Default</h1>
|
||||
<div className="mb-4 flex space-x-2">
|
||||
<Badge variant="gray">Badge</Badge>
|
||||
<Badge variant="red">Badge</Badge>
|
||||
<Badge variant="green">Badge</Badge>
|
||||
<Badge variant="orange">Badge</Badge>
|
||||
<Badge variant="blue">Badge</Badge>
|
||||
</div>
|
||||
<h1>Icons</h1>
|
||||
<div className="mb-4 flex space-x-2">
|
||||
<Badge variant="gray" StartIcon={Bell}>
|
||||
Badge
|
||||
</Badge>
|
||||
<Badge variant="red" StartIcon={Bell}>
|
||||
Badge
|
||||
</Badge>
|
||||
<Badge variant="green" StartIcon={Bell}>
|
||||
Badge
|
||||
</Badge>
|
||||
<Badge variant="orange" StartIcon={Bell}>
|
||||
Badge
|
||||
</Badge>
|
||||
<Badge variant="blue" StartIcon={Bell}>
|
||||
Badge
|
||||
</Badge>
|
||||
</div>
|
||||
<h1>Large</h1>
|
||||
<div className="flex space-x-2">
|
||||
<Badge variant="gray" size="lg">
|
||||
Badge
|
||||
</Badge>
|
||||
<Badge variant="red" size="lg">
|
||||
Badge
|
||||
</Badge>
|
||||
<Badge variant="green" size="lg">
|
||||
Badge
|
||||
</Badge>
|
||||
<Badge variant="orange" size="lg">
|
||||
Badge
|
||||
</Badge>
|
||||
<Badge variant="blue" size="lg">
|
||||
Badge
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
);
|
|
@ -1,46 +0,0 @@
|
|||
import { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { Info } from "react-feather";
|
||||
|
||||
import Banner from "@calcom/ui/v2/core/banner";
|
||||
|
||||
export default {
|
||||
title: "Banner",
|
||||
component: Banner,
|
||||
} as ComponentMeta<typeof Banner>;
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<Banner
|
||||
variant="neutral"
|
||||
title="Summarise what happened"
|
||||
description="Describe what can be done about it here."
|
||||
Icon={Info}
|
||||
onDismiss={() => console.log("dismissed")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Warning = () => {
|
||||
return (
|
||||
<Banner
|
||||
variant="warning"
|
||||
title="Summarise what happened"
|
||||
description="Describe what can be done about it here."
|
||||
Icon={Info}
|
||||
onDismiss={() => console.log("dismissed")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Error = () => {
|
||||
return (
|
||||
<Banner
|
||||
variant="error"
|
||||
title="Summarise what happened"
|
||||
description="Describe what can be done about it here."
|
||||
errorMessage="Event creation failed"
|
||||
Icon={Info}
|
||||
onDismiss={() => console.log("dismissed")}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -1,22 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
|
||||
import Breadcrumb, { BreadcrumbItem } from "@calcom/ui/v2/core/Breadcrumb";
|
||||
|
||||
export default {
|
||||
title: "Breadcrumbs",
|
||||
component: Breadcrumb,
|
||||
} as ComponentMeta<typeof Breadcrumb>;
|
||||
|
||||
export const Default = () => (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem href="/">Home</BreadcrumbItem>
|
||||
<BreadcrumbItem href="/">Test</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
);
|
||||
|
||||
Default.parameters = {
|
||||
nextRouter: {
|
||||
path: "/test",
|
||||
asPath: "/test",
|
||||
},
|
||||
};
|
|
@ -1,98 +0,0 @@
|
|||
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
||||
import { ComponentMeta, ComponentStory } from "@storybook/react";
|
||||
import { Trash2 } from "react-feather";
|
||||
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
|
||||
export default {
|
||||
title: "Button",
|
||||
component: Button,
|
||||
argTypes: {
|
||||
color: {
|
||||
options: ["primary", "secondary", "minimal", "destructive"],
|
||||
control: { type: "select" },
|
||||
},
|
||||
disabled: {
|
||||
options: [true, false],
|
||||
control: { type: "boolean" },
|
||||
},
|
||||
loading: {
|
||||
options: [true, false],
|
||||
control: { type: "boolean" },
|
||||
},
|
||||
size: {
|
||||
options: ["base", "lg", "icon"],
|
||||
control: { type: "radio" },
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof Button>;
|
||||
|
||||
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
|
||||
|
||||
export const All = () => (
|
||||
<div>
|
||||
<TooltipProvider>
|
||||
<h1>Primary</h1>
|
||||
<div className="flex space-x-2">
|
||||
<Button aria-label="Button Text">Button Text</Button>
|
||||
<Button disabled aria-label="Button Text">
|
||||
Button Text
|
||||
</Button>
|
||||
</div>
|
||||
<h1>Secondary</h1>
|
||||
<div className="flex space-x-2">
|
||||
<Button color="secondary" aria-label="Button Text">
|
||||
Button Text
|
||||
</Button>
|
||||
<Button disabled color="secondary" aria-label="Button Text">
|
||||
Button Text
|
||||
</Button>
|
||||
<Button size="icon" color="secondary" StartIcon={Trash2} aria-label="Button Text" />
|
||||
</div>
|
||||
<h1>Minimal</h1>
|
||||
<div className="flex">
|
||||
<Button color="minimal" aria-label="Button Text">
|
||||
Button Text
|
||||
</Button>
|
||||
<Button disabled color="minimal" aria-label="Button Text">
|
||||
Button Text
|
||||
</Button>
|
||||
<Button size="icon" color="minimal" StartIcon={Trash2} aria-label="Button Text" />
|
||||
</div>
|
||||
<h1>Destructive</h1>
|
||||
<Button size="icon" color="destructive" StartIcon={Trash2} aria-label="Button Text" />
|
||||
<h1>Tooltip</h1>
|
||||
<Button
|
||||
tooltip="Deletes EventTypes"
|
||||
size="icon"
|
||||
color="destructive"
|
||||
StartIcon={Trash2}
|
||||
aria-label="Button Text"
|
||||
/>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
color: "primary",
|
||||
children: "Button Text",
|
||||
};
|
||||
|
||||
export const Disabled = Template.bind({});
|
||||
Disabled.args = {
|
||||
...Default.args,
|
||||
disabled: true,
|
||||
};
|
||||
|
||||
export const Loading = Template.bind({});
|
||||
Loading.args = {
|
||||
...Default.args,
|
||||
loading: true,
|
||||
};
|
||||
export const Icon = Template.bind({});
|
||||
Icon.args = {
|
||||
color: "secondary",
|
||||
StartIcon: Trash2,
|
||||
size: "icon",
|
||||
};
|
|
@ -1,41 +0,0 @@
|
|||
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
||||
import { ComponentMeta } from "@storybook/react";
|
||||
import { ArrowLeft, ArrowRight, Clipboard, Navigation2, Trash2 } from "react-feather";
|
||||
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import ButtonGroup from "@calcom/ui/v2/core/ButtonGroup";
|
||||
|
||||
export default {
|
||||
title: "Button Group",
|
||||
component: ButtonGroup,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<TooltipProvider>
|
||||
<Story />
|
||||
</TooltipProvider>
|
||||
),
|
||||
],
|
||||
} as ComponentMeta<typeof ButtonGroup>;
|
||||
|
||||
export const Default = () => (
|
||||
<ButtonGroup>
|
||||
<Button StartIcon={Trash2} size="icon" color="secondary" />
|
||||
<Button StartIcon={Navigation2} size="icon" color="secondary" />
|
||||
<Button StartIcon={Clipboard} size="icon" color="secondary" />
|
||||
</ButtonGroup>
|
||||
);
|
||||
|
||||
export const Combined = () => (
|
||||
<div className="flex flex-col space-y-2">
|
||||
<ButtonGroup combined>
|
||||
<Button StartIcon={Trash2} size="icon" color="secondary" />
|
||||
<Button StartIcon={Navigation2} size="icon" color="secondary" />
|
||||
<Button StartIcon={Clipboard} size="icon" color="secondary" />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup combined>
|
||||
<Button StartIcon={ArrowLeft} size="icon" color="secondary" />
|
||||
<Button StartIcon={ArrowRight} size="icon" color="secondary" />
|
||||
<Button StartIcon={ArrowRight} href="/" size="icon" color="secondary" />
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
);
|
|
@ -1,79 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
import { useState } from "react";
|
||||
|
||||
import Card, { BaseCardProps } from "@calcom/ui/v2/core/Card";
|
||||
|
||||
export default {
|
||||
title: "Cards",
|
||||
component: Card,
|
||||
} as ComponentMeta<typeof Card>;
|
||||
|
||||
const profileProps: BaseCardProps[] = [
|
||||
{
|
||||
variant: "ProfileCard",
|
||||
title: "Alex Fisher",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
|
||||
description: "Co-founder at Attio. Book a meeting to discuss enterprise deals.",
|
||||
},
|
||||
{
|
||||
variant: "ProfileCard",
|
||||
title: "Julian Wan",
|
||||
image:
|
||||
"https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80",
|
||||
description: "Co-founder at Attio. Book a meeting to discuss enterprise deals.",
|
||||
},
|
||||
];
|
||||
|
||||
export const Profile = () => {
|
||||
return (
|
||||
<div className="flex w-full space-x-2 bg-gray-200 p-4">
|
||||
<Card {...profileProps[0]} />
|
||||
<Card {...profileProps[1]} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const appStoreProps: BaseCardProps = {
|
||||
variant: "AppStore",
|
||||
title: "Giphy",
|
||||
image: "https://app.cal.com/api/app-store/giphy/icon.svg",
|
||||
description: "Add GIFs to spice up your booking confirmations.",
|
||||
actionButton: {
|
||||
href: "#",
|
||||
child: "View Details",
|
||||
},
|
||||
};
|
||||
|
||||
export const AppStore = () => {
|
||||
return (
|
||||
<div className="w-full bg-gray-200 p-4">
|
||||
<Card {...appStoreProps} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const sidebarCardProps: BaseCardProps = {
|
||||
variant: "SidebarCard",
|
||||
thumbnailUrl: "https://img.youtube.com/vi/60HJt8DOVNo/0.jpg",
|
||||
mediaLink: "https://www.youtube.com/watch?v=60HJt8DOVNo",
|
||||
title: "Dynamic boooking links",
|
||||
description: "Booking link that allows people to quickly schedule meetings.",
|
||||
learnMore: {
|
||||
href: "https://cal.com/blog/cal-v-1-9",
|
||||
text: "Learn more",
|
||||
},
|
||||
};
|
||||
|
||||
export const SidebarCard = () => {
|
||||
const [visible, setVisible] = useState(true); // save state in localStorage, cookie or db
|
||||
return (
|
||||
<header className="w-full max-w-[225px] bg-gray-100 p-3">
|
||||
{visible && (
|
||||
<div>
|
||||
<Card {...sidebarCardProps} actionButton={{ onClick: () => setVisible(false), child: "Dismiss" }} />
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
};
|
|
@ -1,31 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
|
||||
import Checkbox from "@calcom/ui/v2/core/form/Checkbox";
|
||||
|
||||
export default {
|
||||
title: "Checkbox",
|
||||
component: Checkbox,
|
||||
} as ComponentMeta<typeof Checkbox>;
|
||||
|
||||
export const All = () => (
|
||||
<div className="flex flex-col space-y-2">
|
||||
<Checkbox label="Default" description="Toggle on and off something" />
|
||||
<Checkbox label="Error" description="Toggle on and off something" error />
|
||||
<Checkbox label="Disabled" description="Toggle on and off something" disabled />
|
||||
<Checkbox label="Disabled Checked" description="Toggle on and off something" checked disabled />
|
||||
<hr />
|
||||
<Checkbox description="Default" descriptionAsLabel />
|
||||
<Checkbox description="Error" descriptionAsLabel error />
|
||||
<Checkbox description="Disabled" descriptionAsLabel disabled />
|
||||
<Checkbox description="Disabled" descriptionAsLabel disabled checked />
|
||||
</div>
|
||||
);
|
||||
|
||||
export const CheckboxField = () => <Checkbox description="Default" descriptionAsLabel />;
|
||||
export const CheckboxError = () => <Checkbox description="Error" descriptionAsLabel />;
|
||||
export const CheckboxDisabled = () => (
|
||||
<>
|
||||
<Checkbox description="Disabled" descriptionAsLabel disabled />
|
||||
<Checkbox description="Disabled" descriptionAsLabel disabled checked />
|
||||
</>
|
||||
);
|
|
@ -1,208 +0,0 @@
|
|||
import { useState } from "react";
|
||||
|
||||
import ColorPicker from "@calcom/ui/v2/core/colorpicker";
|
||||
|
||||
// eslint-disable-next-line import/no-anonymous-default-export
|
||||
export default {
|
||||
title: "Colors",
|
||||
component: ColorPicker,
|
||||
};
|
||||
const COLORS = {
|
||||
brand: {
|
||||
50: "#f3f3f4",
|
||||
100: "#e7e8e9",
|
||||
200: "#c4c5c9",
|
||||
300: "#a0a3a9",
|
||||
400: "#585d68",
|
||||
500: "#111827", // Brand color
|
||||
600: "#0f1623",
|
||||
700: "#0d121d",
|
||||
800: "#0a0e17",
|
||||
900: "#080c13",
|
||||
},
|
||||
gray: {
|
||||
50: "#F8F8F8",
|
||||
100: "#F5F5F5",
|
||||
200: "#E1E1E1",
|
||||
300: "#CFCFCF",
|
||||
400: "#ACACAC",
|
||||
500: "#888888",
|
||||
600: "#494949",
|
||||
700: "#3E3E3E",
|
||||
800: "#313131",
|
||||
900: "#292929",
|
||||
},
|
||||
neutral: {
|
||||
50: "#F8F8F8",
|
||||
100: "#F5F5F5",
|
||||
200: "#E1E1E1",
|
||||
300: "#CFCFCF",
|
||||
400: "#ACACAC",
|
||||
500: "#888888",
|
||||
600: "#494949",
|
||||
700: "#3E3E3E",
|
||||
800: "#313131",
|
||||
900: "#292929",
|
||||
},
|
||||
primary: {
|
||||
50: "#F4F4F4",
|
||||
100: "#E8E8E8",
|
||||
200: "#C6C6C6",
|
||||
300: "#A3A3A3",
|
||||
400: "#5F5F5F",
|
||||
500: "#1A1A1A",
|
||||
600: "#171717",
|
||||
700: "#141414",
|
||||
800: "#101010",
|
||||
900: "#0D0D0D",
|
||||
},
|
||||
secondary: {
|
||||
50: "#F5F8F7",
|
||||
100: "#EBF0F0",
|
||||
200: "#CDDAD9",
|
||||
300: "#AEC4C2",
|
||||
400: "#729894",
|
||||
500: "#356C66",
|
||||
600: "#30615C",
|
||||
700: "#28514D",
|
||||
800: "#20413D",
|
||||
900: "#223B41",
|
||||
},
|
||||
red: {
|
||||
50: "#FEF2F2",
|
||||
100: "#FEE2E2",
|
||||
200: "#FECACA",
|
||||
300: "#FCA5A5",
|
||||
400: "#F87171",
|
||||
500: "#EF4444",
|
||||
600: "#DC2626",
|
||||
700: "#B91C1C",
|
||||
800: "#991B1B",
|
||||
900: "#7F1D1D",
|
||||
},
|
||||
orange: {
|
||||
50: "#FFF7ED",
|
||||
100: "#FFEDD5",
|
||||
200: "#FED7AA",
|
||||
300: "#FDBA74",
|
||||
400: "#FB923C",
|
||||
500: "#F97316",
|
||||
600: "#EA580C",
|
||||
700: "#C2410C",
|
||||
800: "#9A3412",
|
||||
900: "#7C2D12",
|
||||
},
|
||||
green: {
|
||||
50: "#ECFDF5",
|
||||
100: "#D1FAE5",
|
||||
200: "#A7F3D0",
|
||||
300: "#6EE7B7",
|
||||
400: "#34D399",
|
||||
500: "#10B981",
|
||||
600: "#059669",
|
||||
700: "#047857",
|
||||
800: "#065F46",
|
||||
900: "#064E3B",
|
||||
},
|
||||
};
|
||||
export const All = () => {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div>
|
||||
{Object.keys(COLORS.brand).map((color) => (
|
||||
<div className="flex flex-row space-x-2" key={COLORS.brand[color]}>
|
||||
<div className="w-full">Brand</div>
|
||||
<div className="w-full">{color}</div>
|
||||
<div className="w-full">{COLORS.brand[color]}</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: COLORS.brand[color],
|
||||
width: "100%",
|
||||
height: "32px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<hr />
|
||||
{Object.keys(COLORS.gray).map((color) => (
|
||||
<div className="flex flex-row space-x-2" key={COLORS.gray[color]}>
|
||||
<div className="w-full">gray</div>
|
||||
<div className="w-full">{color}</div>
|
||||
<div className="w-full">{COLORS.gray[color]}</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: COLORS.gray[color],
|
||||
width: "100%",
|
||||
height: "32px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<hr />
|
||||
{Object.keys(COLORS.secondary).map((color) => (
|
||||
<div className="flex flex-row space-x-2" key={COLORS.secondary[color]}>
|
||||
<div className="w-full">secondary</div>
|
||||
<div className="w-full">{color}</div>
|
||||
<div className="w-full">{COLORS.secondary[color]}</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: COLORS.secondary[color],
|
||||
width: "100%",
|
||||
height: "32px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<hr />
|
||||
{Object.keys(COLORS.red).map((color) => (
|
||||
<div className="flex flex-row space-x-2" key={COLORS.red[color]}>
|
||||
<div className="w-full">red</div>
|
||||
<div className="w-full">{color}</div>
|
||||
<div className="w-full">{COLORS.red[color]}</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: COLORS.red[color],
|
||||
width: "100%",
|
||||
height: "32px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<hr />
|
||||
{Object.keys(COLORS.orange).map((color) => (
|
||||
<div className="flex flex-row space-x-2" key={COLORS.orange[color]}>
|
||||
<div className="w-full">orange</div>
|
||||
<div className="w-full">{color}</div>
|
||||
<div className="w-full">{COLORS.orange[color]}</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: COLORS.orange[color],
|
||||
width: "100%",
|
||||
height: "32px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<hr />
|
||||
{Object.keys(COLORS.green).map((color) => (
|
||||
<div className="flex flex-row space-x-2" key={COLORS.green[color]}>
|
||||
<div className="w-full">green</div>
|
||||
<div className="w-full">{color}</div>
|
||||
<div className="w-full">{COLORS.green[color]}</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: COLORS.green[color],
|
||||
width: "100%",
|
||||
height: "32px",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export const ColorPickerComponent = () => {
|
||||
const [color, setColor] = useState("3B82F6");
|
||||
return <ColorPicker defaultValue={color} onChange={(val) => setColor(val)} />;
|
||||
};
|
|
@ -1,17 +0,0 @@
|
|||
import { useState } from "react";
|
||||
|
||||
import DatePicker from "@calcom/ui/v2/modules/booker/DatePicker";
|
||||
|
||||
export default {
|
||||
title: "Datepicker",
|
||||
component: DatePicker,
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
const [selected, setSelected] = useState<Date>(new Date());
|
||||
return (
|
||||
<div style={{ width: "455px" }}>
|
||||
<DatePicker selected={selected} onChange={setSelected} locale="en" />
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,45 +0,0 @@
|
|||
import { ComponentMeta, ComponentStory } from "@storybook/react";
|
||||
import "@wojtekmaj/react-daterange-picker/dist/DateRangePicker.css";
|
||||
import { useState } from "react";
|
||||
|
||||
import DateRangePicker from "@calcom/ui/v2/core/form/date-range-picker/DateRangePicker";
|
||||
import "@calcom/ui/v2/core/form/date-range-picker/styles.css";
|
||||
|
||||
function getISOLocalDate(date) {
|
||||
const year = padStart(date.getFullYear(), 4);
|
||||
const month = padStart(date.getMonth() + 1);
|
||||
const day = padStart(date.getDate());
|
||||
|
||||
return [`${year}-${month}-${day}`];
|
||||
}
|
||||
|
||||
function padStart(num, val = 2) {
|
||||
const numStr = `${num}`;
|
||||
if (numStr.length >= val) {
|
||||
return num;
|
||||
}
|
||||
|
||||
return `0000${numStr}`.slice(-val);
|
||||
}
|
||||
|
||||
export default {
|
||||
title: "Date Range Picker",
|
||||
component: DateRangePicker,
|
||||
} as ComponentMeta<typeof DateRangePicker>;
|
||||
|
||||
export const Default: ComponentStory<typeof DateRangePicker> = () => {
|
||||
const [startDate, setStartDate] = useState<Date>(new Date());
|
||||
const [endDate, setEndDate] = useState<Date>(new Date());
|
||||
return (
|
||||
<div>
|
||||
<DateRangePicker
|
||||
startDate={getISOLocalDate(startDate) as unknown as Date}
|
||||
endDate={getISOLocalDate(endDate) as unknown as Date}
|
||||
onDatesChange={({ startDate, endDate }) => {
|
||||
setStartDate(startDate);
|
||||
setEndDate(endDate);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,26 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
import { Bell } from "react-feather";
|
||||
|
||||
import EmptyScreen from "@calcom/ui/v2/core/EmptyScreen";
|
||||
|
||||
export default {
|
||||
title: "pattern/Empty Screen",
|
||||
component: EmptyScreen,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div className="flex items-center justify-center">
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
} as ComponentMeta<typeof EmptyScreen>;
|
||||
|
||||
export const Default = () => (
|
||||
<EmptyScreen
|
||||
Icon={Bell}
|
||||
headline="Empty State Header"
|
||||
description="Ullamco dolor nulla sint nulla occaecat aliquip id elit fugiat et excepteur magna. Nisi tempor anim do tempor irure fugiat ad occaecat. Mollit ea eiusmod pariatur sunt deserunt eu eiusmod. Sit reprehenderit "
|
||||
buttonText="Veniam ut ipsum"
|
||||
buttonOnClick={() => console.log("Button Clicked")}
|
||||
/>
|
||||
);
|
|
@ -1,27 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
import { useState } from "react";
|
||||
|
||||
import FormStep from "@calcom/ui/v2/core/form/FormStep";
|
||||
|
||||
export default {
|
||||
title: "Form Step",
|
||||
component: FormStep,
|
||||
} as ComponentMeta<typeof FormStep>;
|
||||
|
||||
export const Default = () => {
|
||||
const STEPS = 4;
|
||||
const [currentStep, setCurrentStep] = useState(1);
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center space-y-14 p-20">
|
||||
<div className="w-1/2">
|
||||
<FormStep steps={STEPS} currentStep={currentStep} />
|
||||
<div className="flex space-x-2 pt-4">
|
||||
<button onClick={() => currentStep - 1 > 0 && setCurrentStep((old) => old - 1)}>Previous</button>
|
||||
<button onClick={() => currentStep + 1 < STEPS + 1 && setCurrentStep((old) => old + 1)}>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,39 +0,0 @@
|
|||
// Disabling until we figure out what is happening with the RouterMock.
|
||||
import { ComponentMeta } from "@storybook/react";
|
||||
|
||||
import { HorizontalTabItemProps } from "@calcom/ui/v2/core/navigation/tabs/HorizontalTabItem";
|
||||
import HorizontalTabs from "@calcom/ui/v2/core/navigation/tabs/HorizontalTabs";
|
||||
|
||||
export default {
|
||||
title: "Horizontal Tabs",
|
||||
component: HorizontalTabs,
|
||||
} as ComponentMeta<typeof HorizontalTabs>;
|
||||
|
||||
const HorizontalTabsPropsDefault: HorizontalTabItemProps[] = [
|
||||
{
|
||||
name: "Tab One",
|
||||
href: "/tab-one",
|
||||
},
|
||||
{
|
||||
name: "Tab Two",
|
||||
href: "/tab-two",
|
||||
},
|
||||
{
|
||||
name: "Tab Disabled",
|
||||
href: "/tab-disabled",
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const Default = () => (
|
||||
<div className="w-full p-4">
|
||||
<HorizontalTabs tabs={HorizontalTabsPropsDefault} />
|
||||
</div>
|
||||
);
|
||||
|
||||
Default.parameters = {
|
||||
nextRouter: {
|
||||
path: "/[page]",
|
||||
asPath: "/tab-one",
|
||||
},
|
||||
};
|
|
@ -1,75 +0,0 @@
|
|||
import { TooltipProvider } from "@radix-ui/react-tooltip";
|
||||
import { ComponentMeta, ComponentStory } from "@storybook/react";
|
||||
import { Copy } from "react-feather";
|
||||
|
||||
import { TextAreaField, TextField, PasswordField } from "@calcom/ui/v2/core/form/fields";
|
||||
import DatePicker from "@calcom/ui/v2/modules/booker/DatePicker";
|
||||
|
||||
export default {
|
||||
title: "Inputs",
|
||||
component: TextField,
|
||||
argTypes: {
|
||||
disabled: {
|
||||
options: [false, true],
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof TextField>;
|
||||
|
||||
const TextInputTemplate: ComponentStory<typeof TextField> = (args) => <TextField {...args} />;
|
||||
// name="demo" label="Demo Label" hint="Hint text"
|
||||
export const TextInput = TextInputTemplate.bind({});
|
||||
TextInput.args = {
|
||||
name: "demo",
|
||||
label: "Demo Label",
|
||||
hint: "Hint Text",
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export const TextInputPrefix = TextInputTemplate.bind({});
|
||||
TextInputPrefix.args = {
|
||||
name: "demo",
|
||||
label: "Demo Label",
|
||||
hint: "Hint Text",
|
||||
addOnLeading: "https://",
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export const TextInputSuffix = TextInputTemplate.bind({});
|
||||
TextInputSuffix.args = {
|
||||
name: "demo",
|
||||
label: "Demo Label",
|
||||
hint: "Hint Text",
|
||||
addOnSuffix: "Minutes",
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export const TextInputPrefixIcon = TextInputTemplate.bind({});
|
||||
TextInputPrefixIcon.args = {
|
||||
name: "demo",
|
||||
label: "Demo Label",
|
||||
hint: "Hint Text",
|
||||
addOnFilled: false,
|
||||
addOnSuffix: <Copy />,
|
||||
disabled: false,
|
||||
};
|
||||
export const TextInputSuffixIcon = TextInputTemplate.bind({});
|
||||
TextInputSuffixIcon.args = {
|
||||
name: "demo",
|
||||
label: "Demo Label",
|
||||
hint: "Hint Text",
|
||||
addOnFilled: false,
|
||||
addOnLeading: <Copy />,
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export const TextAreaInput: ComponentStory<typeof TextAreaField> = () => (
|
||||
<TextAreaField name="Text-area-input" label="Text Area" />
|
||||
);
|
||||
|
||||
export const DatePickerInput: ComponentStory<typeof DatePicker> = () => <DatePicker date={new Date()} />;
|
||||
|
||||
export const PasswordInput: ComponentStory<typeof PasswordField> = () => (
|
||||
<TooltipProvider>
|
||||
<PasswordField />
|
||||
</TooltipProvider>
|
||||
);
|
|
@ -1,50 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { Info } from "react-feather";
|
||||
|
||||
import { Button, Dialog, DialogContent, DialogTrigger, TextField } from "@calcom/ui/v2";
|
||||
|
||||
export default {
|
||||
title: "pattern/Modal",
|
||||
component: Dialog,
|
||||
};
|
||||
|
||||
export const Creation = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button onClick={() => setOpen(true)}>Open Modal</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent
|
||||
title="Header"
|
||||
description="Optional Description"
|
||||
type="creation"
|
||||
actionText="Create"
|
||||
actionOnClick={() => setOpen(false)}>
|
||||
<TextField name="Label" />
|
||||
<TextField name="Label" />
|
||||
<TextField name="Label" />
|
||||
<TextField name="Label" />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export const Confirmation = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button>Open Modal</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent
|
||||
title="Header"
|
||||
description="Optional Description"
|
||||
type="confirmation"
|
||||
actionText="Confirm"
|
||||
Icon={Info}
|
||||
actionOnClick={() => setOpen(false)}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
|
@ -1,24 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { Button, showToast } from "@calcom/ui/v2";
|
||||
|
||||
export default {
|
||||
title: "Notifcations-Toasts",
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<>
|
||||
<Story />
|
||||
<Toaster />
|
||||
</>
|
||||
),
|
||||
],
|
||||
} as ComponentMeta<typeof All>; // We have to fake this type as the story for this component isn't really a component.
|
||||
|
||||
export const All = () => (
|
||||
<div className="flex space-x-2">
|
||||
<Button onClick={() => showToast("This is a Neutral toast", "warning")}>Neutral</Button>
|
||||
<Button onClick={() => showToast("This is a Success toast", "success")}>Success</Button>
|
||||
<Button onClick={() => showToast("This is a Error toast", "error")}>Error</Button>
|
||||
</div>
|
||||
);
|
|
@ -1,52 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
import { Search } from "react-feather";
|
||||
|
||||
import { Button, TextField } from "@calcom/ui/v2";
|
||||
import PageHeader from "@calcom/ui/v2/core/PageHeader";
|
||||
|
||||
export default {
|
||||
title: "pattern/Page Header",
|
||||
component: PageHeader,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div className="">
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
} as ComponentMeta<typeof PageHeader>;
|
||||
|
||||
export const Default = () => (
|
||||
<PageHeader title="Title" description="Some description about the header above" />
|
||||
);
|
||||
|
||||
export const ButtonRight = () => (
|
||||
<PageHeader
|
||||
title="Title"
|
||||
description="Some description about the header above"
|
||||
rightAlignedComponent={<Button>Button Text</Button>}
|
||||
/>
|
||||
);
|
||||
export const ComingSoon = () => (
|
||||
<PageHeader
|
||||
title="Title"
|
||||
description="Some description about the header above"
|
||||
badgeText="Coming Soon"
|
||||
badgeVariant="gray"
|
||||
/>
|
||||
);
|
||||
export const SearchInstalledApps = () => (
|
||||
<PageHeader
|
||||
title="Search booking"
|
||||
description="See upcoming and past events booking through your event type link"
|
||||
rightAlignedComponent={
|
||||
<TextField
|
||||
containerClassName="h-9 max-h-9"
|
||||
addOnLeading={<Search />}
|
||||
name="search"
|
||||
labelSrOnly
|
||||
placeholder="WIP"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
|
@ -1,19 +0,0 @@
|
|||
import * as Radio from "@calcom/ui/v2/core/form/radio-area/Radio";
|
||||
import { RadioField } from "@calcom/ui/v2/core/form/radio-area/Radio";
|
||||
|
||||
export default {
|
||||
title: "Radio",
|
||||
component: RadioField,
|
||||
};
|
||||
|
||||
export const RadioGroupDemo = () => {
|
||||
return (
|
||||
<form>
|
||||
<Radio.Group aria-label="View density" defaultValue="default">
|
||||
<RadioField label="Default" id="r1" value="1" />
|
||||
<RadioField label="Next" id="r2" value="2" />
|
||||
<RadioField label="Disabled" id="r3" disabled value="1" />
|
||||
</Radio.Group>
|
||||
</form>
|
||||
);
|
||||
};
|
|
@ -1,33 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
|
||||
import { Select } from "@calcom/ui/v2";
|
||||
import MultiDropdownSelect from "@calcom/ui/v2/modules/event-types/MultiDropdownSelect";
|
||||
|
||||
export default {
|
||||
title: "Form/Select",
|
||||
component: Select,
|
||||
} as ComponentMeta<typeof Select>;
|
||||
|
||||
const singleOptions = [
|
||||
{
|
||||
label: "Select",
|
||||
value: 0,
|
||||
},
|
||||
...[5, 10, 15, 20, 30, 45, 60, 90, 120].map((minutes) => ({
|
||||
label: minutes + " " + "minutes",
|
||||
value: minutes,
|
||||
})),
|
||||
];
|
||||
const multiOptions = [
|
||||
{
|
||||
label: "Select",
|
||||
value: 0,
|
||||
},
|
||||
...[5, 10, 15, 20, 30, 45, 60, 90, 120].map((minutes) => ({
|
||||
label: minutes + " " + "minutes",
|
||||
value: minutes,
|
||||
})),
|
||||
];
|
||||
|
||||
export const Single = () => <Select options={singleOptions} />;
|
||||
export const Multi = () => <MultiDropdownSelect options={multiOptions} />;
|
|
@ -1,15 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
|
||||
import { Switch } from "@calcom/ui/v2";
|
||||
|
||||
export default {
|
||||
title: "Switch",
|
||||
component: Switch,
|
||||
} as ComponentMeta<typeof Switch>;
|
||||
|
||||
export const All = () => (
|
||||
<div className="flex flex-col space-y-2">
|
||||
<p>Checked works in app but storybook doesnt like it</p>
|
||||
<Switch label="Default" />
|
||||
</div>
|
||||
);
|
|
@ -1,67 +0,0 @@
|
|||
// Disabling until we figure out what is happening with the RouterMock.
|
||||
import { ComponentMeta } from "@storybook/react";
|
||||
import { Link } from "react-feather";
|
||||
|
||||
import { VerticalTabItem } from "@calcom/ui/v2";
|
||||
|
||||
export default {
|
||||
title: "VerticalTabItem",
|
||||
component: VerticalTabItem,
|
||||
} as ComponentMeta<typeof VerticalTabItem>;
|
||||
|
||||
const TabItemProps = {
|
||||
name: "Event Setup",
|
||||
icon: Link,
|
||||
href: "/settings/event",
|
||||
info: "60 mins, Zoom",
|
||||
};
|
||||
|
||||
export const Default = () => (
|
||||
<div className="h-20 w-full bg-gray-100 p-4">
|
||||
<VerticalTabItem {...TabItemProps} />
|
||||
</div>
|
||||
);
|
||||
export const Active = () => (
|
||||
<div className="h-20 w-full bg-gray-100 p-4">
|
||||
<VerticalTabItem {...TabItemProps} />
|
||||
</div>
|
||||
);
|
||||
|
||||
// Mocking next router to show active state
|
||||
Active.parameters = {
|
||||
nextRouter: {
|
||||
path: "/settings/[tab]",
|
||||
asPath: "/settings/event",
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled = () => (
|
||||
<div className="h-20 w-full bg-gray-100 p-4">
|
||||
<VerticalTabItem {...TabItemProps} disabled />
|
||||
</div>
|
||||
);
|
||||
|
||||
export const NoIconNoInfo = () => (
|
||||
<div className="h-20 w-full bg-gray-100 p-4">
|
||||
<VerticalTabItem name="Event Setup" href="/settings/event" />
|
||||
</div>
|
||||
);
|
||||
|
||||
export const IconNoInfo = () => (
|
||||
<div className="h-20 w-full bg-gray-100 p-4">
|
||||
<VerticalTabItem name="Event Setup" href="/settings/event" icon={Link} />
|
||||
</div>
|
||||
);
|
||||
|
||||
export const IconNoInfoActive = () => (
|
||||
<div className="h-20 w-full bg-gray-100 p-4">
|
||||
<VerticalTabItem name="Event Setup" href="/settings/events" icon={Link} />
|
||||
</div>
|
||||
);
|
||||
|
||||
IconNoInfoActive.paramaters = {
|
||||
nextRouter: {
|
||||
path: "/settings/[tab]",
|
||||
asPath: "/settings/events",
|
||||
},
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
import { ComponentMeta } from "@storybook/react";
|
||||
|
||||
import { ToggleGroup } from "@calcom/ui/v2/core/form/ToggleGroup";
|
||||
|
||||
export default {
|
||||
title: "Toggle Group",
|
||||
component: ToggleGroup,
|
||||
} as ComponentMeta<typeof ToggleGroup>;
|
||||
|
||||
export const Default = () => {
|
||||
return (
|
||||
<ToggleGroup
|
||||
defaultValue="12"
|
||||
options={[
|
||||
{ value: "12", label: "12h" },
|
||||
{ value: "24", label: "24h" },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -1,85 +0,0 @@
|
|||
// Disabling until we figure out what is happening with the RouterMock.
|
||||
import { ComponentMeta } from "@storybook/react";
|
||||
import { Calendar, Clock, Grid, Link, RefreshCw, User, Users } from "react-feather";
|
||||
|
||||
import VerticalTabs from "@calcom/ui/v2/core/navigation/tabs/VerticalTabs";
|
||||
|
||||
export default {
|
||||
title: "Vertical Tabs",
|
||||
component: VerticalTabs,
|
||||
} as ComponentMeta<typeof VerticalTabs>;
|
||||
|
||||
const VerticalTabsPropsDefault = [
|
||||
{
|
||||
name: "Security",
|
||||
icon: Link,
|
||||
href: "/security",
|
||||
},
|
||||
{
|
||||
name: "Profile",
|
||||
icon: User,
|
||||
href: "/profile",
|
||||
},
|
||||
{
|
||||
name: "Calendar Sync",
|
||||
icon: RefreshCw,
|
||||
href: "/cal-sync",
|
||||
},
|
||||
{
|
||||
name: "Teams",
|
||||
icon: Users,
|
||||
href: "/Teams",
|
||||
},
|
||||
];
|
||||
|
||||
export const Default = () => (
|
||||
<div className="w-full p-4" style={{ backgroundColor: "#F9FAFB" }}>
|
||||
<VerticalTabs tabs={VerticalTabsPropsDefault} />
|
||||
</div>
|
||||
);
|
||||
|
||||
Default.parameters = {
|
||||
nextRouter: {
|
||||
path: "/[page]",
|
||||
asPath: "/profile",
|
||||
},
|
||||
};
|
||||
|
||||
const VerticalTabsPropsInfo = [
|
||||
{
|
||||
name: "Event Setup",
|
||||
icon: Link,
|
||||
href: "/events",
|
||||
info: "60 Mins, Zoom",
|
||||
},
|
||||
{
|
||||
name: "Availability",
|
||||
icon: Calendar,
|
||||
href: "/availability",
|
||||
info: "Working Hours",
|
||||
},
|
||||
{
|
||||
name: "Limits",
|
||||
icon: Clock,
|
||||
href: "/limits",
|
||||
info: "Buffers, Limits & more...",
|
||||
},
|
||||
{
|
||||
name: "Apps",
|
||||
icon: Grid,
|
||||
href: "/apps",
|
||||
info: "3 apps - 0 active",
|
||||
},
|
||||
];
|
||||
|
||||
export const Info = () => (
|
||||
<div className="w-full p-4" style={{ backgroundColor: "#F9FAFB" }}>
|
||||
<VerticalTabs tabs={VerticalTabsPropsInfo} />
|
||||
</div>
|
||||
);
|
||||
Info.parameters = {
|
||||
nextRouter: {
|
||||
path: "/[page]",
|
||||
asPath: "/events",
|
||||
},
|
||||
};
|
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
After Width: | Height: | Size: 715 KiB |
|
@ -2,13 +2,4 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import "../../../packages/ui/v2/core/form/date-range-picker/styles.css";
|
||||
@import "../../../node_modules/@wojtekmaj/react-daterange-picker/dist/DateRangePicker.css";
|
||||
|
||||
:root {
|
||||
--brand-color: #292929;
|
||||
--brand-text-color: #ffffff;
|
||||
--brand-color-dark-mode: #fafafa;
|
||||
--brand-text-color-dark-mode: #292929;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
@import "../../../packages/ui/styles/shared-globals.css"
|
|
@ -0,0 +1,189 @@
|
|||
.sbdocs {
|
||||
font-family: 'Inter var' !important;
|
||||
padding: 0!important;
|
||||
}
|
||||
|
||||
#docs-root {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sbdocs.sbdocs-h1 {
|
||||
font-family: "Cal Sans", "sans-serif" !important;
|
||||
font-weight: 600;
|
||||
font-size: 36px;
|
||||
margin: 0 0 82px 0;
|
||||
}
|
||||
|
||||
.sbdocs.sbdocs-h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
border-top: 2px solid rgba(0, 0, 0, 0.12);
|
||||
padding-top: 12px!important;
|
||||
padding-bottom: 12px!important;
|
||||
margin: 82px 0 0 0;
|
||||
}
|
||||
|
||||
.sbdocs.sbdocs-p {
|
||||
max-width: 560px;
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.sbdocs.sbdocs-content {
|
||||
width: 1200px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/** Docs table **/
|
||||
.custom-args-wrapper{
|
||||
max-height: 600px;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.docblock-argstable-body {
|
||||
box-shadow: none!important;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* .docblock-argstable-head tr th, */
|
||||
.docblock-argstable-body tr td {
|
||||
/* padding-left: 0!important; */
|
||||
padding: 20px !important;
|
||||
}
|
||||
|
||||
/** Column titles **/
|
||||
.docblock-argstable-body tr td > span,
|
||||
.docblock-argstable-head tr th > span {
|
||||
color: #525252 !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
.docblock-argstable-body div p,
|
||||
.docblock-argstable-body div span {
|
||||
color: #8F8F8F !important;
|
||||
}
|
||||
|
||||
/** Custom components **/
|
||||
.story-title {
|
||||
margin: 0 0 82px 0;
|
||||
}
|
||||
|
||||
.story-title.offset {
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
.story-title h1 {
|
||||
font-family: "Cal Sans", "sans-serif" !important;
|
||||
font-weight: 600;
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.story-title h1 span {
|
||||
color: #9CA3AF;
|
||||
font-family: 'Inter var';
|
||||
font-weight: normal;
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.story-title p {
|
||||
color: #111827;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.examples {
|
||||
background-color: #F9FAFB;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.examples-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
padding: 30px 0;
|
||||
}
|
||||
|
||||
.examples-title {
|
||||
color: #8F8F8F;
|
||||
font-size: 14px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.examples-item-title {
|
||||
font-size: 12px;
|
||||
color: #8F8F8F;
|
||||
margin-bottom: 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.examples-item {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.examples-footnote p,
|
||||
.examples-footnote ul,
|
||||
.examples-footnote li {
|
||||
color: #8F8F8F;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.examples-footnote ul {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.examples-footnote li {
|
||||
margin: 0!important;
|
||||
}
|
||||
|
||||
.story-note {
|
||||
background-color: #F9FAFB;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.story-note > div {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
/** SB Docs Preview **/
|
||||
.sbdocs-preview {
|
||||
width: 100vw;
|
||||
left: calc((100vw - 1200px) / -2);
|
||||
box-shadow: none !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
border-radius: none!important;
|
||||
}
|
||||
|
||||
.docs-story > div:first-child {
|
||||
padding: 0!important;
|
||||
margin: 0!important;
|
||||
}
|
||||
|
||||
.docs-story .innerZoomElementWrapper > div {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.sb-main-padded {
|
||||
padding: 0!important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
.sbdocs-preview {
|
||||
left: -24px;
|
||||
width: calc(100vw + 48px);
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.sbdocs.sbdocs-content {
|
||||
padding: 24px !important;
|
||||
}
|
||||
}
|
|
@ -1,167 +1,7 @@
|
|||
const base = require("@calcom/config/tailwind-preset");
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["../../packages/ui/**/*.{js,ts,jsx,tsx}", "./stories/**/*.{js,ts,tsx,jsx}"],
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
fontFamily: {
|
||||
cal: ["Cal Sans", "sans-serif"],
|
||||
mono: ["Roboto Mono", "monospace"],
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
/* your primary brand color */
|
||||
brandcontrast: "var(--brand-text-color)",
|
||||
darkmodebrand: "var(--brand-color-dark-mode)",
|
||||
darkmodebrandcontrast: "var(--brand-text-color-dark-mode)",
|
||||
bookinglightest: "var(--booking-lightest-color)",
|
||||
bookinglighter: "var(--booking-lighter-color)",
|
||||
bookinglight: "var(--booking-light-color)",
|
||||
bookingmedian: "var(--booking-median-color)",
|
||||
bookingdark: "var(--booking-dark-color)",
|
||||
bookingdarker: "var(--booking-darker-color)",
|
||||
bookinghighlight: "var(--booking-highlight-color)",
|
||||
black: "#111111",
|
||||
brand: {
|
||||
// Figure out a way to automate this for self hosted users
|
||||
// Goto https://javisperez.github.io/tailwindcolorshades to generate your brand color
|
||||
50: "#d1d5db",
|
||||
100: "#9ca3af",
|
||||
200: "#6b7280",
|
||||
300: "#4b5563",
|
||||
400: "#374151",
|
||||
500: "#111827", // Brand color
|
||||
600: "#0f1623",
|
||||
700: "#0d121d",
|
||||
800: "#0a0e17",
|
||||
900: "#080c13",
|
||||
DEFAULT: "#111827",
|
||||
},
|
||||
gray: {
|
||||
50: "#F9FAFB",
|
||||
100: "#F3F4F6",
|
||||
200: "#E5E7EB",
|
||||
300: "#D1D5DB",
|
||||
400: "#9CA3AF",
|
||||
500: "#6B7280",
|
||||
600: "#4B5563",
|
||||
700: "#374151",
|
||||
800: "#1F2937",
|
||||
900: "#111827",
|
||||
},
|
||||
neutral: {
|
||||
50: "#F8F8F8",
|
||||
100: "#F5F5F5",
|
||||
200: "#E1E1E1",
|
||||
300: "#CFCFCF",
|
||||
400: "#ACACAC",
|
||||
500: "#888888",
|
||||
600: "#494949",
|
||||
700: "#3E3E3E",
|
||||
800: "#313131",
|
||||
900: "#292929",
|
||||
},
|
||||
primary: {
|
||||
50: "#F4F4F4",
|
||||
100: "#E8E8E8",
|
||||
200: "#C6C6C6",
|
||||
300: "#A3A3A3",
|
||||
400: "#5F5F5F",
|
||||
500: "#1A1A1A",
|
||||
600: "#171717",
|
||||
700: "#141414",
|
||||
800: "#101010",
|
||||
900: "#0D0D0D",
|
||||
},
|
||||
secondary: {
|
||||
50: "#F5F8F7",
|
||||
100: "#EBF0F0",
|
||||
200: "#CDDAD9",
|
||||
300: "#AEC4C2",
|
||||
400: "#729894",
|
||||
500: "#356C66",
|
||||
600: "#30615C",
|
||||
700: "#28514D",
|
||||
800: "#20413D",
|
||||
900: "#223B41",
|
||||
},
|
||||
red: {
|
||||
50: "#FFF5F5",
|
||||
100: "#FFE3E2",
|
||||
200: "#FFC9C9",
|
||||
300: "#FEA8A8",
|
||||
400: "#FF8787",
|
||||
500: "#FF6B6B",
|
||||
600: "#FA5352",
|
||||
700: "#F03E3F",
|
||||
800: "#E03130",
|
||||
900: "#C92B2B",
|
||||
},
|
||||
orange: {
|
||||
50: "#FFF4E5",
|
||||
100: "#FFE8CC",
|
||||
200: "#FFD8A8",
|
||||
300: "#FFBF78",
|
||||
400: "#FFA94E",
|
||||
500: "#FF922B",
|
||||
600: "#FD7E14",
|
||||
700: "#F76706",
|
||||
800: "#E8580C",
|
||||
900: "#D94810",
|
||||
},
|
||||
green: {
|
||||
50: "#EBFCEE",
|
||||
100: "#D2F9D9",
|
||||
200: "#B1F2BA",
|
||||
300: "#8CE99A",
|
||||
400: "#69DB7C",
|
||||
500: "#51CF66",
|
||||
600: "#40C057",
|
||||
700: "#36B24D",
|
||||
800: "#2F9E44",
|
||||
900: "#2B8A3E",
|
||||
},
|
||||
blue: {
|
||||
50: "#E7F5FF",
|
||||
100: "#D0EBFF",
|
||||
200: "#A4D8FF",
|
||||
300: "#74C0FC",
|
||||
400: "#4DABF7",
|
||||
500: "#339AF0",
|
||||
600: "#238BE6",
|
||||
700: "#1C7ED7",
|
||||
800: "#1971C2",
|
||||
900: "#1763AB",
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
cal: ["inter", "sans-serif"],
|
||||
mono: ["Roboto Mono", "monospace"],
|
||||
},
|
||||
maxHeight: (theme) => ({
|
||||
0: "0",
|
||||
97: "25rem",
|
||||
...theme("spacing"),
|
||||
full: "100%",
|
||||
screen: "100vh",
|
||||
}),
|
||||
minHeight: (theme) => ({
|
||||
0: "0",
|
||||
...theme("spacing"),
|
||||
full: "100%",
|
||||
screen: "100vh",
|
||||
}),
|
||||
minWidth: (theme) => ({
|
||||
0: "0",
|
||||
...theme("spacing"),
|
||||
full: "100%",
|
||||
screen: "100vw",
|
||||
}),
|
||||
maxWidth: (theme, { breakpoints }) => ({
|
||||
0: "0",
|
||||
...theme("spacing"),
|
||||
...breakpoints(theme("screens")),
|
||||
full: "100%",
|
||||
screen: "100vw",
|
||||
}),
|
||||
},
|
||||
},
|
||||
...base,
|
||||
darkMode: ["class", '[data-mode="dark"]'],
|
||||
content: [...base.content, "../../packages/ui/**/*.{js,ts,jsx,tsx,mdx}", "./stories/**/*.{js,ts,tsx,jsx}"],
|
||||
};
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
"dependencies": {
|
||||
"highlight.js": "^11.6.0",
|
||||
"isarray": "2.0.5",
|
||||
"next": "^12.2.5",
|
||||
"next": "^12.3.1",
|
||||
"openapi-snippet": "^0.13.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
@ -23,7 +23,7 @@
|
|||
"devDependencies": {
|
||||
"@types/node": "16.9.1",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "18.0.4",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,4 +67,6 @@ tsconfig.tsbuildinfo
|
|||
public/embed
|
||||
|
||||
# Copied app-store images
|
||||
public/app-store
|
||||
public/app-store
|
||||
# Sentry
|
||||
.sentryclirc
|
||||
|
|
|
@ -11,7 +11,7 @@ import { App as AppType } from "@calcom/types/App";
|
|||
import { Button, SkeletonButton } from "@calcom/ui";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import Shell from "@calcom/ui/Shell";
|
||||
import Badge from "@calcom/ui/v2/core/Badge";
|
||||
import { Badge } from "@calcom/ui/components/badge";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
|
||||
import DisconnectIntegration from "@components/integrations/DisconnectIntegration";
|
||||
|
|
|
@ -6,8 +6,9 @@ import { components, ControlProps } from "react-select";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Button } from "@calcom/ui/components";
|
||||
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
|
||||
import { Button, HorizontalTabs, showToast, Switch } from "@calcom/ui/v2";
|
||||
import { HorizontalTabs, showToast, Switch } from "@calcom/ui/v2";
|
||||
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/v2/core/Dialog";
|
||||
|
||||
import { EMBED_LIB_URL, WEBAPP_URL } from "@lib/config/constants";
|
||||
|
|
|
@ -10,8 +10,9 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||
import { trpc } from "@calcom/trpc/react";
|
||||
import { App as AppType } from "@calcom/types/App";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Button } from "@calcom/ui/components";
|
||||
import { showToast, SkeletonText } from "@calcom/ui/v2";
|
||||
import { Button, SkeletonButton, Shell } from "@calcom/ui/v2";
|
||||
import { SkeletonButton, Shell } from "@calcom/ui/v2";
|
||||
import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/DisconnectIntegration";
|
||||
|
||||
import HeadSeo from "@components/seo/head-seo";
|
||||
|
|
|
@ -8,7 +8,8 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import SkeletonLoader from "@calcom/ui/apps/SkeletonLoader";
|
||||
import { Alert, Button, EmptyScreen } from "@calcom/ui/v2";
|
||||
import { Button } from "@calcom/ui/components";
|
||||
import { Alert, EmptyScreen } from "@calcom/ui/v2";
|
||||
import { List } from "@calcom/ui/v2/core/List";
|
||||
import { ShellSubHeading } from "@calcom/ui/v2/core/Shell";
|
||||
import Switch from "@calcom/ui/v2/core/Switch";
|
||||
|
|
|
@ -5,8 +5,8 @@ import useApp from "@calcom/lib/hooks/useApp";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { showToast } from "@calcom/ui/v2";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
|
||||
/**
|
||||
* Use this component to allow installing an app from anywhere on the app.
|
||||
|
|
|
@ -24,14 +24,7 @@ export function AvailableEventLocations({ locations }: { locations: Props["event
|
|||
alt={`${eventLocationType.label} icon`}
|
||||
/>
|
||||
<Tooltip content={locationKeyToString(location)}>
|
||||
<a
|
||||
target="_blank"
|
||||
href={locationKeyToString(location) ?? "/"}
|
||||
className="truncate"
|
||||
key={location.type}
|
||||
rel="noreferrer">
|
||||
{locationKeyToString(location)}
|
||||
</a>
|
||||
<p className="truncate">{locationKeyToString(location)}</p>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -83,7 +83,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
|
|||
pathname: router.pathname.endsWith("/embed") ? "../book" : "book",
|
||||
query: {
|
||||
...router.query,
|
||||
date: dayjs(slot.time).format(),
|
||||
date: dayjs.utc(slot.time).tz(timeZone()).format(),
|
||||
type: eventTypeId,
|
||||
slug: eventTypeSlug,
|
||||
/** Treat as recurring only when a count exist and it's not a rescheduling workflow */
|
||||
|
|
|
@ -11,15 +11,14 @@ import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
|
|||
import { inferQueryInput, inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Badge } from "@calcom/ui/components/badge";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { TextArea } from "@calcom/ui/form/fields";
|
||||
import Badge from "@calcom/ui/v2/core/Badge";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import MeetingTimeInTimezones from "@calcom/ui/v2/core/MeetingTimeInTimezones";
|
||||
import Tooltip from "@calcom/ui/v2/core/Tooltip";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
|
||||
import useMeQuery from "@lib/hooks/useMeQuery";
|
||||
import { extractRecurringDates } from "@lib/parseDate";
|
||||
|
||||
import { EditLocationDialog } from "@components/dialog/EditLocationDialog";
|
||||
import { RescheduleDialog } from "@components/dialog/RescheduleDialog";
|
||||
|
@ -31,14 +30,14 @@ type BookingItem = inferQueryOutput<"viewer.bookings">["bookings"][number];
|
|||
|
||||
type BookingItemProps = BookingItem & {
|
||||
listingStatus: BookingListingStatus;
|
||||
recurringBookings: inferQueryOutput<"viewer.bookings">["recurringInfo"];
|
||||
recurringInfo: inferQueryOutput<"viewer.bookings">["recurringInfo"][number] | undefined;
|
||||
};
|
||||
|
||||
function BookingListItem(booking: BookingItemProps) {
|
||||
// Get user so we can determine 12/24 hour format preferences
|
||||
const query = useMeQuery();
|
||||
const user = query.data;
|
||||
const { t, i18n } = useLocale();
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const router = useRouter();
|
||||
const [rejectionReason, setRejectionReason] = useState<string>("");
|
||||
|
@ -176,16 +175,11 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
setLocationMutation.mutate({ bookingId: booking.id, newLocation });
|
||||
};
|
||||
|
||||
// Extract recurring dates is intensive to run, so use useMemo.
|
||||
// Calculate the booking date(s) and setup recurring event data to show
|
||||
// @FIXME: This is importing the RRULE library which is already heavy. Find out a more optimal way do this.
|
||||
const [recurringStrings, recurringDates] = useMemo(() => {
|
||||
if (booking.recurringBookings !== undefined && booking.eventType.recurringEvent?.freq !== undefined) {
|
||||
return extractRecurringDates(booking, user?.timeZone, i18n);
|
||||
}
|
||||
return [[], []];
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [user?.timeZone, i18n.language, booking.recurringBookings]);
|
||||
// Getting accepted recurring dates to show
|
||||
const recurringDates = booking.recurringInfo?.bookings[BookingStatus.ACCEPTED]
|
||||
.concat(booking.recurringInfo?.bookings[BookingStatus.CANCELLED])
|
||||
.concat(booking.recurringInfo?.bookings[BookingStatus.PENDING])
|
||||
.sort((date1: Date, date2: Date) => date1.getTime() - date2.getTime());
|
||||
|
||||
const location = booking.location || "";
|
||||
|
||||
|
@ -275,13 +269,11 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
attendees={booking.attendees}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isPending && (
|
||||
<Badge className="ltr:mr-2 rtl:ml-2" variant="orange">
|
||||
{t("unconfirmed")}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{booking.eventType?.team && (
|
||||
<Badge className="ltr:mr-2 rtl:ml-2" variant="gray">
|
||||
{booking.eventType.team.name}
|
||||
|
@ -292,10 +284,11 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
{t("pending_payment")}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
<div className="mt-2 text-sm text-gray-400">
|
||||
<RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} />
|
||||
</div>
|
||||
{recurringDates !== undefined && (
|
||||
<div className="mt-2 text-sm text-gray-400">
|
||||
<RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className={"w-full px-4" + (isRejected ? " line-through" : "")} onClick={onClickTableData}>
|
||||
|
@ -331,9 +324,11 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
{t("pending_payment")}
|
||||
</Badge>
|
||||
)}
|
||||
<div className="text-sm text-gray-400 sm:hidden">
|
||||
<RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} />
|
||||
</div>
|
||||
{recurringDates !== undefined && (
|
||||
<div className="text-sm text-gray-400 sm:hidden">
|
||||
<RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="cursor-pointer py-4">
|
||||
|
@ -403,11 +398,16 @@ const RecurringBookingsTooltip = ({ booking, recurringDates }: RecurringBookings
|
|||
const { t } = useLocale();
|
||||
const now = new Date();
|
||||
const recurringCount = recurringDates.filter((date) => {
|
||||
return date >= now;
|
||||
return (
|
||||
date >= now &&
|
||||
!booking.recurringInfo?.bookings[BookingStatus.CANCELLED]
|
||||
.map((date) => date.toDateString())
|
||||
.includes(date.toDateString())
|
||||
);
|
||||
}).length;
|
||||
|
||||
return (
|
||||
(booking.recurringBookings &&
|
||||
(booking.recurringInfo &&
|
||||
booking.eventType?.recurringEvent?.freq &&
|
||||
(booking.listingStatus === "recurring" ||
|
||||
booking.listingStatus === "unconfirmed" ||
|
||||
|
@ -415,13 +415,20 @@ const RecurringBookingsTooltip = ({ booking, recurringDates }: RecurringBookings
|
|||
<div className="underline decoration-gray-400 decoration-dashed underline-offset-2">
|
||||
<div className="flex">
|
||||
<Tooltip
|
||||
content={recurringDates.map((aDate, key) => (
|
||||
<p key={key} className={classNames(recurringDates[key] < now && "line-through")}>
|
||||
{formatTime(booking.startTime, user?.timeFormat, user?.timeZone)}
|
||||
{" - "}
|
||||
{dayjs(aDate).format("D MMMM YYYY")}
|
||||
</p>
|
||||
))}>
|
||||
content={recurringDates.map((aDate, key) => {
|
||||
const pastOrCancelled =
|
||||
aDate < now ||
|
||||
booking.recurringInfo?.bookings[BookingStatus.CANCELLED]
|
||||
.map((date) => date.toDateString())
|
||||
.includes(aDate.toDateString());
|
||||
return (
|
||||
<p key={key} className={classNames(pastOrCancelled && "line-through")}>
|
||||
{formatTime(aDate, user?.timeFormat, user?.timeZone)}
|
||||
{" - "}
|
||||
{dayjs(aDate).format("D MMMM YYYY")}
|
||||
</p>
|
||||
);
|
||||
})}>
|
||||
<div className="text-gray-600 dark:text-white">
|
||||
<Icon.FiRefreshCcw
|
||||
strokeWidth="3"
|
||||
|
@ -435,7 +442,7 @@ const RecurringBookingsTooltip = ({ booking, recurringDates }: RecurringBookings
|
|||
: getEveryFreqFor({
|
||||
t,
|
||||
recurringEvent: booking.eventType.recurringEvent,
|
||||
recurringCount,
|
||||
recurringCount: booking.recurringInfo.count,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -40,9 +40,10 @@ import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
|
|||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Tooltip } from "@calcom/ui/Tooltip";
|
||||
import AddressInput from "@calcom/ui/form/AddressInputLazy";
|
||||
import { Button } from "@calcom/ui/components";
|
||||
import PhoneInput from "@calcom/ui/form/PhoneInputLazy";
|
||||
import { EmailInput, Form } from "@calcom/ui/form/fields";
|
||||
import { Button } from "@calcom/ui/v2";
|
||||
|
||||
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||
import { timeZone } from "@lib/clock";
|
||||
|
@ -68,6 +69,8 @@ type BookingFormValues = {
|
|||
notes?: string;
|
||||
locationType?: EventLocationType["type"];
|
||||
guests?: string[];
|
||||
address?: string;
|
||||
attendeeAddress?: string;
|
||||
phone?: string;
|
||||
hostPhoneNumber?: string; // Maybe come up with a better way to name this to distingish between two types of phone numbers
|
||||
customInputs?: {
|
||||
|
@ -269,6 +272,7 @@ const BookingPage = ({
|
|||
.refine((val) => isValidPhoneNumber(val))
|
||||
.optional()
|
||||
.nullable(),
|
||||
attendeeAddress: z.string().optional().nullable(),
|
||||
smsReminderNumber: z
|
||||
.string()
|
||||
.refine((val) => isValidPhoneNumber(val))
|
||||
|
@ -297,10 +301,10 @@ const BookingPage = ({
|
|||
|
||||
const selectedLocation = getEventLocationType(selectedLocationType);
|
||||
const AttendeeInput =
|
||||
selectedLocation?.attendeeInputType === "text"
|
||||
? "input"
|
||||
: selectedLocation?.attendeeInputType === "phone"
|
||||
selectedLocation?.attendeeInputType === "phone"
|
||||
? PhoneInput
|
||||
: selectedLocation?.attendeeInputType === "attendeeAddress"
|
||||
? AddressInput
|
||||
: null;
|
||||
|
||||
// Calculate the booking date(s)
|
||||
|
@ -356,6 +360,7 @@ const BookingPage = ({
|
|||
location: getEventLocationValue(locations, {
|
||||
type: booking.locationType ? booking.locationType : selectedLocationType || "",
|
||||
phone: booking.phone,
|
||||
attendeeAddress: booking.attendeeAddress,
|
||||
}),
|
||||
metadata,
|
||||
customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({
|
||||
|
@ -386,6 +391,7 @@ const BookingPage = ({
|
|||
location: getEventLocationValue(locations, {
|
||||
type: (booking.locationType ? booking.locationType : selectedLocationType) || "",
|
||||
phone: booking.phone,
|
||||
attendeeAddress: booking.attendeeAddress,
|
||||
}),
|
||||
metadata,
|
||||
customInputs: Object.keys(booking.customInputs || {}).map((inputId) => ({
|
||||
|
@ -497,26 +503,17 @@ const BookingPage = ({
|
|||
<div className="text-bookinghighlight flex items-start text-sm">
|
||||
<Icon.FiCalendar className="mr-[10px] ml-[2px] mt-[2px] inline-block h-4 w-4" />
|
||||
<div className="text-sm font-medium">
|
||||
{(rescheduleUid || !eventType.recurringEvent?.freq) &&
|
||||
`${formatTime(dayjs(date).toDate(), user?.timeFormat, user?.timeZone)}, ${dayjs(
|
||||
date
|
||||
).format("dddd, D MMMM YYYY")}`}
|
||||
{(rescheduleUid || !eventType.recurringEvent?.freq) && `${parseDate(date, i18n)}`}
|
||||
{!rescheduleUid &&
|
||||
eventType.recurringEvent?.freq &&
|
||||
recurringDates.slice(0, 5).map((aDate, key) => {
|
||||
return (
|
||||
<p key={key}>{`${formatTime(aDate, user?.timeFormat, user?.timeZone)}, ${dayjs(
|
||||
aDate
|
||||
).format("dddd, D MMMM YYYY")}`}</p>
|
||||
);
|
||||
recurringStrings.slice(0, 5).map((timeFormatted, key) => {
|
||||
return <p key={key}>{timeFormatted}</p>;
|
||||
})}
|
||||
{!rescheduleUid && eventType.recurringEvent?.freq && recurringStrings.length > 5 && (
|
||||
<div className="flex">
|
||||
<Tooltip
|
||||
content={recurringDates.slice(5).map((aDate, key) => (
|
||||
<p key={key}>{`${formatTime(aDate, user?.timeFormat, user?.timeZone)}, ${dayjs(
|
||||
aDate
|
||||
).format("dddd, D MMMM YYYY")}`}</p>
|
||||
content={recurringStrings.slice(5).map((timeFormatted, key) => (
|
||||
<p key={key}>{timeFormatted}</p>
|
||||
))}>
|
||||
<p className="dark:text-darkgray-600 text-sm">
|
||||
{t("plus_more", { count: recurringStrings.length - 5 })}
|
||||
|
@ -657,16 +654,39 @@ const BookingPage = ({
|
|||
{AttendeeInput && (
|
||||
<div className="mb-4">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
htmlFor={
|
||||
selectedLocationType === LocationType.Phone
|
||||
? "phone"
|
||||
: selectedLocationType === LocationType.AttendeeInPerson
|
||||
? "attendeeAddress"
|
||||
: ""
|
||||
}
|
||||
className="block text-sm font-medium text-gray-700 dark:text-white">
|
||||
{t("phone_number")}
|
||||
{selectedLocationType === LocationType.Phone
|
||||
? t("phone_number")
|
||||
: selectedLocationType === LocationType.AttendeeInPerson
|
||||
? t("Address")
|
||||
: ""}
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<AttendeeInput<BookingFormValues>
|
||||
control={bookingForm.control}
|
||||
name="phone"
|
||||
bookingForm={bookingForm}
|
||||
name={
|
||||
selectedLocationType === LocationType.Phone
|
||||
? "phone"
|
||||
: selectedLocationType === LocationType.AttendeeInPerson
|
||||
? "attendeeAddress"
|
||||
: ""
|
||||
}
|
||||
placeholder={t(selectedLocation?.attendeeInputPlaceholder || "")}
|
||||
id="phone"
|
||||
id={
|
||||
selectedLocationType === LocationType.Phone
|
||||
? "phone"
|
||||
: selectedLocationType === LocationType.AttendeeInPerson
|
||||
? "attendeeAddress"
|
||||
: ""
|
||||
}
|
||||
required
|
||||
disabled={disableInput}
|
||||
/>
|
||||
|
|
|
@ -293,7 +293,10 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
|
|||
defaultValue={selection}
|
||||
options={
|
||||
booking
|
||||
? locationOptions.filter((location) => location.value !== "phone")
|
||||
? locationOptions.filter(
|
||||
(location) =>
|
||||
location.value !== "phone" && location.value !== "attendeeInPerson"
|
||||
)
|
||||
: locationOptions
|
||||
}
|
||||
isSearchable
|
||||
|
|
|
@ -4,8 +4,8 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { TextArea } from "@calcom/ui/form/fields";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
|
||||
interface IRescheduleDialog {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { InstallAppButtonWithoutPlanCheck } from "@calcom/app-store/components";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { App } from "@calcom/types/App";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
|
||||
interface ICalendarItem {
|
||||
title: string;
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import { DotsHorizontalIcon } from "@heroicons/react/solid";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Button } from "@calcom/ui/v2";
|
||||
|
||||
import { CalendarSwitch } from "./CalendarSwitch";
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ import { DEFAULT_SCHEDULE } from "@calcom/lib/availability";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc, TRPCClientErrorLike } from "@calcom/trpc/react";
|
||||
import { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { Form } from "@calcom/ui/form/fields";
|
||||
import { Button } from "@calcom/ui/v2";
|
||||
|
||||
interface ISetupAvailabilityProps {
|
||||
nextStep: () => void;
|
||||
|
|
|
@ -6,7 +6,9 @@ import { useForm } from "react-hook-form";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { User } from "@calcom/prisma/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button, showToast, TextArea } from "@calcom/ui/v2";
|
||||
import { Button } from "@calcom/ui/components";
|
||||
import { TextArea } from "@calcom/ui/components/form";
|
||||
import { showToast } from "@calcom/ui/v2";
|
||||
import ImageUploader from "@calcom/ui/v2/core/ImageUploader";
|
||||
|
||||
import { AvatarSSR } from "@components/ui/AvatarSSR";
|
||||
|
|
|
@ -6,8 +6,8 @@ import dayjs from "@calcom/dayjs";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { User } from "@calcom/prisma/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import TimezoneSelect from "@calcom/ui/form/TimezoneSelect";
|
||||
import { Button } from "@calcom/ui/v2";
|
||||
|
||||
import { UsernameAvailability } from "@components/ui/UsernameAvailability";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ReactNode } from "react";
|
||||
|
||||
import Badge from "@calcom/ui/Badge";
|
||||
import { Badge } from "@calcom/ui/components/badge";
|
||||
|
||||
function pluralize(opts: { num: number; plural: string; singular: string }) {
|
||||
if (opts.num === 0) {
|
||||
|
|
|
@ -5,8 +5,8 @@ import { ErrorCode } from "@calcom/lib/auth";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogContent } from "@calcom/ui/Dialog";
|
||||
import { PasswordField } from "@calcom/ui/components/form";
|
||||
import { Form, Label } from "@calcom/ui/form/fields";
|
||||
import { PasswordField } from "@calcom/ui/v2/core/form/fields";
|
||||
|
||||
import TwoFactor from "@components/auth/TwoFactor";
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Badge from "@calcom/ui/Badge";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Badge } from "@calcom/ui/components/badge";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
|
||||
const DisableUserImpersonation = ({ disableImpersonation }: { disableImpersonation: boolean }) => {
|
||||
|
|
|
@ -5,7 +5,7 @@ import { ErrorCode } from "@calcom/lib/auth";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogContent } from "@calcom/ui/Dialog";
|
||||
import { Form } from "@calcom/ui/v2/core/form/fields";
|
||||
import { Form } from "@calcom/ui/components/form";
|
||||
|
||||
import TwoFactor from "@components/auth/TwoFactor";
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useState } from "react";
|
||||
|
||||
import Badge from "@calcom/ui/Badge";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Badge } from "@calcom/ui/components/badge";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useMutation } from "@tanstack/react-query";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Icon } from "@calcom/ui";
|
||||
import Badge from "@calcom/ui/v2/core/Badge";
|
||||
import { Badge } from "@calcom/ui/components/badge";
|
||||
import Switch from "@calcom/ui/v2/core/Switch";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@ import { useForm } from "react-hook-form";
|
|||
|
||||
import { ErrorCode } from "@calcom/lib/auth";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { Form, Label } from "@calcom/ui/components/form";
|
||||
import { PasswordField } from "@calcom/ui/components/form";
|
||||
import { Dialog, DialogContent } from "@calcom/ui/v2/core/Dialog";
|
||||
import { Form, Label } from "@calcom/ui/v2/core/form/fields";
|
||||
import { PasswordField } from "@calcom/ui/v2/core/form/fields";
|
||||
|
||||
import TwoFactor from "@components/auth/TwoFactor";
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ import { useForm } from "react-hook-form";
|
|||
|
||||
import { ErrorCode } from "@calcom/lib/auth";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { Form, TextField } from "@calcom/ui/components/form";
|
||||
import { Dialog, DialogContent } from "@calcom/ui/v2/core/Dialog";
|
||||
import { Form, TextField } from "@calcom/ui/v2/core/form/fields";
|
||||
|
||||
import TwoFactor from "@components/auth/TwoFactor";
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Badge from "@calcom/ui/Badge";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Badge } from "@calcom/ui/components/badge";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
|
||||
/** @deprecated Use `packages/features/ee/teams/components/DisableTeamImpersonation.tsx` */
|
||||
|
|
|
@ -6,8 +6,8 @@ import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import ButtonGroup from "@calcom/ui/v2/core/ButtonGroup";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { ButtonGroup } from "@calcom/ui/components/buttonGroup";
|
||||
import ConfirmationDialogContent from "@calcom/ui/v2/core/ConfirmationDialogContent";
|
||||
import { Dialog, DialogTrigger } from "@calcom/ui/v2/core/Dialog";
|
||||
import Dropdown, {
|
||||
|
|
|
@ -2,7 +2,7 @@ import Link from "next/link";
|
|||
import { TeamPageProps } from "pages/team/[slug]";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { Avatar } from "@calcom/ui/v2";
|
||||
import { Avatar } from "@calcom/ui/components";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import Dropdown, {
|
|||
DropdownMenuPortal,
|
||||
} from "@calcom/ui/Dropdown";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
|
||||
import { SVGComponent } from "@lib/types/SVGComponent";
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@ import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
|||
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { Icon, StarIconSolid } from "@calcom/ui/Icon";
|
||||
import { Button } from "@calcom/ui/v2";
|
||||
import { Input, Label } from "@calcom/ui/v2";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { Input, Label } from "@calcom/ui/components/form";
|
||||
|
||||
export enum UsernameChangeStatusEnum {
|
||||
NORMAL = "NORMAL",
|
||||
|
|
|
@ -9,7 +9,7 @@ import { trpc } from "@calcom/trpc/react";
|
|||
import { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Input, Label, Button } from "@calcom/ui/v2";
|
||||
import { Button, Input, Label } from "@calcom/ui/components";
|
||||
|
||||
interface ICustomUsernameProps {
|
||||
currentUsername: string | undefined;
|
||||
|
|
|
@ -7,9 +7,11 @@ import classNames from "@calcom/lib/classNames";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { weekdayNames } from "@calcom/lib/weekday";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
|
||||
import { Icon } from "@calcom/ui";
|
||||
import { Badge } from "@calcom/ui/v2";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import { Badge } from "@calcom/ui/components/badge";
|
||||
import { Button } from "@calcom/ui/components/button";
|
||||
import { SettingsToggle } from "@calcom/ui/v2";
|
||||
import Select from "@calcom/ui/v2/core/form/select";
|
||||
import { SkeletonText } from "@calcom/ui/v2/core/skeleton";
|
||||
|
||||
|
@ -91,93 +93,119 @@ const AvailabilitySelect = ({
|
|||
);
|
||||
};
|
||||
|
||||
const format = (date: Date) =>
|
||||
Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "numeric" }).format(
|
||||
const format = (date: Date, hour12: boolean) =>
|
||||
Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "numeric", hour12 }).format(
|
||||
new Date(dayjs.utc(date).format("YYYY-MM-DDTHH:mm:ss"))
|
||||
);
|
||||
|
||||
export const AvailabilityTab = () => {
|
||||
export const AvailabilityTab = ({ isTeamEvent }: { isTeamEvent: boolean }) => {
|
||||
const { t, i18n } = useLocale();
|
||||
const { watch } = useFormContext<FormValues>();
|
||||
|
||||
const EventTypeSchedule = () => {
|
||||
const me = useMeQuery();
|
||||
const timeFormat = me?.data?.timeFormat;
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="min-w-4 mb-2">
|
||||
<label htmlFor="availability" className="mt-0 flex text-sm font-medium text-neutral-700">
|
||||
{t("availability")}
|
||||
</label>
|
||||
</div>
|
||||
<Controller
|
||||
name="schedule"
|
||||
render={({ field }) => (
|
||||
<AvailabilitySelect
|
||||
value={field.value}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
onChange={(selected) => {
|
||||
field.onChange(selected?.value || null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-4 rounded border p-4 py-6 pt-2 md:p-8">
|
||||
<ol className="table border-collapse text-sm">
|
||||
{weekdayNames(i18n.language, 1, "long").map((day, index) => {
|
||||
const isAvailable = !!filterDays(index).length;
|
||||
return (
|
||||
<li key={day} className="my-6 flex border-transparent last:mb-2">
|
||||
<span
|
||||
className={classNames(
|
||||
"w-20 font-medium sm:w-32",
|
||||
!isAvailable && "text-gray-500 opacity-50"
|
||||
)}>
|
||||
{day}
|
||||
</span>
|
||||
{isLoading ? (
|
||||
<SkeletonText className="block h-5 w-60" />
|
||||
) : isAvailable ? (
|
||||
<div className="space-y-3 text-right">
|
||||
{filterDays(index).map((dayRange, i) => (
|
||||
<div key={i} className="flex items-center leading-4">
|
||||
<span className="w-16 sm:w-28 sm:text-left">
|
||||
{format(dayRange.startTime, timeFormat === 12)}
|
||||
</span>
|
||||
<span className="ml-4">-</span>
|
||||
<div className="ml-6">{format(dayRange.endTime, timeFormat === 12)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="ml-6 text-gray-500 opacity-50 sm:ml-0">{t("unavailable")}</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
<hr />
|
||||
<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">
|
||||
<Icon.FiGlobe className="mr-2" />
|
||||
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
|
||||
</span>
|
||||
<Button
|
||||
href={`/availability/${schedule?.schedule.id}`}
|
||||
color="minimal"
|
||||
EndIcon={Icon.FiExternalLink}
|
||||
target="_blank"
|
||||
className="justify-center border sm:border-0"
|
||||
rel="noopener noreferrer">
|
||||
{t("edit_availability")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const scheduleId = watch("schedule");
|
||||
const { isLoading, data: schedule } = trpc.useQuery(["viewer.availability.schedule", { scheduleId }]);
|
||||
|
||||
const filterDays = (dayNum: number) =>
|
||||
schedule?.schedule.availability.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div className="min-w-4 mb-2">
|
||||
<label htmlFor="availability" className="mt-0 flex text-sm font-medium text-neutral-700">
|
||||
{t("availability")}
|
||||
</label>
|
||||
</div>
|
||||
<Controller
|
||||
name="schedule"
|
||||
render={({ field }) => (
|
||||
<AvailabilitySelect
|
||||
value={field.value}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
onChange={(selected) => {
|
||||
field.onChange(selected?.value || null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
if (!isTeamEvent) {
|
||||
return <EventTypeSchedule />;
|
||||
}
|
||||
|
||||
<div className="space-y-4 rounded border p-4 py-6 pt-2 md:p-8">
|
||||
<ol className="table border-collapse text-sm">
|
||||
{weekdayNames(i18n.language, 1, "long").map((day, index) => {
|
||||
const isAvailable = !!filterDays(index).length;
|
||||
return (
|
||||
<li key={day} className="my-6 flex border-transparent last:mb-2">
|
||||
<span
|
||||
className={classNames(
|
||||
"w-20 font-medium sm:w-32",
|
||||
!isAvailable && "text-gray-500 opacity-50"
|
||||
)}>
|
||||
{day}
|
||||
</span>
|
||||
{isLoading ? (
|
||||
<SkeletonText className="block h-5 w-60" />
|
||||
) : isAvailable ? (
|
||||
<div className="space-y-3 text-right">
|
||||
{filterDays(index).map((dayRange, i) => (
|
||||
<div key={i} className="flex items-center leading-4">
|
||||
<span className="w-16 sm:w-28 sm:text-left">{format(dayRange.startTime)}</span>
|
||||
<span className="ml-4">-</span>
|
||||
<div className="ml-6">{format(dayRange.endTime)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="ml-6 text-gray-500 opacity-50 sm:ml-0">{t("unavailable")}</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
<hr />
|
||||
<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">
|
||||
<Icon.FiGlobe className="mr-2" />
|
||||
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
|
||||
</span>
|
||||
<Button
|
||||
href={`/availability/${schedule?.schedule.id}`}
|
||||
color="minimal"
|
||||
EndIcon={Icon.FiExternalLink}
|
||||
target="_blank"
|
||||
className="justify-center border sm:border-0"
|
||||
rel="noopener noreferrer">
|
||||
{t("edit_availability")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
return (
|
||||
<Controller
|
||||
name="metadata.config.useHostSchedulesForTeamEvent"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<SettingsToggle
|
||||
checked={!value}
|
||||
onCheckedChange={(checked) => {
|
||||
onChange(!checked);
|
||||
}}
|
||||
title={t("choose_common_schedule_team_event")}
|
||||
description={t("choose_common_schedule_team_event_description")}>
|
||||
<EventTypeSchedule />
|
||||
</SettingsToggle>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue