Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17db644be1 | ||
|
|
e4037be79a | ||
|
|
ebe6a60366 | ||
|
|
fa9c6ea0ef | ||
|
|
9b22f44cba | ||
|
|
c84f75df99 | ||
|
|
8f9a4fc78f | ||
|
|
7b30ade284 | ||
|
|
24a4646fdc | ||
|
|
0860036607 | ||
|
|
d080a0ef2d | ||
|
|
a6eab74fd1 | ||
|
|
0a2e275908 | ||
|
|
3b05ebc5b5 | ||
|
|
e5b4d2fb80 | ||
|
|
00a2a3d8a8 | ||
|
|
30ba168fc3 | ||
|
|
12eec186b2 | ||
|
|
4417d649ea | ||
|
|
7058da70e6 | ||
|
|
2cb383c337 | ||
|
|
fe5eca8eb3 | ||
|
|
7c6468953a | ||
|
|
28a0553d18 | ||
|
|
ffcb04fa99 | ||
|
|
5ca216b922 | ||
|
|
0abff9f562 | ||
|
|
942dc38b58 | ||
|
|
827d8995a1 | ||
|
|
850e222f0e | ||
|
|
ede2357203 | ||
|
|
559ab4192f | ||
|
|
3c8c6d0574 | ||
|
|
2ae2c15083 | ||
|
|
313bb89789 | ||
|
|
95e037c664 | ||
|
|
da026d909d | ||
|
|
c6b50bfd14 | ||
|
|
30b433397a | ||
|
|
ff1aefe539 | ||
|
|
5685d343b1 | ||
|
|
ae3f30fabf | ||
|
|
c11e76bac9 | ||
|
|
eed34559b5 | ||
|
|
bf958c82da | ||
|
|
728a386bf0 | ||
|
|
41efb5ea04 | ||
|
|
b268540d35 | ||
|
|
bdd1d3a99d | ||
|
|
dd7673b1f9 | ||
|
|
29f37a7e98 | ||
|
|
3983351dac | ||
|
|
01322431fc | ||
|
|
dcd06b0663 | ||
|
|
26e50d24b0 | ||
|
|
74803dc122 | ||
|
|
de65b85862 | ||
|
|
0ee56be1d8 | ||
|
|
de6f7dcfc0 | ||
|
|
956f2c7a2b | ||
|
|
14de0a1d62 | ||
|
|
51cca7481f | ||
|
|
3fe0280dee | ||
|
|
876fc188d4 | ||
|
|
d1810d9f14 | ||
|
|
a25cd43ddf | ||
|
|
833a91375e | ||
|
|
8124627cc5 | ||
|
|
749747ad7e | ||
|
|
967f70a5ec | ||
|
|
51c9b94fb5 | ||
|
|
1976dd15c3 | ||
|
|
03671e2a58 | ||
|
|
5e5f223908 | ||
|
|
9076a7eb42 | ||
|
|
a9e50d76ca | ||
|
|
bd4722554d | ||
|
|
105d373b8e | ||
|
|
df91dacc3f | ||
|
|
396b81af58 | ||
|
|
8467a9c53b | ||
|
|
e99e8b169a | ||
|
|
7b8902a769 | ||
|
|
2408338ed4 | ||
|
|
8f0ff58bcf | ||
|
|
fac47544d1 | ||
|
|
b09f3e5c8d | ||
|
|
8bb051ac4e | ||
|
|
fd3fffd7dd | ||
|
|
312a3f2543 | ||
|
|
f52f2f32fa | ||
|
|
fdd0a5a03d | ||
|
|
ca3e3600f7 | ||
|
|
ba944b471a | ||
|
|
7978f0317a | ||
|
|
d918f34ae8 | ||
|
|
1fed08fa9b | ||
|
|
aabfd3b3f6 | ||
|
|
60e0bbf4b7 | ||
|
|
0a7f65cd9b | ||
|
|
f41f4b01a9 | ||
|
|
a7b38b1b58 | ||
|
|
a0f1ceba19 | ||
|
|
5c6e104ff5 | ||
|
|
29b1f7bf51 | ||
|
|
7ec3d04acc | ||
|
|
13a765e50a | ||
|
|
18f637d94c | ||
|
|
e5eb121ab7 | ||
|
|
6612926aa5 | ||
|
|
93dc1247f1 | ||
|
|
24607873c0 | ||
|
|
8b37d91108 | ||
|
|
22b0644700 | ||
|
|
404a7c1550 | ||
|
|
19a2cd5ae4 | ||
|
|
0798e2940a | ||
|
|
b1bf94aa58 | ||
|
|
0a921c2929 | ||
|
|
67c318e527 | ||
|
|
41b8d2ab1a | ||
|
|
57124b48fe | ||
|
|
b76a2a4019 | ||
|
|
a28b9cacd2 | ||
|
|
abd90f6af8 | ||
|
|
0bdc45a1a5 | ||
|
|
65d9704f2b | ||
|
|
d8ba783369 | ||
|
|
782127a993 | ||
|
|
fbc3f7b51f | ||
|
|
619ccf4065 | ||
|
|
40d8f34e8d | ||
|
|
9c1e1d7312 | ||
|
|
710a1a7d38 | ||
|
|
f25605ef4d | ||
|
|
684575d0a4 | ||
|
|
bc6267e99b | ||
|
|
9901c91e9b | ||
|
|
dfe03efc37 | ||
|
|
0eabb56f07 | ||
|
|
cdd066c653 | ||
|
|
026f22fbaf | ||
|
|
5950c5a756 | ||
|
|
cd9d16be3e | ||
|
|
993f92acba | ||
|
|
6f110d21fd | ||
|
|
0df6777814 | ||
|
|
5f14cd31d1 | ||
|
|
070ec326aa | ||
|
|
4ca79af13f | ||
|
|
a2e70f9aad | ||
|
|
f186d2e41d | ||
|
|
aaeea250bc | ||
|
|
96af17d8d7 | ||
|
|
dfaa6d28e4 | ||
|
|
6a2de993bb | ||
|
|
b69d885ee6 | ||
|
|
d001c4e2ae | ||
|
|
558f1c9478 | ||
|
|
74748a6183 | ||
|
|
986f17f54b |
No files matched your search
@@ -138,6 +138,11 @@ FORMBRICKS_HOST_URL=https://app.formbricks.com
|
|||||||
FORMBRICKS_ENVIRONMENT_ID=
|
FORMBRICKS_ENVIRONMENT_ID=
|
||||||
FORMBRICKS_FEEDBACK_SURVEY_ID=
|
FORMBRICKS_FEEDBACK_SURVEY_ID=
|
||||||
|
|
||||||
|
# AvatarAPI
|
||||||
|
# Used to pre-fill avatar during signup
|
||||||
|
AVATARAPI_USERNAME=
|
||||||
|
AVATARAPI_PASSWORD=
|
||||||
|
|
||||||
# Twilio
|
# Twilio
|
||||||
# Used to send SMS reminders in workflows
|
# Used to send SMS reminders in workflows
|
||||||
TWILIO_SID=
|
TWILIO_SID=
|
||||||
@@ -231,6 +236,8 @@ CSP_POLICY=
|
|||||||
EDGE_CONFIG=
|
EDGE_CONFIG=
|
||||||
|
|
||||||
NEXT_PUBLIC_MINUTES_TO_BOOK=5 # Minutes
|
NEXT_PUBLIC_MINUTES_TO_BOOK=5 # Minutes
|
||||||
|
NEXT_PUBLIC_BOOKER_NUMBER_OF_DAYS_TO_LOAD=0 # Override the booker to only load X number of days worth of data
|
||||||
|
|
||||||
# Control time intervals on a user's Schedule availability
|
# Control time intervals on a user's Schedule availability
|
||||||
NEXT_PUBLIC_AVAILABILITY_SCHEDULE_INTERVAL=
|
NEXT_PUBLIC_AVAILABILITY_SCHEDULE_INTERVAL=
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ jobs:
|
|||||||
shard: [1, 2]
|
shard: [1, 2]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
- uses: ./.github/actions/dangerous-git-checkout
|
||||||
- uses: ./.github/actions/yarn-install
|
- uses: ./.github/actions/yarn-install
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ jobs:
|
|||||||
shard: [1, 2]
|
shard: [1, 2]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
- uses: ./.github/actions/dangerous-git-checkout
|
||||||
- uses: ./.github/actions/yarn-install
|
- uses: ./.github/actions/yarn-install
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ jobs:
|
|||||||
shard: [1, 2]
|
shard: [1, 2]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
- uses: ./.github/actions/dangerous-git-checkout
|
||||||
- uses: ./.github/actions/yarn-install
|
- uses: ./.github/actions/yarn-install
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
shard: [1, 2, 3, 4, 5]
|
shard: [1, 2, 3, 4, 5]
|
||||||
steps:
|
steps:
|
||||||
|
- uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/dangerous-git-checkout
|
- uses: ./.github/actions/dangerous-git-checkout
|
||||||
- uses: ./.github/actions/yarn-install
|
- uses: ./.github/actions/yarn-install
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.EQUITY_BEE_TEAM_LABELER_ACTION_TOKEN }}
|
repo-token: ${{ secrets.EQUITY_BEE_TEAM_LABELER_ACTION_TOKEN }}
|
||||||
organization-name: calcom
|
organization-name: calcom
|
||||||
ignore-labels: "app-store, ai, authentication, automated-testing, platform, billing, bookings, caldav, calendar-apps, ci, console, crm-apps, docs, documentation, emails, embeds, event-types, i18n, impersonation, manual-testing, ui, performance, ops-stack, organizations, public-api, routing-forms, seats, teams, webhooks, workflows, zapier"
|
ignore-labels: "admin, app-store, ai, authentication, automated-testing, devops, platform, billing, bookings, caldav, calendar-apps, ci, console, crm-apps, docs, documentation, emails, embeds, event-types, i18n, impersonation, manual-testing, ui, performance, ops-stack, organizations, public-api, routing-forms, seats, teams, webhooks, workflows, zapier"
|
||||||
apply-labels-from-issue:
|
apply-labels-from-issue:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
|||||||
@@ -62,18 +62,18 @@ jobs:
|
|||||||
|
|
||||||
- name: Get comment body
|
- name: Get comment body
|
||||||
id: get-comment-body
|
id: get-comment-body
|
||||||
if: success()
|
if: success() && github.event.number
|
||||||
run: |
|
run: |
|
||||||
cd apps/web
|
cd apps/web
|
||||||
body=$(cat .next/analyze/__bundle_analysis_comment.txt)
|
body=$(cat .next/analyze/__bundle_analysis_comment.txt)
|
||||||
body="${body//'%'/'%25'}"
|
body="${body//'%'/'%25'}"
|
||||||
body="${body//$'\n'/'%0A'}"
|
body="${body//$'\n'/'%0A'}"
|
||||||
body="${body//$'\r'/'%0D'}"
|
body="${body//$'\r'/'%0D'}"
|
||||||
echo "{name}={$body}" >> $GITHUB_OUTPUT
|
echo ::set-output name=body::$body
|
||||||
|
|
||||||
- name: Find Comment
|
- name: Find Comment
|
||||||
uses: peter-evans/find-comment@v2
|
uses: peter-evans/find-comment@v2
|
||||||
if: success()
|
if: success() && github.event.number
|
||||||
id: fc
|
id: fc
|
||||||
with:
|
with:
|
||||||
issue-number: ${{ github.event.number }}
|
issue-number: ${{ github.event.number }}
|
||||||
|
|||||||
@@ -448,7 +448,7 @@ Don't code but still want to contribute? Join our [discord](https://go.cal.com/d
|
|||||||
3. Enable the selected API.
|
3. Enable the selected API.
|
||||||
4. Next, go to the [OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) from the side pane. Select the app type (Internal or External) and enter the basic app details on the first page.
|
4. Next, go to the [OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) from the side pane. Select the app type (Internal or External) and enter the basic app details on the first page.
|
||||||
5. In the second page on Scopes, select Add or Remove Scopes. Search for Calendar.event and select the scope with scope value `.../auth/calendar.events`, `.../auth/calendar.readonly` and select Update.
|
5. In the second page on Scopes, select Add or Remove Scopes. Search for Calendar.event and select the scope with scope value `.../auth/calendar.events`, `.../auth/calendar.readonly` and select Update.
|
||||||
6. In the third page (Test Users), add the Google account(s) you'll using. Make sure the details are correct on the last page of the wizard and your consent screen will be configured.
|
6. In the third page (Test Users), add the Google account(s) you'll be using. Make sure the details are correct on the last page of the wizard and your consent screen will be configured.
|
||||||
7. Now select [Credentials](https://console.cloud.google.com/apis/credentials) from the side pane and then select Create Credentials. Select the OAuth Client ID option.
|
7. Now select [Credentials](https://console.cloud.google.com/apis/credentials) from the side pane and then select Create Credentials. Select the OAuth Client ID option.
|
||||||
8. Select Web Application as the Application Type.
|
8. Select Web Application as the Application Type.
|
||||||
9. Under Authorized redirect URI's, select Add URI and then add the URI `<Cal.com URL>/api/integrations/googlecalendar/callback` and `<Cal.com URL>/api/auth/callback/google` replacing Cal.com URL with the URI at which your application runs.
|
9. Under Authorized redirect URI's, select Add URI and then add the URI `<Cal.com URL>/api/integrations/googlecalendar/callback` and `<Cal.com URL>/api/auth/callback/google` replacing Cal.com URL with the URI at which your application runs.
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ const fetchBookings = async ({
|
|||||||
const params = {
|
const params = {
|
||||||
apiKey,
|
apiKey,
|
||||||
userId: userId.toString(),
|
userId: userId.toString(),
|
||||||
|
dateFrom: from,
|
||||||
|
dateTo: to,
|
||||||
};
|
};
|
||||||
|
|
||||||
const urlParams = new URLSearchParams(params);
|
const urlParams = new URLSearchParams(params);
|
||||||
@@ -40,11 +42,9 @@ const fetchBookings = async ({
|
|||||||
|
|
||||||
const bookings: Booking[] = data.bookings
|
const bookings: Booking[] = data.bookings
|
||||||
.filter((booking: Booking) => {
|
.filter((booking: Booking) => {
|
||||||
const afterFrom = new Date(booking.startTime).getTime() > new Date(from).getTime();
|
|
||||||
const beforeTo = new Date(booking.endTime).getTime() < new Date(to).getTime();
|
|
||||||
const notCancelled = booking.status !== BOOKING_STATUS.CANCELLED;
|
const notCancelled = booking.status !== BOOKING_STATUS.CANCELLED;
|
||||||
|
|
||||||
return afterFrom && beforeTo && notCancelled;
|
return notCancelled;
|
||||||
})
|
})
|
||||||
.map(({ endTime, eventTypeId, id, startTime, status, title }: Booking) => ({
|
.map(({ endTime, eventTypeId, id, startTime, status, title }: Booking) => ({
|
||||||
endTime,
|
endTime,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import type { User, UserList } from "../types/user";
|
|||||||
import type { WorkingHours } from "../types/workingHours";
|
import type { WorkingHours } from "../types/workingHours";
|
||||||
import now from "./now";
|
import now from "./now";
|
||||||
|
|
||||||
const gptModel = "gpt-4";
|
const gptModel = "gpt-4-0613";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core of the Cal.ai booking agent: a LangChain Agent Executor.
|
* Core of the Cal.ai booking agent: a LangChain Agent Executor.
|
||||||
@@ -58,7 +58,14 @@ If you can't find a referenced user, ask the user for their email or @username.
|
|||||||
|
|
||||||
The primary user's id is: ${userId}
|
The primary user's id is: ${userId}
|
||||||
The primary user's username is: ${user.username}
|
The primary user's username is: ${user.username}
|
||||||
The current time in the primary user's timezone is: ${now(user.timeZone)}
|
The current time in the primary user's timezone is: ${now(user.timeZone, {
|
||||||
|
weekday: "long",
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
})}
|
||||||
The primary user's time zone is: ${user.timeZone}
|
The primary user's time zone is: ${user.timeZone}
|
||||||
The primary user's event types are: ${user.eventTypes
|
The primary user's event types are: ${user.eventTypes
|
||||||
.map((e: EventType) => `ID: ${e.id}, Slug: ${e.slug}, Title: ${e.title}, Length: ${e.length};`)
|
.map((e: EventType) => `ID: ${e.id}, Slug: ${e.slug}, Title: ${e.title}, Length: ${e.length};`)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export default function now(timeZone: string) {
|
export default function now(timeZone: string, options: Intl.DateTimeFormatOptions = {}) {
|
||||||
return new Date().toLocaleString("en-US", {
|
return new Date().toLocaleString("en-US", {
|
||||||
timeZone,
|
timeZone,
|
||||||
|
...options,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,12 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": [
|
||||||
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
"../../packages/types/window.d.ts"
|
||||||
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,11 @@ const schemaBookingBaseBodyParams = Booking.pick({
|
|||||||
|
|
||||||
export const schemaBookingCreateBodyParams = extendedBookingCreateBody.merge(schemaQueryUserId.partial());
|
export const schemaBookingCreateBodyParams = extendedBookingCreateBody.merge(schemaQueryUserId.partial());
|
||||||
|
|
||||||
|
export const schemaBookingGetParams = z.object({
|
||||||
|
dateFrom: iso8601.optional(),
|
||||||
|
dateTo: iso8601.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
const schemaBookingEditParams = z
|
const schemaBookingEditParams = z
|
||||||
.object({
|
.object({
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
@@ -28,7 +33,9 @@ const schemaBookingEditParams = z
|
|||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
export const schemaBookingEditBodyParams = schemaBookingBaseBodyParams.merge(schemaBookingEditParams);
|
export const schemaBookingEditBodyParams = schemaBookingBaseBodyParams
|
||||||
|
.merge(schemaBookingEditParams)
|
||||||
|
.omit({ uid: true });
|
||||||
|
|
||||||
export const schemaBookingReadPublic = Booking.extend({
|
export const schemaBookingReadPublic = Booking.extend({
|
||||||
attendees: z
|
attendees: z
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export const membershipEditBodySchema = Membership.omit({
|
|||||||
/** To avoid complication, let's avoid updating these, instead you can delete and create a new invite */
|
/** To avoid complication, let's avoid updating these, instead you can delete and create a new invite */
|
||||||
teamId: true,
|
teamId: true,
|
||||||
userId: true,
|
userId: true,
|
||||||
|
id: true,
|
||||||
})
|
})
|
||||||
.partial({
|
.partial({
|
||||||
accepted: true,
|
accepted: true,
|
||||||
|
|||||||
@@ -5,14 +5,23 @@ import { _TeamModel as Team } from "@calcom/prisma/zod";
|
|||||||
export const schemaTeamBaseBodyParams = Team.omit({ id: true, createdAt: true }).partial({
|
export const schemaTeamBaseBodyParams = Team.omit({ id: true, createdAt: true }).partial({
|
||||||
hideBranding: true,
|
hideBranding: true,
|
||||||
metadata: true,
|
metadata: true,
|
||||||
|
pendingPayment: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const schemaTeamRequiredParams = z.object({});
|
const schemaTeamRequiredParams = z.object({
|
||||||
|
name: z.string().max(255),
|
||||||
|
});
|
||||||
|
|
||||||
export const schemaTeamBodyParams = schemaTeamBaseBodyParams.merge(schemaTeamRequiredParams).strict();
|
export const schemaTeamBodyParams = schemaTeamBaseBodyParams.merge(schemaTeamRequiredParams).strict();
|
||||||
|
|
||||||
export const schemaTeamUpdateBodyParams = schemaTeamBodyParams.partial();
|
export const schemaTeamUpdateBodyParams = schemaTeamBodyParams.partial();
|
||||||
|
|
||||||
|
const schemaOwnerId = z.object({
|
||||||
|
ownerId: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const schemaTeamCreateBodyParams = schemaTeamBodyParams.merge(schemaOwnerId).strict();
|
||||||
|
|
||||||
export const schemaTeamReadPublic = Team.omit({});
|
export const schemaTeamReadPublic = Team.omit({});
|
||||||
|
|
||||||
export const schemaTeamsReadPublic = z.array(schemaTeamReadPublic);
|
export const schemaTeamsReadPublic = z.array(schemaTeamReadPublic);
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"@sentry/nextjs": "^7.73.0",
|
"@sentry/nextjs": "^7.73.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
"next": "^13.4.6",
|
"next": "^13.5.4",
|
||||||
"next-api-middleware": "^1.0.1",
|
"next-api-middleware": "^1.0.1",
|
||||||
"next-axiom": "^0.17.0",
|
"next-axiom": "^0.17.0",
|
||||||
"next-swagger-doc": "^0.3.6",
|
"next-swagger-doc": "^0.3.6",
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransform
|
|||||||
* type: boolean
|
* type: boolean
|
||||||
* description: Delete all remaining bookings
|
* description: Delete all remaining bookings
|
||||||
* - in: query
|
* - in: query
|
||||||
* name: reason
|
* name: cancellationReason
|
||||||
* required: false
|
* required: false
|
||||||
* schema:
|
* schema:
|
||||||
* type: string
|
* type: string
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { NextApiRequest } from "next";
|
|||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import { defaultResponder } from "@calcom/lib/server";
|
import { defaultResponder } from "@calcom/lib/server";
|
||||||
|
|
||||||
import { schemaBookingReadPublic } from "~/lib/validations/booking";
|
import { schemaBookingGetParams, schemaBookingReadPublic } from "~/lib/validations/booking";
|
||||||
import { schemaQuerySingleOrMultipleAttendeeEmails } from "~/lib/validations/shared/queryAttendeeEmail";
|
import { schemaQuerySingleOrMultipleAttendeeEmails } from "~/lib/validations/shared/queryAttendeeEmail";
|
||||||
import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/queryUserId";
|
import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/queryUserId";
|
||||||
|
|
||||||
@@ -119,13 +119,13 @@ import { schemaQuerySingleOrMultipleUserIds } from "~/lib/validations/shared/que
|
|||||||
* @returns An object that represents the WHERE clause for the findMany/findUnique operation.
|
* @returns An object that represents the WHERE clause for the findMany/findUnique operation.
|
||||||
*/
|
*/
|
||||||
function buildWhereClause(
|
function buildWhereClause(
|
||||||
userId: number,
|
userId: number | null,
|
||||||
attendeeEmails: string[],
|
attendeeEmails: string[],
|
||||||
userIds: number[] = [],
|
userIds: number[] = [],
|
||||||
userEmails: string[] = []
|
userEmails: string[] = []
|
||||||
) {
|
) {
|
||||||
const filterByAttendeeEmails = attendeeEmails.length > 0;
|
const filterByAttendeeEmails = attendeeEmails.length > 0;
|
||||||
const userFilter = userIds.length > 0 ? { userId: { in: userIds } } : { userId };
|
const userFilter = userIds.length > 0 ? { userId: { in: userIds } } : !!userId ? { userId } : {};
|
||||||
let whereClause = {};
|
let whereClause = {};
|
||||||
if (filterByAttendeeEmails) {
|
if (filterByAttendeeEmails) {
|
||||||
whereClause = {
|
whereClause = {
|
||||||
@@ -162,6 +162,9 @@ function buildWhereClause(
|
|||||||
|
|
||||||
async function handler(req: NextApiRequest) {
|
async function handler(req: NextApiRequest) {
|
||||||
const { userId, isAdmin, prisma } = req;
|
const { userId, isAdmin, prisma } = req;
|
||||||
|
|
||||||
|
const { dateFrom, dateTo } = schemaBookingGetParams.parse(req.query);
|
||||||
|
|
||||||
const args: Prisma.BookingFindManyArgs = {};
|
const args: Prisma.BookingFindManyArgs = {};
|
||||||
args.include = {
|
args.include = {
|
||||||
attendees: true,
|
attendees: true,
|
||||||
@@ -189,7 +192,7 @@ async function handler(req: NextApiRequest) {
|
|||||||
const userEmails = users.map((u) => u.email);
|
const userEmails = users.map((u) => u.email);
|
||||||
args.where = buildWhereClause(userId, attendeeEmails, userIds, userEmails);
|
args.where = buildWhereClause(userId, attendeeEmails, userIds, userEmails);
|
||||||
} else if (filterByAttendeeEmails) {
|
} else if (filterByAttendeeEmails) {
|
||||||
args.where = buildWhereClause(userId, attendeeEmails, [], []);
|
args.where = buildWhereClause(null, attendeeEmails, [], []);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
@@ -203,6 +206,20 @@ async function handler(req: NextApiRequest) {
|
|||||||
}
|
}
|
||||||
args.where = buildWhereClause(userId, attendeeEmails, [], []);
|
args.where = buildWhereClause(userId, attendeeEmails, [], []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dateFrom) {
|
||||||
|
args.where = {
|
||||||
|
...args.where,
|
||||||
|
startTime: { gte: dateFrom },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (dateTo) {
|
||||||
|
args.where = {
|
||||||
|
...args.where,
|
||||||
|
endTime: { lte: dateTo },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const data = await prisma.booking.findMany(args);
|
const data = await prisma.booking.findMany(args);
|
||||||
return { bookings: data.map((booking) => schemaBookingReadPublic.parse(booking)) };
|
return { bookings: data.map((booking) => schemaBookingReadPublic.parse(booking)) };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import prismock from "../../../../../tests/libs/__mocks__/prisma";
|
||||||
|
|
||||||
|
import type { Request, Response } from "express";
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import { createMocks } from "node-mocks-http";
|
||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
|
||||||
|
import dayjs from "@calcom/dayjs";
|
||||||
|
|
||||||
|
import handler from "./_get";
|
||||||
|
|
||||||
|
type CustomNextApiRequest = NextApiRequest & Request;
|
||||||
|
type CustomNextApiResponse = NextApiResponse & Response;
|
||||||
|
|
||||||
|
function buildMockData() {
|
||||||
|
prismock.user.create({
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
username: "test",
|
||||||
|
name: "Test User",
|
||||||
|
email: "test@example.com",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
prismock.eventType.create({
|
||||||
|
data: {
|
||||||
|
id: 1,
|
||||||
|
slug: "test",
|
||||||
|
length: 30,
|
||||||
|
title: "Test Event Type",
|
||||||
|
userId: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("GET /api/slots", () => {
|
||||||
|
describe("Errors", () => {
|
||||||
|
test("Missing required data", async () => {
|
||||||
|
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
|
||||||
|
await handler(req, res);
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(400);
|
||||||
|
expect(JSON.parse(res._getData())).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"message": "invalid_type in 'startTime': Required; invalid_type in 'endTime': Required",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Success", () => {
|
||||||
|
describe("Regular event-type", () => {
|
||||||
|
test("Returns and event type available slots", async () => {
|
||||||
|
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||||
|
method: "GET",
|
||||||
|
query: {
|
||||||
|
eventTypeId: 1,
|
||||||
|
startTime: dayjs().format(),
|
||||||
|
endTime: dayjs().add(1, "day").format(),
|
||||||
|
usernameList: "test",
|
||||||
|
},
|
||||||
|
prisma: prismock,
|
||||||
|
});
|
||||||
|
buildMockData();
|
||||||
|
await handler(req, res);
|
||||||
|
console.log({ statusCode: res._getStatusCode(), data: JSON.parse(res._getData()) });
|
||||||
|
expect(JSON.parse(res._getData())).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"slots": {},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
test("Returns and event type available slots with passed timeZone", async () => {
|
||||||
|
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||||
|
method: "GET",
|
||||||
|
query: {
|
||||||
|
eventTypeId: 1,
|
||||||
|
startTime: dayjs().format(),
|
||||||
|
endTime: dayjs().add(1, "day").format(),
|
||||||
|
usernameList: "test",
|
||||||
|
timeZone: "UTC",
|
||||||
|
},
|
||||||
|
prisma: prismock,
|
||||||
|
});
|
||||||
|
buildMockData();
|
||||||
|
await handler(req, res);
|
||||||
|
console.log({ statusCode: res._getStatusCode(), data: JSON.parse(res._getData()) });
|
||||||
|
expect(JSON.parse(res._getData())).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"slots": {},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -22,7 +22,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
const { usernameList, ...rest } = req.query;
|
const { usernameList, ...rest } = req.query;
|
||||||
let slugs = usernameList;
|
let slugs = usernameList;
|
||||||
if (!Array.isArray(usernameList)) {
|
if (!Array.isArray(usernameList)) {
|
||||||
slugs = usernameList ? [usernameList] : [];
|
slugs = usernameList ? [usernameList] : undefined;
|
||||||
}
|
}
|
||||||
const input = getScheduleSchema.parse({ usernameList: slugs, ...rest });
|
const input = getScheduleSchema.parse({ usernameList: slugs, ...rest });
|
||||||
const timeZoneSupported = input.timeZone ? isSupportedTimeZone(input.timeZone) : false;
|
const timeZoneSupported = input.timeZone ? isSupportedTimeZone(input.timeZone) : false;
|
||||||
@@ -38,9 +38,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
: availableSlots;
|
: availableSlots.slots;
|
||||||
|
|
||||||
return slotsInProvidedTimeZone;
|
return { slots: slotsInProvidedTimeZone };
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
if (cause instanceof TRPCError) {
|
if (cause instanceof TRPCError) {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export async function patchHandler(req: NextApiRequest) {
|
|||||||
const { prisma, body, userId } = req;
|
const { prisma, body, userId } = req;
|
||||||
const data = schemaTeamUpdateBodyParams.parse(body);
|
const data = schemaTeamUpdateBodyParams.parse(body);
|
||||||
const { teamId } = schemaQueryTeamId.parse(req.query);
|
const { teamId } = schemaQueryTeamId.parse(req.query);
|
||||||
|
|
||||||
/** Only OWNERS and ADMINS can edit teams */
|
/** Only OWNERS and ADMINS can edit teams */
|
||||||
const _team = await prisma.team.findFirst({
|
const _team = await prisma.team.findFirst({
|
||||||
include: { members: true },
|
include: { members: true },
|
||||||
@@ -65,6 +66,18 @@ export async function patchHandler(req: NextApiRequest) {
|
|||||||
});
|
});
|
||||||
if (!_team) throw new HttpError({ statusCode: 401, message: "Unauthorized: OWNER or ADMIN required" });
|
if (!_team) throw new HttpError({ statusCode: 401, message: "Unauthorized: OWNER or ADMIN required" });
|
||||||
|
|
||||||
|
const slugAlreadyExists = await prisma.team.findFirst({
|
||||||
|
where: {
|
||||||
|
slug: {
|
||||||
|
mode: "insensitive",
|
||||||
|
equals: data.slug,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (slugAlreadyExists && data.slug !== _team.slug)
|
||||||
|
throw new HttpError({ statusCode: 409, message: "Team slug already exists" });
|
||||||
|
|
||||||
// Check if parentId is related to this user
|
// Check if parentId is related to this user
|
||||||
if (data.parentId && data.parentId === teamId) {
|
if (data.parentId && data.parentId === teamId) {
|
||||||
throw new HttpError({
|
throw new HttpError({
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import type { NextApiRequest } from "next";
|
import type { NextApiRequest } from "next";
|
||||||
|
|
||||||
import { IS_TEAM_BILLING_ENABLED } from "@calcom/lib/constants";
|
import { getStripeCustomerIdFromUserId } from "@calcom/app-store/stripepayment/lib/customer";
|
||||||
|
import stripe from "@calcom/app-store/stripepayment/lib/server";
|
||||||
|
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
import { defaultResponder } from "@calcom/lib/server";
|
import { defaultResponder } from "@calcom/lib/server";
|
||||||
import { MembershipRole } from "@calcom/prisma/enums";
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
import { schemaMembershipPublic } from "~/lib/validations/membership";
|
import { schemaMembershipPublic } from "~/lib/validations/membership";
|
||||||
import { schemaTeamBodyParams, schemaTeamReadPublic } from "~/lib/validations/team";
|
import { schemaTeamCreateBodyParams, schemaTeamReadPublic } from "~/lib/validations/team";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @swagger
|
* @swagger
|
||||||
@@ -31,6 +33,12 @@ import { schemaTeamBodyParams, schemaTeamReadPublic } from "~/lib/validations/te
|
|||||||
* required:
|
* required:
|
||||||
* - name
|
* - name
|
||||||
* - slug
|
* - slug
|
||||||
|
* - hideBookATeamMember
|
||||||
|
* - brandColor
|
||||||
|
* - darkBrandColor
|
||||||
|
* - timeZone
|
||||||
|
* - weekStart
|
||||||
|
* - isPrivate
|
||||||
* properties:
|
* properties:
|
||||||
* name:
|
* name:
|
||||||
* type: string
|
* type: string
|
||||||
@@ -38,6 +46,27 @@ import { schemaTeamBodyParams, schemaTeamReadPublic } from "~/lib/validations/te
|
|||||||
* slug:
|
* slug:
|
||||||
* type: string
|
* type: string
|
||||||
* description: A unique slug that works as path for the team public page
|
* description: A unique slug that works as path for the team public page
|
||||||
|
* hideBookATeamMember:
|
||||||
|
* type: boolean
|
||||||
|
* description: Flag to hide or show the book a team member option
|
||||||
|
* brandColor:
|
||||||
|
* type: string
|
||||||
|
* description: Primary brand color for the team
|
||||||
|
* darkBrandColor:
|
||||||
|
* type: string
|
||||||
|
* description: Dark variant of the primary brand color for the team
|
||||||
|
* timeZone:
|
||||||
|
* type: string
|
||||||
|
* description: Time zone of the team
|
||||||
|
* weekStart:
|
||||||
|
* type: string
|
||||||
|
* description: Starting day of the week for the team
|
||||||
|
* isPrivate:
|
||||||
|
* type: boolean
|
||||||
|
* description: Flag indicating if the team is private
|
||||||
|
* ownerId:
|
||||||
|
* type: number
|
||||||
|
* description: ID of the team owner - only admins can set this.
|
||||||
* tags:
|
* tags:
|
||||||
* - teams
|
* - teams
|
||||||
* responses:
|
* responses:
|
||||||
@@ -49,23 +78,23 @@ import { schemaTeamBodyParams, schemaTeamReadPublic } from "~/lib/validations/te
|
|||||||
* description: Authorization information is missing or invalid.
|
* description: Authorization information is missing or invalid.
|
||||||
*/
|
*/
|
||||||
async function postHandler(req: NextApiRequest) {
|
async function postHandler(req: NextApiRequest) {
|
||||||
const { prisma, body, userId } = req;
|
const { prisma, body, userId, isAdmin } = req;
|
||||||
const data = schemaTeamBodyParams.parse(body);
|
const { ownerId, ...data } = schemaTeamCreateBodyParams.parse(body);
|
||||||
|
|
||||||
|
await checkPermissions(req);
|
||||||
|
|
||||||
|
const effectiveUserId = isAdmin && ownerId ? ownerId : userId;
|
||||||
|
|
||||||
if (data.slug) {
|
if (data.slug) {
|
||||||
const alreadyExist = await prisma.team.findFirst({
|
const alreadyExist = await prisma.team.findFirst({
|
||||||
where: {
|
where: {
|
||||||
slug: data.slug,
|
slug: {
|
||||||
|
mode: "insensitive",
|
||||||
|
equals: data.slug,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (alreadyExist) throw new HttpError({ statusCode: 409, message: "Team slug already exists" });
|
if (alreadyExist) throw new HttpError({ statusCode: 409, message: "Team slug already exists" });
|
||||||
if (IS_TEAM_BILLING_ENABLED) {
|
|
||||||
// Setting slug in metadata, so it can be published later
|
|
||||||
data.metadata = {
|
|
||||||
requestedSlug: data.slug,
|
|
||||||
};
|
|
||||||
delete data.slug;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if parentId is related to this user
|
// Check if parentId is related to this user
|
||||||
@@ -87,24 +116,102 @@ async function postHandler(req: NextApiRequest) {
|
|||||||
...data,
|
...data,
|
||||||
metadata: data.metadata === null ? {} : data.metadata || undefined,
|
metadata: data.metadata === null ? {} : data.metadata || undefined,
|
||||||
};
|
};
|
||||||
const team = await prisma.team.create({
|
|
||||||
|
if (!IS_TEAM_BILLING_ENABLED) {
|
||||||
|
const team = await prisma.team.create({
|
||||||
|
data: {
|
||||||
|
...cloneData,
|
||||||
|
members: {
|
||||||
|
create: { userId: effectiveUserId, role: MembershipRole.OWNER, accepted: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: { members: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
req.statusCode = 201;
|
||||||
|
|
||||||
|
return {
|
||||||
|
team: schemaTeamReadPublic.parse(team),
|
||||||
|
owner: schemaMembershipPublic.parse(team.members[0]),
|
||||||
|
message: `Team created successfully. We also made user with ID=${ownerId} the owner of this team.`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const pendingPaymentTeam = await prisma.team.create({
|
||||||
data: {
|
data: {
|
||||||
...cloneData,
|
...cloneData,
|
||||||
createdAt: new Date(),
|
pendingPayment: true,
|
||||||
members: {
|
|
||||||
// We're also creating the relation membership of team ownership in this call.
|
|
||||||
create: { userId, role: MembershipRole.OWNER, accepted: true },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
include: { members: true },
|
|
||||||
});
|
});
|
||||||
req.statusCode = 201;
|
|
||||||
// We are also returning the new ownership relation as owner besides team.
|
const checkoutSession = await generateTeamCheckoutSession({
|
||||||
|
pendingPaymentTeamId: pendingPaymentTeam.id,
|
||||||
|
ownerId: effectiveUserId,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
team: schemaTeamReadPublic.parse(team),
|
message:
|
||||||
owner: schemaMembershipPublic.parse(team.members[0]),
|
"Your team will be created once we receive your payment. Please complete the payment using the payment link.",
|
||||||
message: "Team created successfully, we also made you the owner of this team",
|
paymentLink: checkoutSession.url,
|
||||||
|
pendingTeam: {
|
||||||
|
...schemaTeamReadPublic.parse(pendingPaymentTeam),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkPermissions(req: NextApiRequest) {
|
||||||
|
const { isAdmin } = req;
|
||||||
|
const body = schemaTeamCreateBodyParams.parse(req.body);
|
||||||
|
|
||||||
|
/* Non-admin users can only create teams for themselves */
|
||||||
|
if (!isAdmin && body.ownerId)
|
||||||
|
throw new HttpError({
|
||||||
|
statusCode: 401,
|
||||||
|
message: "ADMIN required for `ownerId`",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateTeamCheckoutSession = async ({
|
||||||
|
pendingPaymentTeamId,
|
||||||
|
ownerId,
|
||||||
|
}: {
|
||||||
|
pendingPaymentTeamId: number;
|
||||||
|
ownerId: number;
|
||||||
|
}) => {
|
||||||
|
const customer = await getStripeCustomerIdFromUserId(ownerId);
|
||||||
|
const session = await stripe.checkout.sessions.create({
|
||||||
|
customer,
|
||||||
|
mode: "subscription",
|
||||||
|
allow_promotion_codes: true,
|
||||||
|
success_url: `${WEBAPP_URL}/api/teams/api/create?session_id={CHECKOUT_SESSION_ID}`,
|
||||||
|
cancel_url: `${WEBAPP_URL}/settings/my-account/profile`,
|
||||||
|
line_items: [
|
||||||
|
{
|
||||||
|
/** We only need to set the base price and we can upsell it directly on Stripe's checkout */
|
||||||
|
price: process.env.STRIPE_TEAM_MONTHLY_PRICE_ID,
|
||||||
|
/**Initially it will be just the team owner */
|
||||||
|
quantity: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
customer_update: {
|
||||||
|
address: "auto",
|
||||||
|
},
|
||||||
|
automatic_tax: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
pendingPaymentTeamId,
|
||||||
|
ownerId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!session.url)
|
||||||
|
throw new HttpError({
|
||||||
|
statusCode: 500,
|
||||||
|
message: "Failed generating a checkout session URL.",
|
||||||
|
});
|
||||||
|
|
||||||
|
return session;
|
||||||
|
};
|
||||||
|
|
||||||
export default defaultResponder(postHandler);
|
export default defaultResponder(postHandler);
|
||||||
@@ -69,7 +69,12 @@ import { schemaWebhookEditBodyParams, schemaWebhookReadPublic } from "~/lib/vali
|
|||||||
export async function patchHandler(req: NextApiRequest) {
|
export async function patchHandler(req: NextApiRequest) {
|
||||||
const { prisma, query, userId, isAdmin } = req;
|
const { prisma, query, userId, isAdmin } = req;
|
||||||
const { id } = schemaQueryIdAsString.parse(query);
|
const { id } = schemaQueryIdAsString.parse(query);
|
||||||
const { eventTypeId, userId: bodyUserId, ...data } = schemaWebhookEditBodyParams.parse(req.body);
|
const {
|
||||||
|
eventTypeId,
|
||||||
|
userId: bodyUserId,
|
||||||
|
eventTriggers,
|
||||||
|
...data
|
||||||
|
} = schemaWebhookEditBodyParams.parse(req.body);
|
||||||
const args: Prisma.WebhookUpdateArgs = { where: { id }, data };
|
const args: Prisma.WebhookUpdateArgs = { where: { id }, data };
|
||||||
|
|
||||||
if (eventTypeId) {
|
if (eventTypeId) {
|
||||||
@@ -87,6 +92,11 @@ export async function patchHandler(req: NextApiRequest) {
|
|||||||
args.data.userId = bodyUserId;
|
args.data.userId = bodyUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eventTriggers) {
|
||||||
|
const eventTriggersSet = new Set(eventTriggers);
|
||||||
|
args.data.eventTriggers = Array.from(eventTriggersSet);
|
||||||
|
}
|
||||||
|
|
||||||
const result = await prisma.webhook.update(args);
|
const result = await prisma.webhook.update(args);
|
||||||
return { webhook: schemaWebhookReadPublic.parse(result) };
|
return { webhook: schemaWebhookReadPublic.parse(result) };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,12 @@ import { schemaWebhookCreateBodyParams, schemaWebhookReadPublic } from "~/lib/va
|
|||||||
*/
|
*/
|
||||||
async function postHandler(req: NextApiRequest) {
|
async function postHandler(req: NextApiRequest) {
|
||||||
const { userId, isAdmin, prisma } = req;
|
const { userId, isAdmin, prisma } = req;
|
||||||
const { eventTypeId, userId: bodyUserId, ...body } = schemaWebhookCreateBodyParams.parse(req.body);
|
const {
|
||||||
|
eventTypeId,
|
||||||
|
userId: bodyUserId,
|
||||||
|
eventTriggers,
|
||||||
|
...body
|
||||||
|
} = schemaWebhookCreateBodyParams.parse(req.body);
|
||||||
const args: Prisma.WebhookCreateArgs = { data: { id: uuidv4(), ...body } };
|
const args: Prisma.WebhookCreateArgs = { data: { id: uuidv4(), ...body } };
|
||||||
|
|
||||||
// If no event type, we assume is for the current user. If admin we run more checks below...
|
// If no event type, we assume is for the current user. If admin we run more checks below...
|
||||||
@@ -87,6 +92,11 @@ async function postHandler(req: NextApiRequest) {
|
|||||||
args.data.userId = bodyUserId;
|
args.data.userId = bodyUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (eventTriggers) {
|
||||||
|
const eventTriggersSet = new Set(eventTriggers);
|
||||||
|
args.data.eventTriggers = Array.from(eventTriggersSet);
|
||||||
|
}
|
||||||
|
|
||||||
const data = await prisma.webhook.create(args);
|
const data = await prisma.webhook.create(args);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
"@storybook/blocks": "^7.6.3",
|
"@storybook/blocks": "^7.6.3",
|
||||||
"@storybook/nextjs": "^7.6.3",
|
"@storybook/nextjs": "^7.6.3",
|
||||||
"@storybook/preview-api": "^7.6.3",
|
"@storybook/preview-api": "^7.6.3",
|
||||||
"next": "^13.4.6",
|
"next": "^13.5.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"storybook-addon-rtl-direction": "^0.0.19"
|
"storybook-addon-rtl-direction": "^0.0.19"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"highlight.js": "^11.6.0",
|
"highlight.js": "^11.6.0",
|
||||||
"isarray": "2.0.5",
|
"isarray": "2.0.5",
|
||||||
"next": "^13.4.6",
|
"next": "^13.5.4",
|
||||||
"openapi-snippet": "^0.13.0",
|
"openapi-snippet": "^0.13.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import type { GetStaticProps, GetStaticPropsContext } from "next";
|
||||||
|
import { notFound, redirect } from "next/navigation";
|
||||||
|
|
||||||
|
export const withAppDirSsg =
|
||||||
|
<T extends Record<string, any>>(getStaticProps: GetStaticProps<T>) =>
|
||||||
|
async (context: GetStaticPropsContext) => {
|
||||||
|
const ssgResponse = await getStaticProps(context);
|
||||||
|
|
||||||
|
if ("redirect" in ssgResponse) {
|
||||||
|
redirect(ssgResponse.redirect.destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("notFound" in ssgResponse) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = await Promise.resolve(ssgResponse.props);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...ssgResponse.props,
|
||||||
|
// includes dehydratedState required for future page trpcPropvider
|
||||||
|
...("trpcState" in props && { dehydratedState: props.trpcState }),
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import type { GetServerSideProps, GetServerSidePropsContext } from "next";
|
||||||
|
import { notFound, redirect } from "next/navigation";
|
||||||
|
|
||||||
|
export const withAppDirSsr =
|
||||||
|
<T extends Record<string, any>>(getServerSideProps: GetServerSideProps<T>) =>
|
||||||
|
async (context: GetServerSidePropsContext) => {
|
||||||
|
const ssrResponse = await getServerSideProps(context);
|
||||||
|
|
||||||
|
if ("redirect" in ssrResponse) {
|
||||||
|
redirect(ssrResponse.redirect.destination);
|
||||||
|
}
|
||||||
|
if ("notFound" in ssrResponse) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = await Promise.resolve(ssrResponse.props);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
// includes dehydratedState required for future page trpcPropvider
|
||||||
|
...("trpcState" in props && { dehydratedState: props.trpcState }),
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
import { isNotFoundError } from "next/dist/client/components/not-found";
|
||||||
|
import { getURLFromRedirectError, isRedirectError } from "next/dist/client/components/redirect";
|
||||||
|
import { notFound, redirect } from "next/navigation";
|
||||||
|
|
||||||
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
|
|
||||||
|
export type EmbedProps = {
|
||||||
|
isEmbed?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function withEmbedSsrAppDir<T extends Record<string, any>>(
|
||||||
|
getData: (context: GetServerSidePropsContext) => Promise<T>
|
||||||
|
) {
|
||||||
|
return async (context: GetServerSidePropsContext): Promise<T> => {
|
||||||
|
const { embed, layout } = context.query;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const props = await getData(context);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
isEmbed: true,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
if (isRedirectError(e)) {
|
||||||
|
const destinationUrl = getURLFromRedirectError(e);
|
||||||
|
let urlPrefix = "";
|
||||||
|
|
||||||
|
// Get the URL parsed from URL so that we can reliably read pathname and searchParams from it.
|
||||||
|
const destinationUrlObj = new URL(destinationUrl, WEBAPP_URL);
|
||||||
|
|
||||||
|
// If it's a complete URL, use the origin as the prefix to ensure we redirect to the same domain.
|
||||||
|
if (destinationUrl.search(/^(http:|https:).*/) !== -1) {
|
||||||
|
urlPrefix = destinationUrlObj.origin;
|
||||||
|
} else {
|
||||||
|
// Don't use any prefix for relative URLs to ensure we stay on the same domain
|
||||||
|
urlPrefix = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const destinationQueryStr = destinationUrlObj.searchParams.toString();
|
||||||
|
// Make sure that redirect happens to /embed page and pass on embed query param as is for preserving Cal JS API namespace
|
||||||
|
const newDestinationUrl = `${urlPrefix}${destinationUrlObj.pathname}/embed?${
|
||||||
|
destinationQueryStr ? `${destinationQueryStr}&` : ""
|
||||||
|
}layout=${layout}&embed=${embed}`;
|
||||||
|
|
||||||
|
redirect(newDestinationUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotFoundError(e)) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { type DehydratedState, Hydrate } from "@tanstack/react-query";
|
import { type DehydratedState, HydrationBoundary } from "@tanstack/react-query";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
import type { DataTransformer } from "@trpc/server";
|
type DataTransformer = {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
serialize: (data: any) => any;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
deserialize: (data: any) => any;
|
||||||
|
};
|
||||||
|
|
||||||
export function createHydrateClient(opts: { transformer?: DataTransformer }) {
|
export function createHydrateClient(opts: { transformer: DataTransformer }) {
|
||||||
return function HydrateClient(props: { children: React.ReactNode; state: DehydratedState }) {
|
return function HydrateClient(props: { children: React.ReactNode; state: DehydratedState }) {
|
||||||
const { state, children } = props;
|
const { state, children } = props;
|
||||||
|
|
||||||
@@ -16,6 +21,6 @@ export function createHydrateClient(opts: { transformer?: DataTransformer }) {
|
|||||||
return state;
|
return state;
|
||||||
}, [state]);
|
}, [state]);
|
||||||
|
|
||||||
return <Hydrate state={transformedState}>{children}</Hydrate>;
|
return <HydrationBoundary state={transformedState}>{children}</HydrationBoundary>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,7 @@ import { trpc } from "app/_trpc/client";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import superjson from "superjson";
|
import superjson from "superjson";
|
||||||
|
|
||||||
import { httpBatchLink } from "@calcom/trpc/client/links/httpBatchLink";
|
import { httpBatchLink, httpLink, loggerLink, splitLink } from "@calcom/trpc/client";
|
||||||
import { httpLink } from "@calcom/trpc/client/links/httpLink";
|
|
||||||
import { loggerLink } from "@calcom/trpc/client/links/loggerLink";
|
|
||||||
import { splitLink } from "@calcom/trpc/client/links/splitLink";
|
|
||||||
import { ENDPOINTS } from "@calcom/trpc/react/shared";
|
import { ENDPOINTS } from "@calcom/trpc/react/shared";
|
||||||
|
|
||||||
export type Endpoint = (typeof ENDPOINTS)[number];
|
export type Endpoint = (typeof ENDPOINTS)[number];
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
import LegacyPage, { getLayout } from "@pages/apps/[slug]/[...pages]";
|
||||||
|
import type { PageProps, SearchParams } from "app/_types";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
import type { Params } from "next/dist/shared/lib/router/utils/route-matcher";
|
||||||
|
import { cookies, headers } from "next/headers";
|
||||||
|
import { notFound, redirect } from "next/navigation";
|
||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { getAppWithMetadata } from "@calcom/app-store/_appRegistry";
|
||||||
|
import RoutingFormsRoutingConfig, {
|
||||||
|
serverSidePropsConfig,
|
||||||
|
} from "@calcom/app-store/routing-forms/pages/app-routing.config";
|
||||||
|
import TypeformRoutingConfig from "@calcom/app-store/typeform/pages/app-routing.config";
|
||||||
|
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
|
import prisma from "@calcom/prisma";
|
||||||
|
import type { AppGetServerSideProps } from "@calcom/types/AppGetServerSideProps";
|
||||||
|
|
||||||
|
import type { AppProps } from "@lib/app-providers";
|
||||||
|
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||||
|
|
||||||
|
import PageWrapper from "@components/PageWrapperAppDir";
|
||||||
|
|
||||||
|
import { ssrInit } from "@server/lib/ssr";
|
||||||
|
|
||||||
|
type AppPageType = {
|
||||||
|
getServerSideProps: AppGetServerSideProps;
|
||||||
|
// A component than can accept any properties
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
default: ((props: any) => JSX.Element) &
|
||||||
|
Pick<AppProps["Component"], "isBookingPage" | "getLayout" | "PageWrapper">;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Found = {
|
||||||
|
notFound: false;
|
||||||
|
Component: AppPageType["default"];
|
||||||
|
getServerSideProps: AppPageType["getServerSideProps"];
|
||||||
|
};
|
||||||
|
|
||||||
|
const AppsRouting = {
|
||||||
|
"routing-forms": RoutingFormsRoutingConfig,
|
||||||
|
typeform: TypeformRoutingConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
const paramsSchema = z.object({
|
||||||
|
slug: z.string(),
|
||||||
|
pages: z.array(z.string()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const generateMetadata = async ({
|
||||||
|
params,
|
||||||
|
searchParams,
|
||||||
|
}: {
|
||||||
|
params: Params;
|
||||||
|
searchParams: SearchParams;
|
||||||
|
}) => {
|
||||||
|
const p = paramsSchema.safeParse(params);
|
||||||
|
|
||||||
|
if (!p.success) {
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const mainPage = p.data.pages[0];
|
||||||
|
|
||||||
|
if (mainPage === "forms") {
|
||||||
|
return await _generateMetadata(
|
||||||
|
() => `Forms | ${APP_NAME}`,
|
||||||
|
() => ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const legacyContext = buildLegacyCtx(
|
||||||
|
headers(),
|
||||||
|
cookies(),
|
||||||
|
params,
|
||||||
|
searchParams
|
||||||
|
) as unknown as GetServerSidePropsContext;
|
||||||
|
const { form } = await getPageProps(legacyContext);
|
||||||
|
|
||||||
|
return await _generateMetadata(
|
||||||
|
() => `${form.name} | ${APP_NAME}`,
|
||||||
|
() => form.description
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function getRoute(appName: string, pages: string[]) {
|
||||||
|
const routingConfig = AppsRouting[appName as keyof typeof AppsRouting] as Record<string, AppPageType>;
|
||||||
|
|
||||||
|
if (!routingConfig) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const mainPage = pages[0];
|
||||||
|
const appPage = routingConfig.layoutHandler || (routingConfig[mainPage] as AppPageType);
|
||||||
|
|
||||||
|
const getServerSidePropsHandler = serverSidePropsConfig[mainPage];
|
||||||
|
|
||||||
|
if (!appPage) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
notFound: false,
|
||||||
|
Component: appPage.default,
|
||||||
|
...appPage,
|
||||||
|
getServerSideProps: getServerSidePropsHandler,
|
||||||
|
} as Found;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPageProps = async ({ params, query, req }: GetServerSidePropsContext) => {
|
||||||
|
const p = paramsSchema.safeParse(params);
|
||||||
|
|
||||||
|
if (!p.success) {
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { slug: appName, pages } = p.data;
|
||||||
|
|
||||||
|
const route = getRoute(appName, pages);
|
||||||
|
|
||||||
|
if (route.notFound) {
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.getServerSideProps) {
|
||||||
|
// TODO: Document somewhere that right now it is just a convention that filename should have appPages in it's name.
|
||||||
|
// appPages is actually hardcoded here and no matter the fileName the same variable would be used.
|
||||||
|
// We can write some validation logic later on that ensures that [...appPages].tsx file exists
|
||||||
|
params!.appPages = pages.slice(1);
|
||||||
|
|
||||||
|
const ctx = { req, params, query };
|
||||||
|
|
||||||
|
const session = await getServerSession({ req });
|
||||||
|
const user = session?.user;
|
||||||
|
const app = await getAppWithMetadata({ slug: appName });
|
||||||
|
|
||||||
|
if (!app) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await route.getServerSideProps(
|
||||||
|
{
|
||||||
|
...ctx,
|
||||||
|
params: {
|
||||||
|
...ctx.params,
|
||||||
|
appPages: pages.slice(1),
|
||||||
|
},
|
||||||
|
} as GetServerSidePropsContext<{
|
||||||
|
slug: string;
|
||||||
|
pages: string[];
|
||||||
|
appPages: string[];
|
||||||
|
}>,
|
||||||
|
prisma,
|
||||||
|
user,
|
||||||
|
ssrInit
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
//@ts-ignore
|
||||||
|
|
||||||
|
if (result.notFound) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.redirect) {
|
||||||
|
redirect(result.redirect.destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
appName,
|
||||||
|
appUrl: app.simplePath || `/apps/${appName}`,
|
||||||
|
...result.props,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
appName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function Page({ params, searchParams }: PageProps) {
|
||||||
|
const h = headers();
|
||||||
|
const nonce = h.get("x-nonce") ?? undefined;
|
||||||
|
|
||||||
|
const legacyContext = buildLegacyCtx(
|
||||||
|
h,
|
||||||
|
cookies(),
|
||||||
|
params,
|
||||||
|
searchParams
|
||||||
|
) as unknown as GetServerSidePropsContext;
|
||||||
|
const props = await getPageProps(legacyContext);
|
||||||
|
return (
|
||||||
|
<PageWrapper getLayout={getLayout} requiresLicense={false} nonce={nonce} themeBasis={null} {...props}>
|
||||||
|
<LegacyPage {...props} />
|
||||||
|
</PageWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { WithLayout } from "app/layoutHOC";
|
|
||||||
|
|
||||||
export default WithLayout({ getLayout: null })<"L">;
|
|
||||||
@@ -1,40 +1,33 @@
|
|||||||
import AppPage from "@pages/apps/[slug]/index";
|
import Page from "@pages/apps/[slug]/index";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { withAppDirSsg } from "app/WithAppDirSsg";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import fs from "fs";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import matter from "gray-matter";
|
import type { InferGetStaticPropsType } from "next";
|
||||||
import { notFound } from "next/navigation";
|
import { cookies, headers } from "next/headers";
|
||||||
import path from "path";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { getAppWithMetadata } from "@calcom/app-store/_appRegistry";
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
import { getAppAssetFullPath } from "@calcom/app-store/getAppAssetFullPath";
|
|
||||||
import { APP_NAME, IS_PRODUCTION } from "@calcom/lib/constants";
|
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
|
|
||||||
const sourceSchema = z.object({
|
import { getStaticProps } from "@lib/apps/[slug]/getStaticProps";
|
||||||
content: z.string(),
|
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||||
data: z.object({
|
|
||||||
description: z.string().optional(),
|
|
||||||
items: z
|
|
||||||
.array(
|
|
||||||
z.union([
|
|
||||||
z.string(),
|
|
||||||
z.object({
|
|
||||||
iframe: z.object({ src: z.string() }),
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const generateMetadata = async ({ params }: { params: Record<string, string | string[]> }) => {
|
type Y = InferGetStaticPropsType<typeof getStaticProps>;
|
||||||
const { data } = await getPageProps({ params });
|
const getData = withAppDirSsg<Y>(getStaticProps);
|
||||||
|
|
||||||
|
export const generateMetadata = async ({
|
||||||
|
params,
|
||||||
|
searchParams,
|
||||||
|
}: {
|
||||||
|
params: Record<string, string | string[]>;
|
||||||
|
searchParams: { [key: string]: string | string[] | undefined };
|
||||||
|
}) => {
|
||||||
|
const legacyContext = buildLegacyCtx(headers(), cookies(), params, searchParams);
|
||||||
|
const res = await getData(legacyContext);
|
||||||
|
|
||||||
return await _generateMetadata(
|
return await _generateMetadata(
|
||||||
() => `${data.name} | ${APP_NAME}`,
|
() => `${res?.data.name} | ${APP_NAME}`,
|
||||||
() => data.description
|
() => res?.data.description ?? ""
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,74 +46,6 @@ export const generateStaticParams = async () => {
|
|||||||
return [];
|
return [];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPageProps = async ({ params }: { params: Record<string, string | string[]> }) => {
|
export default WithLayout({ getLayout: null, Page, getData });
|
||||||
if (typeof params?.slug !== "string") {
|
|
||||||
notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const appMeta = await getAppWithMetadata({
|
|
||||||
slug: params?.slug,
|
|
||||||
});
|
|
||||||
|
|
||||||
const appFromDb = await prisma.app.findUnique({
|
|
||||||
where: { slug: params.slug.toLowerCase() },
|
|
||||||
});
|
|
||||||
|
|
||||||
const isAppAvailableInFileSystem = appMeta;
|
|
||||||
const isAppDisabled = isAppAvailableInFileSystem && (!appFromDb || !appFromDb.enabled);
|
|
||||||
|
|
||||||
if (!IS_PRODUCTION && isAppDisabled) {
|
|
||||||
return {
|
|
||||||
isAppDisabled: true as const,
|
|
||||||
data: {
|
|
||||||
...appMeta,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!appFromDb || !appMeta || isAppDisabled) {
|
|
||||||
notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const isTemplate = appMeta.isTemplate;
|
|
||||||
const appDirname = path.join(isTemplate ? "templates" : "", appFromDb.dirName);
|
|
||||||
const README_PATH = path.join(process.cwd(), "..", "..", `packages/app-store/${appDirname}/DESCRIPTION.md`);
|
|
||||||
const postFilePath = path.join(README_PATH);
|
|
||||||
let source = "";
|
|
||||||
|
|
||||||
try {
|
|
||||||
source = fs.readFileSync(postFilePath).toString();
|
|
||||||
source = source.replace(/{DESCRIPTION}/g, appMeta.description);
|
|
||||||
} catch (error) {
|
|
||||||
/* If the app doesn't have a README we fallback to the package description */
|
|
||||||
console.log(`No DESCRIPTION.md provided for: ${appDirname}`);
|
|
||||||
source = appMeta.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = matter(source);
|
|
||||||
const { content, data } = sourceSchema.parse({ content: result.content, data: result.data });
|
|
||||||
if (data.items) {
|
|
||||||
data.items = data.items.map((item) => {
|
|
||||||
if (typeof item === "string") {
|
|
||||||
return getAppAssetFullPath(item, {
|
|
||||||
dirName: appMeta.dirName,
|
|
||||||
isTemplate: appMeta.isTemplate,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
isAppDisabled: false as const,
|
|
||||||
source: { content, data },
|
|
||||||
data: appMeta,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function Page({ params }: { params: Record<string, string | string[]> }) {
|
|
||||||
const pageProps = await getPageProps({ params });
|
|
||||||
|
|
||||||
return <AppPage {...pageProps} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const dynamic = "force-static";
|
export const dynamic = "force-static";
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import SetupPage from "@pages/apps/[slug]/setup";
|
import Page from "@pages/apps/[slug]/setup";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import { cookies, headers } from "next/headers";
|
import type { InferGetServerSidePropsType } from "next";
|
||||||
import { notFound, redirect } from "next/navigation";
|
|
||||||
|
|
||||||
import { getServerSideProps } from "@calcom/app-store/_pages/setup/_getServerSideProps";
|
import { getServerSideProps } from "@calcom/app-store/_pages/setup/_getServerSideProps";
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
@@ -14,23 +14,8 @@ export const generateMetadata = async ({ params }: { params: Record<string, stri
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPageProps = async ({ params }: { params: Record<string, string | string[]> }) => {
|
type T = InferGetServerSidePropsType<typeof getServerSideProps>;
|
||||||
const req = { headers: headers(), cookies: cookies() };
|
|
||||||
|
|
||||||
const result = await getServerSideProps({ params, req } as unknown as GetServerSidePropsContext);
|
const getData = withAppDirSsr<T>(getServerSideProps);
|
||||||
|
|
||||||
if (!result || "notFound" in result) {
|
export default WithLayout({ getLayout: null, Page, getData });
|
||||||
notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("redirect" in result) {
|
|
||||||
redirect(result.redirect.destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.props;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function Page({ params }: { params: Record<string, string | string[]> }) {
|
|
||||||
const pageProps = await getPageProps({ params });
|
|
||||||
return <SetupPage {...pageProps} />;
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
import CategoryPage from "@pages/apps/categories/[category]";
|
import CategoryPage, { type PageProps } from "@pages/apps/categories/[category]";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { withAppDirSsg } from "app/WithAppDirSsg";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import { notFound } from "next/navigation";
|
|
||||||
import z from "zod";
|
|
||||||
|
|
||||||
import { getAppRegistry } from "@calcom/app-store/_appRegistry";
|
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { AppCategories } from "@calcom/prisma/enums";
|
import { AppCategories } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
|
import { getStaticProps } from "@lib/apps/categories/[category]/getStaticProps";
|
||||||
|
|
||||||
export const generateMetadata = async () => {
|
export const generateMetadata = async () => {
|
||||||
return await _generateMetadata(
|
return await _generateMetadata(
|
||||||
() => `${APP_NAME} | ${APP_NAME}`,
|
() => `${APP_NAME} | ${APP_NAME}`,
|
||||||
@@ -34,38 +34,7 @@ export const generateStaticParams = async () => {
|
|||||||
return paths.map((category) => ({ category }));
|
return paths.map((category) => ({ category }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const querySchema = z.object({
|
const getData = withAppDirSsg<PageProps>(getStaticProps);
|
||||||
category: z.nativeEnum(AppCategories),
|
|
||||||
});
|
|
||||||
|
|
||||||
const getPageProps = async ({ params }: { params: Record<string, string | string[]> }) => {
|
export default WithLayout({ getData, Page: CategoryPage, getLayout: null })<"P">;
|
||||||
const p = querySchema.safeParse(params);
|
|
||||||
|
|
||||||
if (!p.success) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const appQuery = await prisma.app.findMany({
|
|
||||||
where: {
|
|
||||||
categories: {
|
|
||||||
has: p.data.category,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
slug: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const dbAppsSlugs = appQuery.map((category) => category.slug);
|
|
||||||
|
|
||||||
const appStore = await getAppRegistry();
|
|
||||||
|
|
||||||
const apps = appStore.filter((app) => dbAppsSlugs.includes(app.slug));
|
|
||||||
return {
|
|
||||||
apps,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// @ts-expect-error getData arg
|
|
||||||
export default WithLayout({ getData: getPageProps, Page: CategoryPage })<"P">;
|
|
||||||
export const dynamic = "force-static";
|
export const dynamic = "force-static";
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
import LegacyPage from "@pages/apps/categories/index";
|
import Page from "@pages/apps/categories/index";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
|
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
|
|
||||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
import { getServerSideProps } from "@lib/apps/categories/getServerSideProps";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
|
||||||
|
|
||||||
export const generateMetadata = async () => {
|
export const generateMetadata = async () => {
|
||||||
return await _generateMetadata(
|
return await _generateMetadata(
|
||||||
@@ -17,31 +14,4 @@ export const generateMetadata = async () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getData = async (ctx: ReturnType<typeof buildLegacyCtx>) => {
|
export default WithLayout({ getData: withAppDirSsr(getServerSideProps), Page, getLayout: null })<"P">;
|
||||||
// @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'.
|
|
||||||
const ssr = await ssrInit(ctx);
|
|
||||||
|
|
||||||
// @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest | IncomingMessage
|
|
||||||
const session = await getServerSession({ req: ctx.req });
|
|
||||||
|
|
||||||
let appStore;
|
|
||||||
if (session?.user?.id) {
|
|
||||||
appStore = await getAppRegistryWithCredentials(session.user.id);
|
|
||||||
} else {
|
|
||||||
appStore = await getAppRegistry();
|
|
||||||
}
|
|
||||||
|
|
||||||
const categories = appStore.reduce((c, app) => {
|
|
||||||
for (const category of app.categories) {
|
|
||||||
c[category] = c[category] ? c[category] + 1 : 1;
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}, {} as Record<string, number>);
|
|
||||||
|
|
||||||
return {
|
|
||||||
categories: Object.entries(categories).map(([name, count]) => ({ name, count })),
|
|
||||||
dehydratedState: ssr.dehydrate(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WithLayout({ getData, Page: LegacyPage, getLayout: null })<"P">;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { WithLayout } from "app/layoutHOC";
|
|
||||||
|
|
||||||
export default WithLayout({ getLayout: null })<"L">;
|
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
import LegacyPage from "@pages/apps/installed/[category]";
|
import Page from "@pages/apps/installed/[category]";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { notFound } from "next/navigation";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
import { AppCategories } from "@calcom/prisma/enums";
|
|
||||||
|
|
||||||
const querySchema = z.object({
|
import { getServerSideProps } from "@lib/apps/installed/[category]/getServerSideProps";
|
||||||
category: z.nativeEnum(AppCategories),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const generateMetadata = async () => {
|
export const generateMetadata = async () => {
|
||||||
return await _generateMetadata(
|
return await _generateMetadata(
|
||||||
@@ -17,20 +14,6 @@ export const generateMetadata = async () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPageProps = async ({ params }: { params: Record<string, string | string[]> }) => {
|
const getData = withAppDirSsr(getServerSideProps);
|
||||||
const p = querySchema.safeParse(params);
|
|
||||||
|
|
||||||
if (!p.success) {
|
export default WithLayout({ getLayout: null, getData, Page });
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
category: p.data.category,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function Page({ params }: { params: Record<string, string | string[]> }) {
|
|
||||||
await getPageProps({ params });
|
|
||||||
|
|
||||||
return <LegacyPage />;
|
|
||||||
}
|
|
||||||
@@ -1,18 +1,12 @@
|
|||||||
import AppsPage from "@pages/apps";
|
import AppsPage from "@pages/apps";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
|
|
||||||
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
|
||||||
import type { UserAdminTeams } from "@calcom/features/ee/teams/lib/getUserAdminTeams";
|
|
||||||
import getUserAdminTeams from "@calcom/features/ee/teams/lib/getUserAdminTeams";
|
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
import type { AppCategories } from "@calcom/prisma/enums";
|
|
||||||
|
|
||||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
import { getServerSideProps } from "@lib/apps/getServerSideProps";
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
|
||||||
|
|
||||||
export const generateMetadata = async () => {
|
export const generateMetadata = async () => {
|
||||||
return await _generateMetadata(
|
return await _generateMetadata(
|
||||||
@@ -21,46 +15,4 @@ export const generateMetadata = async () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getData = async (ctx: ReturnType<typeof buildLegacyCtx>) => {
|
export default WithLayout({ getLayout, getData: withAppDirSsr(getServerSideProps), Page: AppsPage });
|
||||||
// @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'.
|
|
||||||
const ssr = await ssrInit(ctx);
|
|
||||||
|
|
||||||
// @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest
|
|
||||||
const session = await getServerSession({ req: ctx.req });
|
|
||||||
|
|
||||||
let appStore, userAdminTeams: UserAdminTeams;
|
|
||||||
if (session?.user?.id) {
|
|
||||||
userAdminTeams = await getUserAdminTeams({ userId: session.user.id, getUserInfo: true });
|
|
||||||
appStore = await getAppRegistryWithCredentials(session.user.id, userAdminTeams);
|
|
||||||
} else {
|
|
||||||
appStore = await getAppRegistry();
|
|
||||||
userAdminTeams = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const categoryQuery = appStore.map(({ categories }) => ({
|
|
||||||
categories: categories || [],
|
|
||||||
}));
|
|
||||||
|
|
||||||
const categories = categoryQuery.reduce((c, app) => {
|
|
||||||
for (const category of app.categories) {
|
|
||||||
c[category] = c[category] ? c[category] + 1 : 1;
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}, {} as Record<string, number>);
|
|
||||||
|
|
||||||
return {
|
|
||||||
categories: Object.entries(categories)
|
|
||||||
.map(([name, count]): { name: AppCategories; count: number } => ({
|
|
||||||
name: name as AppCategories,
|
|
||||||
count,
|
|
||||||
}))
|
|
||||||
.sort(function (a, b) {
|
|
||||||
return b.count - a.count;
|
|
||||||
}),
|
|
||||||
appStore,
|
|
||||||
userAdminTeams,
|
|
||||||
dehydratedState: ssr.dehydrate(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WithLayout({ getLayout, getData, Page: AppsPage });
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import SetNewUserPassword from "@pages/auth/forgot-password/[id]";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@server/lib/forgot-password/[id]/getServerSideProps";
|
||||||
|
|
||||||
|
export const generateMetadata = async () => {
|
||||||
|
return await _generateMetadata(
|
||||||
|
(t) => t("reset_password"),
|
||||||
|
(t) => t("change_your_password")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
getLayout: null,
|
||||||
|
Page: SetNewUserPassword,
|
||||||
|
getData: withAppDirSsr(getServerSideProps),
|
||||||
|
})<"P">;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import ForgotPassword from "@pages/auth/forgot-password";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@server/lib/forgot-password/getServerSideProps";
|
||||||
|
|
||||||
|
export const generateMetadata = async () => {
|
||||||
|
return await _generateMetadata(
|
||||||
|
(t) => t("reset_password"),
|
||||||
|
(t) => t("change_your_password")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
getLayout: null,
|
||||||
|
Page: ForgotPassword,
|
||||||
|
getData: withAppDirSsr(getServerSideProps),
|
||||||
|
})<"P">;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import Login from "@pages/auth/login";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
import type { InferGetServerSidePropsType } from "next";
|
||||||
|
|
||||||
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@server/lib/auth/login/getServerSideProps";
|
||||||
|
|
||||||
|
export const generateMetadata = async () => {
|
||||||
|
return await _generateMetadata(
|
||||||
|
(t) => `${t("login")} | ${APP_NAME}`,
|
||||||
|
(t) => t("login")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
getLayout: null,
|
||||||
|
Page: Login,
|
||||||
|
getData: withAppDirSsr<InferGetServerSidePropsType<typeof getServerSideProps>>(getServerSideProps),
|
||||||
|
})<"P">;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Logout from "@pages/auth/logout";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@server/lib/auth/logout/getServerSideProps";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, Page: Logout, getData: withAppDirSsr(getServerSideProps) })<"P">;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import Setup from "@pages/auth/setup";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
import type { InferGetServerSidePropsType } from "next";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@server/lib/setup/getServerSideProps";
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
getLayout: null,
|
||||||
|
Page: Setup,
|
||||||
|
getData: withAppDirSsr<InferGetServerSidePropsType<typeof getServerSideProps>>(getServerSideProps),
|
||||||
|
})<"P">;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import signin from "@pages/auth/signin";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
import type { InferGetServerSidePropsType } from "next";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@server/lib/auth/signin/getServerSideProps";
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
getLayout: null,
|
||||||
|
Page: signin,
|
||||||
|
// @ts-expect-error TODO: fix this
|
||||||
|
getData: withAppDirSsr<InferGetServerSidePropsType<typeof getServerSideProps>>(getServerSideProps),
|
||||||
|
})<"P">;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import Provider from "@pages/auth/sso/[provider]";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
import type { InferGetServerSidePropsType } from "next";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@server/lib/auth/sso/[provider]/getServerSideProps";
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
getLayout: null,
|
||||||
|
Page: Provider,
|
||||||
|
getData: withAppDirSsr<InferGetServerSidePropsType<typeof getServerSideProps>>(getServerSideProps),
|
||||||
|
})<"P">;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import DirectSSOLogin from "@pages/auth/sso/direct";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@server/lib/auth/sso/direct/getServerSideProps";
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
getLayout: null,
|
||||||
|
Page: DirectSSOLogin,
|
||||||
|
getData: withAppDirSsr(getServerSideProps),
|
||||||
|
})<"P">;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import VerifyEmailPage from "@pages/auth/verify-email";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
export const generateMetadata = async () => {
|
||||||
|
return await _generateMetadata(
|
||||||
|
(t) => t("check_your_email"),
|
||||||
|
(t) => t("check_your_email")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
getLayout: null,
|
||||||
|
Page: VerifyEmailPage,
|
||||||
|
})<"P">;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import VerifyPage from "@pages/auth/verify";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@server/lib/auth/verify/getServerSideProps";
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
getLayout: null,
|
||||||
|
Page: VerifyPage,
|
||||||
|
getData: withAppDirSsr(getServerSideProps),
|
||||||
|
})<"P">;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { type PageProps, getServerSideProps } from "@pages/booking/[uid]";
|
||||||
|
import OldPage from "@pages/booking/[uid]/embed";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||||
|
|
||||||
|
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, getData: getEmbedData, Page: OldPage });
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import OldPage, { type PageProps } from "@pages/booking/[uid]";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import type { Params, SearchParams } from "app/_types";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
import { type GetServerSidePropsContext } from "next";
|
||||||
|
import { cookies, headers } from "next/headers";
|
||||||
|
|
||||||
|
import { BookingStatus } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@lib/booking/[uid]/getServerSideProps";
|
||||||
|
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||||
|
|
||||||
|
export const generateMetadata = async ({
|
||||||
|
params,
|
||||||
|
searchParams,
|
||||||
|
}: {
|
||||||
|
params: Params;
|
||||||
|
searchParams: SearchParams;
|
||||||
|
}) => {
|
||||||
|
const { bookingInfo, eventType, recurringBookings } = await getData(
|
||||||
|
buildLegacyCtx(headers(), cookies(), params, searchParams) as unknown as GetServerSidePropsContext
|
||||||
|
);
|
||||||
|
const needsConfirmation = bookingInfo.status === BookingStatus.PENDING && eventType.requiresConfirmation;
|
||||||
|
|
||||||
|
return await _generateMetadata(
|
||||||
|
(t) =>
|
||||||
|
t(`booking_${needsConfirmation ? "submitted" : "confirmed"}${recurringBookings ? "_recurring" : ""}`),
|
||||||
|
(t) =>
|
||||||
|
t(`booking_${needsConfirmation ? "submitted" : "confirmed"}${recurringBookings ? "_recurring" : ""}`)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, getData, Page: OldPage });
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { _generateMetadata } from "app/_utils";
|
|
||||||
import { WithLayout } from "app/layoutHOC";
|
|
||||||
import { notFound } from "next/navigation";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
|
||||||
import { APP_NAME } from "@calcom/lib/constants";
|
|
||||||
|
|
||||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
|
||||||
|
|
||||||
import { ssgInit } from "@server/lib/ssg";
|
|
||||||
|
|
||||||
const validStatuses = ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] as const;
|
|
||||||
|
|
||||||
const querySchema = z.object({
|
|
||||||
status: z.enum(validStatuses),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const generateMetadata = async () =>
|
|
||||||
await _generateMetadata(
|
|
||||||
(t) => `${APP_NAME} | ${t("bookings")}`,
|
|
||||||
() => ""
|
|
||||||
);
|
|
||||||
|
|
||||||
export const generateStaticParams = async () => {
|
|
||||||
return validStatuses.map((status) => ({ status }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const getData = async (ctx: ReturnType<typeof buildLegacyCtx>) => {
|
|
||||||
const parsedParams = querySchema.safeParse(ctx.params);
|
|
||||||
|
|
||||||
if (!parsedParams.success) {
|
|
||||||
notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const ssg = await ssgInit(ctx);
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: parsedParams.data.status,
|
|
||||||
dehydratedState: ssg.dehydrate(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WithLayout({ getLayout, getData })<"L">;
|
|
||||||
|
|
||||||
export const dynamic = "force-static";
|
|
||||||
@@ -1 +1,29 @@
|
|||||||
export { default } from "@pages/bookings/[status]";
|
import Page from "@pages/bookings/[status]";
|
||||||
|
import { withAppDirSsg } from "app/WithAppDirSsg";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
import type { InferGetStaticPropsType } from "next";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||||
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
|
|
||||||
|
import { getStaticProps } from "@lib/bookings/[status]/getStaticProps";
|
||||||
|
|
||||||
|
const validStatuses = ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] as const;
|
||||||
|
|
||||||
|
type Y = InferGetStaticPropsType<typeof getStaticProps>;
|
||||||
|
const getData = withAppDirSsg<Y>(getStaticProps);
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => `${APP_NAME} | ${t("bookings")}`,
|
||||||
|
() => ""
|
||||||
|
);
|
||||||
|
|
||||||
|
export const generateStaticParams = async () => {
|
||||||
|
return validStatuses.map((status) => ({ status }));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout, getData, Page })<"P">;
|
||||||
|
|
||||||
|
export const dynamic = "force-static";
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import LegacyPage from "@pages/connect-and-join";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, Page: LegacyPage })<"P">;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import LegacyPage from "@pages/d/[link]/[slug]";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import type { Params, SearchParams } from "app/_types";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
import { cookies, headers } from "next/headers";
|
||||||
|
|
||||||
|
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||||
|
import { getServerSideProps } from "@lib/d/[link]/[slug]/getServerSideProps";
|
||||||
|
|
||||||
|
export const generateMetadata = async ({
|
||||||
|
params,
|
||||||
|
searchParams,
|
||||||
|
}: {
|
||||||
|
params: Params;
|
||||||
|
searchParams: SearchParams;
|
||||||
|
}) => {
|
||||||
|
const pageProps = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams));
|
||||||
|
|
||||||
|
const { entity, booking, user, slug, isTeamEvent } = pageProps;
|
||||||
|
const rescheduleUid = booking?.uid;
|
||||||
|
const { trpc } = await import("@calcom/trpc");
|
||||||
|
const { data: event } = trpc.viewer.public.event.useQuery(
|
||||||
|
{ username: user ?? "", eventSlug: slug ?? "", isTeamEvent, org: entity.orgSlug ?? null },
|
||||||
|
{ refetchOnWindowFocus: false }
|
||||||
|
);
|
||||||
|
const profileName = event?.profile?.name ?? "";
|
||||||
|
const title = event?.title ?? "";
|
||||||
|
return await _generateMetadata(
|
||||||
|
(t) => `${rescheduleUid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
|
||||||
|
(t) => `${rescheduleUid ? t("reschedule") : ""} ${title}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = withAppDirSsr(getServerSideProps);
|
||||||
|
export default WithLayout({ getLayout: null, Page: LegacyPage, getData })<"P">;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||||
|
|
||||||
|
import EnterprisePage from "@components/EnterprisePage";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("create_your_org"),
|
||||||
|
(t) => t("create_your_org_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout, Page: EnterprisePage })<"P">;
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { WithLayout } from "app/layoutHOC";
|
|
||||||
|
|
||||||
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
|
||||||
|
|
||||||
export default WithLayout({ getLayout })<"L">;
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
import EventTypes from "@pages/event-types";
|
import EventTypes from "@pages/event-types";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||||
|
|
||||||
export const generateMetadata = async () =>
|
export const generateMetadata = async () =>
|
||||||
await _generateMetadata(
|
await _generateMetadata(
|
||||||
@@ -7,4 +10,4 @@ export const generateMetadata = async () =>
|
|||||||
(t) => t("event_types_page_subtitle")
|
(t) => t("event_types_page_subtitle")
|
||||||
);
|
);
|
||||||
|
|
||||||
export default EventTypes;
|
export default WithLayout({ getLayout, Page: EventTypes })<"P">;
|
||||||
@@ -1,63 +1,7 @@
|
|||||||
import LegacyPage from "@pages/getting-started/[[...step]]";
|
import Page from "@pages/getting-started/[[...step]]";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
import { WithLayout } from "app/layoutHOC";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import { cookies, headers } from "next/headers";
|
|
||||||
import { redirect } from "next/navigation";
|
|
||||||
|
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
import { getServerSideProps } from "@lib/getting-started/[[...step]]/getServerSideProps";
|
||||||
import prisma from "@calcom/prisma";
|
|
||||||
|
|
||||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
export default WithLayout({ getLayout: null, getData: withAppDirSsr(getServerSideProps), Page });
|
||||||
|
|
||||||
import { ssrInit } from "@server/lib/ssr";
|
|
||||||
|
|
||||||
const getData = async (ctx: ReturnType<typeof buildLegacyCtx>) => {
|
|
||||||
const req = { headers: headers(), cookies: cookies() };
|
|
||||||
|
|
||||||
//@ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest
|
|
||||||
const session = await getServerSession({ req });
|
|
||||||
|
|
||||||
if (!session?.user?.id) {
|
|
||||||
return redirect("/auth/login");
|
|
||||||
}
|
|
||||||
// @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'.
|
|
||||||
const ssr = await ssrInit(ctx);
|
|
||||||
await ssr.viewer.me.prefetch();
|
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
|
||||||
where: {
|
|
||||||
id: session.user.id,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
completedOnboarding: true,
|
|
||||||
teams: {
|
|
||||||
select: {
|
|
||||||
accepted: true,
|
|
||||||
team: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
logo: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
throw new Error("User from session not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.completedOnboarding) {
|
|
||||||
redirect("/event-types");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
dehydratedState: ssr.dehydrate(),
|
|
||||||
hasPendingInvites: user.teams.find((team: any) => team.accepted === false) ?? false,
|
|
||||||
requiresLicense: false,
|
|
||||||
themeBasis: null,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WithLayout({ getLayout: null, getData, Page: LegacyPage });
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import LegacyPage from "@pages/insights/index";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@lib/insights/getServerSideProps";
|
||||||
|
import { type inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
() => "Insights",
|
||||||
|
(t) => t("insights_subtitle")
|
||||||
|
);
|
||||||
|
|
||||||
|
const getData = withAppDirSsr<inferSSRProps<typeof getServerSideProps>>(getServerSideProps);
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout, getData, Page: LegacyPage });
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import LegacyPage from "@pages/maintenance";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => `${t("under_maintenance")} | ${APP_NAME}`,
|
||||||
|
(t) => t("under_maintenance_description", { appName: APP_NAME })
|
||||||
|
);
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, Page: LegacyPage })<"P">;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import Page from "@pages/more";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, Page })<"P">;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { type PageProps } from "@pages/org/[orgSlug]/[user]/[type]";
|
||||||
|
import Page from "@pages/org/[orgSlug]/[user]/[type]/embed";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
|
||||||
|
|
||||||
|
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||||
|
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, getData: getEmbedData, isBookingPage: true, Page });
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import Page, { type PageProps } from "@pages/org/[orgSlug]/[user]/[type]";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
|
||||||
|
|
||||||
|
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, getData, isBookingPage: true, Page });
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { getServerSideProps, type PageProps } from "@pages/org/[orgSlug]/[user]";
|
||||||
|
import Page from "@pages/org/[orgSlug]/[user]/embed";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||||
|
|
||||||
|
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, getData: getEmbedData, isBookingPage: true, Page });
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Page, { getServerSideProps, type PageProps } from "@pages/org/[orgSlug]/[user]";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, getData, isBookingPage: true, Page });
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import PaymentPage from "@calcom/features/ee/payments/components/PaymentPage";
|
||||||
|
import { getServerSideProps, type PaymentPageProps } from "@calcom/features/ee/payments/pages/payment";
|
||||||
|
import { APP_NAME } from "@calcom/lib/constants";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
// the title does not contain the eventName as in the legacy page
|
||||||
|
(t) => `${t("payment")} | ${APP_NAME}`,
|
||||||
|
() => ""
|
||||||
|
);
|
||||||
|
|
||||||
|
const getData = withAppDirSsr<PaymentPageProps>(getServerSideProps);
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout: null, getData, Page: PaymentPage });
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { getServerSideProps } from "@pages/reschedule/[uid]";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import type { SearchParams } from "app/_types";
|
||||||
|
import type { Params } from "next/dist/shared/lib/router/utils/route-matcher";
|
||||||
|
import { cookies, headers } from "next/headers";
|
||||||
|
|
||||||
|
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||||
|
import withEmbedSsr from "@lib/withEmbedSsr";
|
||||||
|
|
||||||
|
type PageProps = Readonly<{
|
||||||
|
params: Params;
|
||||||
|
searchParams: SearchParams;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const Page = async ({ params, searchParams }: PageProps) => {
|
||||||
|
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
|
||||||
|
await withAppDirSsr(withEmbedSsr(getServerSideProps))(legacyCtx);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { getServerSideProps } from "@pages/reschedule/[uid]";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import type { SearchParams } from "app/_types";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import type { Params } from "next/dist/shared/lib/router/utils/route-matcher";
|
||||||
|
import { headers, cookies } from "next/headers";
|
||||||
|
|
||||||
|
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
() => "",
|
||||||
|
() => ""
|
||||||
|
);
|
||||||
|
|
||||||
|
type PageProps = Readonly<{
|
||||||
|
params: Params;
|
||||||
|
searchParams: SearchParams;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const getData = withAppDirSsr(getServerSideProps);
|
||||||
|
|
||||||
|
const Page = async ({ params, searchParams }: PageProps) => {
|
||||||
|
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
|
||||||
|
|
||||||
|
await getData(legacyCtx);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
const getPageProps = () => {
|
||||||
|
return redirect(`/apps/routing-forms/forms`);
|
||||||
|
};
|
||||||
|
const Page = () => {
|
||||||
|
getPageProps();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { type SearchParams } from "app/_types";
|
||||||
|
import { type GetServerSidePropsContext } from "next";
|
||||||
|
import type { Params } from "next/dist/shared/lib/router/utils/route-matcher";
|
||||||
|
import { cookies, headers } from "next/headers";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||||
|
|
||||||
|
const paramsSchema = z
|
||||||
|
.object({
|
||||||
|
pages: z.array(z.string()),
|
||||||
|
})
|
||||||
|
.catch({
|
||||||
|
pages: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const getPageProps = async (context: GetServerSidePropsContext) => {
|
||||||
|
const { pages } = paramsSchema.parse(context.params);
|
||||||
|
|
||||||
|
return redirect(`/apps/routing-forms/${pages.length ? pages.join("/") : ""}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
type PageProps = Readonly<{
|
||||||
|
params: Params;
|
||||||
|
searchParams: SearchParams;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const Page = async ({ params, searchParams }: PageProps) => {
|
||||||
|
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
|
||||||
|
await getPageProps(legacyCtx as unknown as GetServerSidePropsContext);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -1,36 +1,32 @@
|
|||||||
import { getServerCaller } from "app/_trpc/serverClient";
|
|
||||||
import { type Params } from "app/_types";
|
import { type Params } from "app/_types";
|
||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
import { cookies, headers } from "next/headers";
|
import { WithLayout } from "app/layoutHOC";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import Page from "@calcom/features/ee/users/pages/users-edit-view";
|
import Page from "@calcom/features/ee/users/pages/users-edit-view";
|
||||||
import prisma from "@calcom/prisma";
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
const userIdSchema = z.object({ id: z.coerce.number() });
|
const userIdSchema = z.object({ id: z.coerce.number() });
|
||||||
|
|
||||||
export const generateMetadata = async ({ params }: { params: Params }) => {
|
export const generateMetadata = async ({ params }: { params: Params }) => {
|
||||||
const input = userIdSchema.safeParse(params);
|
const input = userIdSchema.safeParse(params);
|
||||||
|
|
||||||
let title = "";
|
|
||||||
if (!input.success) {
|
if (!input.success) {
|
||||||
title = "Editing user";
|
return await _generateMetadata(
|
||||||
} else {
|
() => "",
|
||||||
const req = {
|
() => "Here you can edit a current user."
|
||||||
headers: headers(),
|
);
|
||||||
cookies: cookies(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest'
|
|
||||||
const data = await getServerCaller({ req, prisma }).viewer.users.get({ userId: input.data.id });
|
|
||||||
const { user } = data;
|
|
||||||
title = `Editing user: ${user.username}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const userId = input.data.id;
|
||||||
|
const { trpc } = await import("@calcom/trpc");
|
||||||
|
const [data] = trpc.viewer.users.get.useSuspenseQuery({ userId });
|
||||||
|
const { user } = data;
|
||||||
|
const title = `Editing user: ${user.username}`;
|
||||||
|
|
||||||
return await _generateMetadata(
|
return await _generateMetadata(
|
||||||
() => title,
|
() => title,
|
||||||
() => "Here you can edit a current user."
|
() => "Here you can edit a current user."
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Page;
|
export default WithLayout({ getLayout, Page })<"P">;
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
import Page from "@calcom/features/ee/users/pages/users-add-view";
|
import Page from "@calcom/features/ee/users/pages/users-add-view";
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
export const generateMetadata = async () =>
|
export const generateMetadata = async () =>
|
||||||
await _generateMetadata(
|
await _generateMetadata(
|
||||||
@@ -8,4 +10,4 @@ export const generateMetadata = async () =>
|
|||||||
() => "Here you can add a new user."
|
() => "Here you can add a new user."
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Page;
|
export default WithLayout({ getLayout, Page })<"P">;
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { _generateMetadata } from "app/_utils";
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
import Page from "@calcom/features/ee/users/pages/users-listing-view";
|
import Page from "@calcom/features/ee/users/pages/users-listing-view";
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
export const generateMetadata = async () =>
|
export const generateMetadata = async () =>
|
||||||
await _generateMetadata(
|
await _generateMetadata(
|
||||||
@@ -8,4 +10,4 @@ export const generateMetadata = async () =>
|
|||||||
() => "A list of all the users in your account including their name, title, email and role."
|
() => "A list of all the users in your account including their name, title, email and role."
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Page;
|
export default WithLayout({ getLayout, Page })<"P">;
|
||||||
+1
-1
@@ -2,4 +2,4 @@ import { WithLayout } from "app/layoutHOC";
|
|||||||
|
|
||||||
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
export default WithLayout({ getLayout })<"L">;
|
export default WithLayout({ getLayout });
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import Page from "@pages/settings/my-account/appearance";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("appearance"),
|
||||||
|
(t) => t("appearance_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout });
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import Page from "@pages/settings/my-account/calendars";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("calendars"),
|
||||||
|
(t) => t("calendars_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout });
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import Page from "@pages/settings/my-account/conferencing";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("conferencing"),
|
||||||
|
(t) => t("conferencing_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout });
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import Page from "@pages/settings/my-account/general";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("general"),
|
||||||
|
(t) => t("general_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout });
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import Page from "@pages/settings/my-account/profile";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("profile"),
|
||||||
|
(t) => t("profile_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import LegacyPage, { WrappedAboutOrganizationPage } from "@pages/settings/organizations/[id]/about";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("about_your_organization"),
|
||||||
|
(t) => t("about_your_organization_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default WithLayout({ Page: LegacyPage, getLayout: WrappedAboutOrganizationPage });
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import LegacyPage, { WrapperAddNewTeamsPage } from "@pages/settings/organizations/[id]/add-teams";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("create_your_teams"),
|
||||||
|
(t) => t("create_your_teams_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default WithLayout({ Page: LegacyPage, getLayout: WrapperAddNewTeamsPage });
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import LegacyPage, {
|
||||||
|
buildWrappedOnboardTeamMembersPage,
|
||||||
|
} from "@pages/settings/organizations/[id]/onboard-members";
|
||||||
|
import { type Params } from "app/_types";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { headers } from "next/headers";
|
||||||
|
|
||||||
|
import PageWrapper from "@components/PageWrapperAppDir";
|
||||||
|
|
||||||
|
type PageProps = Readonly<{
|
||||||
|
params: Params;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("invite_organization_admins"),
|
||||||
|
(t) => t("invite_organization_admins_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
const Page = ({ params }: PageProps) => {
|
||||||
|
const h = headers();
|
||||||
|
const nonce = h.get("x-nonce") ?? undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageWrapper
|
||||||
|
getLayout={(page: React.ReactElement) => buildWrappedOnboardTeamMembersPage(params.id, page)}
|
||||||
|
requiresLicense={false}
|
||||||
|
nonce={nonce}
|
||||||
|
themeBasis={null}>
|
||||||
|
<LegacyPage />
|
||||||
|
</PageWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import LegacyPage, { WrappedSetPasswordPage } from "@pages/settings/organizations/[id]/set-password";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("set_a_password"),
|
||||||
|
(t) => t("set_a_password_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default WithLayout({ Page: LegacyPage, getLayout: WrappedSetPasswordPage });
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout });
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
|
import Page from "@calcom/features/ee/organizations/pages/settings/appearance";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("appearance"),
|
||||||
|
(t) => t("appearance_org_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout });
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import Page from "@pages/settings/billing/index";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("billing"),
|
||||||
|
(t) => t("manage_billing_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout });
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
|
import Page from "@calcom/features/ee/organizations/pages/settings/general";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("general"),
|
||||||
|
(t) => t("general_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout });
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
|
import Page from "@calcom/features/ee/organizations/pages/settings/members";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("organization_members"),
|
||||||
|
(t) => t("organization_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import LegacyPage, { getServerSideProps, LayoutWrapperAppDir } from "@pages/settings/organizations/new/index";
|
||||||
|
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { type inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("set_up_your_organization"),
|
||||||
|
(t) => t("organizations_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default WithLayout({
|
||||||
|
getLayout: LayoutWrapperAppDir,
|
||||||
|
Page: LegacyPage,
|
||||||
|
getData: withAppDirSsr<inferSSRProps<typeof getServerSideProps>>(getServerSideProps),
|
||||||
|
});
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { WithLayout } from "app/layoutHOC";
|
||||||
|
|
||||||
|
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||||
|
|
||||||
|
export default WithLayout({ getLayout });
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { _generateMetadata } from "app/_utils";
|
||||||
|
|
||||||
|
import Page from "@calcom/features/ee/organizations/pages/settings/profile";
|
||||||
|
|
||||||
|
export const generateMetadata = async () =>
|
||||||
|
await _generateMetadata(
|
||||||
|
(t) => t("profile"),
|
||||||
|
(t) => t("profile_org_description")
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
Loaded 100 of 719 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user