Compare commits

..
29 Commits
Author SHA1 Message Date
Keith Williams df91dacc3f v3.7.2
release.yml / release (push) Canceled after 0s
2024-01-17 10:59:04 -03:00
Hariom Balhara 396b81af58 Skip code verification for admin (#13278) 2024-01-17 13:21:30 +00:00
DmytroHryshyn 8467a9c53b fix: fix build error caused by 'export' keyword before getData (#13276) 2024-01-17 12:46:45 +00:00
DmytroHryshyn e99e8b169a chore: [app-router-migration 28] Migrate org/[orgSlug]/[user] page group (#13100)
## What does this PR do?

<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->

Fixes # (issue)

<!-- Please provide a loom video for visual changes to speed up reviews
 Loom Video: https://www.loom.com/
-->

## Requirement/Documentation

<!-- Please provide all documents that are important to understand the reason of that PR. -->

- If there is a requirement document, please, share it here.
- If there is ab UI/UX design document, please, share it here.

## Type of change

<!-- Please delete bullets that are not relevant. -->

- Bug fix (non-breaking change which fixes an issue)
- Chore (refactoring code, technical debt, workflow improvements)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing functionality to not work as expected)
- Tests (Unit/Integration/E2E or any other test)
- This change requires a documentation update

## How should this be tested?

<!-- Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. Write details that help to start the tests -->

- Are there environment variables that should be set?
- What are the minimal test data to have?
- What is expected (happy path) to have (input and output)?
- Any other important info that could help to test that PR

## Mandatory Tasks

- [ ] Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.

## Checklist

<!-- Remove bullet points below that don't apply to you -->

- I haven't read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- My code doesn't follow the style guidelines of this project
- I haven't commented my code, particularly in hard-to-understand areas
- I haven't checked if my PR needs changes to the documentation
- I haven't checked if my changes generate no new warnings
- I haven't added tests that prove my fix is effective or that my feature works
- I haven't checked if new and existing unit tests pass locally with my changes
2024-01-16 13:17:58 -07:00
zomars 7b8902a769 Update yarn.lock
release.yml / release (push) Canceled after 0s
2024-01-16 12:21:07 -07:00
zomars 2408338ed4 Revert "chore: Chore team metadata table + isOrganization migration from metadata (#12828)"
This reverts commit aabfd3b3f6.
2024-01-16 12:20:51 -07:00
Manavpreet SinghandManavpreet Singh 8f0ff58bcf fix: datepicker border color fix (#13262)
Co-authored-by: Manavpreet Singh <119919977+manavpreetsingh@users.noreply.github.com>
2024-01-16 18:42:08 +00:00
Omar López fac47544d1 v3.7.1 2024-01-16 11:39:25 -07:00
Benny Joo b09f3e5c8d chore: [app-router-migration 37] migrate connect and join page (#13205)
* Migrate Connect And Join page

* make legacy page client
2024-01-16 18:28:19 +00:00
Riddhesh Mahajan 8bb051ac4e fix: improve slug filtering to prevent same slug with diff case (#13074) 2024-01-16 18:27:34 +00:00
Dexter Storey fd3fffd7dd feat: add booking date range filters and freeze GPT-4 version (#12508)
* add booking date range filters

* fix dateFrom, dateTo strings

* simplify temporal filters

* Freeze GPT-4-0613

* parse and validate booking dateFrom and dateTo fields

* add weekday to agent prompt
2024-01-16 18:26:39 +00:00
Lauris SkraucisandOmar López 312a3f2543 refactor: API POST /teams (#13211)
Co-authored-by: Omar López <zomars@me.com>
2024-01-16 17:47:46 +00:00
Peer RichelsenandKeith Williams f52f2f32fa chore: added dark mode image for signup (#13251)
* added dark mode image for signup

* fixed dark mode border

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2024-01-16 17:01:08 +00:00
Crowdin Bot fdd0a5a03d New Crowdin translations by Github Action 2024-01-16 16:15:13 +00:00
ca3e3600f7 test: Check the workflow tab and your functionalities (teste2e-workflow) (#12474)
* test: Check the workflow tab

* fix type

---------

Co-authored-by: gitstart-calcom <gitstart-calcom@users.noreply.github.com>
Co-authored-by: GitStart-Cal.com <121884634+gitstart-calcom@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-01-16 11:11:36 -05:00
Morgan ba944b471a refactor: app-store imports webhook.ts from @calcom/web (#13257) 2024-01-16 15:10:58 +00:00
Keith Williams 7978f0317a chore: Revert bundle analysis text formatting (#13256) 2024-01-16 14:57:45 +00:00
emmanuel d918f34ae8 Update README.md (#13246) 2024-01-16 07:39:42 -03:00
Udit Takkar 1fed08fa9b fix: untranslated string in event (#13247) 2024-01-16 07:20:33 -03:00
aabfd3b3f6 chore: Chore team metadata table + isOrganization migration from metadata (#12828)
* Migration+schema changes

* Remove org utils from schema

* Users fixture

* Auth signup handlers

* checkIsOrg fn

* use team.isOrganization > metadata

* Add isOrganization to getTeamWithMembers query

* list handler + optimise queries

* Update trpc handlers to use team.isOrganization && orgSettings table

* Update fixture

* Fix typo in prisma.schema

* next auth options use organizationSettings instread of tema metadata

* Admin tooling

* Updating components to use orgsettings over metadata

* Add unique index + fix org lib files

* validateUsername

* Add a seperate unique constraint between isOrg and slug

* Invite Member Logic

* fix resend file

* force isOrg to exist

* Team settings

* update createOrUpdateMemberships

* fix: admin get handler

* fix: team list handler - improve select

* fix: update admin update handler

* fix: settings layout - team get select

* organziationSettings Schema + adminupdate

* organziationSettings Schema + adminupdate

* typefix onSubtmi

* Fix:team repo throwing error if no metadata is found

* fix: add onDelete to be cascade instead of restri

* fix: org - team invite e2e

* fix: team invites not working in subteam context

* rename to organizationID

* fix user fixture team names

* Fix the organization created in tests

* fix: update organization metadata

* variable rename

* Reuse isOrganization

* variable rename

* Remove unnecessary optional chaining

* Update packages/trpc/server/routers/viewer/organizations/adminUpdate.handler.ts

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>

* Update packages/trpc/server/routers/viewer/organizations/adminUpdate.handler.ts

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>

* Update packages/trpc/server/routers/viewer/organizations/create.handler.ts

---------

Co-authored-by: Hariom <hariombalhara@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2024-01-16 10:18:17 +00:00
sean-brydonandUdit Takkar 60e0bbf4b7 feat: impersonation improvements (#13066)
* fix: rename checkPermission to be more fitting

* feat:return to user

* nits: cleanup plus comments

* cleanup

* test: Add e2e

* fix: fix fixtures

* nit: cleanup logs

* chore: move to impersonatedBy in session

* fix: can return to self

* Update apps/web/playwright/impersonation.e2e.ts

* fix: typecheck

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-01-16 08:45:48 +00:00
0a7f65cd9b fix: Use Google recurring events protocol (#12651)
* working create event

--wip-- [skip ci]

fix bug related to adding event

--wip-- [skip ci]

--wip-- [skip ci]

add delete all

remove console.log

remove console.log

add patching of instances

fix yarn.lock

remove comments

* fixed typing

* use rrule

* rollback tsconfig changes

* fix comments

* fix comments

* rename db column

* fix delete all booking

* Update apps/web/pages/api/book/recurring-event.ts

Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>

* remove non-null assertion

* attempt to fix type errors

* fix recurring event logic

* remove skipAvailabilityCheck

---------

Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Hariom <hariombalhara@gmail.com>
2024-01-16 13:49:54 +05:30
Vishal Bisht f41f4b01a9 Fixed [CAL-2930] Org branding isnt centered in shell #13225 (#13237) 2024-01-16 03:02:46 +00:00
Keith Williams a7b38b1b58 feat: Allow admins to filter bookings only by attendee emails (#13236)
* chore: Allow admins to filter bookings only by attendee emails

* Fix type error
2024-01-15 23:36:39 +00:00
a0f1ceba19 refactor: Refactor seats logic (#12905)
* Refactor createBooking

* Type fix

* Abstract handleSeats

* Create Invitee type

* Create OrganizerUser type

* Abstract addVideoCallDataToEvt

* Abstract createLoggerWithEventDetails

* Abstract `handleAppStatus` from handler

* Create ReqAppsStatus type

* Move `deleteMeeting` and `getCalendar`

* Set parameters for `handleSeats`

* Typescript refactor

* Change function params from req

* Type fix

* Move handleSeats

* Abstract lastAttendeeDeleteBooking

* Create function for rescheduling seated events

* Fix imports on reschedule seats function

* Fix imports

* Import handleSeats function

* Fix rescheduleUid type

* Refactor owner reschedule to new time slot

* Refactor combine two booking times together

* Reschedule as an attendee

* Refactor createNewSeat

* Remove old handleSeats

* Remove lastAttendeeDeleteBooking from handleNewBooking

* Test for new attendee right params are passed

* Unit test params for reschedule

* Typo fix

* Clean up

* Create new seat test

* Test when attendee already signs up for booking

* Type fix

* Test reschedule move attendee to existing booking

* On reschedule create new booking

* Test on last attendee cancel booking

* Owner reschedule to new time slot

* Owner rescheduling, merge two bookings together

* Test: when merging more than available seats, then fail

* Test: fail when event is full

* Remove duplicate E2E tests

* Clean up

* Rename `addVideoCallDataToEvt` to `addVideoCallDataToEvent`

* Refactor `calcAppsStatus`

* Assign `evt` to resutl of `addVideoCallDataToEvent`

* Use prisma.transaction when moving attendees

* Clean create seat call

* Use ErrorCode enum

* Use attendeeRescheduledSeatedBooking function

* Await function

* Prevent double triggering of workflows

* Use inviteeToAdd in createNewSeat

* Remove unused error code

* Remove old handleSeats file

* Type fix

* Type fix

* Type fix

* Type fix

* Type fix

* Type fix

* Type fix

* Type fix

---------

Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Erik <erik@erosemberg.com>
2024-01-15 17:04:13 -05:00
Hariom Balhara 5c6e104ff5 fix: Team revert with members (#13228) 2024-01-15 21:26:55 +00:00
Udit Takkar 29b1f7bf51 chore: add error message for no availability (#13230)
* chore: add error message for no default user availability

* chore: check only availability

* chore: change message

* chore: add eventType
2024-01-15 16:04:12 -05:00
Udit TakkarandCarina Wollendorfer 7ec3d04acc test: add test for invalid cal video (#13103)
* test: add test for invalid cal video

* chore: add confirm

* chore: fix type error

* chore: update code

* test: add unit test

* chore: type error

* chore

* chore: type

* chore: fix test

* chore

---------

Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
2024-01-15 15:07:58 -05:00
Udit Takkar 13a765e50a test: In Team Event type when location is Organizer Default App and U… (#12939)
* test: In Team Event type when location is Organizer Default App and User Metadata is emoty

* chore: use variable

* fix: type error
2024-01-15 19:38:16 +00:00
94 changed files with 5226 additions and 2481 deletions

No files matched your search

+1 -1
View File
@@ -69,7 +69,7 @@ jobs:
body="${body//'%'/'%25'}" body="${body//'%'/'%25'}"
body="${body//$'\n'/'%0A'}" body="${body//$'\n'/'%0A'}"
body="${body//$'\r'/'%0D'}" body="${body//$'\r'/'%0D'}"
echo "{body}=${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
+1 -1
View File
@@ -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.
+3 -3
View File
@@ -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,
+9 -2
View File
@@ -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};`)
+2 -1
View File
@@ -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,
}); });
} }
+5
View File
@@ -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(),
+10 -1
View File
@@ -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);
+21 -4
View File
@@ -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)) };
} }
+131 -24
View File
@@ -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);
@@ -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,13 @@
import { type PageProps } from "@pages/org/[orgSlug]/[user]/[type]";
import Page from "@pages/org/[orgSlug]/[user]/[type]/embed";
import { withAppDir } from "app/AppDirSSRHOC";
import withEmbedSsrAppDir from "app/WithEmbedSSR";
import { WithLayout } from "app/layoutHOC";
import { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
const getData = withAppDir<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 { withAppDir } from "app/AppDirSSRHOC";
import { WithLayout } from "app/layoutHOC";
import { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
const getData = withAppDir<PageProps>(getServerSideProps);
export default WithLayout({ getLayout: null, getData, isBookingPage: true, Page });
@@ -0,0 +1,10 @@
import { getServerSideProps, type PageProps } from "@pages/org/[orgSlug]/[user]";
import Page from "@pages/org/[orgSlug]/[user]/embed";
import { withAppDir } from "app/AppDirSSRHOC";
import withEmbedSsrAppDir from "app/WithEmbedSSR";
import { WithLayout } from "app/layoutHOC";
const getData = withAppDir<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 { withAppDir } from "app/AppDirSSRHOC";
import { WithLayout } from "app/layoutHOC";
const getData = withAppDir<PageProps>(getServerSideProps);
export default WithLayout({ getLayout: null, getData, isBookingPage: true, Page });
+14 -2
View File
@@ -10,9 +10,15 @@ type WithLayoutParams<T extends Record<string, any>> = {
getLayout: ((page: React.ReactElement) => React.ReactNode) | null; getLayout: ((page: React.ReactElement) => React.ReactNode) | null;
Page?: (props: T) => React.ReactElement | null; Page?: (props: T) => React.ReactElement | null;
getData?: (arg: GetServerSidePropsContext) => Promise<T>; getData?: (arg: GetServerSidePropsContext) => Promise<T>;
isBookingPage?: boolean;
}; };
export function WithLayout<T extends Record<string, any>>({ getLayout, getData, Page }: WithLayoutParams<T>) { export function WithLayout<T extends Record<string, any>>({
getLayout,
getData,
Page,
isBookingPage,
}: WithLayoutParams<T>) {
return async <P extends "P" | "L">(p: P extends "P" ? PageProps : LayoutProps) => { return async <P extends "P" | "L">(p: P extends "P" ? PageProps : LayoutProps) => {
const h = headers(); const h = headers();
const nonce = h.get("x-nonce") ?? undefined; const nonce = h.get("x-nonce") ?? undefined;
@@ -23,7 +29,13 @@ export function WithLayout<T extends Record<string, any>>({ getLayout, getData,
const children = "children" in p ? p.children : null; const children = "children" in p ? p.children : null;
return ( return (
<PageWrapper getLayout={getLayout} requiresLicense={false} nonce={nonce} themeBasis={null} {...props}> <PageWrapper
getLayout={getLayout}
requiresLicense={false}
nonce={nonce}
themeBasis={null}
isBookingPage={isBookingPage}
{...props}>
{Page ? <Page {...props} /> : children} {Page ? <Page {...props} /> : children}
</PageWrapper> </PageWrapper>
); );
@@ -147,6 +147,7 @@ function BookingListItem(booking: BookingItemProps) {
? [ ? [
{ {
id: "confirm", id: "confirm",
bookingId: booking.id,
label: (isTabRecurring || isTabUnconfirmed) && isRecurring ? t("confirm_all") : t("confirm"), label: (isTabRecurring || isTabUnconfirmed) && isRecurring ? t("confirm_all") : t("confirm"),
onClick: () => { onClick: () => {
bookingConfirm(true); bookingConfirm(true);
@@ -0,0 +1,213 @@
import { type GetServerSidePropsContext } from "next";
import { z } from "zod";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { handleTypeRedirection } from "@calcom/features/booking-redirect/handle-type";
import { getBookingForReschedule, getBookingForSeatedEvent } from "@calcom/features/bookings/lib/get-booking";
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
import { orgDomainConfig, userOrgQuery } from "@calcom/features/ee/organizations/lib/orgDomains";
import { getUsernameList } from "@calcom/lib/defaultEvents";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import { RedirectType } from "@calcom/prisma/client";
import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect";
async function getDynamicGroupPageProps(context: GetServerSidePropsContext) {
const session = await getServerSession(context);
const { user: usernames, type: slug } = paramsSchema.parse(context.params);
const { rescheduleUid, bookingUid } = context.query;
const { ssrInit } = await import("@server/lib/ssr");
const ssr = await ssrInit(context);
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug);
const org = isValidOrgDomain ? currentOrgDomain : null;
if (!org) {
const redirect = await getTemporaryOrgRedirect({
slugs: usernames,
redirectType: RedirectType.User,
eventTypeSlug: slug,
currentQuery: context.query,
});
if (redirect) {
return redirect;
}
}
const users = await prisma.user.findMany({
where: {
username: {
in: usernames,
},
organization: isValidOrgDomain
? {
slug: currentOrgDomain,
}
: null,
},
select: {
allowDynamicBooking: true,
},
});
if (!users.length) {
return {
notFound: true,
} as const;
}
let booking: GetBookingType | null = null;
if (rescheduleUid) {
booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id);
} else if (bookingUid) {
booking = await getBookingForSeatedEvent(`${bookingUid}`);
}
// We use this to both prefetch the query on the server,
// as well as to check if the event exist, so we c an show a 404 otherwise.
const eventData = await ssr.viewer.public.event.fetch({
username: usernames.join("+"),
eventSlug: slug,
org,
});
if (!eventData) {
return {
notFound: true,
} as const;
}
return {
props: {
eventData: {
entity: eventData.entity,
length: eventData.length,
metadata: {
...eventData.metadata,
multipleDuration: [15, 30, 60],
},
},
booking,
user: usernames.join("+"),
slug,
away: false,
trpcState: ssr.dehydrate(),
isBrandingHidden: false,
isSEOIndexable: true,
themeBasis: null,
bookingUid: bookingUid ? `${bookingUid}` : null,
rescheduleUid: rescheduleUid ? `${rescheduleUid}` : null,
},
};
}
async function getUserPageProps(context: GetServerSidePropsContext) {
const session = await getServerSession(context);
const { user: usernames, type: slug } = paramsSchema.parse(context.params);
const username = usernames[0];
const { rescheduleUid, bookingUid } = context.query;
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug);
let outOfOffice = false;
const isOrgContext = currentOrgDomain && isValidOrgDomain;
if (!isOrgContext) {
const redirect = await getTemporaryOrgRedirect({
slugs: usernames,
redirectType: RedirectType.User,
eventTypeSlug: slug,
currentQuery: context.query,
});
if (redirect) {
return redirect;
}
}
const { ssrInit } = await import("@server/lib/ssr");
const ssr = await ssrInit(context);
const user = await prisma.user.findFirst({
where: {
username,
organization: userOrgQuery(context.req, context.params?.orgSlug),
},
select: {
id: true,
hideBranding: true,
allowSEOIndexing: true,
},
});
if (!user) {
return {
notFound: true,
} as const;
}
// If user is found, quickly verify bookingRedirects
const result = await handleTypeRedirection({
userId: user.id,
username,
slug,
});
if (result && result.outOfOffice) {
outOfOffice = true;
}
if (result && result.redirect?.destination) {
return result;
}
let booking: GetBookingType | null = null;
if (rescheduleUid) {
booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id);
} else if (bookingUid) {
booking = await getBookingForSeatedEvent(`${bookingUid}`);
}
const org = isValidOrgDomain ? currentOrgDomain : null;
// We use this to both prefetch the query on the server,
// as well as to check if the event exist, so we can show a 404 otherwise.
const eventData = await ssr.viewer.public.event.fetch({
username,
eventSlug: slug,
org,
});
if (!eventData) {
return {
notFound: true,
} as const;
}
return {
props: {
booking,
eventData: {
entity: eventData.entity,
length: eventData.length,
metadata: eventData.metadata,
},
away: outOfOffice,
user: username,
slug,
trpcState: ssr.dehydrate(),
isBrandingHidden: user?.hideBranding,
isSEOIndexable: user?.allowSEOIndexing,
themeBasis: username,
bookingUid: bookingUid ? `${bookingUid}` : null,
rescheduleUid: rescheduleUid ? `${rescheduleUid}` : null,
},
};
}
const paramsSchema = z.object({
type: z.string().transform((s) => slugify(s)),
user: z.string().transform((s) => getUsernameList(s)),
});
// Booker page fetches a tiny bit of data server side, to determine early
// whether the page should show an away state or dynamic booking not allowed.
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const { user } = paramsSchema.parse(context.params);
const isDynamicGroup = user.length > 1;
return isDynamicGroup ? await getDynamicGroupPageProps(context) : await getUserPageProps(context);
};
@@ -0,0 +1,58 @@
import type { GetServerSidePropsContext } from "next";
import z from "zod";
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import { getServerSideProps as GSSUserTypePage } from "@lib/[user]/[type]/getServerSideProps";
import { getServerSideProps as GSSTeamTypePage } from "@lib/team/[slug]/[type]/getServerSideProps";
const paramsSchema = z.object({
orgSlug: z.string().transform((s) => slugify(s)),
user: z.string(),
type: z.string().transform((s) => slugify(s)),
});
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const { user: teamOrUserSlugOrDynamicGroup, orgSlug, type } = paramsSchema.parse(ctx.params);
const team = await prisma.team.findFirst({
where: {
slug: slugify(teamOrUserSlugOrDynamicGroup),
parentId: {
not: null,
},
parent: getSlugOrRequestedSlug(orgSlug),
},
select: {
id: true,
},
});
if (team) {
const params = { slug: teamOrUserSlugOrDynamicGroup, type };
return GSSTeamTypePage({
...ctx,
params: {
...ctx.params,
...params,
},
query: {
...ctx.query,
...params,
},
});
}
const params = { user: teamOrUserSlugOrDynamicGroup, type };
return GSSUserTypePage({
...ctx,
params: {
...ctx.params,
...params,
},
query: {
...ctx.query,
...params,
},
});
};
@@ -0,0 +1,25 @@
import { getServerSideProps as GSSUserPage } from "@pages/[user]";
import { getServerSideProps as GSSTeamPage } from "@pages/team/[slug]";
import type { GetServerSidePropsContext } from "next";
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
import prisma from "@calcom/prisma";
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const team = await prisma.team.findFirst({
where: {
slug: ctx.query.user as string,
parentId: {
not: null,
},
parent: getSlugOrRequestedSlug(ctx.query.orgSlug as string),
},
select: {
id: true,
},
});
if (team) {
return GSSTeamPage({ ...ctx, query: { slug: ctx.query.user } });
}
return GSSUserPage({ ...ctx, query: { user: ctx.query.user, redirect: ctx.query.redirect } });
};
+162 -1
View File
@@ -4,6 +4,8 @@ import { describe, expect, it } from "vitest";
import type { z } from "zod"; import type { z } from "zod";
import { WEBSITE_URL } from "@calcom/lib/constants"; import { WEBSITE_URL } from "@calcom/lib/constants";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import type { MembershipRole, Prisma } from "@calcom/prisma/client"; import type { MembershipRole, Prisma } from "@calcom/prisma/client";
import { RedirectType } from "@calcom/prisma/enums"; import { RedirectType } from "@calcom/prisma/enums";
import type { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import type { teamMetadataSchema } from "@calcom/prisma/zod-utils";
@@ -1096,6 +1098,142 @@ describe("orgMigration", () => {
orgSlug: data.targetOrg.slug, orgSlug: data.targetOrg.slug,
}); });
}); });
it(`should remove a team from an organization with it's members`, async () => {
const data = {
teamToUnmigrate: {
name: "Team 1",
slug: "team1",
members: [
{
username: "user1-example",
email: "user1@example.com",
usernameBeforeMovingToOrg: "user1",
},
],
},
targetOrg: {
name: "Org 1",
slug: "org1",
},
};
const targetOrg = await prismock.team.create({
data: {
slug: data.targetOrg.slug,
name: data.targetOrg.name,
metadata: {
isOrganization: true,
},
},
});
const { id: teamToUnMigrateId } = await prismock.team.create({
data: {
slug: data.teamToUnmigrate.slug,
name: data.teamToUnmigrate.name,
parent: {
connect: {
id: targetOrg.id,
},
},
},
});
const teamToUnmigrate = await prismock.team.findUnique({
where: {
id: teamToUnMigrateId,
},
include: {
parent: true,
},
});
if (!teamToUnmigrate?.parent || !teamToUnmigrate.parentId) {
throw new Error(`Couldn't setup team to unmigrate properly ID:${teamToUnMigrateId}`);
}
const member1OfTeam = await createUserInsideTheOrg(
{
email: data.teamToUnmigrate.members[0].email,
username: data.teamToUnmigrate.members[0].username,
metadata: {
migratedToOrgFrom: {
username: data.teamToUnmigrate.members[0].usernameBeforeMovingToOrg,
},
},
},
targetOrg.id
);
addMemberShipOfUserWithOrg({
userId: member1OfTeam.id,
teamId: targetOrg.id,
role: "MEMBER",
accepted: true,
});
addMemberShipOfUserWithTeam({
teamId: teamToUnmigrate.id,
userId: member1OfTeam.id,
role: "MEMBER",
accepted: true,
});
expectUserToBeAPartOfOrg({
userId: member1OfTeam.id,
orgId: targetOrg.id,
usernameInOrg: data.teamToUnmigrate.members[0].username,
expectedMembership: {
role: "MEMBER",
accepted: true,
},
});
expectUserToBeAPartOfTeam({
userId: member1OfTeam.id,
teamId: teamToUnmigrate.id,
expectedMembership: {
role: "MEMBER",
accepted: true,
},
});
await removeTeamFromOrg({
teamId: teamToUnMigrateId,
targetOrgId: targetOrg.id,
});
await expectTeamToBeNotPartOfAnyOrganization({
teamId: teamToUnMigrateId,
});
expectTeamRedirectToBeNotEnabled({
from: {
teamSlug: data.teamToUnmigrate.slug,
},
to: data.teamToUnmigrate.slug,
orgSlug: data.targetOrg.slug,
});
expectUserRedirectToBeNotEnabled({
from: {
username: data.teamToUnmigrate.members[0].usernameBeforeMovingToOrg,
},
});
expectUserToBeNotAPartOfTheOrg({
userId: member1OfTeam.id,
orgId: targetOrg.id,
username: data.teamToUnmigrate.members[0].usernameBeforeMovingToOrg,
});
expectUserToBeNotAPartOfTheTeam({
userId: member1OfTeam.id,
teamId: targetOrg.id,
username: data.teamToUnmigrate.members[0].usernameBeforeMovingToOrg,
});
});
}); });
}); });
@@ -1184,6 +1322,22 @@ async function expectUserToBeNotAPartOfTheOrg({
userId: number; userId: number;
orgId: number; orgId: number;
username: string; username: string;
}) {
expectUserToBeNotAPartOfTheTeam({
userId,
teamId: orgId,
username,
});
}
async function expectUserToBeNotAPartOfTheTeam({
userId,
teamId,
username,
}: {
userId: number;
teamId: number;
username: string;
}) { }) {
const user = await prismock.user.findUnique({ const user = await prismock.user.findUnique({
where: { where: {
@@ -1201,7 +1355,7 @@ async function expectUserToBeNotAPartOfTheOrg({
expect(user.organizationId).toBe(null); expect(user.organizationId).toBe(null);
const membership = user.teams.find( const membership = user.teams.find(
(membership) => membership.teamId === orgId && membership.userId === userId (membership) => membership.teamId === teamId && membership.userId === userId
); );
expect(membership).toBeUndefined(); expect(membership).toBeUndefined();
@@ -1456,6 +1610,13 @@ async function createUserInsideTheOrg(
if (!org) { if (!org) {
throw new Error(`Org with id ${orgId} does not exist`); throw new Error(`Org with id ${orgId} does not exist`);
} }
logger.debug(
`Creating user inside org`,
safeStringify({
orgId,
data,
})
);
return await prismock.user.create({ return await prismock.user.create({
data: { data: {
...data, ...data,
+8 -4
View File
@@ -232,9 +232,13 @@ export async function moveTeamToOrg({
*/ */
export async function removeTeamFromOrg({ targetOrgId, teamId }: { targetOrgId: number; teamId: number }) { export async function removeTeamFromOrg({ targetOrgId, teamId }: { targetOrgId: number; teamId: number }) {
const removedTeam = await dbRemoveTeamFromOrg({ teamId }); const removedTeam = await dbRemoveTeamFromOrg({ teamId });
await removeTeamRedirect(removedTeam.slug); await removeTeamRedirect(removedTeam.slug);
for (const membership of removedTeam.members) {
await removeUserFromOrg({
userId: membership.userId,
targetOrgId,
});
}
log.debug(`Successfully removed team ${teamId} from org ${targetOrgId}`); log.debug(`Successfully removed team ${teamId} from org ${targetOrgId}`);
} }
@@ -745,8 +749,8 @@ async function dbRemoveTeamFromOrg({ teamId }: { teamId: number }) {
}, },
}, },
}, },
select: { include: {
slug: true, members: true,
}, },
}); });
} catch (e) { } catch (e) {
@@ -0,0 +1,102 @@
import type { GetServerSidePropsContext } from "next";
import { z } from "zod";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { getBookingForReschedule, getMultipleDurationValue } from "@calcom/features/bookings/lib/get-booking";
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import { RedirectType } from "@calcom/prisma/client";
import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect";
const paramsSchema = z.object({
type: z.string().transform((s) => slugify(s)),
slug: z.string().transform((s) => slugify(s)),
});
// Booker page fetches a tiny bit of data server side:
// 1. Check if team exists, to show 404
// 2. If rescheduling, get the booking details
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const session = await getServerSession(context);
const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(context.params);
const { rescheduleUid, duration: queryDuration, isInstantMeeting: queryIsInstantMeeting } = context.query;
const { ssrInit } = await import("@server/lib/ssr");
const ssr = await ssrInit(context);
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug);
const isOrgContext = currentOrgDomain && isValidOrgDomain;
if (!isOrgContext) {
const redirect = await getTemporaryOrgRedirect({
slugs: teamSlug,
redirectType: RedirectType.Team,
eventTypeSlug: meetingSlug,
currentQuery: context.query,
});
if (redirect) {
return redirect;
}
}
const team = await prisma.team.findFirst({
where: {
...getSlugOrRequestedSlug(teamSlug),
parent: isValidOrgDomain && currentOrgDomain ? getSlugOrRequestedSlug(currentOrgDomain) : null,
},
select: {
id: true,
hideBranding: true,
},
});
if (!team) {
return {
notFound: true,
} as const;
}
let booking: GetBookingType | null = null;
if (rescheduleUid) {
booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id);
}
const org = isValidOrgDomain ? currentOrgDomain : null;
// We use this to both prefetch the query on the server,
// as well as to check if the event exist, so we c an show a 404 otherwise.
const eventData = await ssr.viewer.public.event.fetch({
username: teamSlug,
eventSlug: meetingSlug,
isTeamEvent: true,
org,
});
if (!eventData) {
return {
notFound: true,
} as const;
}
return {
props: {
entity: eventData.entity,
duration: getMultipleDurationValue(
eventData.metadata?.multipleDuration,
queryDuration,
eventData.length
),
booking,
away: false,
user: teamSlug,
teamId: team.id,
slug: meetingSlug,
trpcState: ssr.dehydrate(),
isBrandingHidden: team?.hideBranding,
isInstantMeeting: eventData.isInstantEvent && queryIsInstantMeeting ? true : false,
themeBasis: null,
},
};
};
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@calcom/web", "name": "@calcom/web",
"version": "3.7.0", "version": "3.7.2",
"private": true, "private": true,
"scripts": { "scripts": {
"analyze": "ANALYZE=true next build", "analyze": "ANALYZE=true next build",
+5 -213
View File
@@ -1,28 +1,19 @@
import type { GetServerSidePropsContext } from "next"; "use client";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
import { z } from "zod";
import { Booker } from "@calcom/atoms"; import { Booker } from "@calcom/atoms";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { handleTypeRedirection } from "@calcom/features/booking-redirect/handle-type";
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses"; import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo"; import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
import { getBookingForReschedule, getBookingForSeatedEvent } from "@calcom/features/bookings/lib/get-booking";
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
import { orgDomainConfig, userOrgQuery } from "@calcom/features/ee/organizations/lib/orgDomains";
import { getUsernameList } from "@calcom/lib/defaultEvents";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import { RedirectType } from "@calcom/prisma/client";
import { getServerSideProps } from "@lib/[user]/[type]/getServerSideProps";
import type { inferSSRProps } from "@lib/types/inferSSRProps"; import type { inferSSRProps } from "@lib/types/inferSSRProps";
import type { EmbedProps } from "@lib/withEmbedSsr"; import type { EmbedProps } from "@lib/withEmbedSsr";
import PageWrapper from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper";
import { getTemporaryOrgRedirect } from "../../lib/getTemporaryOrgRedirect"; export type PageProps = Omit<inferSSRProps<typeof getServerSideProps>, "trpcState"> & EmbedProps;
export { getServerSideProps };
export type PageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;
export const getMultipleDurationValue = ( export const getMultipleDurationValue = (
multipleDurationConfig: number[] | undefined, multipleDurationConfig: number[] | undefined,
@@ -81,202 +72,3 @@ export default function Type({
Type.isBookingPage = true; Type.isBookingPage = true;
Type.PageWrapper = PageWrapper; Type.PageWrapper = PageWrapper;
async function getDynamicGroupPageProps(context: GetServerSidePropsContext) {
const session = await getServerSession(context);
const { user: usernames, type: slug } = paramsSchema.parse(context.params);
const { rescheduleUid, bookingUid } = context.query;
const { ssrInit } = await import("@server/lib/ssr");
const ssr = await ssrInit(context);
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug);
const org = isValidOrgDomain ? currentOrgDomain : null;
if (!org) {
const redirect = await getTemporaryOrgRedirect({
slugs: usernames,
redirectType: RedirectType.User,
eventTypeSlug: slug,
currentQuery: context.query,
});
if (redirect) {
return redirect;
}
}
const users = await prisma.user.findMany({
where: {
username: {
in: usernames,
},
organization: isValidOrgDomain
? {
slug: currentOrgDomain,
}
: null,
},
select: {
allowDynamicBooking: true,
},
});
if (!users.length) {
return {
notFound: true,
} as const;
}
let booking: GetBookingType | null = null;
if (rescheduleUid) {
booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id);
} else if (bookingUid) {
booking = await getBookingForSeatedEvent(`${bookingUid}`);
}
// We use this to both prefetch the query on the server,
// as well as to check if the event exist, so we c an show a 404 otherwise.
const eventData = await ssr.viewer.public.event.fetch({
username: usernames.join("+"),
eventSlug: slug,
org,
});
if (!eventData) {
return {
notFound: true,
} as const;
}
return {
props: {
eventData: {
entity: eventData.entity,
length: eventData.length,
metadata: {
...eventData.metadata,
multipleDuration: [15, 30, 60],
},
},
booking,
user: usernames.join("+"),
slug,
away: false,
trpcState: ssr.dehydrate(),
isBrandingHidden: false,
isSEOIndexable: true,
themeBasis: null,
bookingUid: bookingUid ? `${bookingUid}` : null,
rescheduleUid: rescheduleUid ? `${rescheduleUid}` : null,
},
};
}
async function getUserPageProps(context: GetServerSidePropsContext) {
const session = await getServerSession(context);
const { user: usernames, type: slug } = paramsSchema.parse(context.params);
const username = usernames[0];
const { rescheduleUid, bookingUid } = context.query;
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug);
let outOfOffice = false;
const isOrgContext = currentOrgDomain && isValidOrgDomain;
if (!isOrgContext) {
const redirect = await getTemporaryOrgRedirect({
slugs: usernames,
redirectType: RedirectType.User,
eventTypeSlug: slug,
currentQuery: context.query,
});
if (redirect) {
return redirect;
}
}
const { ssrInit } = await import("@server/lib/ssr");
const ssr = await ssrInit(context);
const user = await prisma.user.findFirst({
where: {
username,
organization: userOrgQuery(context.req, context.params?.orgSlug),
},
select: {
id: true,
hideBranding: true,
allowSEOIndexing: true,
},
});
if (!user) {
return {
notFound: true,
} as const;
}
// If user is found, quickly verify bookingRedirects
const result = await handleTypeRedirection({
userId: user.id,
username,
slug,
});
if (result && result.outOfOffice) {
outOfOffice = true;
}
if (result && result.redirect?.destination) {
return result;
}
let booking: GetBookingType | null = null;
if (rescheduleUid) {
booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id);
} else if (bookingUid) {
booking = await getBookingForSeatedEvent(`${bookingUid}`);
}
const org = isValidOrgDomain ? currentOrgDomain : null;
// We use this to both prefetch the query on the server,
// as well as to check if the event exist, so we can show a 404 otherwise.
const eventData = await ssr.viewer.public.event.fetch({
username,
eventSlug: slug,
org,
});
if (!eventData) {
return {
notFound: true,
} as const;
}
return {
props: {
booking,
eventData: {
entity: eventData.entity,
length: eventData.length,
metadata: eventData.metadata,
},
away: outOfOffice,
user: username,
slug,
trpcState: ssr.dehydrate(),
isBrandingHidden: user?.hideBranding,
isSEOIndexable: user?.allowSEOIndexing,
themeBasis: username,
bookingUid: bookingUid ? `${bookingUid}` : null,
rescheduleUid: rescheduleUid ? `${rescheduleUid}` : null,
},
};
}
const paramsSchema = z.object({
type: z.string().transform((s) => slugify(s)),
user: z.string().transform((s) => getUsernameList(s)),
});
// Booker page fetches a tiny bit of data server side, to determine early
// whether the page should show an away state or dynamic booking not allowed.
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const { user } = paramsSchema.parse(context.params);
const isDynamicGroup = user.length > 1;
return isDynamicGroup ? await getDynamicGroupPageProps(context) : await getUserPageProps(context);
};
+26 -5
View File
@@ -20,13 +20,16 @@ async function handler(req: NextApiRequest & { userId?: number }, res: NextApiRe
const data: RecurringBookingCreateBody[] = req.body; const data: RecurringBookingCreateBody[] = req.body;
const session = await getServerSession({ req, res }); const session = await getServerSession({ req, res });
const createdBookings: BookingResponse[] = []; const createdBookings: BookingResponse[] = [];
const allRecurringDates: string[] = data.map((booking) => booking.start); const allRecurringDates: { start: string | undefined; end: string | undefined }[] = data.map((booking) => {
return { start: booking.start, end: booking.end };
});
const appsStatus: AppsStatus[] | undefined = undefined; const appsStatus: AppsStatus[] | undefined = undefined;
/* To mimic API behavior and comply with types */ /* To mimic API behavior and comply with types */
req.userId = session?.user?.id || -1; req.userId = session?.user?.id || -1;
const numSlotsToCheckForAvailability = 2; const numSlotsToCheckForAvailability = 2;
let thirdPartyRecurringEventId = null;
for (let key = 0; key < data.length; key++) { for (let key = 0; key < data.length; key++) {
const booking = data[key]; const booking = data[key];
// Disable AppStatus in Recurring Booking Email as it requires us to iterate backwards to be able to compute the AppsStatus for all the bookings except the very first slot and then send that slot's email with statuses // Disable AppStatus in Recurring Booking Email as it requires us to iterate backwards to be able to compute the AppsStatus for all the bookings except the very first slot and then send that slot's email with statuses
@@ -53,16 +56,34 @@ async function handler(req: NextApiRequest & { userId?: number }, res: NextApiRe
...booking, ...booking,
appsStatus, appsStatus,
allRecurringDates, allRecurringDates,
isFirstRecurringSlot: key == 0,
thirdPartyRecurringEventId,
numSlotsToCheckForAvailability,
currentRecurringIndex: key, currentRecurringIndex: key,
noEmail: key !== 0, noEmail: key !== 0,
}; };
const eachRecurringBooking = await handleNewBooking(recurringEventReq, { const promiseEachRecurringBooking: ReturnType<typeof handleNewBooking> = handleNewBooking(
isNotAnApiCall: true, recurringEventReq,
skipAvailabilityCheck: key >= numSlotsToCheckForAvailability, {
}); isNotAnApiCall: true,
}
);
const eachRecurringBooking = await promiseEachRecurringBooking;
createdBookings.push(eachRecurringBooking); createdBookings.push(eachRecurringBooking);
if (!thirdPartyRecurringEventId) {
if (eachRecurringBooking.references && eachRecurringBooking.references.length > 0) {
for (const reference of eachRecurringBooking.references!) {
if (reference.thirdPartyRecurringEventId) {
thirdPartyRecurringEventId = reference.thirdPartyRecurringEventId;
break;
}
}
}
}
} }
return createdBookings; return createdBookings;
} }
+111
View File
@@ -0,0 +1,111 @@
import type { NextApiRequest, NextApiResponse } from "next";
import type Stripe from "stripe";
import { z } from "zod";
import stripe from "@calcom/features/ee/payments/server/stripe";
import { HttpError } from "@calcom/lib/http-error";
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
import prisma from "@calcom/prisma";
import { MembershipRole } from "@calcom/prisma/enums";
import { _MembershipModel as Membership, _TeamModel as Team } from "@calcom/prisma/zod";
const querySchema = z.object({
session_id: z.string().min(1),
});
const checkoutSessionMetadataSchema = z.object({
pendingPaymentTeamId: z.string().transform(Number),
ownerId: z.string().transform(Number),
});
type CheckoutSessionMetadata = z.infer<typeof checkoutSessionMetadataSchema>;
export const schemaTeamReadPublic = Team.omit({});
export const schemaMembershipPublic = Membership.merge(z.object({ team: Team }).partial());
async function handler(req: NextApiRequest, res: NextApiResponse) {
const checkoutSession = await getCheckoutSession(req);
validateCheckoutSession(checkoutSession);
const checkoutSessionSubscription = getCheckoutSessionSubscription(checkoutSession);
const checkoutSessionMetadata = getCheckoutSessionMetadata(checkoutSession);
const finalizedTeam = await prisma.team.update({
where: { id: checkoutSessionMetadata.pendingPaymentTeamId },
data: {
pendingPayment: false,
members: {
create: {
userId: checkoutSessionMetadata.ownerId as number,
role: MembershipRole.OWNER,
accepted: true,
},
},
metadata: {
paymentId: checkoutSession.id,
subscriptionId: checkoutSessionSubscription.id || null,
subscriptionItemId: checkoutSessionSubscription.items.data[0].id || null,
},
},
include: { members: true },
});
const response = JSON.stringify(
{
message: `Team created successfully. We also made user with ID=${checkoutSessionMetadata.ownerId} the owner of this team.`,
team: schemaTeamReadPublic.parse(finalizedTeam),
owner: schemaMembershipPublic.parse(finalizedTeam.members[0]),
},
null,
2
);
return res.status(200).send(response);
}
async function getCheckoutSession(req: NextApiRequest) {
const { session_id } = querySchema.parse(req.query);
const checkoutSession = await stripe.checkout.sessions.retrieve(session_id, {
expand: ["subscription"],
});
if (!checkoutSession) throw new HttpError({ statusCode: 404, message: "Checkout session not found" });
return checkoutSession;
}
function validateCheckoutSession(checkoutSession: Stripe.Response<Stripe.Checkout.Session>) {
if (checkoutSession.payment_status !== "paid")
throw new HttpError({ statusCode: 402, message: "Payment required" });
}
function getCheckoutSessionSubscription(checkoutSession: Stripe.Response<Stripe.Checkout.Session>) {
if (!checkoutSession.subscription) {
throw new HttpError({
statusCode: 400,
message: "Can't publish team/org without subscription",
});
}
return checkoutSession.subscription as Stripe.Subscription;
}
function getCheckoutSessionMetadata(
checkoutSession: Stripe.Response<Stripe.Checkout.Session>
): CheckoutSessionMetadata {
const parseCheckoutSessionMetadata = checkoutSessionMetadataSchema.safeParse(checkoutSession.metadata);
if (!parseCheckoutSessionMetadata.success) {
throw new HttpError({
statusCode: 400,
message: `Incorrect metadata in checkout session. Error: ${parseCheckoutSessionMetadata.error}`,
});
}
const checkoutSessionMetadata = parseCheckoutSessionMetadata.data;
return checkoutSessionMetadata;
}
export default defaultHandler({
GET: Promise.resolve({ default: defaultResponder(handler) }),
});
+2
View File
@@ -1,3 +1,5 @@
"use client";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { Trans } from "next-i18next"; import { Trans } from "next-i18next";
import Link from "next/link"; import Link from "next/link";
+6 -58
View File
@@ -1,69 +1,17 @@
import type { GetServerSidePropsContext } from "next"; "use client";
import z from "zod";
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import PageWrapper from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper";
import type { PageProps as UserTypePageProps } from "../../../[user]/[type]"; import type { PageProps as UserTypePageProps } from "../../../[user]/[type]";
import UserTypePage, { getServerSideProps as GSSUserTypePage } from "../../../[user]/[type]"; import UserTypePage from "../../../[user]/[type]";
import type { PageProps as TeamTypePageProps } from "../../../team/[slug]/[type]"; import type { PageProps as TeamTypePageProps } from "../../../team/[slug]/[type]";
import TeamTypePage, { getServerSideProps as GSSTeamTypePage } from "../../../team/[slug]/[type]"; import TeamTypePage from "../../../team/[slug]/[type]";
const paramsSchema = z.object({ export { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
orgSlug: z.string().transform((s) => slugify(s)),
user: z.string(),
type: z.string().transform((s) => slugify(s)),
});
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { export type PageProps = UserTypePageProps | TeamTypePageProps;
const { user: teamOrUserSlugOrDynamicGroup, orgSlug, type } = paramsSchema.parse(ctx.params);
const team = await prisma.team.findFirst({
where: {
slug: slugify(teamOrUserSlugOrDynamicGroup),
parentId: {
not: null,
},
parent: getSlugOrRequestedSlug(orgSlug),
},
select: {
id: true,
},
});
if (team) { export default function Page(props: PageProps) {
const params = { slug: teamOrUserSlugOrDynamicGroup, type };
return GSSTeamTypePage({
...ctx,
params: {
...ctx.params,
...params,
},
query: {
...ctx.query,
...params,
},
});
}
const params = { user: teamOrUserSlugOrDynamicGroup, type };
return GSSUserTypePage({
...ctx,
params: {
...ctx.params,
...params,
},
query: {
...ctx.query,
...params,
},
});
};
type Props = UserTypePageProps | TeamTypePageProps;
export default function Page(props: Props) {
if ((props as TeamTypePageProps)?.teamId) return <TeamTypePage {...(props as TeamTypePageProps)} />; if ((props as TeamTypePageProps)?.teamId) return <TeamTypePage {...(props as TeamTypePageProps)} />;
return <UserTypePage {...(props as UserTypePageProps)} />; return <UserTypePage {...(props as UserTypePageProps)} />;
} }
@@ -1,3 +1,5 @@
"use client";
import withEmbedSsr from "@lib/withEmbedSsr"; import withEmbedSsr from "@lib/withEmbedSsr";
import { getServerSideProps as _getServerSideProps } from "../[type]"; import { getServerSideProps as _getServerSideProps } from "../[type]";
@@ -1,3 +1,5 @@
"use client";
import withEmbedSsr from "@lib/withEmbedSsr"; import withEmbedSsr from "@lib/withEmbedSsr";
import { getServerSideProps as _getServerSideProps } from "../[user]"; import { getServerSideProps as _getServerSideProps } from "../[user]";
+6 -26
View File
@@ -1,37 +1,17 @@
import type { GetServerSidePropsContext } from "next"; "use client";
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
import prisma from "@calcom/prisma";
import PageWrapper from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper";
import type { UserPageProps } from "../../../[user]"; import type { UserPageProps } from "../../../[user]";
import UserPage, { getServerSideProps as GSSUserPage } from "../../../[user]"; import UserPage from "../../../[user]";
import type { PageProps as TeamPageProps } from "../../../team/[slug]"; import type { PageProps as TeamPageProps } from "../../../team/[slug]";
import TeamPage, { getServerSideProps as GSSTeamPage } from "../../../team/[slug]"; import TeamPage from "../../../team/[slug]";
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { export { getServerSideProps } from "@lib/org/[orgSlug]/[user]/getServerSideProps";
const team = await prisma.team.findFirst({
where: {
slug: ctx.query.user as string,
parentId: {
not: null,
},
parent: getSlugOrRequestedSlug(ctx.query.orgSlug as string),
},
select: {
id: true,
},
});
if (team) {
return GSSTeamPage({ ...ctx, query: { slug: ctx.query.user } });
}
return GSSUserPage({ ...ctx, query: { user: ctx.query.user, redirect: ctx.query.redirect } });
};
type Props = UserPageProps | TeamPageProps; export type PageProps = UserPageProps | TeamPageProps;
export default function Page(props: Props) { export default function Page(props: PageProps) {
if ((props as TeamPageProps)?.team) return <TeamPage {...(props as TeamPageProps)} />; if ((props as TeamPageProps)?.team) return <TeamPage {...(props as TeamPageProps)} />;
return <UserPage {...(props as UserPageProps)} />; return <UserPage {...(props as UserPageProps)} />;
} }
@@ -31,8 +31,11 @@ function AdminView() {
ref={usernameRef} ref={usernameRef}
hint={t("impersonate_user_tip")} hint={t("impersonate_user_tip")}
defaultValue={undefined} defaultValue={undefined}
data-testid="admin-impersonation-input"
/> />
<Button type="submit">{t("impersonate")}</Button> <Button type="submit" data-testid="impersonation-submit">
{t("impersonate")}
</Button>
</div> </div>
</form> </form>
</> </>
+7 -6
View File
@@ -520,12 +520,13 @@ export default function Signup({
</div> </div>
</> </>
)} )}
<div <div className="border-default hidden rounded-bl-2xl rounded-br-none rounded-tl-2xl border border-r-0 border-dashed bg-black/[3%] dark:bg-white/5 lg:block lg:py-[6px] lg:pl-[6px]">
className="border-default hidden rounded-bl-2xl rounded-br-none rounded-tl-2xl border border-r-0 border-dashed dark:border-0 lg:block lg:py-[6px] lg:pl-[6px]" <img className="block dark:hidden" src="/mock-event-type-list.svg" alt="Cal.com Booking Page" />
style={{ <img
backgroundColor: "rgba(236,237,239,0.9)", className="hidden dark:block"
}}> src="/mock-event-type-list-dark.svg"
<img src="/mock-event-type-list.svg" alt="Cal.com Booking Page" /> alt="Cal.com Booking Page"
/>
</div> </div>
<div className="mr-12 mt-8 hidden h-full w-full grid-cols-3 gap-4 overflow-hidden lg:grid"> <div className="mr-12 mt-8 hidden h-full w-full grid-cols-3 gap-4 overflow-hidden lg:grid">
{FEATURES.map((feature) => ( {FEATURES.map((feature) => (
+4 -101
View File
@@ -1,27 +1,19 @@
import type { GetServerSidePropsContext } from "next"; "use client";
import { z } from "zod";
import { Booker } from "@calcom/atoms"; import { Booker } from "@calcom/atoms";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses"; import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/getBookerWrapperClasses";
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo"; import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
import { getBookingForReschedule, getMultipleDurationValue } from "@calcom/features/bookings/lib/get-booking";
import type { GetBookingType } from "@calcom/features/bookings/lib/get-booking";
import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import { RedirectType } from "@calcom/prisma/client";
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import type { inferSSRProps } from "@lib/types/inferSSRProps"; import type { inferSSRProps } from "@lib/types/inferSSRProps";
import type { EmbedProps } from "@lib/withEmbedSsr"; import type { EmbedProps } from "@lib/withEmbedSsr";
import PageWrapper from "@components/PageWrapper"; import PageWrapper from "@components/PageWrapper";
import { getTemporaryOrgRedirect } from "../../../lib/getTemporaryOrgRedirect";
export type PageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps; export type PageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;
export { getServerSideProps };
export default function Type({ export default function Type({
slug, slug,
user, user,
@@ -61,92 +53,3 @@ export default function Type({
Type.PageWrapper = PageWrapper; Type.PageWrapper = PageWrapper;
Type.isBookingPage = true; Type.isBookingPage = true;
const paramsSchema = z.object({
type: z.string().transform((s) => slugify(s)),
slug: z.string().transform((s) => slugify(s)),
});
// Booker page fetches a tiny bit of data server side:
// 1. Check if team exists, to show 404
// 2. If rescheduling, get the booking details
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const session = await getServerSession(context);
const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(context.params);
const { rescheduleUid, duration: queryDuration, isInstantMeeting: queryIsInstantMeeting } = context.query;
const { ssrInit } = await import("@server/lib/ssr");
const ssr = await ssrInit(context);
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug);
const isOrgContext = currentOrgDomain && isValidOrgDomain;
if (!isOrgContext) {
const redirect = await getTemporaryOrgRedirect({
slugs: teamSlug,
redirectType: RedirectType.Team,
eventTypeSlug: meetingSlug,
currentQuery: context.query,
});
if (redirect) {
return redirect;
}
}
const team = await prisma.team.findFirst({
where: {
...getSlugOrRequestedSlug(teamSlug),
parent: isValidOrgDomain && currentOrgDomain ? getSlugOrRequestedSlug(currentOrgDomain) : null,
},
select: {
id: true,
hideBranding: true,
},
});
if (!team) {
return {
notFound: true,
} as const;
}
let booking: GetBookingType | null = null;
if (rescheduleUid) {
booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id);
}
const org = isValidOrgDomain ? currentOrgDomain : null;
// We use this to both prefetch the query on the server,
// as well as to check if the event exist, so we c an show a 404 otherwise.
const eventData = await ssr.viewer.public.event.fetch({
username: teamSlug,
eventSlug: meetingSlug,
isTeamEvent: true,
org,
});
if (!eventData) {
return {
notFound: true,
} as const;
}
return {
props: {
entity: eventData.entity,
duration: getMultipleDurationValue(
eventData.metadata?.multipleDuration,
queryDuration,
eventData.length
),
booking,
away: false,
user: teamSlug,
teamId: team.id,
slug: meetingSlug,
trpcState: ssr.dehydrate(),
isBrandingHidden: team?.hideBranding,
isInstantMeeting: eventData.isInstantEvent && queryIsInstantMeeting ? true : false,
themeBasis: null,
},
};
};
+5 -6
View File
@@ -8,6 +8,7 @@ import { useState, useEffect, useRef } from "react";
import dayjs from "@calcom/dayjs"; import dayjs from "@calcom/dayjs";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { getCalVideoReference } from "@calcom/features/get-cal-video-reference";
import classNames from "@calcom/lib/classNames"; import classNames from "@calcom/lib/classNames";
import { APP_NAME, SEO_IMG_OGIMG_VIDEO, WEBSITE_URL } from "@calcom/lib/constants"; import { APP_NAME, SEO_IMG_OGIMG_VIDEO, WEBSITE_URL } from "@calcom/lib/constants";
import { formatToLocalizedDate, formatToLocalizedTime } from "@calcom/lib/date-fns"; import { formatToLocalizedDate, formatToLocalizedTime } from "@calcom/lib/date-fns";
@@ -332,15 +333,13 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
bookRef.meetingPassword = null; bookRef.meetingPassword = null;
}); });
} }
const videoReference = getCalVideoReference(bookingObj.references);
const videoReferences = bookingObj.references.filter((reference) => reference.type.includes("_video"));
const latestVideoReference = videoReferences[videoReferences.length - 1];
return { return {
props: { props: {
meetingUrl: latestVideoReference.meetingUrl ?? "", meetingUrl: videoReference.meetingUrl ?? "",
...(typeof latestVideoReference.meetingPassword === "string" && { ...(typeof videoReference.meetingPassword === "string" && {
meetingPassword: latestVideoReference.meetingPassword, meetingPassword: videoReference.meetingPassword,
}), }),
booking: { booking: {
...bookingObj, ...bookingObj,
+3 -294
View File
@@ -1,5 +1,4 @@
import { expect } from "@playwright/test"; import { expect } from "@playwright/test";
import { uuid } from "short-uuid";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { randomString } from "@calcom/lib/random"; import { randomString } from "@calcom/lib/random";
@@ -8,7 +7,6 @@ import { BookingStatus } from "@calcom/prisma/enums";
import { test } from "./lib/fixtures"; import { test } from "./lib/fixtures";
import { import {
bookTimeSlot,
createNewSeatedEventType, createNewSeatedEventType,
selectFirstAvailableTimeSlotNextMonth, selectFirstAvailableTimeSlotNextMonth,
createUserWithSeatedEventAndAttendees, createUserWithSeatedEventAndAttendees,
@@ -29,75 +27,8 @@ test.describe("Booking with Seats", () => {
await expect(page.locator(`text=Event type updated successfully`)).toBeVisible(); await expect(page.locator(`text=Event type updated successfully`)).toBeVisible();
}); });
test("Multiple Attendees can book a seated event time slot", async ({ users, page }) => { test(`Prevent attendees from cancel when having invalid URL params`, async ({ page, users, bookings }) => {
const slug = "my-2-seated-event"; const { booking } = await createUserWithSeatedEventAndAttendees({ users, bookings }, [
const user = await users.create({
name: "Seated event user",
eventTypes: [
{
title: "My 2-seated event",
slug,
length: 60,
seatsPerTimeSlot: 2,
seatsShowAttendees: true,
},
],
});
await page.goto(`/${user.username}/${slug}`);
let bookingUrl = "";
await test.step("Attendee #1 can book a seated event time slot", async () => {
await selectFirstAvailableTimeSlotNextMonth(page);
await bookTimeSlot(page);
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
});
await test.step("Attendee #2 can book the same seated event time slot", async () => {
await page.goto(`/${user.username}/${slug}`);
await selectFirstAvailableTimeSlotNextMonth(page);
await page.waitForURL(/bookingUid/);
bookingUrl = page.url();
await bookTimeSlot(page, { email: "jane.doe@example.com", name: "Jane Doe" });
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
});
await test.step("Attendee #3 cannot click on the same seated event time slot", async () => {
await page.goto(`/${user.username}/${slug}`);
await page.click('[data-testid="incrementMonth"]');
// TODO: Find out why the first day is always booked on tests
await page.locator('[data-testid="day"][data-disabled="false"]').nth(0).click();
await expect(page.locator('[data-testid="time"][data-disabled="true"]')).toBeVisible();
});
await test.step("Attendee #3 cannot book the same seated event time slot accessing via url", async () => {
await page.goto(bookingUrl);
await bookTimeSlot(page, { email: "rick@example.com", name: "Rick" });
await expect(page.locator("[data-testid=success-page]")).toBeHidden();
});
await test.step("User owner should have only 1 booking with 3 attendees", async () => {
// Make sure user owner has only 1 booking with 3 attendees
const bookings = await prisma.booking.findMany({
where: { eventTypeId: user.eventTypes.find((e) => e.slug === slug)?.id },
select: {
id: true,
attendees: {
select: {
id: true,
},
},
},
});
expect(bookings).toHaveLength(1);
expect(bookings[0].attendees).toHaveLength(2);
});
});
test(`Attendees can cancel a seated event time slot`, async ({ page, users, bookings }) => {
const { booking, user } = await createUserWithSeatedEventAndAttendees({ users, bookings }, [
{ name: "John First", email: "first+seats@cal.com", timeZone: "Europe/Berlin" }, { name: "John First", email: "first+seats@cal.com", timeZone: "Europe/Berlin" },
{ name: "Jane Second", email: "second+seats@cal.com", timeZone: "Europe/Berlin" }, { name: "Jane Second", email: "second+seats@cal.com", timeZone: "Europe/Berlin" },
{ name: "John Third", email: "third+seats@cal.com", timeZone: "Europe/Berlin" }, { name: "John Third", email: "third+seats@cal.com", timeZone: "Europe/Berlin" },
@@ -120,30 +51,6 @@ test.describe("Booking with Seats", () => {
data: bookingSeats, data: bookingSeats,
}); });
await test.step("Attendee #1 should be able to cancel their booking", async () => {
await page.goto(`/booking/${booking.uid}?seatReferenceUid=${bookingSeats[0].referenceUid}`);
await page.locator('[data-testid="cancel"]').click();
await page.fill('[data-testid="cancel_reason"]', "Double booked!");
await page.locator('[data-testid="confirm_cancel"]').click();
await page.waitForLoadState("networkidle");
await expect(page).toHaveURL(/\/booking\/.*/);
const cancelledHeadline = page.locator('[data-testid="cancelled-headline"]');
await expect(cancelledHeadline).toBeVisible();
// Old booking should still exist, with one less attendee
const updatedBooking = await prisma.booking.findFirst({
where: { id: bookingSeats[0].bookingId },
include: { attendees: true },
});
const attendeeIds = updatedBooking?.attendees.map(({ id }) => id);
expect(attendeeIds).toHaveLength(2);
expect(attendeeIds).not.toContain(bookingAttendees[0].id);
});
await test.step("Attendee #2 shouldn't be able to cancel booking using only booking/uid", async () => { await test.step("Attendee #2 shouldn't be able to cancel booking using only booking/uid", async () => {
await page.goto(`/booking/${booking.uid}`); await page.goto(`/booking/${booking.uid}`);
@@ -156,29 +63,6 @@ test.describe("Booking with Seats", () => {
// expect cancel button to don't be in the page // expect cancel button to don't be in the page
await expect(page.locator("[text=Cancel]")).toHaveCount(0); await expect(page.locator("[text=Cancel]")).toHaveCount(0);
}); });
await test.step("All attendees cancelling should delete the booking for the user", async () => {
// The remaining 2 attendees cancel
for (let i = 1; i < bookingSeats.length; i++) {
await page.goto(`/booking/${booking.uid}?seatReferenceUid=${bookingSeats[i].referenceUid}`);
await page.locator('[data-testid="cancel"]').click();
await page.fill('[data-testid="cancel_reason"]', "Double booked!");
await page.locator('[data-testid="confirm_cancel"]').click();
await expect(page).toHaveURL(/\/booking\/.*/);
const cancelledHeadline = page.locator('[data-testid="cancelled-headline"]');
await expect(cancelledHeadline).toBeVisible();
}
// Should expect old booking to be cancelled
const updatedBooking = await prisma.booking.findFirst({
where: { id: bookingSeats[0].bookingId },
});
expect(updatedBooking).not.toBeNull();
expect(updatedBooking?.status).toBe(BookingStatus.CANCELLED);
});
}); });
test("Owner shouldn't be able to cancel booking without login in", async ({ page, bookings, users }) => { test("Owner shouldn't be able to cancel booking without login in", async ({ page, bookings, users }) => {
@@ -224,181 +108,6 @@ test.describe("Booking with Seats", () => {
}); });
test.describe("Reschedule for booking with seats", () => { test.describe("Reschedule for booking with seats", () => {
test("Should reschedule booking with seats", async ({ page, users, bookings }) => {
const { booking } = await createUserWithSeatedEventAndAttendees({ users, bookings }, [
{ name: "John First", email: `first+seats-${uuid()}@cal.com`, timeZone: "Europe/Berlin" },
{ name: "Jane Second", email: `second+seats-${uuid()}@cal.com`, timeZone: "Europe/Berlin" },
{ name: "John Third", email: `third+seats-${uuid()}@cal.com`, timeZone: "Europe/Berlin" },
]);
const bookingAttendees = await prisma.attendee.findMany({
where: { bookingId: booking.id },
select: {
id: true,
email: true,
},
});
const bookingSeats = [
{ bookingId: booking.id, attendeeId: bookingAttendees[0].id, referenceUid: uuidv4() },
{ bookingId: booking.id, attendeeId: bookingAttendees[1].id, referenceUid: uuidv4() },
{ bookingId: booking.id, attendeeId: bookingAttendees[2].id, referenceUid: uuidv4() },
];
await prisma.bookingSeat.createMany({
data: bookingSeats,
});
const references = await prisma.bookingSeat.findMany({
where: { bookingId: booking.id },
});
await page.goto(`/reschedule/${references[2].referenceUid}`);
await selectFirstAvailableTimeSlotNextMonth(page);
// expect input to be filled with attendee number 3 data
const thirdAttendeeElement = await page.locator("input[name=name]");
const attendeeName = await thirdAttendeeElement.inputValue();
expect(attendeeName).toBe("John Third");
await page.locator('[data-testid="confirm-reschedule-button"]').click();
// should wait for URL but that path starts with booking/
await page.waitForURL(/\/booking\/.*/);
await expect(page).toHaveURL(/\/booking\/.*/);
// Should expect new booking to be created for John Third
const newBooking = await prisma.booking.findFirst({
where: {
attendees: {
some: { email: bookingAttendees[2].email },
},
},
include: { seatsReferences: true, attendees: true },
});
expect(newBooking?.status).toBe(BookingStatus.PENDING);
expect(newBooking?.attendees.length).toBe(1);
expect(newBooking?.attendees[0].name).toBe("John Third");
expect(newBooking?.seatsReferences.length).toBe(1);
// Should expect old booking to be accepted with two attendees
const oldBooking = await prisma.booking.findFirst({
where: { uid: booking.uid },
include: { seatsReferences: true, attendees: true },
});
expect(oldBooking?.status).toBe(BookingStatus.ACCEPTED);
expect(oldBooking?.attendees.length).toBe(2);
expect(oldBooking?.seatsReferences.length).toBe(2);
});
test("Should reschedule booking with seats and if everyone rescheduled it should be deleted", async ({
page,
users,
bookings,
}) => {
const { booking } = await createUserWithSeatedEventAndAttendees({ users, bookings }, [
{ name: "John First", email: "first+seats@cal.com", timeZone: "Europe/Berlin" },
{ name: "Jane Second", email: "second+seats@cal.com", timeZone: "Europe/Berlin" },
]);
const bookingAttendees = await prisma.attendee.findMany({
where: { bookingId: booking.id },
select: {
id: true,
},
});
const bookingSeats = [
{ bookingId: booking.id, attendeeId: bookingAttendees[0].id, referenceUid: uuidv4() },
{ bookingId: booking.id, attendeeId: bookingAttendees[1].id, referenceUid: uuidv4() },
];
await prisma.bookingSeat.createMany({
data: bookingSeats,
});
const references = await prisma.bookingSeat.findMany({
where: { bookingId: booking.id },
});
await page.goto(`/reschedule/${references[0].referenceUid}`);
await selectFirstAvailableTimeSlotNextMonth(page);
await page.locator('[data-testid="confirm-reschedule-button"]').click();
await page.waitForURL(/\/booking\/.*/);
await page.goto(`/reschedule/${references[1].referenceUid}`);
await selectFirstAvailableTimeSlotNextMonth(page);
await page.locator('[data-testid="confirm-reschedule-button"]').click();
// Using waitForUrl here fails the assertion `expect(oldBooking?.status).toBe(BookingStatus.CANCELLED);` probably because waitForUrl is considered complete before waitForNavigation and till that time the booking is not cancelled
await page.waitForURL(/\/booking\/.*/);
// Should expect old booking to be cancelled
const oldBooking = await prisma.booking.findFirst({
where: { uid: booking.uid },
include: {
seatsReferences: true,
attendees: true,
eventType: {
include: { users: true, hosts: true },
},
},
});
expect(oldBooking?.status).toBe(BookingStatus.CANCELLED);
});
test("Should cancel with seats and have no attendees and cancelled", async ({ page, users, bookings }) => {
const { user, booking } = await createUserWithSeatedEventAndAttendees({ users, bookings }, [
{ name: "John First", email: "first+seats@cal.com", timeZone: "Europe/Berlin" },
{ name: "Jane Second", email: "second+seats@cal.com", timeZone: "Europe/Berlin" },
]);
await user.apiLogin();
const oldBooking = await prisma.booking.findFirst({
where: { uid: booking.uid },
include: { seatsReferences: true, attendees: true },
});
const bookingAttendees = await prisma.attendee.findMany({
where: { bookingId: booking.id },
select: {
id: true,
},
});
const bookingSeats = [
{ bookingId: booking.id, attendeeId: bookingAttendees[0].id, referenceUid: uuidv4() },
{ bookingId: booking.id, attendeeId: bookingAttendees[1].id, referenceUid: uuidv4() },
];
await prisma.bookingSeat.createMany({
data: bookingSeats,
});
// Now we cancel the booking as the organizer
await page.goto(`/booking/${booking.uid}?cancel=true`);
await page.locator('[data-testid="confirm_cancel"]').click();
await expect(page).toHaveURL(/\/booking\/.*/);
// Should expect old booking to be cancelled
const updatedBooking = await prisma.booking.findFirst({
where: { uid: booking.uid },
include: { seatsReferences: true, attendees: true },
});
expect(oldBooking?.startTime).not.toBe(updatedBooking?.startTime);
});
test("If rescheduled/cancelled booking with seats it should display the correct number of seats", async ({ test("If rescheduled/cancelled booking with seats it should display the correct number of seats", async ({
page, page,
users, users,
@@ -457,7 +166,7 @@ test.describe("Reschedule for booking with seats", () => {
expect(await page.locator("text=9 / 10 Seats available").count()).toEqual(0); expect(await page.locator("text=9 / 10 Seats available").count()).toEqual(0);
}); });
test("Should cancel with seats but event should be still accesible and with one less attendee/seat", async ({ test("Should cancel with seats but event should be still accessible and with one less attendee/seat", async ({
page, page,
users, users,
bookings, bookings,
@@ -1,6 +1,7 @@
import { expect, type Page } from "@playwright/test"; import { expect, type Page } from "@playwright/test";
import dayjs from "@calcom/dayjs"; import dayjs from "@calcom/dayjs";
import type { MembershipRole } from "@calcom/prisma/enums";
import { localize } from "../lib/testUtils"; import { localize } from "../lib/testUtils";
import type { createUsersFixture } from "./users"; import type { createUsersFixture } from "./users";
@@ -179,6 +180,14 @@ export async function loginUser(users: UserFixture) {
await pro.apiLogin(); await pro.apiLogin();
} }
export async function loginUserWithTeam(users: UserFixture, role: MembershipRole) {
const pro = await users.create(
{ name: "testuser" },
{ hasTeam: true, teamRole: role, isOrg: true, hasSubteam: true }
);
await pro.apiLogin();
}
const goToNextMonthIfNoAvailabilities = async (eventTypePage: Page) => { const goToNextMonthIfNoAvailabilities = async (eventTypePage: Page) => {
try { try {
if (isLastDayOfMonth()) { if (isLastDayOfMonth()) {
+43 -2
View File
@@ -9,7 +9,7 @@ import stripe from "@calcom/features/ee/payments/server/stripe";
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability"; import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
import { WEBAPP_URL } from "@calcom/lib/constants"; import { WEBAPP_URL } from "@calcom/lib/constants";
import { prisma } from "@calcom/prisma"; import { prisma } from "@calcom/prisma";
import { MembershipRole, SchedulingType } from "@calcom/prisma/enums"; import { MembershipRole, SchedulingType, TimeUnit, WorkflowTriggerEvents } from "@calcom/prisma/enums";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import type { Schedule } from "@calcom/types/schedule"; import type { Schedule } from "@calcom/types/schedule";
@@ -27,6 +27,7 @@ type UserFixture = ReturnType<typeof createUserFixture>;
const userIncludes = PrismaType.validator<PrismaType.UserInclude>()({ const userIncludes = PrismaType.validator<PrismaType.UserInclude>()({
eventTypes: true, eventTypes: true,
workflows: true,
credentials: true, credentials: true,
routingForms: true, routingForms: true,
}); });
@@ -42,6 +43,19 @@ const seededForm = {
type UserWithIncludes = PrismaType.UserGetPayload<typeof userWithEventTypes>; type UserWithIncludes = PrismaType.UserGetPayload<typeof userWithEventTypes>;
const createTeamWorkflow = async (user: { id: number }, team: { id: number }) => {
return await prisma.workflow.create({
data: {
name: "Team Workflow",
trigger: WorkflowTriggerEvents.BEFORE_EVENT,
time: 24,
timeUnit: TimeUnit.HOUR,
userId: user.id,
teamId: team.id,
},
});
};
const createTeamEventType = async ( const createTeamEventType = async (
user: { id: number }, user: { id: number },
team: { id: number }, team: { id: number },
@@ -120,6 +134,7 @@ const createTeamAndAddUser = async (
if (isOrg && hasSubteam) { if (isOrg && hasSubteam) {
const team = await createTeamAndAddUser({ user }, workerInfo); const team = await createTeamAndAddUser({ user }, workerInfo);
await createTeamEventType(user, team); await createTeamEventType(user, team);
await createTeamWorkflow(user, team);
data.children = { connect: [{ id: team.id }] }; data.children = { connect: [{ id: team.id }] };
} }
data.orgUsers = isOrg ? { connect: [{ id: user.id }] } : undefined; data.orgUsers = isOrg ? { connect: [{ id: user.id }] } : undefined;
@@ -170,6 +185,7 @@ export const createUsersFixture = (
scenario: { scenario: {
seedRoutingForms?: boolean; seedRoutingForms?: boolean;
hasTeam?: true; hasTeam?: true;
teamRole?: MembershipRole;
teammates?: CustomUserOpts[]; teammates?: CustomUserOpts[];
schedulingType?: SchedulingType; schedulingType?: SchedulingType;
teamEventTitle?: string; teamEventTitle?: string;
@@ -201,6 +217,18 @@ export const createUsersFixture = (
}); });
} }
const workflows: SupportedTestWorkflows[] = [
{ name: "Default Workflow", trigger: "NEW_EVENT" },
{ name: "Test Workflow", trigger: "EVENT_CANCELLED" },
...(opts?.workflows || []),
];
for (const workflowData of workflows) {
workflowData.user = { connect: { id: _user.id } };
await prisma.workflow.create({
data: workflowData,
});
}
if (scenario.seedRoutingForms) { if (scenario.seedRoutingForms) {
await prisma.app_RoutingForms_Form.create({ await prisma.app_RoutingForms_Form.create({
data: { data: {
@@ -324,7 +352,12 @@ export const createUsersFixture = (
if (scenario.hasTeam) { if (scenario.hasTeam) {
const team = await createTeamAndAddUser( const team = await createTeamAndAddUser(
{ {
user: { id: user.id, email: user.email, username: user.username, role: "OWNER" }, user: {
id: user.id,
email: user.email,
username: user.username,
role: scenario.teamRole || "OWNER",
},
isUnpublished: scenario.isUnpublished, isUnpublished: scenario.isUnpublished,
isOrg: scenario.isOrg, isOrg: scenario.isOrg,
isOrgVerified: scenario.isOrgVerified, isOrgVerified: scenario.isOrgVerified,
@@ -555,6 +588,9 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => {
type SupportedTestEventTypes = PrismaType.EventTypeCreateInput & { type SupportedTestEventTypes = PrismaType.EventTypeCreateInput & {
_bookings?: PrismaType.BookingCreateInput[]; _bookings?: PrismaType.BookingCreateInput[];
}; };
type SupportedTestWorkflows = PrismaType.WorkflowCreateInput;
type CustomUserOptsKeys = type CustomUserOptsKeys =
| "username" | "username"
| "password" | "password"
@@ -563,10 +599,13 @@ type CustomUserOptsKeys =
| "name" | "name"
| "email" | "email"
| "organizationId" | "organizationId"
| "twoFactorEnabled"
| "disableImpersonation"
| "role"; | "role";
type CustomUserOpts = Partial<Pick<Prisma.User, CustomUserOptsKeys>> & { type CustomUserOpts = Partial<Pick<Prisma.User, CustomUserOptsKeys>> & {
timeZone?: TimeZoneEnum; timeZone?: TimeZoneEnum;
eventTypes?: SupportedTestEventTypes[]; eventTypes?: SupportedTestEventTypes[];
workflows?: SupportedTestWorkflows[];
// ignores adding the worker-index after username // ignores adding the worker-index after username
useExactUsername?: boolean; useExactUsername?: boolean;
roleInOrganization?: MembershipRole; roleInOrganization?: MembershipRole;
@@ -594,6 +633,8 @@ const createUser = (
timeZone: opts?.timeZone ?? TimeZoneEnum.UK, timeZone: opts?.timeZone ?? TimeZoneEnum.UK,
locale: opts?.locale ?? "en", locale: opts?.locale ?? "en",
role: opts?.role ?? "USER", role: opts?.role ?? "USER",
twoFactorEnabled: opts?.twoFactorEnabled ?? false,
disableImpersonation: opts?.disableImpersonation ?? false,
...getOrganizationRelatedProps({ organizationId: opts?.organizationId, role: opts?.roleInOrganization }), ...getOrganizationRelatedProps({ organizationId: opts?.organizationId, role: opts?.roleInOrganization }),
schedules: schedules:
opts?.completedOnboarding ?? true opts?.completedOnboarding ?? true
+131
View File
@@ -0,0 +1,131 @@
import type { Locator } from "@playwright/test";
import { expect, type Page } from "@playwright/test";
import prisma from "@calcom/prisma";
import { WorkflowTriggerEvents } from "@calcom/prisma/enums";
import { localize } from "../lib/testUtils";
type CreateWorkflowProps = {
name?: string;
isTeam?: true;
trigger?: WorkflowTriggerEvents;
};
export function createWorkflowPageFixture(page: Page) {
const createWorkflow = async (props: CreateWorkflowProps) => {
const { name, isTeam, trigger } = props;
if (isTeam) {
await page.getByTestId("create-button-dropdown").click();
await page.getByTestId("option-team-1").click();
} else {
await page.getByTestId("create-button").click();
}
if (name) {
await fillNameInput(name);
}
if (trigger) {
page.locator("div").filter({ hasText: WorkflowTriggerEvents.BEFORE_EVENT }).nth(1);
page.getByText(trigger);
await selectEventType("30 min");
}
await saveWorkflow();
};
const saveWorkflow = async () => {
await page.getByTestId("save-workflow").click();
};
const assertListCount = async (count: number) => {
const workflowListCount = await page.locator('[data-testid="workflow-list"] > li');
await expect(workflowListCount).toHaveCount(count);
};
const fillNameInput = async (name: string) => {
await page.getByTestId("workflow-name").fill(name);
};
const editSelectedWorkflow = async (name: string) => {
const selectedWorkflow = page.getByTestId("workflow-list").getByTestId(nameToTestId(name));
const editButton = selectedWorkflow.getByRole("button").nth(0);
await editButton.click();
};
const hasWorkflowInList = async (name: string, negate?: true) => {
const selectedWorkflow = page.getByTestId("workflow-list").getByTestId(nameToTestId(name));
if (negate) {
await expect(selectedWorkflow).toBeHidden();
} else {
await expect(selectedWorkflow).toBeVisible();
}
};
const deleteAndConfirm = async (workflow: Locator) => {
const deleteButton = workflow.getByTestId("delete-button");
const confirmDeleteText = (await localize("en"))("confirm_delete_workflow");
await deleteButton.click();
await page.getByRole("button", { name: confirmDeleteText }).click();
};
const selectEventType = async (name: string) => {
await page.getByText("Select...").click();
await page.getByText(name, { exact: true }).click();
};
const hasReadonlyBadge = async () => {
const readOnlyBadge = page.getByText((await localize("en"))("readonly"));
await expect(readOnlyBadge).toBeVisible();
};
const selectedWorkflowPage = async (name: string) => {
await page.getByTestId("workflow-list").getByTestId(nameToTestId(name)).click();
};
const workflowOptionsAreDisabled = async (workflow: string, negate?: boolean) => {
const getWorkflowButton = async (buttonTestId: string) =>
page.getByTestId(nameToTestId(workflow)).getByTestId(buttonTestId);
const [editButton, deleteButton] = await Promise.all([
getWorkflowButton("edit-button"),
getWorkflowButton("delete-button"),
]);
expect(editButton.isDisabled()).toBeTruthy();
expect(deleteButton.isDisabled()).toBeTruthy();
};
const assertWorkflowReminders = async (eventTypeId: number, count: number) => {
const booking = await prisma.booking.findFirst({
where: {
eventTypeId,
},
});
const workflowReminders = await prisma.workflowReminder.findMany({
where: {
bookingUid: booking?.uid ?? "",
},
});
expect(workflowReminders).toHaveLength(count);
};
function nameToTestId(name: string) {
return `workflow-${name.split(" ").join("-").toLowerCase()}`;
}
return {
createWorkflow,
saveWorkflow,
assertListCount,
fillNameInput,
editSelectedWorkflow,
hasWorkflowInList,
deleteAndConfirm,
selectEventType,
hasReadonlyBadge,
selectedWorkflowPage,
workflowOptionsAreDisabled,
assertWorkflowReminders,
};
}
+50
View File
@@ -0,0 +1,50 @@
import { expect } from "@playwright/test";
import { test } from "./lib/fixtures";
test.describe.configure({ mode: "parallel" });
test.describe("Users can impersonate", async () => {
test.afterAll(async ({ users }) => {
await users.deleteAll();
});
test("App Admin can impersonate users with impersonation enabled", async ({ page, users }) => {
// log in trail user
const user = await users.create({
role: "ADMIN",
password: "ADMINadmin2022!",
});
const userToImpersonate = await users.create({ disableImpersonation: false });
await user.apiLogin();
await page.waitForLoadState();
await page.goto("/settings/admin/impersonation");
await page.waitForLoadState();
const adminInput = page.getByTestId("admin-impersonation-input");
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore the username does exist
await adminInput.fill(userToImpersonate.username);
await page.getByTestId("impersonation-submit").click();
// // Wait for sign in to complete
await page.waitForURL("/settings/my-account/profile");
const stopImpersonatingButton = page.getByTestId("stop-impersonating-button");
const impersonatedUsernameInput = page.locator("input[name='username']");
const impersonatedUser = await impersonatedUsernameInput.inputValue();
await expect(stopImpersonatingButton).toBeVisible();
await expect(impersonatedUser).toBe(userToImpersonate.username);
await stopImpersonatingButton.click();
await page.waitForLoadState("networkidle");
// Return to user
const ogUser = await impersonatedUsernameInput.inputValue();
expect(ogUser).toBe(user.username);
});
});
+6
View File
@@ -15,6 +15,7 @@ import { createBookingPageFixture } from "../fixtures/regularBookings";
import { createRoutingFormsFixture } from "../fixtures/routingForms"; import { createRoutingFormsFixture } from "../fixtures/routingForms";
import { createServersFixture } from "../fixtures/servers"; import { createServersFixture } from "../fixtures/servers";
import { createUsersFixture } from "../fixtures/users"; import { createUsersFixture } from "../fixtures/users";
import { createWorkflowPageFixture } from "../fixtures/workflows";
export interface Fixtures { export interface Fixtures {
page: Page; page: Page;
@@ -28,6 +29,7 @@ export interface Fixtures {
emails: ReturnType<typeof createEmailsFixture>; emails: ReturnType<typeof createEmailsFixture>;
routingForms: ReturnType<typeof createRoutingFormsFixture>; routingForms: ReturnType<typeof createRoutingFormsFixture>;
bookingPage: ReturnType<typeof createBookingPageFixture>; bookingPage: ReturnType<typeof createBookingPageFixture>;
workflowPage: ReturnType<typeof createWorkflowPageFixture>;
features: ReturnType<typeof createFeatureFixture>; features: ReturnType<typeof createFeatureFixture>;
eventTypePage: ReturnType<typeof createEventTypeFixture>; eventTypePage: ReturnType<typeof createEventTypeFixture>;
} }
@@ -94,6 +96,10 @@ export const test = base.extend<Fixtures>({
await features.init(); await features.init();
await use(features); await use(features);
}, },
workflowPage: async ({ page }, use) => {
const workflowPage = createWorkflowPageFixture(page);
await use(workflowPage);
},
eventTypePage: async ({ page }, use) => { eventTypePage: async ({ page }, use) => {
const eventTypePage = createEventTypeFixture(page); const eventTypePage = createEventTypeFixture(page);
await use(eventTypePage); await use(eventTypePage);
+4 -1
View File
@@ -113,7 +113,10 @@ export async function selectSecondAvailableTimeSlotNextMonth(page: Page) {
await page.locator('[data-testid="time"]').nth(0).click(); await page.locator('[data-testid="time"]').nth(0).click();
} }
async function bookEventOnThisPage(page: Page) { export async function bookEventOnThisPage(page: Page, goTo?: string) {
if (goTo) {
await page.goto(goTo);
}
await selectFirstAvailableTimeSlotNextMonth(page); await selectFirstAvailableTimeSlotNextMonth(page);
await bookTimeSlot(page); await bookTimeSlot(page);
+62 -1
View File
@@ -3,9 +3,10 @@ import { expect } from "@playwright/test";
import dayjs from "@calcom/dayjs"; import dayjs from "@calcom/dayjs";
import prisma from "@calcom/prisma"; import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums"; import { BookingStatus } from "@calcom/prisma/enums";
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
import { test } from "./lib/fixtures"; import { test } from "./lib/fixtures";
import { selectFirstAvailableTimeSlotNextMonth } from "./lib/testUtils"; import { selectFirstAvailableTimeSlotNextMonth, bookTimeSlot } from "./lib/testUtils";
const IS_STRIPE_ENABLED = !!( const IS_STRIPE_ENABLED = !!(
process.env.STRIPE_CLIENT_ID && process.env.STRIPE_CLIENT_ID &&
@@ -270,4 +271,64 @@ test.describe("Reschedule Tests", async () => {
await page.locator('[data-testid="confirm-reschedule-button"]').click(); await page.locator('[data-testid="confirm-reschedule-button"]').click();
await expect(page).toHaveURL(/.*booking/); await expect(page).toHaveURL(/.*booking/);
}); });
test("Should load Valid Cal video url after rescheduling Opt in events", async ({
page,
users,
bookings,
}) => {
const user = await users.create();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const eventType = user.eventTypes.find((e) => e.slug === "opt-in")!;
const confirmBooking = async (bookingId: number) => {
await user.apiLogin();
await page.goto("/bookings/upcoming");
const elem = await page.locator(`[data-bookingid="${bookingId}"][data-testid="confirm"]`);
await elem.click();
await page.getByTestId("toast-success").waitFor();
await user.logout();
};
await page.goto(`/${user.username}/${eventType.slug}`);
await selectFirstAvailableTimeSlotNextMonth(page);
await bookTimeSlot(page);
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
const pageUrl = new URL(page.url());
const pathSegments = pageUrl.pathname.split("/");
const bookingUID = pathSegments[pathSegments.length - 1];
const currentBooking = await prisma.booking.findFirst({ where: { uid: bookingUID } });
expect(currentBooking).not.toBeUndefined();
// eslint-disable-next-line playwright/no-conditional-in-test
if (currentBooking) {
await confirmBooking(currentBooking.id);
await page.goto(`/${user.username}/${eventType.slug}?rescheduleUid=${currentBooking.uid}`);
await selectFirstAvailableTimeSlotNextMonth(page);
await page.locator('[data-testid="confirm-reschedule-button"]').click();
await expect(page).toHaveURL(/.*booking/);
const newBooking = await prisma.booking.findFirst({ where: { fromReschedule: currentBooking.uid } });
expect(newBooking).not.toBeUndefined();
expect(newBooking?.status).toBe(BookingStatus.PENDING);
// eslint-disable-next-line playwright/no-conditional-in-test
if (newBooking) {
await confirmBooking(newBooking?.id);
const booking = await prisma.booking.findFirst({ where: { id: newBooking.id } });
expect(booking).not.toBeUndefined();
expect(booking?.status).toBe(BookingStatus.ACCEPTED);
const locationVideoCallUrl = bookingMetadataSchema.parse(booking?.metadata || {})?.videoCallUrl;
expect(locationVideoCallUrl).not.toBeUndefined();
// eslint-disable-next-line playwright/no-conditional-in-test
if (booking && locationVideoCallUrl) {
await page.goto(locationVideoCallUrl);
await expect(page.frameLocator("iFrame").locator('text="Continue"')).toBeVisible();
}
}
}
});
}); });
+63 -66
View File
@@ -1,80 +1,77 @@
import { expect } from "@playwright/test"; import { MembershipRole, WorkflowTriggerEvents } from "@calcom/prisma/enums";
import dayjs from "@calcom/dayjs";
import prisma from "@calcom/prisma";
import { WorkflowMethods } from "@calcom/prisma/enums";
import { loginUser, loginUserWithTeam } from "./fixtures/regularBookings";
import { test } from "./lib/fixtures"; import { test } from "./lib/fixtures";
import { selectSecondAvailableTimeSlotNextMonth, todo } from "./lib/testUtils"; import { bookEventOnThisPage } from "./lib/testUtils";
test.afterEach(({ users }) => users.deleteAll()); test.describe("Workflow Tab - Event Type", () => {
test.describe("Check the functionalities of the Workflow Tab", () => {
test.describe("User Workflows", () => {
test.beforeEach(async ({ page, users }) => {
await loginUser(users);
await page.goto("/workflows");
});
test.describe("Workflow tests", () => { test("Creating a new workflow", async ({ workflowPage }) => {
test.describe("User Workflows", () => { const { createWorkflow, assertListCount } = workflowPage;
// Fixme: This test is failing because the listing isn't immediately updated after the workflow is created
test.fixme( await createWorkflow({ name: "" });
"Create default reminder workflow & trigger when event type is booked", await assertListCount(3);
async ({ page, users }) => { });
const user = await users.create();
test("Editing an existing workflow", async ({ workflowPage }) => {
const { saveWorkflow, fillNameInput, editSelectedWorkflow, hasWorkflowInList } = workflowPage;
await editSelectedWorkflow("Test Workflow");
await fillNameInput("Edited Workflow");
await saveWorkflow();
await hasWorkflowInList("Edited Workflow");
});
test("Deleting an existing workflow", async ({ page, workflowPage }) => {
const { hasWorkflowInList, deleteAndConfirm, assertListCount } = workflowPage;
const firstWorkflow = page
.getByTestId("workflow-list")
.getByTestId(/workflow/)
.first();
await deleteAndConfirm(firstWorkflow);
await hasWorkflowInList("Edited Workflow", true);
await assertListCount(1);
});
test("Create an action and check if workflow is triggered", async ({ page, users, workflowPage }) => {
const { createWorkflow, assertWorkflowReminders } = workflowPage;
const [user] = users.get();
const [eventType] = user.eventTypes; const [eventType] = user.eventTypes;
await user.apiLogin();
await page.goto(`/workflows`);
await page.click('[data-testid="create-button"]'); await createWorkflow({ name: "A New Workflow", trigger: WorkflowTriggerEvents.NEW_EVENT });
await bookEventOnThisPage(page, `/${user.username}/${eventType.slug}`);
await assertWorkflowReminders(eventType.id, 1);
});
});
// select first event type test.describe("Team Workflows", () => {
await page.getByText("Select...").click(); test("Admin user", async ({ page, users, workflowPage }) => {
await page.getByText(eventType.title, { exact: true }).click(); const { createWorkflow, assertListCount } = workflowPage;
// name workflow await loginUserWithTeam(users, MembershipRole.ADMIN);
await page.fill('[data-testid="workflow-name"]', "Test workflow"); await page.goto("/workflows");
// save workflow await createWorkflow({ name: "A New Workflow", isTeam: true });
await page.click('[data-testid="save-workflow"]'); await assertListCount(4);
});
// check if workflow is saved test("Member user", async ({ page, users, workflowPage }) => {
await expect(page.locator('[data-testid="workflow-list"] > li')).toHaveCount(1); const { hasReadonlyBadge, selectedWorkflowPage, workflowOptionsAreDisabled } = workflowPage;
// book event type await loginUserWithTeam(users, MembershipRole.MEMBER);
await page.goto(`/${user.username}/${eventType.slug}`); await page.goto("/workflows");
await selectSecondAvailableTimeSlotNextMonth(page);
await page.fill('[name="name"]', "Test"); await workflowOptionsAreDisabled("Team Workflow");
await page.fill('[name="email"]', "test@example.com"); await selectedWorkflowPage("Team Workflow");
await page.press('[name="email"]', "Enter"); await hasReadonlyBadge();
});
// Make sure booking is completed });
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
const booking = await prisma.booking.findFirst({
where: {
eventTypeId: eventType.id,
},
});
// check if workflow triggered
const workflowReminders = await prisma.workflowReminder.findMany({
where: {
bookingUid: booking?.uid ?? "",
},
});
expect(workflowReminders).toHaveLength(1);
const scheduledDate = dayjs(booking?.startTime).subtract(1, "day").toDate();
expect(workflowReminders[0].method).toBe(WorkflowMethods.EMAIL);
expect(workflowReminders[0].scheduledDate.toISOString()).toBe(scheduledDate.toISOString());
}
);
// add all other actions to this workflow and test if they triggered
// cancel booking and test if workflow reminders are deleted
// test all other triggers
});
test.describe("Team Workflows", () => {
todo("Admin can create and update team workflow");
todo("Members can not create and update team workflows");
}); });
}); });
@@ -0,0 +1,192 @@
<svg width="681" height="520" viewBox="0 0 681 520" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g clip-path="url(#clip0_16843_34774)">
<rect width="274.93" height="519.295" transform="translate(0.724121 1)" fill="#1A1A1A"/>
<rect x="26.4222" y="26.6981" width="18.0973" height="18.0973" rx="9.04865" stroke="#383838" stroke-width="0.723892"/>
<circle cx="35.4709" cy="35.7468" r="8.6867" fill="url(#pattern0)"/>
<circle cx="35.4709" cy="35.7468" r="8.6867" fill="url(#pattern1)"/>
<path d="M51.8613 39.7466H50.2325L53.198 31.3231H55.0818L58.0514 39.7466H56.4226L54.1728 33.0506H54.107L51.8613 39.7466ZM51.9148 36.4438H56.3568V37.6695H51.9148V36.4438ZM60.584 31.3231V39.7466H59.0951V31.3231H60.584ZM64.9119 39.87C64.2785 39.87 63.7315 39.7384 63.2708 39.4751C62.8129 39.2091 62.4605 38.8335 62.2138 38.3482C61.967 37.8601 61.8436 37.2856 61.8436 36.6248C61.8436 35.9749 61.967 35.4046 62.2138 34.9138C62.4633 34.4202 62.8115 34.0363 63.2585 33.7621C63.7054 33.4852 64.2305 33.3467 64.8338 33.3467C65.2231 33.3467 65.5905 33.4098 65.936 33.5359C66.2843 33.6593 66.5914 33.8513 66.8574 34.1117C67.1261 34.3722 67.3372 34.704 67.4908 35.1071C67.6443 35.5074 67.7211 35.9845 67.7211 36.5384V36.995H62.5428V35.9914H66.2939C66.2911 35.7062 66.2294 35.4526 66.1088 35.2305C65.9881 35.0056 65.8195 34.8288 65.6029 34.6999C65.389 34.571 65.1395 34.5066 64.8543 34.5066C64.55 34.5066 64.2826 34.5806 64.0523 34.7287C63.8219 34.874 63.6423 35.066 63.5135 35.3045C63.3873 35.5403 63.3229 35.7995 63.3202 36.0819V36.958C63.3202 37.3254 63.3873 37.6407 63.5217 37.9039C63.6561 38.1644 63.8439 38.3646 64.0852 38.5044C64.3265 38.6416 64.6089 38.7101 64.9325 38.7101C65.1491 38.7101 65.3451 38.6799 65.5206 38.6196C65.6961 38.5565 65.8483 38.4647 65.9772 38.344C66.106 38.2234 66.2034 38.074 66.2692 37.8957L67.6594 38.052C67.5717 38.4194 67.4044 38.7403 67.1576 39.0145C66.9136 39.2859 66.601 39.4971 66.2198 39.6479C65.8387 39.7959 65.4027 39.87 64.9119 39.87ZM70.0624 33.429L71.3375 35.7611L72.6331 33.429H74.2084L72.304 36.5878L74.2413 39.7466H72.6742L71.3375 37.4721L70.0131 39.7466H68.4337L70.3586 36.5878L68.483 33.429H70.0624ZM78.2988 39.7466V31.3231H83.695V32.6023H79.8247V34.8891H83.3249V36.1683H79.8247V39.7466H78.2988ZM85.0194 39.7466V33.429H86.5083V39.7466H85.0194ZM85.768 32.5323C85.5322 32.5323 85.3293 32.4542 85.1593 32.2979C84.9893 32.1389 84.9043 31.9483 84.9043 31.7262C84.9043 31.5013 84.9893 31.3108 85.1593 31.1545C85.3293 30.9954 85.5322 30.9159 85.768 30.9159C86.0065 30.9159 86.2095 30.9954 86.3767 31.1545C86.5467 31.3108 86.6317 31.5013 86.6317 31.7262C86.6317 31.9483 86.5467 32.1389 86.3767 32.2979C86.2095 32.4542 86.0065 32.5323 85.768 32.5323ZM93.0326 35.0989L91.6753 35.2469C91.6369 35.1098 91.5698 34.981 91.4738 34.8603C91.3806 34.7397 91.2544 34.6423 91.0954 34.5683C90.9363 34.4943 90.7417 34.4572 90.5113 34.4572C90.2015 34.4572 89.941 34.5244 89.7299 34.6588C89.5215 34.7931 89.4186 34.9673 89.4214 35.1811C89.4186 35.3648 89.4858 35.5143 89.6229 35.6294C89.7628 35.7446 89.9931 35.8392 90.3139 35.9132L91.3915 36.1436C91.9893 36.2725 92.4335 36.4767 92.7241 36.7564C93.0175 37.0361 93.1656 37.4022 93.1683 37.8546C93.1656 38.2522 93.0491 38.6032 92.8187 38.9075C92.5912 39.2091 92.2744 39.445 91.8686 39.615C91.4628 39.785 90.9967 39.87 90.4702 39.87C89.697 39.87 89.0745 39.7082 88.6029 39.3846C88.1313 39.0583 87.8502 38.6045 87.7597 38.0232L89.2116 37.8834C89.2774 38.1686 89.4173 38.3838 89.6311 38.5291C89.845 38.6745 90.1233 38.7471 90.4661 38.7471C90.8198 38.7471 91.1036 38.6745 91.3175 38.5291C91.5341 38.3838 91.6424 38.2042 91.6424 37.9903C91.6424 37.8093 91.5725 37.6599 91.4327 37.542C91.2956 37.4241 91.0817 37.3336 90.791 37.2705L89.7134 37.0443C89.1074 36.9182 88.6591 36.7057 88.3685 36.4068C88.0778 36.1052 87.9338 35.724 87.9366 35.2634C87.9338 34.874 88.0394 34.5368 88.2533 34.2516C88.4699 33.9637 88.7702 33.7416 89.154 33.5853C89.5407 33.4262 89.9862 33.3467 90.4908 33.3467C91.2311 33.3467 91.8138 33.5044 92.2388 33.8197C92.6666 34.1351 92.9312 34.5614 93.0326 35.0989ZM95.885 36.0449V39.7466H94.3961V31.3231H95.8521V34.5025H95.9261C96.0742 34.146 96.3032 33.865 96.613 33.6593C96.9256 33.4509 97.3232 33.3467 97.8058 33.3467C98.2445 33.3467 98.627 33.4386 98.9533 33.6223C99.2796 33.806 99.5319 34.0747 99.7101 34.4284C99.8911 34.7822 99.9816 35.214 99.9816 35.724V39.7466H98.4927V35.9544C98.4927 35.5294 98.383 35.199 98.1636 34.9631C97.947 34.7246 97.6426 34.6053 97.2505 34.6053C96.9873 34.6053 96.7515 34.6629 96.5431 34.7781C96.3374 34.8905 96.1757 35.0536 96.0577 35.2675C95.9426 35.4814 95.885 35.7405 95.885 36.0449ZM104.296 39.87C103.663 39.87 103.116 39.7384 102.655 39.4751C102.197 39.2091 101.845 38.8335 101.598 38.3482C101.351 37.8601 101.228 37.2856 101.228 36.6248C101.228 35.9749 101.351 35.4046 101.598 34.9138C101.848 34.4202 102.196 34.0363 102.643 33.7621C103.09 33.4852 103.615 33.3467 104.218 33.3467C104.607 33.3467 104.975 33.4098 105.32 33.5359C105.669 33.6593 105.976 33.8513 106.242 34.1117C106.51 34.3722 106.721 34.704 106.875 35.1071C107.029 35.5074 107.105 35.9845 107.105 36.5384V36.995H101.927V35.9914H105.678C105.675 35.7062 105.614 35.4526 105.493 35.2305C105.372 35.0056 105.204 34.8288 104.987 34.6999C104.773 34.571 104.524 34.5066 104.239 34.5066C103.934 34.5066 103.667 34.5806 103.437 34.7287C103.206 34.874 103.027 35.066 102.898 35.3045C102.772 35.5403 102.707 35.7995 102.704 36.0819V36.958C102.704 37.3254 102.772 37.6407 102.906 37.9039C103.04 38.1644 103.228 38.3646 103.469 38.5044Line truncated
<path d="M27.6528 69.1201V56.9587H31.9962C36.2006 56.9587 38.2332 59.5647 38.2332 63.0568C38.2332 66.5315 36.2006 69.1201 31.9962 69.1201H27.6528ZM30.0851 66.9311H31.7009C34.5848 66.9311 35.7141 65.2632 35.7141 63.0568C35.7141 60.8504 34.5848 59.1478 31.7009 59.1478H30.0851V66.9311ZM43.6855 69.346C40.8884 69.346 39.0121 67.2264 39.0121 64.6204C39.0121 62.0144 40.9753 59.8601 43.6855 59.8601C46.3958 59.8601 48.2721 62.0144 48.2721 64.6204C48.2721 64.881 48.2547 65.159 48.2026 65.4543H41.4443C41.7049 66.5141 42.5041 67.2612 43.6855 67.2612C44.6932 67.2612 45.475 66.74 45.8919 66.045L47.7161 67.4175C46.9865 68.5468 45.475 69.346 43.6855 69.346ZM41.4443 63.6996H45.9093C45.6487 62.692 44.7974 61.8754 43.6508 61.8754C42.5389 61.8754 41.7049 62.6225 41.4443 63.6996ZM52.3134 69.346C50.7846 69.346 49.5511 68.8248 48.5608 67.765L50.1418 66.323C50.802 67.0701 51.4969 67.4349 52.2613 67.4349C53.0605 67.4349 53.5122 67.0353 53.5122 66.4967C53.5122 66.0277 53.2863 65.7671 51.8617 65.437C49.4468 64.8636 49.082 63.7691 49.082 62.6225C49.082 61.0415 50.3329 59.8601 52.4524 59.8601C53.9118 59.8601 54.8847 60.2249 55.8055 61.4237L54.1029 62.7267C53.6686 62.0318 53.1126 61.7538 52.5045 61.7538C51.8617 61.7538 51.3753 61.997 51.3753 62.553C51.3753 62.8657 51.5143 63.1437 52.5914 63.4216C55.2148 64.0992 55.8229 65.0721 55.8229 66.4272C55.8229 68.0951 54.3114 69.346 52.3134 69.346ZM56.9107 69.1201V60.0859H59.2561V69.1201H56.9107ZM56.6675 57.949C56.6675 57.202 57.2756 56.5765 58.0748 56.5765C58.8913 56.5765 59.482 57.202 59.482 57.949C59.482 58.7308 58.8913 59.3389 58.0748 59.3389C57.2756 59.3389 56.6675 58.7135 56.6675 57.949ZM65.2125 72.8207C63.3883 72.8207 61.9637 72.3863 60.8865 71.1181L62.3459 69.3981C63.1972 70.371 64.1006 70.649 65.2299 70.649C66.4808 70.649 67.5753 69.6239 67.5753 68.182V67.7824C67.0888 68.6163 66.307 69.1722 64.8477 69.1722C62.3633 69.1722 60.4696 67.1222 60.4696 64.5162C60.4696 61.9102 62.3633 59.8601 64.8477 59.8601C66.3418 59.8601 67.141 60.4334 67.61 61.3195V60.0859H69.9207V68.3209C69.9207 71.1007 67.9749 72.8207 65.2125 72.8207ZM62.815 64.5162C62.815 65.8887 63.701 67.0353 65.2299 67.0353C66.7761 67.0353 67.6795 65.9234 67.6795 64.5162C67.6795 63.1089 66.7761 61.997 65.2299 61.997C63.701 61.997 62.815 63.1089 62.815 64.5162ZM71.4759 69.1201V60.0859H73.7866V61.4063C74.2556 60.3987 75.159 59.8775 76.5489 59.8775C78.4947 59.8775 79.9889 61.3195 79.9889 63.5954V69.1201H77.6434V64.1687C77.6434 62.6051 76.8616 61.9275 75.6976 61.9275C74.7073 61.9275 73.8213 62.6051 73.8213 64.1687V69.1201H71.4759ZM87.5415 69.1201L83.4762 56.9587H86.2559L88.2017 63.8386C88.3928 64.5335 88.5839 65.2458 88.6882 65.9234C88.775 65.2458 88.9488 64.5162 89.192 63.8386L91.6069 56.9587H93.796L96.2109 63.8386C96.4541 64.5162 96.6452 65.2458 96.7494 65.9234C96.8363 65.2458 97.01 64.5335 97.2011 63.8386L99.147 56.9587H101.927L97.8613 69.1201H95.6723L93.2053 62.2576C92.9794 61.6148 92.8057 60.9199 92.7014 60.2597C92.5972 60.9199 92.4235 61.6148 92.1976 62.2576L89.7306 69.1201H87.5415ZM106 69.346C103.203 69.346 101.188 67.2264 101.188 64.6204C101.188 62.0144 103.203 59.8775 106 59.8775C108.797 59.8775 110.813 62.0144 110.813 64.6204C110.813 67.2264 108.797 69.346 106 69.346ZM103.533 64.6204C103.533 66.0277 104.506 67.209 106 67.209C107.494 67.209 108.467 66.0277 108.467 64.6204C108.467 63.2132 107.494 62.0144 106 62.0144C104.506 62.0144 103.533 63.2132 103.533 64.6204ZM112.024 69.1201V60.0859H114.3V61.8059C114.613 60.5029 115.534 59.7906 117.097 59.8775V62.0839H116.767C115.412 62.0839 114.37 62.9873 114.37 64.4988V69.1201H112.024ZM124.028 69.1201L120.397 64.8289V69.1201H118.052V56.4375H120.397V63.7344L123.785 60.0859H127.12L122.864 64.2903L127.138 69.1201H124.028ZM130.496 69.346C128.968 69.346 127.734 68.8248 126.744 67.765L128.325 66.323C128.985 67.0701 129.68 67.4349 130.444 67.4349C131.243 67.4349 131.695 67.0353 131.695 66.4967C131.695 66.0277 131.469 65.7671 130.045 65.437C127.63 64.8636 127.265 63.7691 127.265 62.6225C127.265 61.0415 128.516 59.8601 130.635 59.8601C132.095 59.8601 133.068 60.2249 133.988 61.4237L132.286 62.7267C131.852 62.0318 131.296 61.7538 130.688 61.7538C130.045 61.7538 129.558 61.997 129.558 62.553C129.558 62.8657 129.697 63.1437 130.774 63.4216C133.398 64.0992 134.006 65.0721 134.006 66.4272C134.006 68.0951 132.494 69.346 130.496 69.346ZM135.094 69.1201V56.4375H137.439V61.3368C137.926 60.3639 138.812 59.8775 140.167 59.8775C142.113 59.8775 143.607 61.3195 143.607 63.5954V69.1201H141.261V64.1687C141.261 62.6051 140.479 61.9275 139.315 61.9275C138.325 61.9275 137.439 62.6051 137.439 64.1687V69.1201H135.094ZM149.556 69.346C146.759 69.346 144.743 67.2264 144.743 64.6204C144.743 62.0144 146.759 59.8775 149.556 59.8775C152.353 59.8775 154.368 62.0144 154.368 64.6204C154.368 67.2264 152.353 69.346 149.556 69.346ZM147.089 64.6204C147.089 66.0277 148.062 67.209 149.556 67.209C151.05 67.209 152.023 66.0277 152.023 64.6204C152.023 63.2132 151.05 62.0144 149.556 62.0144C148.062 62.0144 147.089 63.2132 147.089 64.6204ZMLine truncated
<g clip-path="url(#clip1_16843_34774)">
<path d="M32.5755 97.6631V95.7327M32.5755 93.8023H32.5803M37.4014 95.7327C37.4014 98.398 35.2408 100.559 32.5755 100.559C29.9102 100.559 27.7495 98.398 27.7495 95.7327C27.7495 93.0674 29.9102 90.9067 32.5755 90.9067C35.2408 90.9067 37.4014 93.0674 37.4014 95.7327Z" stroke="#A5A5A5" stroke-width="1.08584" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<path d="M45.5901 97.4937H44.4096L47.062 90.1231H48.3468L50.9992 97.4937H49.8188L47.735 91.4619H47.6774L45.5901 97.4937ZM45.788 94.6073H49.6172V95.5431H45.788V94.6073ZM55.7354 90.1231V97.4937H54.6593V90.1231H55.7354ZM59.5115 97.6052C58.9932 97.6052 58.541 97.4865 58.1547 97.2489C57.7684 97.0114 57.4685 96.6791 57.255 96.252C57.0415 95.825 56.9347 95.3259 56.9347 94.7549C56.9347 94.1815 57.0415 93.68 57.255 93.2506C57.4685 92.8211 57.7684 92.4876 58.1547 92.2501C58.541 92.0125 58.9932 91.8938 59.5115 91.8938C60.0297 91.8938 60.482 92.0125 60.8683 92.2501C61.2546 92.4876 61.5545 92.8211 61.768 93.2506C61.9815 93.68 62.0883 94.1815 62.0883 94.7549C62.0883 95.3259 61.9815 95.825 61.768 96.252C61.5545 96.6791 61.2546 97.0114 60.8683 97.2489C60.482 97.4865 60.0297 97.6052 59.5115 97.6052ZM59.5151 96.7019C59.851 96.7019 60.1293 96.6131 60.35 96.4356C60.5708 96.258 60.7339 96.0217 60.8395 95.7266C60.9474 95.4315 61.0014 95.1064 61.0014 94.7513C61.0014 94.3986 60.9474 94.0747 60.8395 93.7796C60.7339 93.4821 60.5708 93.2434 60.35 93.0634C60.1293 92.8835 59.851 92.7935 59.5151 92.7935C59.1768 92.7935 58.8961 92.8835 58.6729 93.0634C58.4522 93.2434 58.2879 93.4821 58.1799 93.7796C58.0743 94.0747 58.0215 94.3986 58.0215 94.7513C58.0215 95.1064 58.0743 95.4315 58.1799 95.7266C58.2879 96.0217 58.4522 96.258 58.6729 96.4356C58.8961 96.6131 59.1768 96.7019 59.5151 96.7019ZM64.3655 94.2115V97.4937H63.2894V91.9658H64.3223V92.8655H64.3907C64.5179 92.5728 64.717 92.3376 64.9881 92.1601C65.2616 91.9825 65.6059 91.8938 66.021 91.8938C66.3977 91.8938 66.7276 91.973 67.0107 92.1313C67.2938 92.2873 67.5133 92.52 67.6693 92.8295C67.8252 93.139 67.9032 93.5217 67.9032 93.9775V97.4937H66.8271V94.1071C66.8271 93.7064 66.7228 93.3933 66.514 93.1678C66.3053 92.9399 66.0186 92.8259 65.6539 92.8259C65.4044 92.8259 65.1824 92.8799 64.9881 92.9878C64.7962 93.0958 64.6438 93.2542 64.531 93.4629C64.4207 93.6692 64.3655 93.9188 64.3655 94.2115ZM71.6641 99.6818C71.225 99.6818 70.8471 99.6242 70.5304 99.509C70.2161 99.3939 69.9594 99.2415 69.7602 99.052C69.5611 98.8624 69.4123 98.6549 69.314 98.4294L70.2389 98.0479C70.3037 98.1534 70.39 98.265 70.498 98.3826C70.6084 98.5025 70.7571 98.6045 70.9443 98.6885C71.1338 98.7725 71.3773 98.8144 71.6748 98.8144C72.0827 98.8144 72.4198 98.7149 72.6861 98.5157C72.9525 98.319 73.0856 98.0047 73.0856 97.5728V96.486H73.0172C72.9525 96.6035 72.8589 96.7343 72.7365 96.8782C72.6166 97.0222 72.451 97.147 72.2399 97.2525C72.0287 97.3581 71.754 97.4109 71.4157 97.4109C70.9791 97.4109 70.5856 97.3089 70.2353 97.105C69.8874 96.8986 69.6115 96.5951 69.4075 96.1945C69.206 95.7914 69.1052 95.2959 69.1052 94.7081C69.1052 94.1203 69.2048 93.6164 69.4039 93.1966C69.6055 92.7767 69.8814 92.4552 70.2317 92.2321C70.582 92.0065 70.9791 91.8938 71.4229 91.8938C71.766 91.8938 72.0431 91.9514 72.2543 92.0665C72.4654 92.1793 72.6298 92.3112 72.7473 92.4624C72.8673 92.6136 72.9597 92.7467 73.0244 92.8619H73.1036V91.9658H74.1581V97.616C74.1581 98.0911 74.0477 98.4809 73.827 98.7857C73.6063 99.0904 73.3075 99.3159 72.9309 99.4622C72.5566 99.6086 72.1343 99.6818 71.6641 99.6818ZM71.6533 96.5184C71.9628 96.5184 72.2243 96.4464 72.4378 96.3024C72.6537 96.1561 72.8169 95.9473 72.9273 95.6762C73.04 95.4027 73.0964 95.0752 73.0964 94.6937C73.0964 94.3218 73.0412 93.9943 72.9309 93.7112C72.8205 93.4281 72.6585 93.2074 72.445 93.049C72.2315 92.8883 71.9676 92.8079 71.6533 92.8079C71.3294 92.8079 71.0594 92.8919 70.8435 93.0598C70.6276 93.2254 70.4644 93.4509 70.3541 93.7364C70.2461 94.0219 70.1921 94.341 70.1921 94.6937C70.1921 95.056 70.2473 95.3739 70.3577 95.6474C70.468 95.9209 70.6312 96.1345 70.8471 96.288C71.0654 96.4416 71.3342 96.5184 71.6533 96.5184ZM77.9837 97.6052C77.4391 97.6052 76.97 97.4889 76.5765 97.2561C76.1855 97.021 75.8832 96.6911 75.6696 96.2664C75.4585 95.8394 75.3529 95.3391 75.3529 94.7657C75.3529 94.1995 75.4585 93.7004 75.6696 93.2686C75.8832 92.8367 76.1807 92.4996 76.5621 92.2573C76.946 92.0149 77.3947 91.8938 77.9081 91.8938C78.22 91.8938 78.5223 91.9454 78.815 92.0485C79.1078 92.1517 79.3705 92.3136 79.6032 92.5344C79.8359 92.7551 80.0195 93.0418 80.1538 93.3945C80.2882 93.7448 80.3554 94.1707 80.3554 94.6721V95.0536H75.9611V94.2475H79.3009C79.3009 93.9643 79.2433 93.7136 79.1282 93.4953C79.013 93.2746 78.851 93.1006 78.6423 92.9734C78.436 92.8463 78.1936 92.7827 77.9153 92.7827C77.613 92.7827 77.3491 92.8571 77.1236 93.0058C76.9004 93.1522 76.7277 93.3441 76.6053 93.5817C76.4854 93.8168 76.4254 94.0723 76.4254 94.3482V94.978C76.4254 95.3475 76.4902 95.6618 76.6197 95.9209C76.7517 96.1801 76.9352 96.378 77.1704 96.5148C77.4055 96.6491 77.6802 96.7163 77.9945 96.7163C78.1984 96.7163 78.3844 96.6875 78.5523 96.6299C78.7203 96.5699 78.8654 96.4812 78.9878 96.3636C79.1102 96.246 79.2037 96.1009 79.2685 95.9281L80.287 96.1117C80.2054 96.4116 80.0591 96.6743 79.8479 96.8998C79.6392 97.123 79.3765 97.2969 79.0598 97.4217C78.7455 97.544 78.3868 97.6052 77.9837 97.6052ZM81.5493 97.4937V91.9658H82.5894V92.8439H82.647C82.74Line truncated
<g clip-path="url(#clip2_16843_34774)">
<path d="M32.5755 119.106V122.002L34.5058 122.967M37.4014 122.002C37.4014 124.667 35.2408 126.828 32.5755 126.828C29.9102 126.828 27.7495 124.667 27.7495 122.002C27.7495 119.336 29.9102 117.176 32.5755 117.176C35.2408 117.176 37.4014 119.336 37.4014 122.002Z" stroke="#A5A5A5" stroke-width="1.08584" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<path d="M47.4327 125.102C46.9385 125.102 46.497 125.017 46.1083 124.847C45.722 124.676 45.4161 124.44 45.1906 124.138C44.9675 123.833 44.8475 123.48 44.8307 123.08H45.9608C45.9752 123.298 46.0483 123.488 46.1803 123.648C46.3147 123.807 46.4898 123.929 46.7057 124.015C46.9217 124.102 47.1616 124.145 47.4255 124.145C47.7158 124.145 47.9725 124.095 48.1957 123.994C48.4212 123.893 48.5975 123.753 48.7247 123.573C48.8519 123.39 48.9155 123.18 48.9155 122.943C48.9155 122.696 48.8519 122.479 48.7247 122.291C48.5999 122.102 48.4164 121.953 48.1741 121.845C47.9342 121.737 47.6438 121.683 47.3031 121.683H46.6805V120.776H47.3031C47.5767 120.776 47.8166 120.727 48.0229 120.629C48.2317 120.53 48.3948 120.394 48.5124 120.219C48.6299 120.041 48.6887 119.833 48.6887 119.596C48.6887 119.368 48.6371 119.17 48.534 119.002C48.4332 118.832 48.2892 118.699 48.1021 118.603C47.9174 118.507 47.699 118.459 47.4471 118.459C47.2072 118.459 46.9828 118.503 46.7741 118.592C46.5678 118.678 46.3998 118.803 46.2703 118.966C46.1407 119.127 46.0711 119.32 46.0615 119.546H44.9855C44.9975 119.147 45.115 118.797 45.3382 118.495C45.5637 118.192 45.8612 117.956 46.2307 117.786C46.6002 117.615 47.0104 117.53 47.4615 117.53C47.9342 117.53 48.342 117.623 48.6851 117.807C49.0306 117.99 49.2969 118.233 49.4841 118.538C49.6736 118.843 49.7672 119.176 49.7648 119.538C49.7672 119.951 49.652 120.301 49.4193 120.589C49.189 120.877 48.8819 121.07 48.498 121.169V121.226C48.9874 121.301 49.3665 121.495 49.6352 121.809C49.9063 122.124 50.0407 122.513 50.0383 122.979C50.0407 123.384 49.9279 123.748 49.7 124.069C49.4745 124.391 49.1662 124.644 48.7751 124.829C48.384 125.011 47.9366 125.102 47.4327 125.102ZM53.9647 125.124C53.3961 125.121 52.9102 124.971 52.5071 124.674C52.1041 124.376 51.7958 123.943 51.5822 123.375C51.3687 122.806 51.2619 122.121 51.2619 121.32C51.2619 120.521 51.3687 119.838 51.5822 119.272C51.7982 118.706 52.1077 118.274 52.5107 117.976C52.9162 117.679 53.4009 117.53 53.9647 117.53C54.5285 117.53 55.012 117.68 55.4151 117.98C55.8181 118.278 56.1264 118.709 56.34 119.276C56.5559 119.839 56.6639 120.521 56.6639 121.32C56.6639 122.124 56.5571 122.81 56.3436 123.378C56.13 123.945 55.8217 124.378 55.4186 124.678C55.0156 124.975 54.5309 125.124 53.9647 125.124ZM53.9647 124.163C54.4637 124.163 54.8536 123.919 55.1343 123.432C55.4175 122.945 55.559 122.241 55.559 121.32C55.559 120.708 55.4942 120.191 55.3647 119.769C55.2375 119.344 55.054 119.023 54.814 118.804C54.5765 118.583 54.2934 118.473 53.9647 118.473C53.468 118.473 53.0782 118.718 52.7951 119.207C52.5119 119.697 52.3692 120.401 52.3668 121.32C52.3668 121.934 52.4304 122.453 52.5575 122.878C52.6871 123.3 52.8706 123.621 53.1082 123.839C53.3457 124.055 53.6312 124.163 53.9647 124.163ZM60.637 125.001V119.474H61.6699V120.373H61.7383C61.8535 120.069 62.0418 119.831 62.3033 119.661C62.5649 119.488 62.878 119.402 63.2426 119.402C63.6121 119.402 63.9216 119.488 64.1712 119.661C64.4231 119.833 64.609 120.071 64.729 120.373H64.7866C64.9185 120.078 65.1285 119.843 65.4164 119.668C65.7043 119.49 66.0474 119.402 66.4457 119.402C66.9471 119.402 67.3562 119.559 67.6729 119.873C67.992 120.187 68.1515 120.661 68.1515 121.295V125.001H67.0755V121.395C67.0755 121.021 66.9735 120.75 66.7696 120.582C66.5656 120.414 66.3221 120.33 66.039 120.33C65.6887 120.33 65.4164 120.438 65.222 120.654C65.0277 120.868 64.9305 121.142 64.9305 121.478V125.001H63.8581V121.327C63.8581 121.027 63.7645 120.786 63.5773 120.604C63.3902 120.421 63.1467 120.33 62.8468 120.33C62.6428 120.33 62.4545 120.384 62.2817 120.492C62.1114 120.598 61.9734 120.745 61.8679 120.935C61.7647 121.124 61.7131 121.344 61.7131 121.593V125.001H60.637ZM69.5938 125.001V119.474H70.6699V125.001H69.5938ZM70.1372 118.621C69.9501 118.621 69.7893 118.558 69.655 118.433C69.523 118.306 69.457 118.155 69.457 117.98C69.457 117.802 69.523 117.651 69.655 117.527C69.7893 117.399 69.9501 117.336 70.1372 117.336C70.3244 117.336 70.4839 117.399 70.6159 117.527C70.7502 117.651 70.8174 117.802 70.8174 117.98C70.8174 118.155 70.7502 118.306 70.6159 118.433C70.4839 118.558 70.3244 118.621 70.1372 118.621ZM73.1936 121.719V125.001H72.1175V119.474H73.1504V120.373H73.2188C73.3459 120.081 73.5451 119.845 73.8162 119.668C74.0897 119.49 74.434 119.402 74.8491 119.402C75.2258 119.402 75.5557 119.481 75.8388 119.639C76.1219 119.795 76.3414 120.028 76.4974 120.337C76.6533 120.647 76.7313 121.029 76.7313 121.485V125.001H75.6552V121.615C75.6552 121.214 75.5509 120.901 75.3421 120.676C75.1334 120.448 74.8467 120.334 74.482 120.334C74.2325 120.334 74.0105 120.388 73.8162 120.496C73.6242 120.604 73.4719 120.762 73.3591 120.971C73.2488 121.177 73.1936 121.427 73.1936 121.719ZM82.3168 120.823L81.3415 120.996C81.3007 120.871 81.2359 120.752 81.1471 120.64C81.0608 120.527 80.9432 120.434 80.7944 120.362C80.6457 120.291 80.4597 120.255 80.2366 120.255C79.9319 120.255 79.6776 120.323 79.4736 120.46C79.2697 120.594 79.1677 120.768 79.1677 120.982C79.1677 121.166 79.2361 121.315 79.3729 121.428C79.50Line truncated
<g clip-path="url(#clip3_16843_34774)">
<path d="M36.9186 147.514C36.9186 150.892 32.5753 153.788 32.5753 153.788C32.5753 153.788 28.2319 150.892 28.2319 147.514C28.2319 146.362 28.6895 145.258 29.5041 144.443C30.3186 143.628 31.4234 143.171 32.5753 143.171C33.7272 143.171 34.832 143.628 35.6465 144.443C36.461 145.258 36.9186 146.362 36.9186 147.514Z" stroke="#A5A5A5" stroke-width="1.08584" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M32.5753 148.962C33.3749 148.962 34.0231 148.314 34.0231 147.514C34.0231 146.715 33.3749 146.066 32.5753 146.066C31.7757 146.066 31.1275 146.715 31.1275 147.514C31.1275 148.314 31.7757 148.962 32.5753 148.962Z" stroke="#A5A5A5" stroke-width="1.08584" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<path d="M44.7767 151.479V150.738L48.6527 145.066H44.7443V144.109H50.0131V144.85L46.1371 150.522H50.0455V151.479H44.7767ZM53.6822 151.591C53.1639 151.591 52.7117 151.472 52.3254 151.235C51.9391 150.997 51.6392 150.665 51.4257 150.238C51.2121 149.811 51.1054 149.312 51.1054 148.741C51.1054 148.167 51.2121 147.666 51.4257 147.236C51.6392 146.807 51.9391 146.473 52.3254 146.236C52.7117 145.998 53.1639 145.88 53.6822 145.88C54.2004 145.88 54.6527 145.998 55.039 146.236C55.4252 146.473 55.7252 146.807 55.9387 147.236C56.1522 147.666 56.259 148.167 56.259 148.741C56.259 149.312 56.1522 149.811 55.9387 150.238C55.7252 150.665 55.4252 150.997 55.039 151.235C54.6527 151.472 54.2004 151.591 53.6822 151.591ZM53.6858 150.688C54.0217 150.688 54.3 150.599 54.5207 150.421C54.7415 150.244 54.9046 150.008 55.0102 149.712C55.1181 149.417 55.1721 149.092 55.1721 148.737C55.1721 148.384 55.1181 148.061 55.0102 147.765C54.9046 147.468 54.7415 147.229 54.5207 147.049C54.3 146.869 54.0217 146.779 53.6858 146.779C53.3475 146.779 53.0668 146.869 52.8436 147.049C52.6229 147.229 52.4586 147.468 52.3506 147.765C52.245 148.061 52.1922 148.384 52.1922 148.737C52.1922 149.092 52.245 149.417 52.3506 149.712C52.4586 150.008 52.6229 150.244 52.8436 150.421C53.0668 150.599 53.3475 150.688 53.6858 150.688ZM59.7886 151.591C59.2704 151.591 58.8181 151.472 58.4318 151.235C58.0455 150.997 57.7456 150.665 57.5321 150.238C57.3186 149.811 57.2118 149.312 57.2118 148.741C57.2118 148.167 57.3186 147.666 57.5321 147.236C57.7456 146.807 58.0455 146.473 58.4318 146.236C58.8181 145.998 59.2704 145.88 59.7886 145.88C60.3068 145.88 60.7591 145.998 61.1454 146.236C61.5317 146.473 61.8316 146.807 62.0451 147.236C62.2586 147.666 62.3654 148.167 62.3654 148.741C62.3654 149.312 62.2586 149.811 62.0451 150.238C61.8316 150.665 61.5317 150.997 61.1454 151.235C60.7591 151.472 60.3068 151.591 59.7886 151.591ZM59.7922 150.688C60.1281 150.688 60.4064 150.599 60.6271 150.421C60.8479 150.244 61.011 150.008 61.1166 149.712C61.2246 149.417 61.2785 149.092 61.2785 148.737C61.2785 148.384 61.2246 148.061 61.1166 147.765C61.011 147.468 60.8479 147.229 60.6271 147.049C60.4064 146.869 60.1281 146.779 59.7922 146.779C59.4539 146.779 59.1732 146.869 58.9501 147.049C58.7293 147.229 58.565 147.468 58.457 147.765C58.3514 148.061 58.2987 148.384 58.2987 148.737C58.2987 149.092 58.3514 149.417 58.457 149.712C58.565 150.008 58.7293 150.244 58.9501 150.421C59.1732 150.599 59.4539 150.688 59.7922 150.688ZM63.5665 151.479V145.952H64.5994V146.851H64.6678C64.783 146.547 64.9713 146.309 65.2328 146.139C65.4943 145.966 65.8075 145.88 66.1721 145.88C66.5416 145.88 66.8511 145.966 67.1007 146.139C67.3526 146.311 67.5385 146.549 67.6585 146.851H67.7161C67.848 146.556 68.058 146.321 68.3459 146.146C68.6338 145.968 68.9769 145.88 69.3752 145.88C69.8766 145.88 70.2857 146.037 70.6024 146.351C70.9215 146.665 71.081 147.139 71.081 147.773V151.479H70.005V147.873C70.005 147.499 69.903 147.228 69.6991 147.06C69.4951 146.892 69.2516 146.808 68.9685 146.808C68.6182 146.808 68.3459 146.916 68.1515 147.132C67.9572 147.346 67.86 147.62 67.86 147.956V151.479H66.7876V147.805C66.7876 147.505 66.694 147.264 66.5068 147.082C66.3197 146.899 66.0762 146.808 65.7763 146.808C65.5723 146.808 65.384 146.862 65.2112 146.97C65.0409 147.076 64.9029 147.223 64.7974 147.413C64.6942 147.602 64.6426 147.822 64.6426 148.071V151.479H63.5665Z" fill="#D6D6D6"/>
<g clip-path="url(#clip4_16843_34774)">
<path d="M37.4014 174.957C37.4014 177.623 35.2408 179.783 32.5755 179.783M37.4014 174.957C37.4014 172.292 35.2408 170.131 32.5755 170.131M37.4014 174.957H27.7495M32.5755 179.783C29.9102 179.783 27.7495 177.623 27.7495 174.957M32.5755 179.783C33.7826 178.462 34.4686 176.747 34.5058 174.957C34.4686 173.168 33.7826 171.453 32.5755 170.131M32.5755 179.783C31.3684 178.462 30.6824 176.747 30.6451 174.957C30.6824 173.168 31.3684 171.453 32.5755 170.131M27.7495 174.957C27.7495 172.292 29.9102 170.131 32.5755 170.131" stroke="#A5A5A5" stroke-width="1.08584" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<g clip-path="url(#clip5_16843_34774)">
<path d="M44.9675 177.957V170.586H49.5884V171.544H46.0795V173.79H49.3473V174.743H46.0795V177H49.6316V177.957H44.9675ZM54.4892 175.665V172.429H55.5689V177.957H54.5108V177H54.4532C54.3261 177.295 54.1221 177.541 53.8414 177.737C53.5631 177.932 53.2164 178.029 52.8014 178.029C52.4463 178.029 52.132 177.951 51.8584 177.795C51.5873 177.637 51.3738 177.403 51.2178 177.093C51.0643 176.784 50.9875 176.401 50.9875 175.945V172.429H52.0636V175.816C52.0636 176.192 52.168 176.492 52.3767 176.715C52.5854 176.939 52.8565 177.05 53.19 177.05C53.3916 177.05 53.5919 177 53.7911 176.899C53.9926 176.798 54.1593 176.646 54.2913 176.442C54.4257 176.238 54.4916 175.979 54.4892 175.665ZM57.0148 177.957V172.429H58.0548V173.307H58.1124C58.2132 173.01 58.3907 172.776 58.6451 172.605C58.9018 172.433 59.1921 172.346 59.516 172.346C59.5832 172.346 59.6623 172.349 59.7535 172.354C59.8471 172.358 59.9203 172.364 59.973 172.372V173.401C59.9299 173.389 59.8531 173.376 59.7427 173.361C59.6324 173.344 59.522 173.336 59.4116 173.336C59.1573 173.336 58.9306 173.39 58.7314 173.498C58.5347 173.604 58.3787 173.751 58.2636 173.941C58.1484 174.128 58.0908 174.341 58.0908 174.581V177.957H57.0148ZM63.0348 178.069C62.5166 178.069 62.0643 177.95 61.678 177.712C61.2917 177.475 60.9918 177.142 60.7783 176.715C60.5648 176.288 60.458 175.789 60.458 175.218C60.458 174.645 60.5648 174.143 60.7783 173.714C60.9918 173.284 61.2917 172.951 61.678 172.713C62.0643 172.476 62.5166 172.357 63.0348 172.357C63.553 172.357 64.0053 172.476 64.3916 172.713C64.7779 172.951 65.0778 173.284 65.2913 173.714C65.5048 174.143 65.6116 174.645 65.6116 175.218C65.6116 175.789 65.5048 176.288 65.2913 176.715C65.0778 177.142 64.7779 177.475 64.3916 177.712C64.0053 177.95 63.553 178.069 63.0348 178.069ZM63.0384 177.165C63.3743 177.165 63.6526 177.077 63.8733 176.899C64.0941 176.721 64.2572 176.485 64.3628 176.19C64.4708 175.895 64.5247 175.57 64.5247 175.215C64.5247 174.862 64.4708 174.538 64.3628 174.243C64.2572 173.945 64.0941 173.707 63.8733 173.527C63.6526 173.347 63.3743 173.257 63.0384 173.257C62.7001 173.257 62.4194 173.347 62.1963 173.527C61.9755 173.707 61.8112 173.945 61.7032 174.243C61.5976 174.538 61.5449 174.862 61.5449 175.215C61.5449 175.57 61.5976 175.895 61.7032 176.19C61.8112 176.485 61.9755 176.721 62.1963 176.899C62.4194 177.077 62.7001 177.165 63.0384 177.165ZM66.8127 180.03V172.429H67.8636V173.325H67.9536C68.016 173.21 68.1059 173.077 68.2235 172.926C68.3411 172.775 68.5042 172.643 68.713 172.53C68.9217 172.415 69.1976 172.357 69.5407 172.357C69.987 172.357 70.3852 172.47 70.7355 172.695C71.0858 172.921 71.3605 173.246 71.5597 173.671C71.7612 174.095 71.862 174.606 71.862 175.204C71.862 175.801 71.7624 176.314 71.5633 176.741C71.3641 177.165 71.0906 177.493 70.7427 177.723C70.3948 177.951 69.9978 178.065 69.5515 178.065C69.2156 178.065 68.9409 178.009 68.7274 177.896C68.5162 177.783 68.3507 177.651 68.2307 177.5C68.1107 177.349 68.0184 177.214 67.9536 177.097H67.8888V180.03H66.8127ZM67.8672 175.193C67.8672 175.582 67.9236 175.922 68.0364 176.215C68.1491 176.508 68.3123 176.737 68.5258 176.903C68.7394 177.066 69.0009 177.147 69.3104 177.147C69.6319 177.147 69.9006 177.062 70.1165 176.892C70.3325 176.719 70.4956 176.485 70.606 176.19C70.7187 175.895 70.7751 175.563 70.7751 175.193C70.7751 174.828 70.7199 174.501 70.6096 174.211C70.5016 173.92 70.3385 173.691 70.1201 173.523C69.9042 173.355 69.6343 173.271 69.3104 173.271C68.9985 173.271 68.7346 173.352 68.5186 173.512C68.3051 173.673 68.1431 173.897 68.0328 174.185C67.9224 174.473 67.8672 174.809 67.8672 175.193ZM75.4501 178.069C74.9055 178.069 74.4364 177.952 74.0429 177.72C73.6518 177.484 73.3495 177.154 73.136 176.73C72.9249 176.303 72.8193 175.802 72.8193 175.229C72.8193 174.663 72.9249 174.164 73.136 173.732C73.3495 173.3 73.647 172.963 74.0285 172.721C74.4124 172.478 74.8611 172.357 75.3745 172.357C75.6864 172.357 75.9887 172.409 76.2814 172.512C76.5741 172.615 76.8369 172.777 77.0696 172.998C77.3023 173.218 77.4859 173.505 77.6202 173.858C77.7546 174.208 77.8218 174.634 77.8218 175.135V175.517H73.4275V174.711H76.7673C76.7673 174.428 76.7097 174.177 76.5945 173.959C76.4794 173.738 76.3174 173.564 76.1087 173.437C75.9023 173.31 75.66 173.246 75.3817 173.246C75.0794 173.246 74.8155 173.32 74.59 173.469C74.3668 173.616 74.1941 173.808 74.0717 174.045C73.9517 174.28 73.8918 174.536 73.8918 174.812V175.441C73.8918 175.811 73.9565 176.125 74.0861 176.384C74.2181 176.643 74.4016 176.841 74.6367 176.978C74.8719 177.112 75.1466 177.18 75.4609 177.18C75.6648 177.18 75.8508 177.151 76.0187 177.093C76.1867 177.033 76.3318 176.945 76.4542 176.827C76.5765 176.709 76.6701 176.564 76.7349 176.392L77.7534 176.575C77.6718 176.875 77.5254 177.138 77.3143 177.363C77.1056 177.586 76.8429 177.76 76.5262 177.885C76.2119 178.007 75.8532 178.069 75.4501 178.069ZM81.8228 170.241L79.4476 179.065H78.4867L80.8619 170.241H81.8228ZM85.2094 177.957H82.8233V170.586H85.285C86.0071 170.586 86.6273 170.734 87.1456 171.029C87.6638 171.322 88.06Line truncated
<path d="M118.949 173.509L121.844 176.405L124.74 173.509" stroke="#D6D6D6" stroke-width="1.08584" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<rect x="0.724121" y="255.595" width="1" height="275" transform="rotate(-90 0.724121 255.595)" fill="#525252"/>
<path d="M14.6908 272.963H16.0342V278.22C16.0318 278.702 15.9296 279.117 15.7276 279.465C15.5256 279.81 15.2433 280.077 14.8807 280.264C14.5205 280.449 14.1007 280.542 13.6213 280.542C13.1832 280.542 12.789 280.464 12.4385 280.308C12.0905 280.15 11.8143 279.916 11.6099 279.607C11.4055 279.298 11.3032 278.914 11.3032 278.454H12.6502C12.6527 278.656 12.6965 278.83 12.7817 278.976C12.8693 279.122 12.9897 279.234 13.1431 279.311C13.2964 279.389 13.4728 279.428 13.6724 279.428C13.889 279.428 14.0727 279.383 14.2236 279.293C14.3745 279.201 14.4888 279.064 14.5667 278.884C14.647 278.704 14.6884 278.483 14.6908 278.22V272.963ZM21.0189 278.081V274.832H22.3403V280.439H21.059V279.443H21.0006C20.8741 279.757 20.666 280.014 20.3764 280.213C20.0892 280.413 19.7351 280.512 19.3141 280.512C18.9466 280.512 18.6217 280.431 18.3394 280.268C18.0596 280.102 17.8406 279.863 17.6824 279.549C17.5242 279.232 17.4451 278.85 17.4451 278.403V274.832H18.7665V278.198C18.7665 278.553 18.8639 278.836 19.0586 279.045C19.2533 279.254 19.5088 279.359 19.8252 279.359C20.0199 279.359 20.2085 279.311 20.391 279.217C20.5735 279.122 20.7232 278.98 20.84 278.793C20.9592 278.603 21.0189 278.366 21.0189 278.081ZM25.0206 277.154V280.439H23.6992V274.832H24.9622V275.785H25.0279C25.1569 275.471 25.3626 275.222 25.6449 275.037C25.9296 274.852 26.2813 274.759 26.6998 274.759C27.0868 274.759 27.4238 274.842 27.711 275.008C28.0006 275.173 28.2245 275.413 28.3827 275.727C28.5433 276.041 28.6224 276.422 28.62 276.869V280.439H27.2985V277.074C27.2985 276.699 27.2012 276.406 27.0065 276.194C26.8142 275.982 26.5477 275.876 26.207 275.876C25.9758 275.876 25.7702 275.928 25.5901 276.03C25.4125 276.13 25.2725 276.274 25.1703 276.464C25.0705 276.654 25.0206 276.884 25.0206 277.154ZM32.4456 280.549C31.8834 280.549 31.3979 280.432 30.9891 280.199C30.5827 279.962 30.27 279.629 30.0509 279.198C29.8319 278.765 29.7224 278.255 29.7224 277.669C29.7224 277.092 29.8319 276.586 30.0509 276.15C30.2724 275.712 30.5815 275.371 30.9781 275.128C31.3748 274.882 31.8409 274.759 32.3763 274.759C32.7218 274.759 33.0479 274.815 33.3546 274.927C33.6636 275.037 33.9362 275.207 34.1723 275.438C34.4108 275.67 34.5981 275.964 34.7344 276.322C34.8707 276.677 34.9389 277.101 34.9389 277.592V277.997H30.343V277.107H33.6722C33.6697 276.854 33.615 276.628 33.5079 276.431C33.4008 276.232 33.2511 276.075 33.0589 275.96C32.8691 275.846 32.6476 275.789 32.3945 275.789C32.1244 275.789 31.8871 275.855 31.6827 275.986C31.4782 276.115 31.3188 276.285 31.2045 276.497C31.0925 276.706 31.0353 276.936 31.0329 277.187V277.964C31.0329 278.291 31.0925 278.57 31.2118 278.804C31.331 279.035 31.4977 279.213 31.7119 279.337C31.926 279.459 32.1767 279.52 32.4639 279.52C32.6561 279.52 32.8301 279.493 32.9859 279.439C33.1416 279.383 33.2767 279.302 33.3911 279.195C33.5055 279.088 33.5918 278.955 33.6503 278.797L34.8841 278.935C34.8062 279.262 34.6578 279.546 34.4387 279.79C34.2222 280.031 33.9447 280.218 33.6064 280.352C33.2682 280.483 32.8812 280.549 32.4456 280.549Z" fill="#FCFCFD"/>
<path d="M39.203 280.439V279.724L41.4165 277.431C41.6529 277.181 41.8477 276.963 42.001 276.776C42.1565 276.586 42.2725 276.406 42.3492 276.236C42.4258 276.066 42.4642 275.885 42.4642 275.693C42.4642 275.476 42.4131 275.288 42.3109 275.131C42.2086 274.971 42.0692 274.849 41.8924 274.763C41.7157 274.676 41.5166 274.633 41.2951 274.633C41.0609 274.633 40.8565 274.68 40.6819 274.776C40.5072 274.872 40.3731 275.007 40.2794 275.182C40.1857 275.357 40.1389 275.561 40.1389 275.795H39.1966C39.1966 275.397 39.2881 275.049 39.4713 274.751C39.6544 274.453 39.9057 274.222 40.2251 274.058C40.5445 273.891 40.9076 273.808 41.3143 273.808C41.7253 273.808 42.0873 273.89 42.4003 274.054C42.7154 274.216 42.9614 274.438 43.1381 274.719C43.3149 274.998 43.4032 275.313 43.4032 275.664C43.4032 275.907 43.3575 276.144 43.2659 276.377C43.1765 276.609 43.02 276.867 42.7964 277.153C42.5728 277.436 42.2619 277.78 41.8637 278.184L40.5637 279.545V279.593H43.5087V280.439H39.203ZM46.9966 280.548C46.492 280.546 46.0608 280.413 45.703 280.149C45.3453 279.885 45.0716 279.5 44.8821 278.996C44.6926 278.491 44.5978 277.883 44.5978 277.172C44.5978 276.463 44.6926 275.857 44.8821 275.354C45.0738 274.852 45.3485 274.469 45.7062 274.205C46.0661 273.94 46.4962 273.808 46.9966 273.808C47.4971 273.808 47.9261 273.942 48.2839 274.208C48.6416 274.472 48.9152 274.855 49.1048 275.358C49.2964 275.858 49.3922 276.463 49.3922 277.172C49.3922 277.885 49.2975 278.494 49.108 278.999C48.9184 279.501 48.6448 279.886 48.2871 280.152C47.9293 280.416 47.4992 280.548 46.9966 280.548ZM46.9966 279.695C47.4396 279.695 47.7856 279.479 48.0347 279.047C48.286 278.615 48.4116 277.99 48.4116 277.172C48.4116 276.629 48.3541 276.17 48.2392 275.795C48.1263 275.418 47.9634 275.133 47.7505 274.939C47.5396 274.743 47.2884 274.645 46.9966 274.645C46.5558 274.645 46.2098 274.863 45.9585 275.297C45.7073 275.731 45.5806 276.356 45.5784 277.172C45.5784 277.717 45.6349 278.178 45.7477 278.555C45.8627 278.93 46.0256 279.214 46.2364 279.408C46.4472 279.599 46.7006 279.695 46.9966 279.695ZM50.5253 280.439V279.724L52.7389 277.431C52.9752 277.181 53.1701 276.963 53.3234 276.776C53.4788 276.586 53.5949 276.406 53.6716 276.236C53.7482 276.066 53.7865 275.885 53.7865 275.693C53.7865 275.476 53.7354 275.288 53.6332 275.131C53.531 274.971 53.3915 274.849 53.2148 274.763C53.0381 274.676 52.839 274.633 52.6175 274.633C52.3833 274.633 52.1788 274.68 52.0042 274.776C51.8296 274.872 51.6955 275.007 51.6018 275.182C51.5081 275.357 51.4612 275.561 51.4612 275.795H50.519C50.519 275.397 50.6105 275.049 50.7937 274.751C50.9768 274.453 51.2281 274.222 51.5475 274.058C51.8669 273.891 52.2299 273.808 52.6367 273.808C53.0476 273.808 53.4096 273.89 53.7227 274.054C54.0378 274.216 54.2838 274.438 54.4605 274.719C54.6373 274.998 54.7256 275.313 54.7256 275.664C54.7256 275.907 54.6798 276.144 54.5883 276.377C54.4988 276.609 54.3423 276.867 54.1187 277.153C53.8951 277.436 53.5843 277.78 53.1861 278.184L51.886 279.545V279.593H54.831V280.439H50.5253ZM58.319 280.529C57.8804 280.529 57.4885 280.453 57.1436 280.302C56.8007 280.151 56.5292 279.941 56.3291 279.673C56.131 279.402 56.0246 279.089 56.0097 278.734H57.0126C57.0254 278.928 57.0903 279.096 57.2075 279.238C57.3267 279.379 57.4822 279.488 57.6738 279.564C57.8654 279.641 58.0784 279.679 58.3126 279.679C58.5703 279.679 58.7981 279.635 58.9962 279.545C59.1963 279.456 59.3528 279.331 59.4657 279.171C59.5786 279.01 59.635 278.823 59.635 278.612C59.635 278.393 59.5786 278.2 59.4657 278.034C59.355 277.866 59.1921 277.734 58.977 277.638C58.7641 277.542 58.5064 277.494 58.204 277.494H57.6514V276.69H58.204C58.4468 276.69 58.6597 276.646 58.8428 276.559C59.0281 276.471 59.1729 276.35 59.2772 276.194C59.3816 276.037 59.4338 275.853 59.4338 275.642C59.4338 275.44 59.388 275.264 59.2964 275.115C59.207 274.964 59.0792 274.845 58.9131 274.76C58.7492 274.675 58.5554 274.633 58.3318 274.633C58.1188 274.633 57.9197 274.672 57.7345 274.751C57.5514 274.827 57.4023 274.938 57.2873 275.083C57.1723 275.226 57.1106 275.397 57.102 275.597H56.147C56.1577 275.244 56.262 274.933 56.46 274.664C56.6602 274.396 56.9242 274.186 57.2522 274.035C57.5801 273.884 57.9442 273.808 58.3446 273.808C58.7641 273.808 59.1261 273.89 59.4306 274.054C59.7372 274.216 59.9736 274.432 60.1397 274.703C60.3079 274.973 60.3909 275.269 60.3888 275.591C60.3909 275.957 60.2887 276.268 60.0822 276.523C59.8777 276.779 59.6052 276.95 59.2645 277.038V277.089C59.6989 277.155 60.0353 277.327 60.2738 277.606C60.5144 277.885 60.6337 278.231 60.6316 278.644C60.6337 279.004 60.5336 279.327 60.3313 279.612C60.1311 279.898 59.8575 280.122 59.5104 280.286C59.1633 280.448 58.7662 280.529 58.319 280.529Z" fill="#A5A5A5"/>
<path d="M229.445 280.009L226.875 277.44L229.445 274.87" stroke="#A5A5A5" stroke-width="0.963713" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M250.004 280.009L252.574 277.44L250.004 274.87" stroke="#A5A5A5" stroke-width="0.963713" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M24.1673 308.429C24.1381 308.17 24.0177 307.969 23.8059 307.827C23.5942 307.682 23.3277 307.61 23.0065 307.61C22.7765 307.61 22.5776 307.647 22.4096 307.72C22.2417 307.791 22.1112 307.89 22.0181 308.015C21.9269 308.14 21.8812 308.281 21.8812 308.44C21.8812 308.573 21.9123 308.688 21.9743 308.785C22.0382 308.882 22.1213 308.963 22.2235 309.028C22.3275 309.092 22.4388 309.146 22.5575 309.19C22.6761 309.232 22.7902 309.267 22.8997 309.294L23.4473 309.436C23.6261 309.48 23.8096 309.54 23.9976 309.614C24.1856 309.689 24.3599 309.788 24.5205 309.91C24.6811 310.032 24.8107 310.184 24.9093 310.365C25.0097 310.545 25.0599 310.762 25.0599 311.013C25.0599 311.331 24.9777 311.613 24.8134 311.859C24.651 312.106 24.4146 312.3 24.1044 312.443C23.7959 312.585 23.4226 312.656 22.9846 312.656C22.5648 312.656 22.2016 312.589 21.8949 312.456C21.5883 312.323 21.3483 312.134 21.1749 311.889C21.0015 311.643 20.9057 311.351 20.8874 311.013H21.7361C21.7526 311.216 21.8183 311.385 21.9333 311.52C22.0501 311.653 22.1988 311.753 22.3795 311.818C22.562 311.882 22.7619 311.914 22.9791 311.914C23.2182 311.914 23.4308 311.877 23.617 311.802C23.805 311.725 23.9529 311.619 24.0605 311.484C24.1682 311.347 24.2221 311.188 24.2221 311.005C24.2221 310.839 24.1746 310.703 24.0797 310.597C23.9866 310.491 23.8598 310.404 23.6992 310.334C23.5404 310.265 23.3606 310.204 23.1598 310.151L22.4972 309.97C22.0482 309.848 21.6923 309.668 21.4295 309.431C21.1685 309.194 21.038 308.88 21.038 308.489C21.038 308.166 21.1256 307.884 21.3008 307.643C21.476 307.402 21.7133 307.215 22.0127 307.082C22.312 306.947 22.6497 306.879 23.0256 306.879C23.4053 306.879 23.7402 306.946 24.0304 307.079C24.3225 307.212 24.5524 307.396 24.7204 307.629C24.8883 307.861 24.9759 308.128 24.9832 308.429H24.1673ZM29.6765 306.956H30.5252V310.644C30.5252 311.036 30.4331 311.384 30.2487 311.687C30.0644 311.988 29.8052 312.225 29.4712 312.399C29.1372 312.57 28.7456 312.656 28.2966 312.656C27.8495 312.656 27.4589 312.57 27.1249 312.399C26.7908 312.225 26.5317 311.988 26.3473 311.687C26.163 311.384 26.0708 311.036 26.0708 310.644V306.956H26.9168V310.575C26.9168 310.829 26.9724 311.054 27.0838 311.252C27.1969 311.449 27.3567 311.604 27.5629 311.717C27.7692 311.828 28.0137 311.884 28.2966 311.884C28.5814 311.884 28.8269 311.828 29.0331 311.717C29.2412 311.604 29.4 311.449 29.5095 311.252C29.6208 311.054 29.6765 310.829 29.6765 310.575V306.956ZM36.2808 306.956V312.563H35.5033L32.6532 308.451H32.6012V312.563H31.7552V306.956H32.5382L35.391 311.074H35.443V306.956H36.2808Z" fill="#757575"/>
<path d="M56.0877 306.956H57.1144L58.8994 311.315H58.9651L60.7502 306.956H61.7769V312.563H60.972V308.506H60.9199L59.2663 312.555H58.5983L56.9446 308.503H56.8926V312.563H56.0877V306.956ZM67.8774 309.759C67.8774 310.358 67.7679 310.873 67.5489 311.304C67.3299 311.733 67.0296 312.063 66.6482 312.295C66.2685 312.525 65.8368 312.64 65.3532 312.64C64.8677 312.64 64.4342 312.525 64.0527 312.295C63.6731 312.063 63.3737 311.732 63.1547 311.301C62.9357 310.87 62.8262 310.356 62.8262 309.759C62.8262 309.161 62.9357 308.647 63.1547 308.218C63.3737 307.787 63.6731 307.457 64.0527 307.227C64.4342 306.995 64.8677 306.879 65.3532 306.879C65.8368 306.879 66.2685 306.995 66.6482 307.227C67.0296 307.457 67.3299 307.787 67.5489 308.218C67.7679 308.647 67.8774 309.161 67.8774 309.759ZM67.0397 309.759C67.0397 309.303 66.9657 308.919 66.8179 308.607C66.6719 308.293 66.4711 308.056 66.2156 307.895C65.9619 307.733 65.6744 307.651 65.3532 307.651C65.0301 307.651 64.7417 307.733 64.488 307.895C64.2343 308.056 64.0335 308.293 63.8857 308.607C63.7397 308.919 63.6667 309.303 63.6667 309.759C63.6667 310.216 63.7397 310.601 63.8857 310.915C64.0335 311.227 64.2343 311.464 64.488 311.627C64.7417 311.787 65.0301 311.868 65.3532 311.868C65.6744 311.868 65.9619 311.787 66.2156 311.627C66.4711 311.464 66.6719 311.227 66.8179 310.915C66.9657 310.601 67.0397 310.216 67.0397 309.759ZM73.4578 306.956V312.563H72.6803L69.8302 308.451H69.7782V312.563H68.9322V306.956H69.7152L72.568 311.074H72.62V306.956H73.4578Z" fill="#D6D6D6"/>
<path d="M93.2897 307.684V306.956H97.6291V307.684H95.8797V312.563H95.0364V307.684H93.2897ZM102.194 306.956H103.042V310.644C103.042 311.036 102.95 311.384 102.766 311.687C102.582 311.988 102.322 312.225 101.988 312.399C101.654 312.57 101.263 312.656 100.814 312.656C100.367 312.656 99.9761 312.57 99.6421 312.399C99.3081 312.225 99.0489 311.988 98.8646 311.687C98.6802 311.384 98.5881 311.036 98.5881 310.644V306.956H99.434V310.575C99.434 310.829 99.4897 311.054 99.601 311.252C99.7142 311.449 99.8739 311.604 100.08 311.717C100.286 311.828 100.531 311.884 100.814 311.884C101.099 311.884 101.344 311.828 101.55 311.717C101.758 311.604 101.917 311.449 102.027 311.252C102.138 311.054 102.194 310.829 102.194 310.575V306.956ZM104.272 312.563V306.956H107.788V307.684H105.118V309.393H107.604V310.118H105.118V311.835H107.821V312.563H104.272Z" fill="#D6D6D6"/>
<path d="M129.643 312.563L128.088 306.956H128.977L130.07 311.298H130.122L131.258 306.956H132.139L133.276 311.301H133.328L134.417 306.956H135.31L133.752 312.563H132.901L131.721 308.363H131.677L130.497 312.563H129.643ZM136.081 312.563V306.956H139.597V307.684H136.927V309.393H139.413V310.118H136.927V311.835H139.629V312.563H136.081ZM142.542 312.563H140.727V306.956H142.599C143.149 306.956 143.621 307.068 144.015 307.293C144.409 307.515 144.711 307.836 144.921 308.254C145.133 308.67 145.239 309.169 145.239 309.751C145.239 310.335 145.132 310.837 144.918 311.257C144.707 311.677 144.4 312 143.998 312.226C143.597 312.451 143.111 312.563 142.542 312.563ZM141.573 311.824H142.495C142.922 311.824 143.277 311.743 143.56 311.583C143.843 311.42 144.055 311.186 144.195 310.879C144.336 310.571 144.406 310.195 144.406 309.751C144.406 309.311 144.336 308.938 144.195 308.631C144.057 308.325 143.85 308.092 143.574 307.933C143.298 307.775 142.956 307.695 142.547 307.695H141.573V311.824Z" fill="#D6D6D6"/>
<path d="M164.747 307.684V306.956H169.087V307.684H167.337V312.563H166.494V307.684H164.747ZM170.046 312.563V306.956H170.892V309.393H173.687V306.956H174.536V312.563H173.687V310.118H170.892V312.563H170.046ZM179.373 306.956H180.222V310.644C180.222 311.036 180.13 311.384 179.946 311.687C179.761 311.988 179.502 312.225 179.168 312.399C178.834 312.57 178.442 312.656 177.993 312.656C177.546 312.656 177.156 312.57 176.822 312.399C176.488 312.225 176.228 311.988 176.044 311.687C175.86 311.384 175.768 311.036 175.768 310.644V306.956H176.614V310.575C176.614 310.829 176.669 311.054 176.781 311.252C176.894 311.449 177.053 311.604 177.26 311.717C177.466 311.828 177.711 311.884 177.993 311.884C178.278 311.884 178.524 311.828 178.73 311.717C178.938 311.604 179.097 311.449 179.206 311.252C179.318 311.054 179.373 310.829 179.373 310.575V306.956Z" fill="#D6D6D6"/>
<path d="M203.502 312.563V306.956H206.979V307.684H204.348V309.393H206.73V310.118H204.348V312.563H203.502ZM208.02 312.563V306.956H210.018C210.453 306.956 210.813 307.031 211.1 307.18C211.388 307.33 211.603 307.537 211.746 307.802C211.888 308.065 211.959 308.369 211.959 308.714C211.959 309.057 211.887 309.359 211.743 309.62C211.601 309.879 211.385 310.081 211.097 310.225C210.81 310.369 210.45 310.441 210.016 310.441H208.502V309.713H209.939C210.213 309.713 210.435 309.674 210.607 309.595C210.78 309.517 210.907 309.403 210.987 309.253C211.068 309.103 211.108 308.924 211.108 308.714C211.108 308.502 211.067 308.318 210.985 308.163C210.904 308.008 210.778 307.89 210.604 307.807C210.433 307.723 210.207 307.681 209.928 307.681H208.866V312.563H208.02ZM210.788 310.033L212.173 312.563H211.209L209.851 310.033H210.788ZM213.835 306.956V312.563H212.989V306.956H213.835Z" fill="#D6D6D6"/>
<path d="M241.039 308.429C241.01 308.17 240.889 307.969 240.678 307.827C240.466 307.682 240.199 307.61 239.878 307.61C239.648 307.61 239.449 307.647 239.281 307.72C239.113 307.791 238.983 307.89 238.89 308.015C238.798 308.14 238.753 308.281 238.753 308.44C238.753 308.573 238.784 308.688 238.846 308.785C238.91 308.882 238.993 308.963 239.095 309.028C239.199 309.092 239.31 309.146 239.429 309.19C239.548 309.232 239.662 309.267 239.771 309.294L240.319 309.436C240.498 309.48 240.681 309.54 240.869 309.614C241.057 309.689 241.231 309.788 241.392 309.91C241.553 310.032 241.682 310.184 241.781 310.365C241.881 310.545 241.931 310.762 241.931 311.013C241.931 311.331 241.849 311.613 241.685 311.859C241.523 312.106 241.286 312.3 240.976 312.443C240.667 312.585 240.294 312.656 239.856 312.656C239.436 312.656 239.073 312.589 238.767 312.456C238.46 312.323 238.22 312.134 238.046 311.889C237.873 311.643 237.777 311.351 237.759 311.013H238.608C238.624 311.216 238.69 311.385 238.805 311.52C238.922 311.653 239.07 311.753 239.251 311.818C239.434 311.882 239.633 311.914 239.851 311.914C240.09 311.914 240.302 311.877 240.489 311.802C240.677 311.725 240.824 311.619 240.932 311.484C241.04 311.347 241.094 311.188 241.094 311.005C241.094 310.839 241.046 310.703 240.951 310.597C240.858 310.491 240.731 310.404 240.571 310.334C240.412 310.265 240.232 310.204 240.031 310.151L239.369 309.97C238.92 309.848 238.564 309.668 238.301 309.431C238.04 309.194 237.91 308.88 237.91 308.489C237.91 308.166 237.997 307.884 238.172 307.643C238.348 307.402 238.585 307.215 238.884 307.082C239.184 306.947 239.521 306.879 239.897 306.879C240.277 306.879 240.612 306.946 240.902 307.079C241.194 307.212 241.424 307.396 241.592 307.629C241.76 307.861 241.847 308.128 241.855 308.429H241.039ZM243.371 312.563H242.473L244.491 306.956H245.468L247.486 312.563H246.588L245.003 307.974H244.959L243.371 312.563ZM243.521 310.367H246.434V311.079H243.521V310.367ZM247.356 307.684V306.956H251.696V307.684H249.946V312.563H249.103V307.684H247.356Z" fill="#757575"/>
<path d="M137.669 338.925V345.466H136.679V339.915H136.641L135.076 340.937V339.992L136.708 338.925H137.669Z" fill="#757575"/>
<path d="M170.534 345.466V344.751L172.748 342.457C172.984 342.208 173.179 341.99 173.332 341.803C173.488 341.613 173.604 341.433 173.68 341.263C173.757 341.092 173.795 340.911 173.795 340.72C173.795 340.503 173.744 340.315 173.642 340.158C173.54 339.998 173.4 339.876 173.224 339.79C173.047 339.703 172.848 339.659 172.626 339.659C172.392 339.659 172.188 339.707 172.013 339.803C171.838 339.899 171.704 340.034 171.611 340.209C171.517 340.383 171.47 340.588 171.47 340.822H170.528C170.528 340.424 170.619 340.076 170.803 339.778C170.986 339.479 171.237 339.248 171.556 339.084C171.876 338.918 172.239 338.835 172.646 338.835C173.057 338.835 173.419 338.917 173.732 339.081C174.047 339.243 174.293 339.465 174.469 339.746C174.646 340.025 174.734 340.34 174.734 340.691C174.734 340.934 174.689 341.171 174.597 341.403C174.508 341.635 174.351 341.894 174.128 342.18C173.904 342.463 173.593 342.807 173.195 343.211L171.895 344.572V344.62H174.84V345.466H170.534Z" fill="#757575"/>
<path d="M208.65 345.556C208.211 345.556 207.819 345.48 207.474 345.329C207.131 345.178 206.86 344.968 206.66 344.7C206.462 344.429 206.355 344.116 206.34 343.761H207.343C207.356 343.954 207.421 344.123 207.538 344.265C207.657 344.406 207.813 344.514 208.004 344.591C208.196 344.668 208.409 344.706 208.643 344.706C208.901 344.706 209.129 344.661 209.327 344.572C209.527 344.483 209.683 344.358 209.796 344.198C209.909 344.036 209.966 343.85 209.966 343.639C209.966 343.42 209.909 343.227 209.796 343.061C209.686 342.893 209.523 342.761 209.308 342.665C209.095 342.569 208.837 342.521 208.535 342.521H207.982V341.716H208.535C208.777 341.716 208.99 341.673 209.173 341.585C209.359 341.498 209.503 341.377 209.608 341.221C209.712 341.064 209.764 340.88 209.764 340.669C209.764 340.466 209.719 340.291 209.627 340.142C209.538 339.991 209.41 339.872 209.244 339.787C209.08 339.702 208.886 339.659 208.662 339.659C208.449 339.659 208.25 339.699 208.065 339.778C207.882 339.854 207.733 339.965 207.618 340.11C207.503 340.252 207.441 340.424 207.433 340.624H206.478C206.488 340.271 206.593 339.96 206.791 339.691C206.991 339.423 207.255 339.213 207.583 339.062C207.911 338.911 208.275 338.835 208.675 338.835C209.095 338.835 209.457 338.917 209.761 339.081C210.068 339.243 210.304 339.459 210.47 339.73C210.638 340 210.722 340.296 210.719 340.618C210.722 340.984 210.619 341.295 210.413 341.55C210.208 341.806 209.936 341.977 209.595 342.065V342.116C210.029 342.182 210.366 342.354 210.604 342.633C210.845 342.912 210.964 343.258 210.962 343.671C210.964 344.031 210.864 344.354 210.662 344.639C210.462 344.924 210.188 345.149 209.841 345.313C209.494 345.475 209.097 345.556 208.65 345.556Z" fill="#757575"/>
<path d="M242.169 344.189V343.39L244.996 338.925H245.625V340.1H245.226L243.204 343.301V343.352H247.079V344.189H242.169ZM245.271 345.466V343.946L245.277 343.582V338.925H246.213V345.466H245.271Z" fill="#757575"/>
<path d="M28.7481 381.534C28.3478 381.534 27.9879 381.458 27.6685 381.304C27.3512 381.149 27.0978 380.936 26.9083 380.665C26.7188 380.395 26.6177 380.086 26.6049 379.739H27.5631C27.5865 380.02 27.7111 380.251 27.9368 380.432C28.1626 380.613 28.433 380.704 28.7481 380.704C28.9994 380.704 29.2219 380.646 29.4157 380.531C29.6116 380.414 29.7649 380.253 29.8757 380.049C29.9885 379.845 30.045 379.611 30.045 379.349C30.045 379.083 29.9875 378.846 29.8725 378.637C29.7575 378.429 29.5988 378.265 29.3965 378.145C29.1964 378.026 28.9664 377.965 28.7066 377.963C28.5086 377.963 28.3095 377.997 28.1093 378.065C27.9092 378.134 27.7473 378.223 27.6238 378.334L26.7199 378.2L27.0872 374.903H30.6838V375.75H27.9081L27.7005 377.58H27.7388C27.8666 377.456 28.0359 377.353 28.2467 377.27C28.4596 377.187 28.6875 377.146 28.9302 377.146C29.3284 377.146 29.683 377.24 29.9939 377.43C30.3069 377.619 30.5528 377.878 30.7317 378.206C30.9127 378.532 31.0021 378.907 31 379.33C31.0021 379.754 30.9063 380.132 30.7125 380.464C30.5209 380.796 30.2547 381.058 29.914 381.25C29.5754 381.44 29.1868 381.534 28.7481 381.534Z" fill="#757575"/>
<path d="M64.781 381.534C64.4892 381.53 64.2018 381.477 63.9185 381.375C63.6375 381.272 63.3819 381.102 63.152 380.863C62.922 380.625 62.7378 380.305 62.5994 379.902C62.4631 379.5 62.3949 378.997 62.3949 378.394C62.3949 377.824 62.4514 377.317 62.5642 376.874C62.6792 376.431 62.8443 376.057 63.0593 375.753C63.2744 375.446 63.5342 375.213 63.8387 375.053C64.1432 374.894 64.485 374.814 64.864 374.814C65.2537 374.814 65.5997 374.89 65.9021 375.044C66.2045 375.197 66.4494 375.409 66.6367 375.679C66.8263 375.95 66.9466 376.258 66.9977 376.603H66.0235C65.9575 376.33 65.8265 376.107 65.6306 375.935C65.4347 375.762 65.1792 375.676 64.864 375.676C64.3849 375.676 64.0112 375.885 63.7429 376.302C63.4767 376.72 63.3425 377.3 63.3404 378.043H63.3883C63.5012 377.858 63.6396 377.7 63.8036 377.57C63.9696 377.438 64.1549 377.337 64.3593 377.267C64.5659 377.195 64.7831 377.158 65.0109 377.158C65.39 377.158 65.7328 377.251 66.0394 377.436C66.3482 377.619 66.5942 377.873 66.7773 378.196C66.9604 378.52 67.052 378.891 67.052 379.308C67.052 379.725 66.9572 380.103 66.7677 380.442C66.5803 380.78 66.3163 381.049 65.9756 381.247C65.6349 381.443 65.2367 381.539 64.781 381.534ZM64.7778 380.704C65.029 380.704 65.2537 380.642 65.4517 380.519C65.6498 380.395 65.8063 380.229 65.9213 380.02C66.0362 379.812 66.0937 379.578 66.0937 379.321C66.0937 379.069 66.0373 378.841 65.9245 378.634C65.8137 378.427 65.6604 378.263 65.4645 378.142C65.2707 378.021 65.0493 377.96 64.8001 377.96C64.6106 377.96 64.4349 377.996 64.2731 378.069C64.1134 378.141 63.9728 378.241 63.8515 378.369C63.7301 378.497 63.6343 378.644 63.564 378.81C63.4959 378.974 63.4618 379.147 63.4618 379.33C63.4618 379.575 63.5182 379.802 63.6311 380.011C63.7461 380.219 63.9026 380.388 64.1006 380.515C64.3008 380.641 64.5265 380.704 64.7778 380.704Z" fill="#757575"/>
<path d="M98.9508 381.445L101.806 375.798V375.75H98.5036V374.903H102.828V375.778L99.9825 381.445H98.9508Z" fill="#757575"/>
<path d="M136.682 381.534C136.224 381.534 135.82 381.455 135.468 381.298C135.119 381.14 134.845 380.923 134.647 380.646C134.449 380.369 134.351 380.054 134.354 379.701C134.351 379.424 134.408 379.17 134.523 378.937C134.64 378.703 134.799 378.508 134.999 378.353C135.199 378.195 135.423 378.095 135.67 378.053V378.014C135.344 377.936 135.083 377.761 134.887 377.49C134.691 377.22 134.594 376.909 134.596 376.558C134.594 376.223 134.683 375.925 134.861 375.663C135.042 375.399 135.291 375.192 135.606 375.041C135.921 374.889 136.28 374.814 136.682 374.814C137.08 374.814 137.436 374.89 137.749 375.044C138.064 375.195 138.312 375.403 138.493 375.667C138.674 375.929 138.766 376.226 138.768 376.558C138.766 376.909 138.666 377.22 138.468 377.49C138.27 377.761 138.012 377.936 137.695 378.014V378.053C137.94 378.095 138.16 378.195 138.356 378.353C138.554 378.508 138.711 378.703 138.829 378.937C138.948 379.17 139.008 379.424 139.011 379.701C139.008 380.054 138.908 380.369 138.71 380.646C138.512 380.923 138.238 381.14 137.886 381.298C137.537 381.455 137.136 381.534 136.682 381.534ZM136.682 380.726C136.953 380.726 137.187 380.681 137.385 380.592C137.583 380.5 137.736 380.374 137.845 380.212C137.953 380.048 138.009 379.856 138.011 379.637C138.009 379.409 137.949 379.208 137.832 379.033C137.717 378.859 137.56 378.721 137.362 378.621C137.164 378.521 136.938 378.471 136.682 378.471C136.424 378.471 136.196 378.521 135.995 378.621C135.795 378.721 135.638 378.859 135.523 379.033C135.408 379.208 135.351 379.409 135.353 379.637C135.351 379.856 135.403 380.048 135.51 380.212C135.618 380.374 135.773 380.5 135.973 380.592C136.173 380.681 136.41 380.726 136.682 380.726ZM136.682 377.682C136.899 377.682 137.092 377.638 137.26 377.551C137.428 377.464 137.56 377.343 137.656 377.187C137.754 377.032 137.804 376.85 137.806 376.641C137.804 376.436 137.755 376.258 137.659 376.104C137.566 375.951 137.435 375.833 137.267 375.75C137.098 375.665 136.904 375.622 136.682 375.622C136.456 375.622 136.258 375.665 136.088 375.75C135.92 375.833 135.789 375.951 135.695 376.104C135.601 376.258 135.556 376.436 135.558 376.641C135.556 376.85 135.602 377.032 135.698 377.187C135.794 377.343 135.926 377.464 136.094 377.551C136.265 377.638 136.461 377.682 136.682 377.682Z" fill="#757575"/>
<path d="M172.608 374.814C172.9 374.816 173.188 374.869 173.471 374.974C173.754 375.078 174.01 375.248 174.237 375.485C174.467 375.721 174.651 376.04 174.787 376.443C174.925 376.843 174.995 377.341 174.998 377.938C174.998 378.51 174.94 379.02 174.825 379.468C174.71 379.913 174.545 380.289 174.33 380.595C174.117 380.902 173.858 381.135 173.554 381.295C173.249 381.454 172.907 381.534 172.525 381.534C172.136 381.534 171.79 381.458 171.487 381.304C171.185 381.151 170.939 380.939 170.749 380.669C170.56 380.396 170.442 380.083 170.395 379.73H171.369C171.433 380.011 171.563 380.239 171.759 380.413C171.957 380.586 172.212 380.672 172.525 380.672C173.004 380.672 173.378 380.463 173.646 380.046C173.915 379.626 174.05 379.041 174.052 378.289H174.001C173.89 378.472 173.752 378.63 173.586 378.762C173.422 378.894 173.238 378.996 173.033 379.068C172.829 379.141 172.611 379.177 172.378 379.177C172.002 379.177 171.659 379.084 171.35 378.899C171.041 378.714 170.795 378.459 170.612 378.136C170.429 377.812 170.337 377.443 170.337 377.027C170.337 376.614 170.431 376.239 170.618 375.903C170.808 375.567 171.072 375.3 171.411 375.104C171.751 374.906 172.151 374.81 172.608 374.814ZM172.612 375.644C172.362 375.644 172.138 375.706 171.938 375.83C171.74 375.951 171.583 376.116 171.468 376.325C171.353 376.531 171.296 376.761 171.296 377.015C171.296 377.268 171.351 377.498 171.462 377.705C171.575 377.909 171.728 378.072 171.922 378.193C172.118 378.312 172.341 378.372 172.592 378.372C172.78 378.372 172.954 378.336 173.116 378.263C173.278 378.191 173.42 378.091 173.541 377.963C173.662 377.833 173.757 377.686 173.825 377.522C173.893 377.358 173.928 377.186 173.928 377.005C173.928 376.764 173.87 376.541 173.755 376.334C173.642 376.128 173.487 375.962 173.289 375.836C173.091 375.708 172.865 375.644 172.612 375.644Z" fill="#757575"/>
<path d="M206.724 374.903V381.445H205.734V375.893H205.695L204.13 376.916V375.97L205.763 374.903H206.724ZM210.771 381.553C210.266 381.551 209.835 381.418 209.477 381.154C209.12 380.89 208.846 380.506 208.656 380.001C208.467 379.496 208.372 378.888 208.372 378.177C208.372 377.468 208.467 376.862 208.656 376.36C208.848 375.857 209.123 375.474 209.48 375.21C209.84 374.946 210.27 374.814 210.771 374.814C211.271 374.814 211.7 374.947 212.058 375.213C212.416 375.477 212.689 375.86 212.879 376.363C213.071 376.863 213.166 377.468 213.166 378.177C213.166 378.891 213.072 379.5 212.882 380.004C212.693 380.507 212.419 380.891 212.061 381.157C211.704 381.421 211.273 381.553 210.771 381.553ZM210.771 380.701C211.214 380.701 211.56 380.484 211.809 380.052C212.06 379.62 212.186 378.995 212.186 378.177C212.186 377.634 212.128 377.175 212.013 376.801C211.901 376.424 211.738 376.138 211.525 375.945C211.314 375.749 211.063 375.651 210.771 375.651C210.33 375.651 209.984 375.868 209.733 376.302C209.482 376.737 209.355 377.362 209.353 378.177C209.353 378.722 209.409 379.183 209.522 379.56C209.637 379.935 209.8 380.219 210.011 380.413C210.222 380.605 210.475 380.701 210.771 380.701Z" fill="#757575"/>
<path d="M243.48 374.903V381.445H242.49V375.893H242.451L240.886 376.916V375.97L242.518 374.903H243.48ZM247.731 374.903V381.445H246.741V375.893H246.703L245.138 376.916V375.97L246.77 374.903H247.731Z" fill="#757575"/>
<path d="M26.9758 410.882V417.423H25.9856V411.872H25.9473L24.3822 412.894V411.949L26.0144 410.882H26.9758ZM28.7453 417.423V416.708L30.9589 414.414C31.1952 414.165 31.3901 413.947 31.5434 413.76C31.6988 413.57 31.8149 413.39 31.8916 413.22C31.9682 413.05 32.0065 412.869 32.0065 412.677C32.0065 412.46 31.9554 412.272 31.8532 412.115C31.751 411.955 31.6115 411.833 31.4348 411.747C31.2581 411.66 31.059 411.616 30.8375 411.616C30.6033 411.616 30.3988 411.664 30.2242 411.76C30.0496 411.856 29.9155 411.991 29.8218 412.166C29.7281 412.34 29.6812 412.545 29.6812 412.779H28.739C28.739 412.381 28.8305 412.033 29.0137 411.735C29.1968 411.436 29.4481 411.205 29.7675 411.041C30.0869 410.875 30.4499 410.792 30.8567 410.792C31.2676 410.792 31.6296 410.874 31.9427 411.038C32.2578 411.2 32.5038 411.422 32.6805 411.703C32.8573 411.982 32.9456 412.297 32.9456 412.648C32.9456 412.891 32.8998 413.128 32.8083 413.36C32.7188 413.593 32.5623 413.851 32.3387 414.137C32.1151 414.42 31.8043 414.764 31.4061 415.168L30.106 416.529V416.577H33.051V417.423H28.7453Z" fill="#757575"/>
<path d="M62.8138 410.882V417.423H61.8236V411.872H61.7853L60.2201 412.894V411.949L61.8523 410.882H62.8138ZM66.8607 417.513C66.4221 417.513 66.0303 417.437 65.6853 417.286C65.3425 417.135 65.071 416.925 64.8708 416.657C64.6728 416.386 64.5663 416.073 64.5514 415.718H65.5543C65.5671 415.911 65.6321 416.08 65.7492 416.222C65.8684 416.363 66.0239 416.471 66.2155 416.548C66.4072 416.625 66.6201 416.663 66.8543 416.663C67.112 416.663 67.3399 416.618 67.5379 416.529C67.7381 416.44 67.8946 416.315 68.0074 416.155C68.1203 415.993 68.1767 415.807 68.1767 415.596C68.1767 415.377 68.1203 415.184 68.0074 415.018C67.8967 414.85 67.7338 414.718 67.5187 414.622C67.3058 414.526 67.0481 414.478 66.7457 414.478H66.1932V413.673H66.7457C66.9885 413.673 67.2014 413.63 67.3846 413.542C67.5698 413.455 67.7146 413.334 67.819 413.178C67.9233 413.021 67.9755 412.837 67.9755 412.626C67.9755 412.423 67.9297 412.248 67.8381 412.099C67.7487 411.948 67.6209 411.829 67.4548 411.744C67.2909 411.659 67.0971 411.616 66.8735 411.616C66.6606 411.616 66.4615 411.656 66.2762 411.735C66.0931 411.811 65.944 411.922 65.829 412.067C65.714 412.209 65.6523 412.381 65.6438 412.581H64.6887C64.6994 412.228 64.8037 411.917 65.0018 411.648C65.2019 411.38 65.466 411.17 65.7939 411.019C66.1218 410.868 66.486 410.792 66.8863 410.792C67.3058 410.792 67.6678 410.874 67.9723 411.038C68.2789 411.2 68.5153 411.416 68.6814 411.687C68.8496 411.957 68.9327 412.253 68.9305 412.575C68.9327 412.941 68.8304 413.252 68.6239 413.507C68.4195 413.763 68.1469 413.934 67.8062 414.022V414.073C68.2406 414.139 68.577 414.311 68.8155 414.59C69.0562 414.869 69.1754 415.215 69.1733 415.628C69.1754 415.988 69.0753 416.311 68.873 416.596C68.6729 416.881 68.3992 417.106 68.0521 417.27C67.705 417.432 67.3079 417.513 66.8607 417.513Z" fill="#757575"/>
<path d="M98.7484 410.882V417.423H97.7582V411.872H97.7199L96.1547 412.894V411.949L97.7869 410.882H98.7484ZM100.381 416.146V415.347L103.207 410.882H103.837V412.057H103.437L101.415 415.258V415.309H105.29V416.146H100.381ZM103.482 417.423V415.903L103.488 415.539V410.882H104.424V417.423H103.482Z" fill="#757575"/>
<path d="M134.881 410.882V417.423H133.89V411.872H133.852L132.287 412.894V411.949L133.919 410.882H134.881ZM138.809 417.513C138.409 417.513 138.049 417.436 137.73 417.283C137.412 417.127 137.159 416.914 136.97 416.644C136.78 416.374 136.679 416.065 136.666 415.718H137.624C137.648 415.999 137.772 416.23 137.998 416.411C138.224 416.592 138.494 416.682 138.809 416.682C139.061 416.682 139.283 416.625 139.477 416.51C139.673 416.393 139.826 416.232 139.937 416.028C140.05 415.823 140.106 415.59 140.106 415.328C140.106 415.062 140.049 414.824 139.934 414.616C139.819 414.407 139.66 414.243 139.458 414.124C139.258 414.005 139.028 413.944 138.768 413.942C138.57 413.942 138.371 413.976 138.171 414.044C137.97 414.112 137.809 414.202 137.685 414.312L136.781 414.178L137.148 410.882H140.745V411.728H137.969L137.762 413.558H137.8C137.928 413.435 138.097 413.332 138.308 413.249C138.521 413.166 138.749 413.124 138.991 413.124C139.39 413.124 139.744 413.219 140.055 413.408C140.368 413.598 140.614 413.857 140.793 414.184C140.974 414.51 141.063 414.885 141.061 415.309C141.063 415.733 140.968 416.111 140.774 416.443C140.582 416.775 140.316 417.037 139.975 417.228C139.637 417.418 139.248 417.513 138.809 417.513Z" fill="#757575"/>
<path d="M170.789 410.882V417.423H169.799V411.872H169.761L168.196 412.894V411.949L169.828 410.882H170.789ZM174.843 417.513C174.551 417.509 174.263 417.455 173.98 417.353C173.699 417.251 173.444 417.081 173.214 416.842C172.984 416.604 172.8 416.283 172.661 415.881C172.525 415.478 172.457 414.976 172.457 414.373C172.457 413.802 172.513 413.295 172.626 412.853C172.741 412.41 172.906 412.036 173.121 411.731C173.336 411.425 173.596 411.192 173.9 411.032C174.205 410.872 174.547 410.792 174.926 410.792C175.315 410.792 175.661 410.869 175.964 411.022C176.266 411.176 176.511 411.388 176.698 411.658C176.888 411.928 177.008 412.236 177.059 412.581H176.085C176.019 412.308 175.888 412.086 175.692 411.913C175.496 411.741 175.241 411.655 174.926 411.655C174.447 411.655 174.073 411.863 173.805 412.281C173.538 412.698 173.404 413.278 173.402 414.022H173.45C173.563 413.836 173.701 413.679 173.865 413.549C174.031 413.417 174.217 413.316 174.421 413.245C174.628 413.173 174.845 413.137 175.073 413.137C175.452 413.137 175.795 413.229 176.101 413.415C176.41 413.598 176.656 413.851 176.839 414.175C177.022 414.499 177.114 414.869 177.114 415.286C177.114 415.704 177.019 416.082 176.829 416.42C176.642 416.759 176.378 417.027 176.037 417.225C175.697 417.421 175.298 417.517 174.843 417.513ZM174.839 416.682C175.091 416.682 175.315 416.621 175.513 416.497C175.711 416.374 175.868 416.207 175.983 415.999C176.098 415.79 176.155 415.557 176.155 415.299C176.155 415.048 176.099 414.819 175.986 414.613C175.875 414.406 175.722 414.242 175.526 414.121C175.332 413.999 175.111 413.939 174.862 413.939C174.672 413.939 174.497 413.975 174.335 414.047C174.175 414.12 174.035 414.22 173.913 414.347C173.792 414.475 173.696 414.622 173.626 414.788C173.558 414.952 173.524 415.126 173.524 415.309C173.524 415.554 173.58 415.78 173.693 415.989C173.808 416.198 173.964 416.366 174.162 416.494C174.362 416.619 174.588 416.682 174.839 416.682Z" fill="#757575"/>
<path d="M207.023 410.882V417.423H206.032V411.872H205.994L204.429 412.894V411.949L206.061 410.882H207.023ZM209.013 417.423L211.868 411.776V411.728H208.565V410.882H212.89V411.757L210.044 417.423H209.013Z" fill="#757575"/>
<path d="M242.764 410.882V417.423H241.774V411.872H241.735L240.17 412.894V411.949L241.803 410.882H242.764ZM246.744 417.513C246.286 417.513 245.881 417.434 245.53 417.276C245.181 417.119 244.907 416.902 244.709 416.625C244.511 416.348 244.413 416.033 244.415 415.679C244.413 415.403 244.47 415.148 244.585 414.916C244.702 414.682 244.86 414.487 245.061 414.331C245.261 414.174 245.484 414.074 245.731 414.031V413.993C245.405 413.914 245.145 413.739 244.949 413.469C244.753 413.199 244.656 412.888 244.658 412.536C244.656 412.202 244.744 411.904 244.923 411.642C245.104 411.378 245.352 411.17 245.667 411.019C245.983 410.868 246.341 410.792 246.744 410.792C247.142 410.792 247.498 410.869 247.811 411.022C248.126 411.174 248.374 411.381 248.555 411.645C248.736 411.907 248.827 412.204 248.83 412.536C248.827 412.888 248.727 413.199 248.529 413.469C248.331 413.739 248.074 413.914 247.756 413.993V414.031C248.001 414.074 248.222 414.174 248.418 414.331C248.616 414.487 248.773 414.682 248.89 414.916C249.01 415.148 249.07 415.403 249.072 415.679C249.07 416.033 248.97 416.348 248.772 416.625C248.574 416.902 248.299 417.119 247.948 417.276C247.599 417.434 247.197 417.513 246.744 417.513ZM246.744 416.705C247.014 416.705 247.248 416.66 247.447 416.571C247.645 416.479 247.798 416.352 247.906 416.19C248.015 416.026 248.07 415.835 248.073 415.615C248.07 415.388 248.011 415.186 247.894 415.012C247.779 414.837 247.622 414.7 247.424 414.6C247.226 414.5 246.999 414.45 246.744 414.45C246.486 414.45 246.257 414.5 246.057 414.6C245.857 414.7 245.699 414.837 245.584 415.012C245.469 415.186 245.413 415.388 245.415 415.615C245.413 415.835 245.465 416.026 245.572 416.19C245.68 416.352 245.835 416.479 246.035 416.571C246.235 416.66 246.471 416.705 246.744 416.705ZM246.744 413.661C246.961 413.661 247.154 413.617 247.322 413.53C247.49 413.442 247.622 413.321 247.718 413.166C247.816 413.01 247.866 412.828 247.868 412.619C247.866 412.415 247.817 412.236 247.721 412.083C247.628 411.929 247.497 411.811 247.328 411.728C247.16 411.643 246.965 411.6 246.744 411.6C246.518 411.6 246.32 411.643 246.15 411.728C245.981 411.811 245.851 411.929 245.757 412.083C245.663 412.236 245.617 412.415 245.619 412.619C245.617 412.828 245.664 413.01 245.76 413.166C245.856 413.321 245.988 413.442 246.156 413.53C246.326 413.617 246.522 413.661 246.744 413.661Z" fill="#757575"/>
<path d="M26.8748 446.86V453.402H25.8846V447.85H25.8463L24.2812 448.873V447.927L25.9134 446.86H26.8748ZM30.8195 446.771C31.1113 446.773 31.3987 446.826 31.6819 446.931C31.9652 447.035 32.2207 447.205 32.4485 447.442C32.6785 447.678 32.8616 447.997 32.9979 448.4C33.1363 448.8 33.2066 449.298 33.2087 449.895C33.2087 450.468 33.1512 450.978 33.0363 451.425C32.9213 451.87 32.7562 452.246 32.5412 452.552C32.3282 452.859 32.0695 453.092 31.765 453.252C31.4605 453.411 31.1176 453.491 30.7365 453.491C30.3468 453.491 30.0008 453.415 29.6984 453.261C29.396 453.108 29.1501 452.896 28.9606 452.626C28.771 452.353 28.6529 452.04 28.606 451.687H29.5802C29.6441 451.968 29.774 452.196 29.9699 452.37C30.1679 452.543 30.4235 452.629 30.7365 452.629C31.2156 452.629 31.5893 452.42 31.8576 452.003C32.1259 451.583 32.2611 450.998 32.2633 450.246H32.2122C32.1014 450.429 31.963 450.587 31.7969 450.719C31.633 450.851 31.4488 450.953 31.2444 451.025C31.0399 451.098 30.8217 451.134 30.5896 451.134C30.2126 451.134 29.8698 451.041 29.561 450.856C29.2523 450.671 29.0063 450.416 28.8232 450.093C28.6401 449.769 28.5485 449.4 28.5485 448.984C28.5485 448.571 28.6422 448.196 28.8296 447.86C29.0191 447.524 29.2832 447.257 29.6217 447.062C29.9624 446.863 30.3617 446.767 30.8195 446.771ZM30.8227 447.601C30.5736 447.601 30.3489 447.663 30.1488 447.787C29.9507 447.908 29.7942 448.073 29.6792 448.282C29.5642 448.488 29.5067 448.718 29.5067 448.972C29.5067 449.225 29.5621 449.455 29.6728 449.662C29.7857 449.866 29.939 450.029 30.1328 450.15C30.3287 450.269 30.5523 450.329 30.8036 450.329C30.9909 450.329 31.1656 450.293 31.3274 450.221C31.4892 450.148 31.6308 450.048 31.7522 449.92C31.8736 449.79 31.9684 449.643 32.0365 449.479C32.1046 449.316 32.1387 449.143 32.1387 448.962C32.1387 448.721 32.0812 448.498 31.9662 448.291C31.8534 448.085 31.6979 447.919 31.4999 447.793C31.3018 447.665 31.0761 447.601 30.8227 447.601Z" fill="#757575"/>
<rect x="48.2676" y="434.203" width="33.4087" height="33.4087" rx="2.5699" fill="#525252"/>
<path d="M59.9402 453.408V452.692L62.1538 450.399C62.3901 450.15 62.585 449.931 62.7383 449.744C62.8937 449.555 63.0098 449.375 63.0864 449.204C63.1631 449.034 63.2014 448.853 63.2014 448.661C63.2014 448.444 63.1503 448.257 63.0481 448.099C62.9459 447.939 62.8064 447.817 62.6297 447.732C62.4529 447.644 62.2538 447.601 62.0324 447.601C61.7981 447.601 61.5937 447.649 61.4191 447.745C61.2445 447.84 61.1103 447.976 61.0166 448.15C60.9229 448.325 60.8761 448.529 60.8761 448.763H59.9338C59.9338 448.365 60.0254 448.017 60.2085 447.719C60.3917 447.421 60.6429 447.19 60.9623 447.026C61.2818 446.86 61.6448 446.777 62.0515 446.777C62.4625 446.777 62.8245 446.859 63.1375 447.023C63.4527 447.184 63.6986 447.406 63.8754 447.687C64.0521 447.966 64.1405 448.281 64.1405 448.632C64.1405 448.875 64.0947 449.113 64.0032 449.345C63.9137 449.577 63.7572 449.836 63.5336 450.121C63.31 450.404 62.9991 450.748 62.6009 451.153L61.3009 452.513V452.561H64.2459V453.408H59.9402ZM67.7339 453.516C67.2292 453.514 66.798 453.381 66.4403 453.117C66.0825 452.853 65.8089 452.469 65.6194 451.964C65.4299 451.459 65.3351 450.851 65.3351 450.14C65.3351 449.431 65.4299 448.825 65.6194 448.323C65.811 447.82 66.0857 447.437 66.4435 447.173C66.8033 446.909 67.2335 446.777 67.7339 446.777C68.2343 446.777 68.6634 446.91 69.0211 447.176C69.3789 447.44 69.6525 447.823 69.842 448.326C70.0337 448.826 70.1295 449.431 70.1295 450.14C70.1295 450.853 70.0347 451.462 69.8452 451.967C69.6557 452.47 69.3821 452.854 69.0243 453.12C68.6666 453.384 68.2364 453.516 67.7339 453.516ZM67.7339 452.663C68.1768 452.663 68.5228 452.447 68.772 452.015C69.0232 451.583 69.1489 450.958 69.1489 450.14C69.1489 449.597 69.0914 449.138 68.9764 448.763C68.8635 448.387 68.7006 448.101 68.4877 447.907C68.2769 447.712 68.0256 447.614 67.7339 447.614C67.2931 447.614 66.9471 447.831 66.6958 448.265C66.4445 448.7 66.3178 449.325 66.3157 450.14C66.3157 450.685 66.3721 451.146 66.485 451.523C66.6 451.898 66.7629 452.182 66.9737 452.376C67.1845 452.568 67.4379 452.663 67.7339 452.663Z" fill="#FCFCFD"/>
<circle cx="64.972" cy="461.187" r="1.28495" fill="#111827"/>
<rect x="84.2461" y="434.203" width="33.4087" height="33.4087" rx="2.5699" fill="#525252"/>
<path d="M96.6961 453.408V452.692L98.9096 450.399C99.146 450.15 99.3408 449.931 99.4942 449.744C99.6496 449.555 99.7657 449.375 99.8423 449.204C99.919 449.034 99.9573 448.853 99.9573 448.661C99.9573 448.444 99.9062 448.257 99.804 448.099C99.7018 447.939 99.5623 447.817 99.3856 447.732C99.2088 447.644 99.0097 447.601 98.7883 447.601C98.554 447.601 98.3496 447.649 98.175 447.745C98.0004 447.84 97.8662 447.976 97.7725 448.15C97.6788 448.325 97.632 448.529 97.632 448.763H96.6897C96.6897 448.365 96.7813 448.017 96.9644 447.719C97.1475 447.421 97.3988 447.19 97.7182 447.026C98.0376 446.86 98.4007 446.777 98.8074 446.777C99.2184 446.777 99.5804 446.859 99.8934 447.023C100.209 447.184 100.455 447.406 100.631 447.687C100.808 447.966 100.896 448.281 100.896 448.632C100.896 448.875 100.851 449.113 100.759 449.345C100.67 449.577 100.513 449.836 100.29 450.121C100.066 450.404 99.755 450.748 99.3568 451.153L98.0568 452.513V452.561H101.002V453.408H96.6961ZM104.694 446.866V453.408H103.704V447.856H103.666L102.101 448.878V447.933L103.733 446.866H104.694Z" fill="#FCFCFD"/>
<rect x="120.225" y="434.203" width="33.4087" height="33.4087" rx="2.5699" fill="#525252"/>
<path d="M132.042 453.408V452.692L134.256 450.399C134.492 450.15 134.687 449.931 134.84 449.744C134.996 449.555 135.112 449.375 135.188 449.204C135.265 449.034 135.303 448.853 135.303 448.661C135.303 448.444 135.252 448.257 135.15 448.099C135.048 447.939 134.908 447.817 134.732 447.732C134.555 447.644 134.356 447.601 134.134 447.601C133.9 447.601 133.696 447.649 133.521 447.745C133.346 447.84 133.212 447.976 133.119 448.15C133.025 448.325 132.978 448.529 132.978 448.763H132.036C132.036 448.365 132.127 448.017 132.31 447.719C132.494 447.421 132.745 447.19 133.064 447.026C133.384 446.86 133.747 446.777 134.154 446.777C134.564 446.777 134.926 446.859 135.24 447.023C135.555 447.184 135.801 447.406 135.977 447.687C136.154 447.966 136.242 448.281 136.242 448.632C136.242 448.875 136.197 449.113 136.105 449.345C136.016 449.577 135.859 449.836 135.636 450.121C135.412 450.404 135.101 450.748 134.703 451.153L133.403 452.513V452.561H136.348V453.408H132.042ZM137.558 453.408V452.692L139.772 450.399C140.008 450.15 140.203 449.931 140.356 449.744C140.512 449.555 140.628 449.375 140.705 449.204C140.781 449.034 140.82 448.853 140.82 448.661C140.82 448.444 140.769 448.257 140.666 448.099C140.564 447.939 140.425 447.817 140.248 447.732C140.071 447.644 139.872 447.601 139.651 447.601C139.416 447.601 139.212 447.649 139.037 447.745C138.863 447.84 138.729 447.976 138.635 448.15C138.541 448.325 138.494 448.529 138.494 448.763H137.552C137.552 448.365 137.644 448.017 137.827 447.719C138.01 447.421 138.261 447.19 138.581 447.026C138.9 446.86 139.263 446.777 139.67 446.777C140.081 446.777 140.443 446.859 140.756 447.023C141.071 447.184 141.317 447.406 141.494 447.687C141.67 447.966 141.759 448.281 141.759 448.632C141.759 448.875 141.713 449.113 141.621 449.345C141.532 449.577 141.375 449.836 141.152 450.121C140.928 450.404 140.617 450.748 140.219 451.153L138.919 452.513V452.561H141.864V453.408H137.558Z" fill="#FCFCFD"/>
<rect x="156.203" y="434.203" width="33.4087" height="33.4087" rx="2.5699" fill="#525252"/>
<path d="M167.88 453.408V452.692L170.094 450.399C170.33 450.15 170.525 449.931 170.678 449.744C170.834 449.555 170.95 449.375 171.026 449.204C171.103 449.034 171.141 448.853 171.141 448.661C171.141 448.444 171.09 448.257 170.988 448.099C170.886 447.939 170.746 447.817 170.57 447.732C170.393 447.644 170.194 447.601 169.972 447.601C169.738 447.601 169.534 447.649 169.359 447.745C169.184 447.84 169.05 447.976 168.957 448.15C168.863 448.325 168.816 448.529 168.816 448.763H167.874C167.874 448.365 167.965 448.017 168.148 447.719C168.332 447.421 168.583 447.19 168.902 447.026C169.222 446.86 169.585 446.777 169.991 446.777C170.402 446.777 170.764 446.859 171.077 447.023C171.393 447.184 171.639 447.406 171.815 447.687C171.992 447.966 172.08 448.281 172.08 448.632C172.08 448.875 172.035 449.113 171.943 449.345C171.854 449.577 171.697 449.836 171.474 450.121C171.25 450.404 170.939 450.748 170.541 451.153L169.241 452.513V452.561H172.186V453.408H167.88ZM175.674 453.497C175.235 453.497 174.843 453.422 174.498 453.27C174.156 453.119 173.884 452.909 173.684 452.641C173.486 452.371 173.379 452.058 173.364 451.702H174.367C174.38 451.896 174.445 452.064 174.562 452.207C174.682 452.347 174.837 452.456 175.029 452.533C175.22 452.609 175.433 452.648 175.667 452.648C175.925 452.648 176.153 452.603 176.351 452.513C176.551 452.424 176.708 452.299 176.821 452.14C176.933 451.978 176.99 451.791 176.99 451.581C176.99 451.361 176.933 451.169 176.821 451.003C176.71 450.834 176.547 450.702 176.332 450.606C176.119 450.511 175.861 450.463 175.559 450.463H175.006V449.658H175.559C175.802 449.658 176.015 449.614 176.198 449.527C176.383 449.44 176.528 449.318 176.632 449.163C176.736 449.005 176.789 448.821 176.789 448.61C176.789 448.408 176.743 448.232 176.651 448.083C176.562 447.932 176.434 447.814 176.268 447.729C176.104 447.643 175.91 447.601 175.687 447.601C175.474 447.601 175.275 447.64 175.089 447.719C174.906 447.796 174.757 447.906 174.642 448.051C174.527 448.194 174.465 448.365 174.457 448.565H173.502C173.512 448.212 173.617 447.901 173.815 447.633C174.015 447.364 174.279 447.155 174.607 447.003C174.935 446.852 175.299 446.777 175.699 446.777C176.119 446.777 176.481 446.859 176.785 447.023C177.092 447.184 177.328 447.401 177.494 447.671C177.663 447.942 177.746 448.237 177.744 448.559C177.746 448.925 177.644 449.236 177.437 449.492C177.233 449.747 176.96 449.919 176.619 450.006V450.057C177.054 450.123 177.39 450.296 177.629 450.575C177.869 450.853 177.989 451.2 177.986 451.613C177.989 451.972 177.888 452.295 177.686 452.58C177.486 452.866 177.212 453.09 176.865 453.254C176.518 453.416 176.121 453.497 175.674 453.497Z" fill="#FCFCFD"/>
<path d="M203.64 453.402V452.686L205.854 450.393C206.09 450.144 206.285 449.926 206.438 449.738C206.594 449.549 206.71 449.369 206.787 449.198C206.863 449.028 206.902 448.847 206.902 448.655C206.902 448.438 206.851 448.251 206.748 448.093C206.646 447.934 206.507 447.811 206.33 447.726C206.153 447.639 205.954 447.595 205.733 447.595C205.498 447.595 205.294 447.643 205.119 447.739C204.945 447.834 204.811 447.97 204.717 448.144C204.623 448.319 204.576 448.523 204.576 448.758H203.634C203.634 448.359 203.726 448.011 203.909 447.713C204.092 447.415 204.343 447.184 204.663 447.02C204.982 446.854 205.345 446.771 205.752 446.771C206.163 446.771 206.525 446.853 206.838 447.017C207.153 447.179 207.399 447.4 207.576 447.681C207.752 447.96 207.841 448.275 207.841 448.627C207.841 448.869 207.795 449.107 207.703 449.339C207.614 449.571 207.457 449.83 207.234 450.115C207.01 450.398 206.699 450.742 206.301 451.147L205.001 452.508V452.555H207.946V453.402H203.64ZM208.879 452.124V451.326L211.706 446.86H212.335V448.036H211.936L209.914 451.236V451.287H213.788V452.124H208.879ZM211.98 453.402V451.881L211.987 451.517V446.86H212.923V453.402H211.98Z" fill="#757575"/>
<path d="M239.702 453.402V452.686L241.916 450.393C242.152 450.144 242.347 449.926 242.5 449.738C242.656 449.549 242.772 449.369 242.849 449.198C242.925 449.028 242.964 448.847 242.964 448.655C242.964 448.438 242.912 448.251 242.81 448.093C242.708 447.934 242.569 447.811 242.392 447.726C242.215 447.639 242.016 447.595 241.795 447.595C241.56 447.595 241.356 447.643 241.181 447.739C241.007 447.834 240.872 447.97 240.779 448.144C240.685 448.319 240.638 448.523 240.638 448.758H239.696C239.696 448.359 239.788 448.011 239.971 447.713C240.154 447.415 240.405 447.184 240.724 447.02C241.044 446.854 241.407 446.771 241.814 446.771C242.225 446.771 242.587 446.853 242.9 447.017C243.215 447.179 243.461 447.4 243.638 447.681C243.814 447.96 243.903 448.275 243.903 448.627C243.903 448.869 243.857 449.107 243.765 449.339C243.676 449.571 243.519 449.83 243.296 450.115C243.072 450.398 242.761 450.742 242.363 451.147L241.063 452.508V452.555H244.008V453.402H239.702ZM247.378 453.491C246.978 453.491 246.618 453.415 246.298 453.261C245.981 453.106 245.728 452.893 245.538 452.622C245.349 452.352 245.247 452.043 245.235 451.696H246.193C246.216 451.977 246.341 452.208 246.567 452.389C246.792 452.57 247.063 452.661 247.378 452.661C247.629 452.661 247.852 452.603 248.045 452.488C248.241 452.371 248.395 452.21 248.505 452.006C248.618 451.802 248.675 451.568 248.675 451.307C248.675 451.04 248.617 450.803 248.502 450.594C248.387 450.386 248.229 450.222 248.026 450.102C247.826 449.983 247.596 449.922 247.336 449.92C247.138 449.92 246.939 449.954 246.739 450.022C246.539 450.091 246.377 450.18 246.254 450.291L245.35 450.157L245.717 446.86H249.313V447.707H246.538L246.33 449.537H246.369C246.496 449.413 246.666 449.31 246.876 449.227C247.089 449.144 247.317 449.103 247.56 449.103C247.958 449.103 248.313 449.197 248.624 449.387C248.937 449.576 249.183 449.835 249.361 450.163C249.542 450.489 249.632 450.864 249.63 451.287C249.632 451.711 249.536 452.089 249.342 452.421C249.151 452.753 248.884 453.015 248.544 453.207C248.205 453.397 247.817 453.491 247.378 453.491Z" fill="#757575"/>
<path d="M23.7605 489.381V488.665L25.974 486.372C26.2104 486.123 26.4053 485.905 26.5586 485.717C26.714 485.528 26.8301 485.348 26.9067 485.177C26.9834 485.007 27.0217 484.826 27.0217 484.634C27.0217 484.417 26.9706 484.23 26.8684 484.072C26.7662 483.913 26.6267 483.79 26.45 483.705C26.2732 483.618 26.0741 483.574 25.8527 483.574C25.6184 483.574 25.414 483.622 25.2394 483.718C25.0648 483.813 24.9306 483.949 24.8369 484.123C24.7432 484.298 24.6964 484.502 24.6964 484.737H23.7541C23.7541 484.338 23.8457 483.99 24.0288 483.692C24.212 483.394 24.4632 483.163 24.7826 482.999C25.102 482.833 25.4651 482.75 25.8718 482.75C26.2828 482.75 26.6448 482.832 26.9578 482.996C27.273 483.158 27.5189 483.379 27.6957 483.66C27.8724 483.939 27.9608 484.254 27.9608 484.606C27.9608 484.848 27.915 485.086 27.8234 485.318C27.734 485.55 27.5775 485.809 27.3539 486.094C27.1303 486.377 26.8194 486.721 26.4212 487.126L25.1212 488.487V488.534H28.0662V489.381H23.7605ZM31.5606 489.47C31.2688 489.466 30.9814 489.413 30.6982 489.311C30.4171 489.208 30.1615 489.038 29.9316 488.8C29.7016 488.561 29.5174 488.241 29.379 487.838C29.2427 487.436 29.1746 486.933 29.1746 486.33C29.1746 485.76 29.231 485.253 29.3438 484.81C29.4588 484.367 29.6239 483.993 29.8389 483.689C30.054 483.382 30.3138 483.149 30.6183 482.989C30.9228 482.83 31.2646 482.75 31.6436 482.75C32.0333 482.75 32.3793 482.827 32.6817 482.98C32.9841 483.133 33.229 483.345 33.4164 483.615C33.6059 483.886 33.7262 484.194 33.7773 484.539H32.8031C32.7371 484.266 32.6061 484.043 32.4102 483.871C32.2143 483.699 31.9588 483.612 31.6436 483.612C31.1645 483.612 30.7908 483.821 30.5225 484.238C30.2563 484.656 30.1221 485.236 30.12 485.979H30.1679C30.2808 485.794 30.4192 485.636 30.5832 485.506C30.7493 485.374 30.9345 485.273 31.1389 485.203C31.3455 485.131 31.5627 485.094 31.7905 485.094C32.1696 485.094 32.5124 485.187 32.8191 485.372C33.1278 485.555 33.3738 485.809 33.5569 486.132C33.74 486.456 33.8316 486.827 33.8316 487.244C33.8316 487.661 33.7368 488.039 33.5473 488.378C33.3599 488.716 33.0959 488.985 32.7552 489.183C32.4145 489.379 32.0163 489.475 31.5606 489.47ZM31.5574 488.64C31.8086 488.64 32.0333 488.578 32.2313 488.455C32.4294 488.331 32.5859 488.165 32.7009 487.956C32.8159 487.748 32.8734 487.514 32.8734 487.257C32.8734 487.005 32.8169 486.777 32.7041 486.57C32.5933 486.363 32.44 486.2 32.2441 486.078C32.0503 485.957 31.8289 485.896 31.5797 485.896C31.3902 485.896 31.2145 485.932 31.0527 486.005C30.893 486.077 30.7525 486.177 30.6311 486.305C30.5097 486.433 30.4139 486.58 30.3436 486.746C30.2755 486.91 30.2414 487.083 30.2414 487.266C30.2414 487.511 30.2978 487.738 30.4107 487.947C30.5257 488.155 30.6822 488.324 30.8802 488.451C31.0804 488.577 31.3061 488.64 31.5574 488.64Z" fill="#757575"/>
<rect x="48.2676" y="470.182" width="33.4087" height="33.4087" rx="2.5699" fill="#525252"/>
<path d="M60.2389 489.386V488.671L62.4524 486.377C62.6888 486.128 62.8836 485.91 63.0369 485.723C63.1924 485.533 63.3084 485.353 63.3851 485.183C63.4617 485.012 63.5001 484.831 63.5001 484.64C63.5001 484.423 63.449 484.235 63.3468 484.078C63.2445 483.918 63.1051 483.795 62.9283 483.71C62.7516 483.623 62.5525 483.579 62.331 483.579C62.0968 483.579 61.8924 483.627 61.7178 483.723C61.5431 483.819 61.409 483.954 61.3153 484.129C61.2216 484.303 61.1748 484.508 61.1748 484.742H60.2325C60.2325 484.344 60.3241 483.996 60.5072 483.697C60.6903 483.399 60.9416 483.168 61.261 483.004C61.5804 482.838 61.9435 482.755 62.3502 482.755C62.7612 482.755 63.1232 482.837 63.4362 483.001C63.7513 483.163 63.9973 483.384 64.174 483.666C64.3508 483.945 64.4391 484.26 64.4391 484.611C64.4391 484.854 64.3934 485.091 64.3018 485.323C64.2124 485.555 64.0559 485.814 63.8323 486.099C63.6087 486.383 63.2978 486.727 62.8996 487.131L61.5996 488.492V488.54H64.5446V489.386H60.2389ZM65.9755 489.386L68.8311 483.739V483.691H65.5283V482.845H69.8532V483.72L67.0072 489.386H65.9755Z" fill="#FCFCFD"/>
<rect x="84.2461" y="470.182" width="33.4087" height="33.4087" rx="2.5699" fill="#525252"/>
<path d="M95.9802 489.386V488.671L98.1938 486.377C98.4301 486.128 98.625 485.91 98.7783 485.723C98.9337 485.533 99.0498 485.353 99.1264 485.183C99.2031 485.012 99.2414 484.831 99.2414 484.64C99.2414 484.423 99.1903 484.235 99.0881 484.078C98.9859 483.918 98.8464 483.795 98.6697 483.71C98.4929 483.623 98.2938 483.579 98.0724 483.579C97.8381 483.579 97.6337 483.627 97.4591 483.723C97.2845 483.819 97.1503 483.954 97.0566 484.129C96.963 484.303 96.9161 484.508 96.9161 484.742H95.9738C95.9738 484.344 96.0654 483.996 96.2485 483.697C96.4317 483.399 96.6829 483.168 97.0023 483.004C97.3218 482.838 97.6848 482.755 98.0915 482.755C98.5025 482.755 98.8645 482.837 99.1775 483.001C99.4927 483.163 99.7386 483.384 99.9154 483.666C100.092 483.945 100.181 484.26 100.181 484.611C100.181 484.854 100.135 485.091 100.043 485.323C99.9537 485.555 99.7972 485.814 99.5736 486.099C99.35 486.383 99.0391 486.727 98.6409 487.131L97.3409 488.492V488.54H100.286V489.386H95.9802ZM103.707 489.476C103.249 489.476 102.844 489.397 102.493 489.239C102.144 489.082 101.87 488.865 101.672 488.588C101.474 488.311 101.376 487.996 101.378 487.642C101.376 487.365 101.433 487.111 101.548 486.879C101.665 486.645 101.823 486.45 102.024 486.294C102.224 486.137 102.447 486.037 102.694 485.994V485.956C102.368 485.877 102.108 485.702 101.912 485.432C101.716 485.161 101.619 484.851 101.621 484.499C101.619 484.165 101.707 483.867 101.886 483.605C102.067 483.341 102.315 483.133 102.63 482.982C102.946 482.831 103.304 482.755 103.707 482.755C104.105 482.755 104.461 482.832 104.774 482.985C105.089 483.136 105.337 483.344 105.518 483.608C105.699 483.87 105.79 484.167 105.793 484.499C105.79 484.851 105.69 485.161 105.492 485.432C105.294 485.702 105.037 485.877 104.719 485.956V485.994C104.964 486.037 105.185 486.137 105.381 486.294C105.579 486.45 105.736 486.645 105.853 486.879C105.973 487.111 106.033 487.365 106.035 487.642C106.033 487.996 105.933 488.311 105.735 488.588C105.537 488.865 105.262 489.082 104.911 489.239C104.562 489.397 104.16 489.476 103.707 489.476ZM103.707 488.668C103.977 488.668 104.211 488.623 104.41 488.533C104.608 488.442 104.761 488.315 104.869 488.153C104.978 487.989 105.033 487.798 105.036 487.578C105.033 487.351 104.974 487.149 104.857 486.975C104.742 486.8 104.585 486.663 104.387 486.563C104.189 486.463 103.962 486.413 103.707 486.413C103.449 486.413 103.22 486.463 103.02 486.563C102.82 486.663 102.662 486.8 102.547 486.975C102.432 487.149 102.376 487.351 102.378 487.578C102.376 487.798 102.428 487.989 102.535 488.153C102.643 488.315 102.798 488.442 102.998 488.533C103.198 488.623 103.434 488.668 103.707 488.668ZM103.707 485.624C103.924 485.624 104.117 485.58 104.285 485.493C104.453 485.405 104.585 485.284 104.681 485.128C104.779 484.973 104.829 484.791 104.831 484.582C104.829 484.378 104.78 484.199 104.684 484.046C104.591 483.892 104.46 483.774 104.291 483.691C104.123 483.606 103.928 483.563 103.707 483.563C103.481 483.563 103.283 483.606 103.113 483.691C102.944 483.774 102.814 483.892 102.72 484.046C102.626 484.199 102.58 484.378 102.582 484.582C102.58 484.791 102.627 484.973 102.723 485.128C102.819 485.284 102.951 485.405 103.119 485.493C103.289 485.58 103.485 485.624 103.707 485.624Z" fill="#FCFCFD"/>
<rect x="120.225" y="470.182" width="33.4087" height="33.4087" rx="2.5699" fill="#525252"/>
<path d="M131.941 489.386V488.671L134.155 486.377C134.391 486.128 134.586 485.91 134.739 485.723C134.895 485.533 135.011 485.353 135.087 485.183C135.164 485.012 135.202 484.831 135.202 484.64C135.202 484.423 135.151 484.235 135.049 484.078C134.947 483.918 134.807 483.795 134.631 483.71C134.454 483.623 134.255 483.579 134.033 483.579C133.799 483.579 133.595 483.627 133.42 483.723C133.245 483.819 133.111 483.954 133.018 484.129C132.924 484.303 132.877 484.508 132.877 484.742H131.935C131.935 484.344 132.026 483.996 132.209 483.697C132.393 483.399 132.644 483.168 132.963 483.004C133.283 482.838 133.646 482.755 134.052 482.755C134.463 482.755 134.825 482.837 135.138 483.001C135.454 483.163 135.7 483.384 135.876 483.666C136.053 483.945 136.141 484.26 136.141 484.611C136.141 484.854 136.096 485.091 136.004 485.323C135.915 485.555 135.758 485.814 135.535 486.099C135.311 486.383 135 486.727 134.602 487.131L133.302 488.492V488.54H136.247V489.386H131.941ZM139.633 482.755C139.924 482.757 140.212 482.811 140.495 482.915C140.778 483.019 141.034 483.19 141.262 483.426C141.492 483.662 141.675 483.982 141.811 484.384C141.949 484.785 142.02 485.283 142.022 485.879C142.022 486.452 141.964 486.962 141.849 487.409C141.734 487.854 141.569 488.23 141.354 488.537C141.141 488.843 140.883 489.076 140.578 489.236C140.274 489.396 139.931 489.476 139.55 489.476C139.16 489.476 138.814 489.399 138.511 489.246C138.209 489.092 137.963 488.88 137.774 488.61C137.584 488.337 137.466 488.024 137.419 487.671H138.393C138.457 487.952 138.587 488.18 138.783 488.355C138.981 488.527 139.237 488.613 139.55 488.613C140.029 488.613 140.402 488.405 140.671 487.987C140.939 487.568 141.074 486.982 141.076 486.23H141.025C140.915 486.414 140.776 486.571 140.61 486.703C140.446 486.835 140.262 486.937 140.057 487.01C139.853 487.082 139.635 487.118 139.403 487.118C139.026 487.118 138.683 487.026 138.374 486.841C138.065 486.655 137.819 486.401 137.636 486.077C137.453 485.753 137.362 485.384 137.362 484.969C137.362 484.556 137.455 484.181 137.643 483.844C137.832 483.508 138.096 483.242 138.435 483.046C138.776 482.848 139.175 482.751 139.633 482.755ZM139.636 483.586C139.387 483.586 139.162 483.647 138.962 483.771C138.764 483.892 138.607 484.057 138.492 484.266C138.377 484.473 138.32 484.703 138.32 484.956C138.32 485.209 138.375 485.439 138.486 485.646C138.599 485.85 138.752 486.013 138.946 486.135C139.142 486.254 139.365 486.313 139.617 486.313C139.804 486.313 139.979 486.277 140.14 486.205C140.302 486.132 140.444 486.032 140.565 485.905C140.687 485.775 140.781 485.628 140.85 485.464C140.918 485.3 140.952 485.127 140.952 484.946C140.952 484.706 140.894 484.482 140.779 484.276C140.666 484.069 140.511 483.903 140.313 483.777C140.115 483.65 139.889 483.586 139.636 483.586Z" fill="#FCFCFD"/>
<rect x="156.203" y="470.182" width="33.4087" height="33.4087" rx="2.5699" fill="#525252"/>
<path d="M170.013 489.476C169.574 489.476 169.182 489.4 168.837 489.249C168.494 489.098 168.223 488.888 168.023 488.62C167.825 488.349 167.718 488.036 167.703 487.681H168.706C168.719 487.874 168.784 488.043 168.901 488.185C169.02 488.326 169.176 488.434 169.367 488.511C169.559 488.588 169.772 488.626 170.006 488.626C170.264 488.626 170.492 488.581 170.69 488.492C170.89 488.402 171.046 488.278 171.159 488.118C171.272 487.956 171.329 487.77 171.329 487.559C171.329 487.34 171.272 487.147 171.159 486.981C171.049 486.813 170.886 486.681 170.671 486.585C170.458 486.489 170.2 486.441 169.898 486.441H169.345V485.636H169.898C170.14 485.636 170.353 485.593 170.536 485.505C170.722 485.418 170.867 485.297 170.971 485.141C171.075 484.984 171.127 484.799 171.127 484.589C171.127 484.386 171.082 484.211 170.99 484.062C170.901 483.91 170.773 483.792 170.607 483.707C170.443 483.622 170.249 483.579 170.025 483.579C169.812 483.579 169.613 483.619 169.428 483.697C169.245 483.774 169.096 483.885 168.981 484.03C168.866 484.172 168.804 484.344 168.796 484.544H167.841C167.851 484.19 167.956 483.88 168.154 483.611C168.354 483.343 168.618 483.133 168.946 482.982C169.274 482.831 169.638 482.755 170.038 482.755C170.458 482.755 170.82 482.837 171.124 483.001C171.431 483.163 171.667 483.379 171.833 483.65C172.002 483.92 172.085 484.216 172.082 484.538C172.085 484.904 171.982 485.215 171.776 485.47C171.571 485.726 171.299 485.897 170.958 485.984V486.036C171.393 486.102 171.729 486.274 171.967 486.553C172.208 486.832 172.327 487.178 172.325 487.591C172.327 487.951 172.227 488.274 172.025 488.559C171.825 488.844 171.551 489.069 171.204 489.233C170.857 489.395 170.46 489.476 170.013 489.476ZM175.81 489.495C175.305 489.493 174.874 489.36 174.516 489.096C174.159 488.832 173.885 488.447 173.695 487.942C173.506 487.438 173.411 486.83 173.411 486.119C173.411 485.41 173.506 484.804 173.695 484.301C173.887 483.799 174.162 483.415 174.52 483.151C174.879 482.887 175.31 482.755 175.81 482.755C176.31 482.755 176.739 482.888 177.097 483.154C177.455 483.419 177.729 483.802 177.918 484.304C178.11 484.805 178.206 485.41 178.206 486.119C178.206 486.832 178.111 487.441 177.921 487.946C177.732 488.448 177.458 488.833 177.1 489.099C176.743 489.363 176.313 489.495 175.81 489.495ZM175.81 488.642C176.253 488.642 176.599 488.426 176.848 487.994C177.099 487.561 177.225 486.936 177.225 486.119C177.225 485.576 177.167 485.117 177.052 484.742C176.94 484.365 176.777 484.08 176.564 483.886C176.353 483.69 176.102 483.592 175.81 483.592C175.369 483.592 175.023 483.809 174.772 484.244C174.521 484.678 174.394 485.303 174.392 486.119C174.392 486.664 174.448 487.125 174.561 487.502C174.676 487.876 174.839 488.161 175.05 488.355C175.261 488.546 175.514 488.642 175.81 488.642Z" fill="#FCFCFD"/>
<mask id="path-102-inside-1_16843_34774" fill="white">
<path d="M276 1H965V520H276V1Z"/>
</mask>
<path d="M276 1H965V520H276V1Z" fill="#1F1F1F"/>
<path d="M276.724 520V1H275.276V520H276.724Z" fill="#525252" mask="url(#path-102-inside-1_16843_34774)"/>
<path d="M965 49.9185H276V51.3663H965V49.9185Z" fill="#575757" mask="url(#path-104-inside-2_16843_34774)"/>
<path d="M294.632 21.1888H296.146V27.1116C296.143 27.6545 296.028 28.122 295.8 28.5141C295.573 28.9035 295.255 29.2037 294.846 29.4149C294.44 29.6233 293.967 29.7275 293.427 29.7275C292.934 29.7275 292.489 29.6397 292.094 29.4642C291.702 29.286 291.391 29.0228 291.161 28.6745C290.93 28.3263 290.815 27.8931 290.815 27.3748H292.333C292.336 27.6024 292.385 27.7985 292.481 27.963C292.58 28.1275 292.716 28.2536 292.888 28.3414C293.061 28.4291 293.26 28.473 293.485 28.473C293.729 28.473 293.936 28.4223 294.106 28.3208C294.276 28.2166 294.405 28.0631 294.492 27.8602C294.583 27.6572 294.629 27.4077 294.632 27.1116V21.1888ZM301.762 26.9553V23.2947H303.251V29.6123H301.807V28.4895H301.742C301.599 28.8432 301.365 29.1325 301.038 29.3573C300.715 29.5821 300.316 29.6946 299.841 29.6946C299.427 29.6946 299.061 29.6027 298.743 29.419C298.428 29.2325 298.181 28.9624 298.003 28.6087C297.825 28.2523 297.735 27.8218 297.735 27.3172V23.2947H299.224V27.0869C299.224 27.4872 299.334 27.8053 299.553 28.0411C299.773 28.2769 300.061 28.3949 300.417 28.3949C300.637 28.3949 300.849 28.3414 301.055 28.2344C301.26 28.1275 301.429 27.9685 301.561 27.7573C301.695 27.5435 301.762 27.2761 301.762 26.9553ZM306.271 25.9106V29.6123H304.782V23.2947H306.205V24.3682H306.279C306.425 24.0145 306.656 23.7334 306.974 23.525C307.295 23.3166 307.691 23.2124 308.163 23.2124C308.599 23.2124 308.979 23.3057 309.302 23.4921C309.629 23.6786 309.881 23.9487 310.059 24.3024C310.24 24.6561 310.329 25.0852 310.326 25.5898V29.6123H308.838V25.8201C308.838 25.3978 308.728 25.0674 308.508 24.8289C308.292 24.5903 307.992 24.471 307.608 24.471C307.347 24.471 307.116 24.5286 306.913 24.6438C306.712 24.7562 306.555 24.9193 306.44 25.1332C306.327 25.3471 306.271 25.6062 306.271 25.9106ZM314.697 29.6123V28.51L317.621 25.6432C317.901 25.3608 318.134 25.1099 318.32 24.8906C318.507 24.6712 318.646 24.4587 318.74 24.253C318.833 24.0474 318.879 23.828 318.879 23.595C318.879 23.329 318.819 23.1014 318.698 22.9122C318.578 22.7203 318.412 22.5722 318.201 22.468C317.99 22.3638 317.75 22.3117 317.481 22.3117C317.204 22.3117 316.961 22.3693 316.753 22.4844C316.545 22.5969 316.383 22.7573 316.268 22.9657C316.155 23.1741 316.099 23.4222 316.099 23.7101H314.647C314.647 23.1754 314.769 22.7107 315.013 22.3158C315.257 21.921 315.593 21.6152 316.021 21.3986C316.451 21.182 316.945 21.0737 317.502 21.0737C318.066 21.0737 318.563 21.1792 318.991 21.3904C319.418 21.6015 319.75 21.8908 319.986 22.2582C320.224 22.6257 320.344 23.0452 320.344 23.5168C320.344 23.8321 320.283 24.142 320.163 24.4464C320.042 24.7507 319.83 25.088 319.525 25.4582C319.224 25.8283 318.8 26.2766 318.254 26.8031L316.802 28.2797V28.3373H320.471V29.6123H314.697ZM325.004 29.7727C324.326 29.7727 323.745 29.6013 323.26 29.2586C322.777 28.9131 322.406 28.4154 322.145 27.7656C321.887 27.113 321.759 26.3274 321.759 25.4088C321.761 24.4902 321.892 23.7087 322.149 23.0644C322.41 22.4173 322.781 21.9237 323.264 21.5837C323.749 21.2437 324.329 21.0737 325.004 21.0737C325.678 21.0737 326.258 21.2437 326.744 21.5837C327.229 21.9237 327.6 22.4173 327.858 23.0644C328.119 23.7115 328.249 24.493 328.249 25.4088C328.249 26.3301 328.119 27.1171 327.858 27.7697C327.6 28.4195 327.229 28.9158 326.744 29.2586C326.261 29.6013 325.681 29.7727 325.004 29.7727ZM325.004 28.4853C325.53 28.4853 325.946 28.2262 326.25 27.708C326.557 27.187 326.711 26.4206 326.711 25.4088C326.711 24.7397 326.641 24.1776 326.501 23.7225C326.361 23.2673 326.164 22.9245 325.909 22.6942C325.654 22.4611 325.352 22.3446 325.004 22.3446C324.48 22.3446 324.066 22.6051 323.762 23.1261C323.457 23.6443 323.304 24.4052 323.301 25.4088C323.298 26.0806 323.365 26.6454 323.502 27.1034C323.642 27.5613 323.84 27.9068 324.095 28.1398C324.35 28.3702 324.653 28.4853 325.004 28.4853ZM331.052 28.4607L330.998 28.9131C330.96 29.2586 330.892 29.6096 330.793 29.966C330.697 30.3252 330.595 30.6556 330.488 30.9573C330.382 31.2589 330.295 31.4961 330.229 31.6688H329.226C329.264 31.5016 329.316 31.274 329.382 30.9861C329.451 30.6981 329.516 30.3746 329.579 30.0154C329.643 29.6562 329.685 29.2915 329.707 28.9213L329.736 28.4607H331.052Z" fill="#FCFCFD"/>
<path d="M335.316 29.6123V28.51L338.24 25.6432C338.52 25.3608 338.753 25.1099 338.94 24.8906C339.126 24.6712 339.266 24.4587 339.359 24.253C339.452 24.0474 339.499 23.828 339.499 23.595C339.499 23.329 339.439 23.1014 339.318 22.9122C339.197 22.7203 339.032 22.5722 338.82 22.468C338.609 22.3638 338.369 22.3117 338.101 22.3117C337.824 22.3117 337.581 22.3693 337.373 22.4844C337.164 22.5969 337.002 22.7573 336.887 22.9657C336.775 23.1741 336.719 23.4222 336.719 23.7101H335.267C335.267 23.1754 335.389 22.7107 335.633 22.3158C335.877 21.921 336.213 21.6152 336.641 21.3986C337.071 21.182 337.565 21.0737 338.121 21.0737C338.686 21.0737 339.182 21.1792 339.61 21.3904C340.038 21.6015 340.37 21.8908 340.605 22.2582C340.844 22.6257 340.963 23.0452 340.963 23.5168C340.963 23.8321 340.903 24.142 340.782 24.4464C340.662 24.7507 340.449 25.088 340.145 25.4582C339.843 25.8283 339.42 26.2766 338.874 26.8031L337.422 28.2797V28.3373H341.091V29.6123H335.316ZM345.623 29.7727C344.946 29.7727 344.365 29.6013 343.879 29.2586C343.397 28.9131 343.025 28.4154 342.765 27.7656C342.507 27.113 342.378 26.3274 342.378 25.4088C342.381 24.4902 342.511 23.7087 342.769 23.0644C343.029 22.4173 343.401 21.9237 343.884 21.5837C344.369 21.2437 344.949 21.0737 345.623 21.0737C346.298 21.0737 346.878 21.2437 347.363 21.5837C347.848 21.9237 348.22 22.4173 348.478 23.0644C348.738 23.7115 348.869 24.493 348.869 25.4088C348.869 26.3301 348.738 27.1171 348.478 27.7697C348.22 28.4195 347.848 28.9158 347.363 29.2586C346.881 29.6013 346.301 29.7727 345.623 29.7727ZM345.623 28.4853C346.15 28.4853 346.565 28.2262 346.87 27.708C347.177 27.187 347.33 26.4206 347.33 25.4088C347.33 24.7397 347.26 24.1776 347.12 23.7225C346.981 23.2673 346.783 22.9245 346.528 22.6942C346.273 22.4611 345.972 22.3446 345.623 22.3446C345.1 22.3446 344.686 22.6051 344.381 23.1261C344.077 23.6443 343.923 24.4052 343.921 25.4088C343.918 26.0806 343.985 26.6454 344.122 27.1034C344.262 27.5613 344.459 27.9068 344.714 28.1398C344.969 28.3702 345.272 28.4853 345.623 28.4853ZM350.235 29.6123V28.51L353.159 25.6432C353.439 25.3608 353.672 25.1099 353.859 24.8906C354.045 24.6712 354.185 24.4587 354.278 24.253C354.371 24.0474 354.418 23.828 354.418 23.595C354.418 23.329 354.358 23.1014 354.237 22.9122C354.116 22.7203 353.95 22.5722 353.739 22.468C353.528 22.3638 353.288 22.3117 353.02 22.3117C352.743 22.3117 352.5 22.3693 352.292 22.4844C352.083 22.5969 351.921 22.7573 351.806 22.9657C351.694 23.1741 351.638 23.4222 351.638 23.7101H350.186C350.186 23.1754 350.308 22.7107 350.552 22.3158C350.796 21.921 351.132 21.6152 351.559 21.3986C351.99 21.182 352.484 21.0737 353.04 21.0737C353.605 21.0737 354.101 21.1792 354.529 21.3904C354.957 21.6015 355.289 21.8908 355.524 22.2582C355.763 22.6257 355.882 23.0452 355.882 23.5168C355.882 23.8321 355.822 24.142 355.701 24.4464C355.581 24.7507 355.368 25.088 355.064 25.4582C354.762 25.8283 354.338 26.2766 353.793 26.8031L352.341 28.2797V28.3373H356.01V29.6123H350.235ZM360.452 29.7275C359.86 29.7275 359.333 29.626 358.872 29.4231C358.414 29.2202 358.053 28.9378 357.787 28.5758C357.521 28.2139 357.379 27.7957 357.363 27.3214H358.909C358.923 27.5489 358.999 27.7477 359.136 27.9177C359.273 28.085 359.455 28.2152 359.683 28.3085C359.91 28.4017 360.165 28.4483 360.448 28.4483C360.749 28.4483 361.017 28.3962 361.25 28.292C361.483 28.1851 361.665 28.037 361.797 27.8478C361.928 27.6586 361.993 27.4406 361.99 27.1938C361.993 26.9388 361.927 26.714 361.793 26.5193C361.658 26.3246 361.464 26.1724 361.209 26.0628C360.956 25.9531 360.652 25.8982 360.296 25.8982H359.551V24.7219H360.296C360.589 24.7219 360.845 24.6712 361.065 24.5697C361.287 24.4683 361.461 24.3257 361.587 24.142C361.713 23.9555 361.775 23.7403 361.772 23.4962C361.775 23.2577 361.721 23.0507 361.612 22.8752C361.505 22.6969 361.353 22.5585 361.155 22.4598C360.96 22.361 360.732 22.3117 360.468 22.3117C360.211 22.3117 359.972 22.3583 359.753 22.4515C359.533 22.5448 359.356 22.6778 359.222 22.8505C359.088 23.0205 359.016 23.2234 359.008 23.4592H357.54C357.551 22.9876 357.686 22.5736 357.947 22.2171C358.21 21.8579 358.561 21.5782 359 21.378C359.439 21.1751 359.931 21.0737 360.476 21.0737C361.039 21.0737 361.527 21.1792 361.941 21.3904C362.358 21.5988 362.68 21.8798 362.907 22.2335C363.135 22.5873 363.249 22.978 363.249 23.4058C363.251 23.8801 363.112 24.2777 362.829 24.5985C362.549 24.9193 362.182 25.1291 361.727 25.2278V25.2936C362.319 25.3759 362.773 25.5953 363.088 25.9517C363.406 26.3054 363.564 26.7455 363.561 27.272C363.561 27.7436 363.427 28.1659 363.158 28.5388C362.892 28.909 362.525 29.1996 362.056 29.4108C361.59 29.6219 361.055 29.7275 360.452 29.7275Z" fill="#A5A5A5"/>
<path d="M390.538 28.5074L387.642 25.6119L390.538 22.7163" stroke="#D6D6D6" stroke-width="1.08584" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M413.703 28.5074L416.598 25.6119L413.703 22.7163" stroke="#FCFCFD" stroke-width="1.08584" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M335.118 68.2503H336.519L338.394 72.8281H338.468L340.344 68.2503H341.744V74.5679H340.646V70.2276H340.588L338.842 74.5494H338.021L336.275 70.2184H336.216V74.5679H335.118V68.2503ZM348.636 71.4091C348.636 72.0898 348.508 72.6728 348.253 73.1581C348 73.6414 347.655 74.0116 347.217 74.2686C346.781 74.5257 346.286 74.6542 345.733 74.6542C345.18 74.6542 344.684 74.5257 344.246 74.2686C343.81 74.0095 343.465 73.6383 343.21 73.155C342.957 72.6697 342.83 72.0877 342.83 71.4091C342.83 70.7284 342.957 70.1464 343.21 69.6631C343.465 69.1778 343.81 68.8066 344.246 68.5495C344.684 68.2924 345.18 68.1639 345.733 68.1639C346.286 68.1639 346.781 68.2924 347.217 68.5495C347.655 68.8066 348 69.1778 348.253 69.6631C348.508 70.1464 348.636 70.7284 348.636 71.4091ZM347.485 71.4091C347.485 70.9299 347.41 70.5258 347.26 70.1968C347.112 69.8657 346.906 69.6158 346.643 69.4472C346.38 69.2765 346.076 69.1911 345.733 69.1911C345.389 69.1911 345.086 69.2765 344.823 69.4472C344.56 69.6158 344.353 69.8657 344.203 70.1968C344.055 70.5258 343.981 70.9299 343.981 71.4091C343.981 71.8882 344.055 72.2934 344.203 72.6245C344.353 72.9535 344.56 73.2034 344.823 73.3741C345.086 73.5427 345.389 73.627 345.733 73.627C346.076 73.627 346.38 73.5427 346.643 73.3741C346.906 73.2034 347.112 72.9535 347.26 72.6245C347.41 72.2934 347.485 71.8882 347.485 71.4091ZM354.909 68.2503V74.5679H353.891L350.915 70.2646H350.862V74.5679H349.718V68.2503H350.742L353.716 72.5566H353.771V68.2503H354.909Z" fill="#FCFCFD"/>
<rect x="359.391" y="65.1201" width="16.7911" height="11.8956" rx="5.94778" fill="#FCFCFD"/>
<path d="M363.332 73.5679V72.9345L365.292 70.9042C365.501 70.6836 365.673 70.4904 365.809 70.3245C365.947 70.1567 366.049 69.9974 366.117 69.8466C366.185 69.6958 366.219 69.5356 366.219 69.3659C366.219 69.1736 366.174 69.0077 366.083 68.8682C365.993 68.7268 365.869 68.6185 365.713 68.543C365.556 68.4658 365.38 68.4271 365.184 68.4271C364.977 68.4271 364.796 68.4695 364.641 68.5544C364.487 68.6392 364.368 68.7589 364.285 68.9135C364.202 69.0681 364.16 69.249 364.16 69.4564H363.326C363.326 69.1039 363.407 68.7957 363.569 68.5317C363.732 68.2678 363.954 68.0633 364.237 67.9181C364.52 67.7711 364.841 67.6976 365.201 67.6976C365.565 67.6976 365.885 67.7701 366.162 67.9153C366.441 68.0586 366.659 68.2546 366.816 68.5035C366.972 68.7504 367.05 69.0294 367.05 69.3405C367.05 69.5554 367.01 69.7656 366.929 69.971C366.85 70.1765 366.711 70.4056 366.513 70.6582C366.315 70.9089 366.04 71.2133 365.687 71.5715L364.537 72.7761V72.8185H367.144V73.5679H363.332ZM370.232 73.664C369.785 73.6621 369.403 73.5443 369.086 73.3106C368.77 73.0768 368.527 72.7365 368.36 72.2897C368.192 71.843 368.108 71.3048 368.108 70.6751C368.108 70.0474 368.192 69.5111 368.36 69.0662C368.529 68.6213 368.772 68.282 369.089 68.0482C369.408 67.8144 369.789 67.6976 370.232 67.6976C370.675 67.6976 371.054 67.8154 371.371 68.051C371.688 68.2848 371.93 68.6241 372.098 69.069C372.267 69.512 372.352 70.0474 372.352 70.6751C372.352 71.3067 372.268 71.8458 372.101 72.2926C371.933 72.7375 371.691 73.0777 371.374 73.3134C371.057 73.5471 370.676 73.664 370.232 73.664ZM370.232 72.909C370.624 72.909 370.93 72.7177 371.151 72.335C371.373 71.9523 371.484 71.399 371.484 70.6751C371.484 70.1944 371.433 69.7882 371.331 69.4564C371.232 69.1227 371.087 68.8701 370.899 68.6986C370.712 68.5251 370.49 68.4384 370.232 68.4384C369.841 68.4384 369.535 68.6307 369.313 69.0153C369.09 69.3998 368.978 69.9531 368.976 70.6751C368.976 71.1577 369.026 71.5659 369.126 71.8995C369.228 72.2313 369.372 72.483 369.559 72.6545C369.745 72.8242 369.969 72.909 370.232 72.909Z" fill="#1A1A1A"/>
<path d="M290.84 95.5475C290.84 94.3253 290.84 93.4304 290.898 92.7243C290.955 92.0224 291.068 91.539 291.274 91.134C291.656 90.3848 292.265 89.7757 293.014 89.394C293.419 89.1877 293.902 89.0754 294.604 89.0181C295.31 88.9604 296.205 88.9601 297.427 88.9601H413.25C414.472 88.9601 415.367 88.9604 416.073 89.0181C416.775 89.0754 417.259 89.1877 417.664 89.394C418.413 89.7757 419.022 90.3848 419.403 91.134C419.61 91.539 419.722 92.0224 419.779 92.7243C419.837 93.4304 419.837 94.3253 419.837 95.5475V108.127C419.837 109.349 419.837 110.244 419.779 110.95C419.722 111.652 419.61 112.135 419.403 112.54C419.022 113.289 418.413 113.898 417.664 114.28C417.259 114.486 416.775 114.599 416.073 114.656C415.367 114.714 414.472 114.714 413.25 114.714H297.427C296.205 114.714 295.31 114.714 294.604 114.656C293.902 114.599 293.419 114.486 293.014 114.28C292.265 113.898 291.656 113.289 291.274 112.54C291.068 112.135 290.955 111.652 290.898 110.95C290.84 110.244 290.84 109.349 290.84 108.127V95.5475Z" fill="#1A1A1A"/>
<path d="M290.84 95.5475C290.84 94.3253 290.84 93.4304 290.898 92.7243C290.955 92.0224 291.068 91.539 291.274 91.134C291.656 90.3848 292.265 89.7757 293.014 89.394C293.419 89.1877 293.902 89.0754 294.604 89.0181C295.31 88.9604 296.205 88.9601 297.427 88.9601H413.25C414.472 88.9601 415.367 88.9604 416.073 89.0181C416.775 89.0754 417.259 89.1877 417.664 89.394C418.413 89.7757 419.022 90.3848 419.403 91.134C419.61 91.539 419.722 92.0224 419.779 92.7243C419.837 93.4304 419.837 94.3253 419.837 95.5475V108.127C419.837 109.349 419.837 110.244 419.779 110.95C419.722 111.652 419.61 112.135 419.403 112.54C419.022 113.289 418.413 113.898 417.664 114.28C417.259 114.486 416.775 114.599 416.073 114.656C415.367 114.714 414.472 114.714 413.25 114.714H297.427C296.205 114.714 295.31 114.714 294.604 114.656C293.902 114.599 293.419 114.486 293.014 114.28C292.265 113.898 291.656 113.289 291.274 112.54C291.068 112.135 290.955 111.652 290.898 110.95C290.84 110.244 290.84 109.349 290.84 108.127V95.5475Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M338.499 97.3656C338.827 97.368 339.151 97.428 339.47 97.5456C339.789 97.6631 340.077 97.8551 340.334 98.1214C340.593 98.3877 340.8 98.7476 340.953 99.201C341.109 99.6521 341.188 100.214 341.191 100.885C341.191 101.531 341.126 102.105 340.996 102.609C340.867 103.111 340.681 103.534 340.438 103.88C340.199 104.225 339.907 104.488 339.564 104.668C339.221 104.848 338.835 104.938 338.405 104.938C337.966 104.938 337.576 104.851 337.235 104.679C336.895 104.506 336.618 104.267 336.404 103.962C336.191 103.655 336.057 103.303 336.005 102.904H337.102C337.174 103.221 337.321 103.478 337.541 103.674C337.765 103.869 338.052 103.966 338.405 103.966C338.945 103.966 339.366 103.731 339.668 103.261C339.971 102.788 340.123 102.128 340.125 101.281H340.068C339.943 101.488 339.787 101.665 339.6 101.814C339.415 101.963 339.208 102.078 338.977 102.159C338.747 102.241 338.501 102.282 338.24 102.282C337.815 102.282 337.429 102.177 337.081 101.969C336.733 101.76 336.456 101.473 336.249 101.108C336.043 100.744 335.94 100.328 335.94 99.8596C335.94 99.3942 336.045 98.9719 336.257 98.5928C336.47 98.2138 336.768 97.9138 337.149 97.6931C337.533 97.47 337.983 97.3608 338.499 97.3656ZM338.502 98.3013C338.222 98.3013 337.968 98.3709 337.743 98.5101C337.52 98.6468 337.343 98.8328 337.214 99.0679C337.084 99.3006 337.02 99.5597 337.02 99.8452C337.02 100.131 337.082 100.39 337.207 100.623C337.334 100.853 337.507 101.036 337.725 101.173C337.946 101.308 338.198 101.375 338.481 101.375C338.692 101.375 338.889 101.334 339.071 101.252C339.253 101.171 339.413 101.058 339.55 100.914C339.686 100.768 339.793 100.602 339.87 100.417C339.947 100.233 339.985 100.038 339.985 99.8345C339.985 99.5633 339.92 99.3114 339.791 99.0787C339.664 98.846 339.488 98.6588 339.265 98.5173C339.042 98.3733 338.788 98.3013 338.502 98.3013ZM343.214 103.923C343.017 103.923 342.848 103.853 342.707 103.714C342.565 103.573 342.494 103.402 342.494 103.203C342.494 103.006 342.565 102.838 342.707 102.699C342.848 102.558 343.017 102.487 343.214 102.487C343.411 102.487 343.58 102.558 343.722 102.699C343.863 102.838 343.934 103.006 343.934 103.203C343.934 103.335 343.9 103.456 343.833 103.567C343.768 103.674 343.682 103.761 343.574 103.826C343.466 103.89 343.346 103.923 343.214 103.923ZM343.214 100.23C343.017 100.23 342.848 100.161 342.707 100.022C342.565 99.88 342.494 99.7097 342.494 99.5106C342.494 99.3138 342.565 99.1459 342.707 99.0067C342.848 98.8652 343.017 98.7944 343.214 98.7944C343.411 98.7944 343.58 98.8652 343.722 99.0067C343.863 99.1459 343.934 99.3138 343.934 99.5106C343.934 99.6425 343.9 99.7637 343.833 99.874C343.768 99.982 343.682 100.068 343.574 100.133C343.466 100.198 343.346 100.23 343.214 100.23ZM347.927 104.938C347.433 104.938 346.991 104.853 346.603 104.682C346.216 104.512 345.91 104.275 345.685 103.973C345.462 103.668 345.342 103.316 345.325 102.915H346.455C346.469 103.133 346.543 103.323 346.674 103.484C346.809 103.642 346.984 103.764 347.2 103.851C347.416 103.937 347.656 103.98 347.92 103.98C348.21 103.98 348.467 103.93 348.69 103.829C348.915 103.728 349.092 103.588 349.219 103.408C349.346 103.226 349.41 103.016 349.41 102.778C349.41 102.531 349.346 102.314 349.219 102.127C349.094 101.937 348.911 101.789 348.668 101.681C348.428 101.573 348.138 101.519 347.797 101.519H347.175V100.612H347.797C348.071 100.612 348.311 100.563 348.517 100.464C348.726 100.366 348.889 100.229 349.007 100.054C349.124 99.8764 349.183 99.6689 349.183 99.4314C349.183 99.2034 349.131 99.0055 349.028 98.8376C348.927 98.6672 348.783 98.5341 348.596 98.4381C348.412 98.3421 348.193 98.2941 347.941 98.2941C347.701 98.2941 347.477 98.3385 347.268 98.4273C347.062 98.5137 346.894 98.6384 346.764 98.8016C346.635 98.9623 346.565 99.1555 346.556 99.381H345.48C345.492 98.9827 345.609 98.6324 345.832 98.3301C346.058 98.0278 346.355 97.7915 346.725 97.6211C347.094 97.4508 347.505 97.3656 347.956 97.3656C348.428 97.3656 348.836 97.458 349.179 97.6427C349.525 97.8251 349.791 98.0686 349.978 98.3733C350.168 98.678 350.261 99.0115 350.259 99.3738C350.261 99.7865 350.146 100.137 349.913 100.425C349.683 100.713 349.376 100.906 348.992 101.004V101.062C349.482 101.136 349.861 101.33 350.129 101.645C350.401 101.959 350.535 102.349 350.532 102.814C350.535 103.22 350.422 103.583 350.194 103.905C349.969 104.226 349.66 104.479 349.269 104.664C348.878 104.847 348.431 104.938 347.927 104.938ZM354.459 104.959C353.89 104.957 353.404 104.807 353.001 104.509C352.598 104.212 352.29 103.779 352.076 103.21C351.863 102.642 351.756 101.957 351.756 101.155C351.756 100.356 351.863 99.6737 352.076 99.1075C352.292 98.5413 352.602 98.1094 353.005 97.8119C353.41 97.5144 353.895 97.3656 354.459 97.3656C355.023 97.3656 355.506 97.5156 355.909 97.8155C356.312 98.113 356.621 98.5449 356.834 99.1111C357.05 99.6749 357.158 100.356 357.158 101.155C357.158 101.959 357.051 102.645 356.838 103.214C356.624 103.78 356.316 104.213 355.913 104.513C355.51 104.811 355.025 104.959 354.459 104.959ZM354.459 103.998C354.958 103.998 355.348 103.Line truncated
<path d="M290.84 130.712C290.84 129.49 290.84 128.595 290.898 127.889C290.955 127.187 291.068 126.704 291.274 126.299C291.656 125.549 292.265 124.94 293.014 124.559C293.419 124.352 293.902 124.24 294.604 124.183C295.31 124.125 296.205 124.125 297.427 124.125H413.25C414.472 124.125 415.367 124.125 416.073 124.183C416.775 124.24 417.259 124.352 417.664 124.559C418.413 124.94 419.022 125.549 419.403 126.299C419.61 126.704 419.722 127.187 419.779 127.889C419.837 128.595 419.837 129.49 419.837 130.712V143.291C419.837 144.513 419.837 145.408 419.779 146.114C419.722 146.816 419.61 147.3 419.403 147.705C419.022 148.454 418.413 149.063 417.664 149.445C417.259 149.651 416.775 149.763 416.073 149.821C415.367 149.878 414.472 149.879 413.25 149.879H297.427C296.205 149.879 295.31 149.878 294.604 149.821C293.902 149.763 293.419 149.651 293.014 149.445C292.265 149.063 291.656 148.454 291.274 147.705C291.068 147.3 290.955 146.816 290.898 146.114C290.84 145.408 290.84 144.513 290.84 143.291V130.712Z" fill="#1A1A1A"/>
<path d="M290.84 130.712C290.84 129.49 290.84 128.595 290.898 127.889C290.955 127.187 291.068 126.704 291.274 126.299C291.656 125.549 292.265 124.94 293.014 124.559C293.419 124.352 293.902 124.24 294.604 124.183C295.31 124.125 296.205 124.125 297.427 124.125H413.25C414.472 124.125 415.367 124.125 416.073 124.183C416.775 124.24 417.259 124.352 417.664 124.559C418.413 124.94 419.022 125.549 419.403 126.299C419.61 126.704 419.722 127.187 419.779 127.889C419.837 128.595 419.837 129.49 419.837 130.712V143.291C419.837 144.513 419.837 145.408 419.779 146.114C419.722 146.816 419.61 147.3 419.403 147.705C419.022 148.454 418.413 149.063 417.664 149.445C417.259 149.651 416.775 149.763 416.073 149.821C415.367 149.878 414.472 149.879 413.25 149.879H297.427C296.205 149.879 295.31 149.878 294.604 149.821C293.902 149.763 293.419 149.651 293.014 149.445C292.265 149.063 291.656 148.454 291.274 147.705C291.068 147.3 290.955 146.816 290.898 146.114C290.84 145.408 290.84 144.513 290.84 143.291V130.712Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M336.344 132.631V140.001H335.229V133.747H335.185L333.422 134.898V133.833L335.261 132.631H336.344ZM340.904 140.124C340.335 140.121 339.85 139.971 339.446 139.674C339.043 139.376 338.735 138.943 338.522 138.375C338.308 137.806 338.201 137.121 338.201 136.32C338.201 135.521 338.308 134.838 338.522 134.272C338.737 133.706 339.047 133.274 339.45 132.976C339.856 132.679 340.34 132.53 340.904 132.53C341.468 132.53 341.951 132.68 342.354 132.98C342.757 133.278 343.066 133.709 343.279 134.276C343.495 134.839 343.603 135.521 343.603 136.32C343.603 137.124 343.496 137.81 343.283 138.378C343.069 138.945 342.761 139.378 342.358 139.678C341.955 139.975 341.47 140.124 340.904 140.124ZM340.904 139.163C341.403 139.163 341.793 138.919 342.074 138.432C342.357 137.945 342.498 137.241 342.498 136.32C342.498 135.708 342.434 135.191 342.304 134.769C342.177 134.344 341.993 134.023 341.753 133.804C341.516 133.583 341.233 133.473 340.904 133.473C340.407 133.473 340.017 133.718 339.734 134.207C339.451 134.697 339.308 135.401 339.306 136.32C339.306 136.934 339.37 137.453 339.497 137.878C339.626 138.3 339.81 138.621 340.047 138.839C340.285 139.055 340.57 139.163 340.904 139.163ZM345.603 139.087C345.406 139.087 345.237 139.018 345.096 138.879C344.954 138.737 344.883 138.567 344.883 138.368C344.883 138.171 344.954 138.003 345.096 137.864C345.237 137.722 345.406 137.651 345.603 137.651C345.8 137.651 345.969 137.722 346.111 137.864C346.252 138.003 346.323 138.171 346.323 138.368C346.323 138.5 346.289 138.621 346.222 138.731C346.157 138.839 346.071 138.925 345.963 138.99C345.855 139.055 345.735 139.087 345.603 139.087ZM345.603 135.395C345.406 135.395 345.237 135.325 345.096 135.186C344.954 135.045 344.883 134.874 344.883 134.675C344.883 134.478 344.954 134.31 345.096 134.171C345.237 134.03 345.406 133.959 345.603 133.959C345.8 133.959 345.969 134.03 346.111 134.171C346.252 134.31 346.323 134.478 346.323 134.675C346.323 134.807 346.289 134.928 346.222 135.039C346.157 135.147 346.071 135.233 345.963 135.298C345.855 135.362 345.735 135.395 345.603 135.395ZM350.316 140.124C349.747 140.121 349.262 139.971 348.858 139.674C348.455 139.376 348.147 138.943 347.934 138.375C347.72 137.806 347.613 137.121 347.613 136.32C347.613 135.521 347.72 134.838 347.934 134.272C348.149 133.706 348.459 133.274 348.862 132.976C349.268 132.679 349.752 132.53 350.316 132.53C350.88 132.53 351.363 132.68 351.766 132.98C352.169 133.278 352.478 133.709 352.691 134.276C352.907 134.839 353.015 135.521 353.015 136.32C353.015 137.124 352.908 137.81 352.695 138.378C352.481 138.945 352.173 139.378 351.77 139.678C351.367 139.975 350.882 140.124 350.316 140.124ZM350.316 139.163C350.815 139.163 351.205 138.919 351.486 138.432C351.769 137.945 351.91 137.241 351.91 136.32C351.91 135.708 351.846 135.191 351.716 134.769C351.589 134.344 351.405 134.023 351.165 133.804C350.928 133.583 350.645 133.473 350.316 133.473C349.819 133.473 349.429 133.718 349.146 134.207C348.863 134.697 348.72 135.401 348.718 136.32C348.718 136.934 348.782 137.453 348.909 137.878C349.038 138.3 349.222 138.621 349.459 138.839C349.697 139.055 349.982 139.163 350.316 139.163ZM356.858 140.124C356.289 140.121 355.803 139.971 355.4 139.674C354.997 139.376 354.689 138.943 354.475 138.375C354.262 137.806 354.155 137.121 354.155 136.32C354.155 135.521 354.262 134.838 354.475 134.272C354.691 133.706 355.001 133.274 355.404 132.976C355.809 132.679 356.294 132.53 356.858 132.53C357.422 132.53 357.905 132.68 358.308 132.98C358.711 133.278 359.02 133.709 359.233 134.276C359.449 134.839 359.557 135.521 359.557 136.32C359.557 137.124 359.45 137.81 359.237 138.378C359.023 138.945 358.715 139.378 358.312 139.678C357.909 139.975 357.424 140.124 356.858 140.124ZM356.858 139.163C357.357 139.163 357.747 138.919 358.028 138.432C358.311 137.945 358.452 137.241 358.452 136.32C358.452 135.708 358.387 135.191 358.258 134.769C358.131 134.344 357.947 134.023 357.707 133.804C357.47 133.583 357.187 133.473 356.858 133.473C356.361 133.473 355.971 133.718 355.688 134.207C355.405 134.697 355.262 135.401 355.26 136.32C355.26 136.934 355.324 137.453 355.451 137.878C355.58 138.3 355.764 138.621 356.001 138.839C356.239 139.055 356.524 139.163 356.858 139.163ZM365.139 140.124C364.789 140.124 364.472 140.059 364.189 139.929C363.906 139.798 363.681 139.607 363.516 139.357C363.353 139.108 363.271 138.802 363.271 138.44C363.271 138.128 363.331 137.871 363.451 137.669C363.571 137.468 363.733 137.308 363.937 137.191C364.141 137.073 364.369 136.984 364.621 136.924C364.873 136.864 365.129 136.819 365.391 136.788C365.722 136.749 365.991 136.718 366.197 136.694C366.403 136.668 366.553 136.626 366.647 136.568C366.74 136.511 366.787 136.417 366.787 136.287V136.262C366.787 135.948 366.698 135.704 366.521 135.532C366.346 135.359 366.084 135.273 365.736 135.273C365.374 135.273 365.089 135.353 364.88 135.514C364.673 135.672 364.531 135.848 364.452 136.043L363.44 135.812C363.56 135.476 363.735 135.205 363.966 134.999C364.198 134.79 364.466 134.639 364.768 Line truncated
<path d="M290.84 165.877C290.84 164.654 290.84 163.759 290.898 163.053C290.955 162.351 291.068 161.868 291.274 161.463C291.656 160.714 292.265 160.105 293.014 159.723C293.419 159.517 293.902 159.405 294.604 159.347C295.31 159.289 296.205 159.289 297.427 159.289H413.25C414.472 159.289 415.367 159.289 416.073 159.347C416.775 159.405 417.259 159.517 417.664 159.723C418.413 160.105 419.022 160.714 419.403 161.463C419.61 161.868 419.722 162.351 419.779 163.053C419.837 163.759 419.837 164.654 419.837 165.877V178.456C419.837 179.678 419.837 180.573 419.779 181.279C419.722 181.981 419.61 182.464 419.403 182.869C419.022 183.618 418.413 184.227 417.664 184.609C417.259 184.816 416.775 184.928 416.073 184.985C415.367 185.043 414.472 185.043 413.25 185.043H297.427C296.205 185.043 295.31 185.043 294.604 184.985C293.902 184.928 293.419 184.816 293.014 184.609C292.265 184.227 291.656 183.618 291.274 182.869C291.068 182.464 290.955 181.981 290.898 181.279C290.84 180.573 290.84 179.678 290.84 178.456V165.877Z" fill="#1A1A1A"/>
<path d="M290.84 165.877C290.84 164.654 290.84 163.759 290.898 163.053C290.955 162.351 291.068 161.868 291.274 161.463C291.656 160.714 292.265 160.105 293.014 159.723C293.419 159.517 293.902 159.405 294.604 159.347C295.31 159.289 296.205 159.289 297.427 159.289H413.25C414.472 159.289 415.367 159.289 416.073 159.347C416.775 159.405 417.259 159.517 417.664 159.723C418.413 160.105 419.022 160.714 419.403 161.463C419.61 161.868 419.722 162.351 419.779 163.053C419.837 163.759 419.837 164.654 419.837 165.877V178.456C419.837 179.678 419.837 180.573 419.779 181.279C419.722 181.981 419.61 182.464 419.403 182.869C419.022 183.618 418.413 184.227 417.664 184.609C417.259 184.816 416.775 184.928 416.073 184.985C415.367 185.043 414.472 185.043 413.25 185.043H297.427C296.205 185.043 295.31 185.043 294.604 184.985C293.902 184.928 293.419 184.816 293.014 184.609C292.265 184.227 291.656 183.618 291.274 182.869C291.068 182.464 290.955 181.981 290.898 181.279C290.84 180.573 290.84 179.678 290.84 178.456V165.877Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M336.344 167.795V175.166H335.229V168.911H335.185L333.422 170.063V168.998L335.261 167.795H336.344ZM340.904 175.288C340.335 175.286 339.85 175.136 339.446 174.839C339.043 174.541 338.735 174.108 338.522 173.539C338.308 172.971 338.201 172.286 338.201 171.484C338.201 170.685 338.308 170.003 338.522 169.437C338.737 168.87 339.047 168.438 339.45 168.141C339.856 167.843 340.34 167.695 340.904 167.695C341.468 167.695 341.951 167.845 342.354 168.145C342.757 168.442 343.066 168.874 343.279 169.44C343.495 170.004 343.603 170.685 343.603 171.484C343.603 172.288 343.496 172.974 343.283 173.543C343.069 174.109 342.761 174.542 342.358 174.842C341.955 175.14 341.47 175.288 340.904 175.288ZM340.904 174.327C341.403 174.327 341.793 174.084 342.074 173.597C342.357 173.11 342.498 172.406 342.498 171.484C342.498 170.873 342.434 170.355 342.304 169.933C342.177 169.509 341.993 169.187 341.753 168.969C341.516 168.748 341.233 168.638 340.904 168.638C340.407 168.638 340.017 168.882 339.734 169.372C339.451 169.861 339.308 170.565 339.306 171.484C339.306 172.099 339.37 172.618 339.497 173.043C339.626 173.465 339.81 173.785 340.047 174.004C340.285 174.22 340.57 174.327 340.904 174.327ZM345.603 174.252C345.406 174.252 345.237 174.182 345.096 174.043C344.954 173.902 344.883 173.731 344.883 173.532C344.883 173.335 344.954 173.167 345.096 173.028C345.237 172.887 345.406 172.816 345.603 172.816C345.8 172.816 345.969 172.887 346.111 173.028C346.252 173.167 346.323 173.335 346.323 173.532C346.323 173.664 346.289 173.785 346.222 173.896C346.157 174.004 346.071 174.09 345.963 174.155C345.855 174.22 345.735 174.252 345.603 174.252ZM345.603 170.559C345.406 170.559 345.237 170.49 345.096 170.351C344.954 170.209 344.883 170.039 344.883 169.84C344.883 169.643 344.954 169.475 345.096 169.336C345.237 169.194 345.406 169.123 345.603 169.123C345.8 169.123 345.969 169.194 346.111 169.336C346.252 169.475 346.323 169.643 346.323 169.84C346.323 169.972 346.289 170.093 346.222 170.203C346.157 170.311 346.071 170.397 345.963 170.462C345.855 170.527 345.735 170.559 345.603 170.559ZM350.316 175.267C349.822 175.267 349.38 175.182 348.992 175.011C348.605 174.841 348.299 174.605 348.074 174.302C347.851 173.998 347.731 173.645 347.714 173.244H348.844C348.858 173.463 348.932 173.652 349.064 173.813C349.198 173.971 349.373 174.094 349.589 174.18C349.805 174.266 350.045 174.309 350.309 174.309C350.599 174.309 350.856 174.259 351.079 174.158C351.304 174.058 351.481 173.917 351.608 173.737C351.735 173.555 351.799 173.345 351.799 173.107C351.799 172.86 351.735 172.643 351.608 172.456C351.483 172.267 351.3 172.118 351.057 172.01C350.817 171.902 350.527 171.848 350.186 171.848H349.564V170.941H350.186C350.46 170.941 350.7 170.892 350.906 170.793C351.115 170.695 351.278 170.558 351.396 170.383C351.513 170.206 351.572 169.998 351.572 169.76C351.572 169.533 351.52 169.335 351.417 169.167C351.316 168.996 351.173 168.863 350.985 168.767C350.801 168.671 350.582 168.623 350.33 168.623C350.09 168.623 349.866 168.668 349.657 168.756C349.451 168.843 349.283 168.968 349.154 169.131C349.024 169.291 348.954 169.485 348.945 169.71H347.869C347.881 169.312 347.998 168.962 348.221 168.659C348.447 168.357 348.744 168.121 349.114 167.95C349.483 167.78 349.894 167.695 350.345 167.695C350.817 167.695 351.225 167.787 351.568 167.972C351.914 168.154 352.18 168.398 352.367 168.702C352.557 169.007 352.65 169.341 352.648 169.703C352.65 170.116 352.535 170.466 352.303 170.754C352.072 171.042 351.765 171.235 351.381 171.333V171.391C351.871 171.465 352.25 171.659 352.519 171.974C352.79 172.288 352.924 172.678 352.922 173.143C352.924 173.549 352.811 173.912 352.583 174.234C352.358 174.555 352.049 174.809 351.658 174.993C351.267 175.176 350.82 175.267 350.316 175.267ZM356.848 175.288C356.279 175.286 355.794 175.136 355.39 174.839C354.987 174.541 354.679 174.108 354.466 173.539C354.252 172.971 354.145 172.286 354.145 171.484C354.145 170.685 354.252 170.003 354.466 169.437C354.681 168.87 354.991 168.438 355.394 168.141C355.8 167.843 356.284 167.695 356.848 167.695C357.412 167.695 357.895 167.845 358.298 168.145C358.701 168.442 359.01 168.874 359.223 169.44C359.439 170.004 359.547 170.685 359.547 171.484C359.547 172.288 359.44 172.974 359.227 173.543C359.013 174.109 358.705 174.542 358.302 174.842C357.899 175.14 357.414 175.288 356.848 175.288ZM356.848 174.327C357.347 174.327 357.737 174.084 358.018 173.597C358.301 173.11 358.442 172.406 358.442 171.484C358.442 170.873 358.378 170.355 358.248 169.933C358.121 169.509 357.937 169.187 357.697 168.969C357.46 168.748 357.177 168.638 356.848 168.638C356.351 168.638 355.961 168.882 355.678 169.372C355.395 169.861 355.252 170.565 355.25 171.484C355.25 172.099 355.314 172.618 355.441 173.043C355.57 173.465 355.754 173.785 355.991 174.004C356.229 174.22 356.514 174.327 356.848 174.327ZM365.129 175.288C364.779 175.288 364.462 175.224 364.179 175.094C363.896 174.962 363.671 174.771 363.506 174.522C363.343 174.272 363.261 173.966 363.261 173.604C363.261 173.29Line truncated
<path d="M290.84 201.041C290.84 199.819 290.84 198.924 290.898 198.218C290.955 197.516 291.068 197.033 291.274 196.628C291.656 195.878 292.265 195.269 293.014 194.888C293.419 194.681 293.902 194.569 294.604 194.512C295.31 194.454 296.205 194.454 297.427 194.454H413.25C414.472 194.454 415.367 194.454 416.073 194.512C416.775 194.569 417.259 194.681 417.664 194.888C418.413 195.269 419.022 195.878 419.403 196.628C419.61 197.033 419.722 197.516 419.779 198.218C419.837 198.924 419.837 199.819 419.837 201.041V213.62C419.837 214.842 419.837 215.737 419.779 216.443C419.722 217.145 419.61 217.629 419.403 218.034C419.022 218.783 418.413 219.392 417.664 219.774C417.259 219.98 416.775 220.092 416.073 220.15C415.367 220.207 414.472 220.208 413.25 220.208H297.427C296.205 220.208 295.31 220.207 294.604 220.15C293.902 220.092 293.419 219.98 293.014 219.774C292.265 219.392 291.656 218.783 291.274 218.034C291.068 217.629 290.955 217.145 290.898 216.443C290.84 215.737 290.84 214.842 290.84 213.62V201.041Z" fill="#1A1A1A"/>
<path d="M290.84 201.041C290.84 199.819 290.84 198.924 290.898 198.218C290.955 197.516 291.068 197.033 291.274 196.628C291.656 195.878 292.265 195.269 293.014 194.888C293.419 194.681 293.902 194.569 294.604 194.512C295.31 194.454 296.205 194.454 297.427 194.454H413.25C414.472 194.454 415.367 194.454 416.073 194.512C416.775 194.569 417.259 194.681 417.664 194.888C418.413 195.269 419.022 195.878 419.403 196.628C419.61 197.033 419.722 197.516 419.779 198.218C419.837 198.924 419.837 199.819 419.837 201.041V213.62C419.837 214.842 419.837 215.737 419.779 216.443C419.722 217.145 419.61 217.629 419.403 218.034C419.022 218.783 418.413 219.392 417.664 219.774C417.259 219.98 416.775 220.092 416.073 220.15C415.367 220.207 414.472 220.208 413.25 220.208H297.427C296.205 220.208 295.31 220.207 294.604 220.15C293.902 220.092 293.419 219.98 293.014 219.774C292.265 219.392 291.656 218.783 291.274 218.034C291.068 217.629 290.955 217.145 290.898 216.443C290.84 215.737 290.84 214.842 290.84 213.62V201.041Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M337.344 202.96V210.331H336.229V204.076H336.185L334.422 205.227V204.162L336.261 202.96H337.344ZM342.134 202.96V210.331H341.019V204.076H340.975L339.212 205.227V204.162L341.051 202.96H342.134ZM344.851 209.416C344.655 209.416 344.486 209.347 344.344 209.208C344.202 209.066 344.132 208.896 344.132 208.697C344.132 208.5 344.202 208.332 344.344 208.193C344.486 208.051 344.655 207.98 344.851 207.98C345.048 207.98 345.217 208.051 345.359 208.193C345.5 208.332 345.571 208.5 345.571 208.697C345.571 208.829 345.538 208.95 345.47 209.06C345.406 209.168 345.319 209.254 345.211 209.319C345.103 209.384 344.983 209.416 344.851 209.416ZM344.851 205.724C344.655 205.724 344.486 205.654 344.344 205.515C344.202 205.374 344.132 205.203 344.132 205.004C344.132 204.807 344.202 204.64 344.344 204.5C344.486 204.359 344.655 204.288 344.851 204.288C345.048 204.288 345.217 204.359 345.359 204.5C345.5 204.64 345.571 204.807 345.571 205.004C345.571 205.136 345.538 205.257 345.47 205.368C345.406 205.476 345.319 205.562 345.211 205.627C345.103 205.692 344.983 205.724 344.851 205.724ZM349.564 210.453C348.996 210.451 348.51 210.301 348.107 210.003C347.704 209.706 347.395 209.272 347.182 208.704C346.968 208.135 346.861 207.45 346.861 206.649C346.861 205.85 346.968 205.167 347.182 204.601C347.398 204.035 347.707 203.603 348.11 203.306C348.516 203.008 349 202.859 349.564 202.859C350.128 202.859 350.612 203.009 351.015 203.309C351.418 203.607 351.726 204.039 351.939 204.605C352.155 205.169 352.263 205.85 352.263 206.649C352.263 207.453 352.157 208.139 351.943 208.707C351.73 209.274 351.421 209.707 351.018 210.007C350.615 210.304 350.13 210.453 349.564 210.453ZM349.564 209.492C350.063 209.492 350.453 209.248 350.734 208.761C351.017 208.274 351.159 207.57 351.159 206.649C351.159 206.037 351.094 205.52 350.964 205.098C350.837 204.673 350.653 204.352 350.414 204.133C350.176 203.913 349.893 203.802 349.564 203.802C349.068 203.802 348.678 204.047 348.395 204.536C348.111 205.026 347.969 205.73 347.966 206.649C347.966 207.263 348.03 207.783 348.157 208.207C348.287 208.629 348.47 208.95 348.708 209.168C348.945 209.384 349.231 209.492 349.564 209.492ZM356.106 210.453C355.537 210.451 355.052 210.301 354.649 210.003C354.245 209.706 353.937 209.272 353.724 208.704C353.51 208.135 353.403 207.45 353.403 206.649C353.403 205.85 353.51 205.167 353.724 204.601C353.94 204.035 354.249 203.603 354.652 203.306C355.058 203.008 355.542 202.859 356.106 202.859C356.67 202.859 357.153 203.009 357.556 203.309C357.96 203.607 358.268 204.039 358.481 204.605C358.697 205.169 358.805 205.85 358.805 206.649C358.805 207.453 358.699 208.139 358.485 208.707C358.271 209.274 357.963 209.707 357.56 210.007C357.157 210.304 356.672 210.453 356.106 210.453ZM356.106 209.492C356.605 209.492 356.995 209.248 357.276 208.761C357.559 208.274 357.7 207.57 357.7 206.649C357.7 206.037 357.636 205.52 357.506 205.098C357.379 204.673 357.195 204.352 356.955 204.133C356.718 203.913 356.435 203.802 356.106 203.802C355.609 203.802 355.22 204.047 354.936 204.536C354.653 205.026 354.511 205.73 354.508 206.649C354.508 207.263 354.572 207.783 354.699 208.207C354.829 208.629 355.012 208.95 355.25 209.168C355.487 209.384 355.773 209.492 356.106 209.492ZM364.387 210.453C364.037 210.453 363.72 210.388 363.437 210.259C363.154 210.127 362.93 209.936 362.764 209.686C362.601 209.437 362.519 209.131 362.519 208.769C362.519 208.457 362.579 208.2 362.699 207.998C362.819 207.797 362.981 207.637 363.185 207.52C363.389 207.402 363.617 207.313 363.869 207.254C364.121 207.194 364.378 207.148 364.639 207.117C364.97 207.078 365.239 207.047 365.445 207.023C365.652 206.997 365.802 206.955 365.895 206.897C365.989 206.84 366.035 206.746 366.035 206.617V206.591C366.035 206.277 365.947 206.033 365.769 205.861C365.594 205.688 365.332 205.602 364.985 205.602C364.622 205.602 364.337 205.682 364.128 205.843C363.922 206.001 363.779 206.177 363.7 206.372L362.688 206.141C362.808 205.806 362.984 205.534 363.214 205.328C363.447 205.119 363.714 204.968 364.016 204.875C364.319 204.779 364.637 204.731 364.97 204.731C365.191 204.731 365.425 204.757 365.672 204.81C365.921 204.86 366.154 204.954 366.37 205.091C366.588 205.227 366.767 205.423 366.906 205.677C367.046 205.929 367.115 206.257 367.115 206.66V210.331H366.064V209.575H366.021C365.951 209.714 365.847 209.851 365.708 209.985C365.569 210.119 365.39 210.231 365.172 210.32C364.953 210.409 364.692 210.453 364.387 210.453ZM364.621 209.589C364.919 209.589 365.173 209.53 365.384 209.413C365.598 209.295 365.76 209.142 365.87 208.952C365.983 208.76 366.039 208.555 366.039 208.337V207.624C366.001 207.663 365.926 207.699 365.816 207.732C365.708 207.763 365.584 207.791 365.445 207.815C365.306 207.837 365.171 207.857 365.039 207.876C364.907 207.893 364.796 207.907 364.707 207.919C364.499 207.946 364.308 207.99 364.135 208.052C363.965 208.115 363.828 208.205 363.725 208.322C363.624 208.438 363.574 208.591 363.574 208.783C363.574 209.049 363.672 209.251 363.869 209.388C364.066 209.522 364.316 Line truncated
<path d="M290.84 236.206C290.84 234.983 290.84 234.089 290.898 233.382C290.955 232.681 291.068 232.197 291.274 231.792C291.656 231.043 292.265 230.434 293.014 230.052C293.419 229.846 293.902 229.734 294.604 229.676C295.31 229.619 296.205 229.618 297.427 229.618H413.25C414.472 229.618 415.367 229.619 416.073 229.676C416.775 229.734 417.259 229.846 417.664 230.052C418.413 230.434 419.022 231.043 419.403 231.792C419.61 232.197 419.722 232.681 419.779 233.382C419.837 234.089 419.837 234.983 419.837 236.206V248.785C419.837 250.007 419.837 250.902 419.779 251.608C419.722 252.31 419.61 252.793 419.403 253.198C419.022 253.947 418.413 254.557 417.664 254.938C417.259 255.145 416.775 255.257 416.073 255.314C415.367 255.372 414.472 255.372 413.25 255.372H297.427C296.205 255.372 295.31 255.372 294.604 255.314C293.902 255.257 293.419 255.145 293.014 254.938C292.265 254.557 291.656 253.947 291.274 253.198C291.068 252.793 290.955 252.31 290.898 251.608C290.84 250.902 290.84 250.007 290.84 248.785V236.206Z" fill="#1A1A1A"/>
<path d="M290.84 236.206C290.84 234.983 290.84 234.089 290.898 233.382C290.955 232.681 291.068 232.197 291.274 231.792C291.656 231.043 292.265 230.434 293.014 230.052C293.419 229.846 293.902 229.734 294.604 229.676C295.31 229.619 296.205 229.618 297.427 229.618H413.25C414.472 229.618 415.367 229.619 416.073 229.676C416.775 229.734 417.259 229.846 417.664 230.052C418.413 230.434 419.022 231.043 419.403 231.792C419.61 232.197 419.722 232.681 419.779 233.382C419.837 234.089 419.837 234.983 419.837 236.206V248.785C419.837 250.007 419.837 250.902 419.779 251.608C419.722 252.31 419.61 252.793 419.403 253.198C419.022 253.947 418.413 254.557 417.664 254.938C417.259 255.145 416.775 255.257 416.073 255.314C415.367 255.372 414.472 255.372 413.25 255.372H297.427C296.205 255.372 295.31 255.372 294.604 255.314C293.902 255.257 293.419 255.145 293.014 254.938C292.265 254.557 291.656 253.947 291.274 253.198C291.068 252.793 290.955 252.31 290.898 251.608C290.84 250.902 290.84 250.007 290.84 248.785V236.206Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M337.344 238.125V245.495H336.229V239.24H336.185L334.422 240.392V239.327L336.261 238.125H337.344ZM342.134 238.125V245.495H341.019V239.24H340.975L339.212 240.392V239.327L341.051 238.125H342.134ZM344.851 244.581C344.655 244.581 344.486 244.511 344.344 244.372C344.202 244.231 344.132 244.06 344.132 243.861C344.132 243.664 344.202 243.497 344.344 243.357C344.486 243.216 344.655 243.145 344.851 243.145C345.048 243.145 345.217 243.216 345.359 243.357C345.5 243.497 345.571 243.664 345.571 243.861C345.571 243.993 345.538 244.114 345.47 244.225C345.406 244.333 345.319 244.419 345.211 244.484C345.103 244.549 344.983 244.581 344.851 244.581ZM344.851 240.889C344.655 240.889 344.486 240.819 344.344 240.68C344.202 240.538 344.132 240.368 344.132 240.169C344.132 239.972 344.202 239.804 344.344 239.665C344.486 239.523 344.655 239.453 344.851 239.453C345.048 239.453 345.217 239.523 345.359 239.665C345.5 239.804 345.571 239.972 345.571 240.169C345.571 240.301 345.538 240.422 345.47 240.532C345.406 240.64 345.319 240.727 345.211 240.791C345.103 240.856 344.983 240.889 344.851 240.889ZM349.564 245.596C349.07 245.596 348.629 245.511 348.24 245.34C347.854 245.17 347.548 244.934 347.322 244.631C347.099 244.327 346.979 243.974 346.962 243.573H348.092C348.107 243.792 348.18 243.981 348.312 244.142C348.446 244.3 348.621 244.423 348.837 244.509C349.053 244.595 349.293 244.639 349.557 244.639C349.847 244.639 350.104 244.588 350.327 244.487C350.553 244.387 350.729 244.246 350.856 244.066C350.983 243.884 351.047 243.674 351.047 243.437C351.047 243.189 350.983 242.972 350.856 242.785C350.731 242.596 350.548 242.447 350.306 242.339C350.066 242.231 349.775 242.177 349.435 242.177H348.812V241.27H349.435C349.708 241.27 349.948 241.221 350.154 241.122C350.363 241.024 350.526 240.887 350.644 240.712C350.761 240.535 350.82 240.327 350.82 240.09C350.82 239.862 350.769 239.664 350.665 239.496C350.565 239.325 350.421 239.192 350.234 239.096C350.049 239 349.831 238.952 349.579 238.952C349.339 238.952 349.114 238.997 348.906 239.085C348.699 239.172 348.531 239.297 348.402 239.46C348.272 239.621 348.203 239.814 348.193 240.039H347.117C347.129 239.641 347.247 239.291 347.47 238.988C347.695 238.686 347.993 238.45 348.362 238.279C348.732 238.109 349.142 238.024 349.593 238.024C350.066 238.024 350.474 238.116 350.817 238.301C351.162 238.483 351.428 238.727 351.616 239.032C351.805 239.336 351.899 239.67 351.896 240.032C351.899 240.445 351.784 240.795 351.551 241.083C351.32 241.371 351.013 241.564 350.63 241.662V241.72C351.119 241.794 351.498 241.989 351.767 242.303C352.038 242.617 352.172 243.007 352.17 243.473C352.172 243.878 352.059 244.242 351.832 244.563C351.606 244.885 351.298 245.138 350.907 245.322C350.516 245.505 350.068 245.596 349.564 245.596ZM356.096 245.617C355.528 245.615 355.042 245.465 354.639 245.168C354.236 244.87 353.927 244.437 353.714 243.868C353.5 243.3 353.393 242.615 353.393 241.813C353.393 241.014 353.5 240.332 353.714 239.766C353.93 239.199 354.239 238.768 354.642 238.47C355.048 238.173 355.532 238.024 356.096 238.024C356.66 238.024 357.143 238.174 357.547 238.474C357.95 238.771 358.258 239.203 358.471 239.769C358.687 240.333 358.795 241.014 358.795 241.813C358.795 242.617 358.689 243.303 358.475 243.872C358.262 244.438 357.953 244.871 357.55 245.171C357.147 245.469 356.662 245.617 356.096 245.617ZM356.096 244.657C356.595 244.657 356.985 244.413 357.266 243.926C357.549 243.439 357.691 242.735 357.691 241.813C357.691 241.202 357.626 240.685 357.496 240.262C357.369 239.838 357.185 239.516 356.946 239.298C356.708 239.077 356.425 238.967 356.096 238.967C355.6 238.967 355.21 239.211 354.927 239.701C354.643 240.19 354.501 240.895 354.498 241.813C354.498 242.428 354.562 242.947 354.689 243.372C354.819 243.794 355.002 244.114 355.24 244.333C355.477 244.549 355.763 244.657 356.096 244.657ZM364.377 245.617C364.027 245.617 363.71 245.553 363.427 245.423C363.144 245.291 362.92 245.1 362.754 244.851C362.591 244.601 362.509 244.295 362.509 243.933C362.509 243.621 362.569 243.365 362.689 243.163C362.809 242.961 362.971 242.802 363.175 242.684C363.379 242.567 363.607 242.478 363.859 242.418C364.111 242.358 364.368 242.312 364.629 242.281C364.96 242.243 365.229 242.212 365.435 242.188C365.642 242.161 365.792 242.119 365.885 242.062C365.979 242.004 366.026 241.911 366.026 241.781V241.756C366.026 241.442 365.937 241.198 365.759 241.025C365.584 240.853 365.323 240.766 364.975 240.766C364.612 240.766 364.327 240.847 364.118 241.007C363.912 241.166 363.769 241.342 363.69 241.536L362.679 241.306C362.799 240.97 362.974 240.699 363.204 240.493C363.437 240.284 363.704 240.133 364.007 240.039C364.309 239.943 364.627 239.895 364.96 239.895C365.181 239.895 365.415 239.922 365.662 239.974C365.912 240.025 366.144 240.118 366.36 240.255C366.579 240.392 366.757 240.587 366.896 240.842C367.036 241.094 367.105 241.421 367.105 241.824V245.495H366.054V244.739H366.011C365.942 244.879 365.837 245.015 365.698 245.15C365.559 245.284 365.38 245.396 365.162 245.48Line truncated
<path d="M290.84 271.37C290.84 270.148 290.84 269.253 290.898 268.547C290.955 267.845 291.068 267.362 291.274 266.957C291.656 266.208 292.265 265.599 293.014 265.217C293.419 265.01 293.902 264.898 294.604 264.841C295.31 264.783 296.205 264.783 297.427 264.783H413.25C414.472 264.783 415.367 264.783 416.073 264.841C416.775 264.898 417.259 265.01 417.664 265.217C418.413 265.599 419.022 266.208 419.403 266.957C419.61 267.362 419.722 267.845 419.779 268.547C419.837 269.253 419.837 270.148 419.837 271.37V283.949C419.837 285.172 419.837 286.066 419.779 286.773C419.722 287.474 419.61 287.958 419.403 288.363C419.022 289.112 418.413 289.721 417.664 290.103C417.259 290.309 416.775 290.421 416.073 290.479C415.367 290.537 414.472 290.537 413.25 290.537H297.427C296.205 290.537 295.31 290.537 294.604 290.479C293.902 290.421 293.419 290.309 293.014 290.103C292.265 289.721 291.656 289.112 291.274 288.363C291.068 287.958 290.955 287.474 290.898 286.773C290.84 286.066 290.84 285.172 290.84 283.949V271.37Z" fill="#1A1A1A"/>
<path d="M290.84 271.37C290.84 270.148 290.84 269.253 290.898 268.547C290.955 267.845 291.068 267.362 291.274 266.957C291.656 266.208 292.265 265.599 293.014 265.217C293.419 265.01 293.902 264.898 294.604 264.841C295.31 264.783 296.205 264.783 297.427 264.783H413.25C414.472 264.783 415.367 264.783 416.073 264.841C416.775 264.898 417.259 265.01 417.664 265.217C418.413 265.599 419.022 266.208 419.403 266.957C419.61 267.362 419.722 267.845 419.779 268.547C419.837 269.253 419.837 270.148 419.837 271.37V283.949C419.837 285.172 419.837 286.066 419.779 286.773C419.722 287.474 419.61 287.958 419.403 288.363C419.022 289.112 418.413 289.721 417.664 290.103C417.259 290.309 416.775 290.421 416.073 290.479C415.367 290.537 414.472 290.537 413.25 290.537H297.427C296.205 290.537 295.31 290.537 294.604 290.479C293.902 290.421 293.419 290.309 293.014 290.103C292.265 289.721 291.656 289.112 291.274 288.363C291.068 287.958 290.955 287.474 290.898 286.773C290.84 286.066 290.84 285.172 290.84 283.949V271.37Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M336.344 273.289V280.66H335.229V274.405H335.185L333.422 275.556V274.491L335.261 273.289H336.344ZM338.338 280.66V279.854L340.832 277.27C341.098 276.989 341.318 276.743 341.491 276.532C341.666 276.318 341.797 276.115 341.883 275.924C341.969 275.732 342.012 275.528 342.012 275.312C342.012 275.067 341.955 274.856 341.84 274.678C341.725 274.498 341.567 274.36 341.368 274.264C341.169 274.166 340.945 274.117 340.695 274.117C340.431 274.117 340.201 274.171 340.004 274.279C339.808 274.387 339.656 274.539 339.551 274.736C339.445 274.933 339.392 275.163 339.392 275.427H338.331C338.331 274.978 338.434 274.586 338.64 274.25C338.847 273.914 339.13 273.654 339.49 273.469C339.85 273.282 340.259 273.188 340.717 273.188C341.18 273.188 341.588 273.281 341.94 273.465C342.296 273.648 342.573 273.897 342.772 274.214C342.971 274.528 343.071 274.883 343.071 275.279C343.071 275.553 343.019 275.82 342.916 276.082C342.815 276.343 342.639 276.635 342.387 276.956C342.135 277.276 341.785 277.663 341.336 278.119L339.871 279.652V279.706H343.189V280.66H338.338ZM345.277 279.746C345.08 279.746 344.911 279.676 344.769 279.537C344.628 279.395 344.557 279.225 344.557 279.026C344.557 278.829 344.628 278.661 344.769 278.522C344.911 278.38 345.08 278.31 345.277 278.31C345.473 278.31 345.643 278.38 345.784 278.522C345.926 278.661 345.996 278.829 345.996 279.026C345.996 279.158 345.963 279.279 345.896 279.389C345.831 279.497 345.744 279.584 345.637 279.648C345.529 279.713 345.409 279.746 345.277 279.746ZM345.277 276.053C345.08 276.053 344.911 275.984 344.769 275.844C344.628 275.703 344.557 275.532 344.557 275.333C344.557 275.137 344.628 274.969 344.769 274.829C344.911 274.688 345.08 274.617 345.277 274.617C345.473 274.617 345.643 274.688 345.784 274.829C345.926 274.969 345.996 275.137 345.996 275.333C345.996 275.465 345.963 275.586 345.896 275.697C345.831 275.805 345.744 275.891 345.637 275.956C345.529 276.021 345.409 276.053 345.277 276.053ZM349.989 280.782C349.421 280.78 348.935 280.63 348.532 280.332C348.129 280.035 347.82 279.602 347.607 279.033C347.393 278.464 347.287 277.779 347.287 276.978C347.287 276.179 347.393 275.496 347.607 274.93C347.823 274.364 348.132 273.932 348.535 273.635C348.941 273.337 349.426 273.188 349.989 273.188C350.553 273.188 351.037 273.338 351.44 273.638C351.843 273.936 352.151 274.368 352.365 274.934C352.581 275.498 352.689 276.179 352.689 276.978C352.689 277.782 352.582 278.468 352.368 279.037C352.155 279.603 351.846 280.036 351.443 280.336C351.04 280.633 350.556 280.782 349.989 280.782ZM349.989 279.821C350.488 279.821 350.878 279.578 351.159 279.091C351.442 278.604 351.584 277.899 351.584 276.978C351.584 276.366 351.519 275.849 351.389 275.427C351.262 275.002 351.079 274.681 350.839 274.462C350.601 274.242 350.318 274.131 349.989 274.131C349.493 274.131 349.103 274.376 348.82 274.865C348.537 275.355 348.394 276.059 348.391 276.978C348.391 277.592 348.455 278.112 348.582 278.536C348.712 278.959 348.895 279.279 349.133 279.497C349.37 279.713 349.656 279.821 349.989 279.821ZM356.531 280.782C355.963 280.78 355.477 280.63 355.074 280.332C354.671 280.035 354.362 279.602 354.149 279.033C353.935 278.464 353.829 277.779 353.829 276.978C353.829 276.179 353.935 275.496 354.149 274.93C354.365 274.364 354.674 273.932 355.077 273.635C355.483 273.337 355.967 273.188 356.531 273.188C357.095 273.188 357.579 273.338 357.982 273.638C358.385 273.936 358.693 274.368 358.907 274.934C359.122 275.498 359.23 276.179 359.23 276.978C359.23 277.782 359.124 278.468 358.91 279.037C358.697 279.603 358.388 280.036 357.985 280.336C357.582 280.633 357.098 280.782 356.531 280.782ZM356.531 279.821C357.03 279.821 357.42 279.578 357.701 279.091C357.984 278.604 358.126 277.899 358.126 276.978C358.126 276.366 358.061 275.849 357.931 275.427C357.804 275.002 357.621 274.681 357.381 274.462C357.143 274.242 356.86 274.131 356.531 274.131C356.035 274.131 355.645 274.376 355.362 274.865C355.079 275.355 354.936 276.059 354.933 276.978C354.933 277.592 354.997 278.112 355.124 278.536C355.254 278.959 355.437 279.279 355.675 279.497C355.912 279.713 356.198 279.821 356.531 279.821ZM363.204 282.733V275.132H364.254V276.028H364.344C364.407 275.913 364.497 275.78 364.614 275.628C364.732 275.477 364.895 275.345 365.104 275.233C365.313 275.117 365.588 275.06 365.932 275.06C366.378 275.06 366.776 275.173 367.126 275.398C367.477 275.624 367.751 275.949 367.951 276.373C368.152 276.798 368.253 277.309 368.253 277.907C368.253 278.504 368.153 279.016 367.954 279.443C367.755 279.868 367.482 280.195 367.134 280.426C366.786 280.654 366.389 280.768 365.942 280.768C365.606 280.768 365.332 280.711 365.118 280.598C364.907 280.486 364.742 280.354 364.622 280.203C364.502 280.051 364.409 279.917 364.344 279.8H364.28V282.733H363.204ZM364.258 277.896C364.258 278.284 364.314 278.625 364.427 278.918C364.54 279.211 364.703 279.44 364.917 279.605C365.13 279.768 365.392 279.85 365.701 279.85C366.023 279.85 366.291 279.765 366.507 279.594C366.723 279.422 366.886 279.188 366.997 278.893C36Line truncated
<path d="M290.84 306.535C290.84 305.313 290.84 304.418 290.898 303.712C290.955 303.01 291.068 302.526 291.274 302.121C291.656 301.372 292.265 300.763 293.014 300.381C293.419 300.175 293.902 300.063 294.604 300.005C295.31 299.948 296.205 299.947 297.427 299.947H413.25C414.472 299.947 415.367 299.948 416.073 300.005C416.775 300.063 417.259 300.175 417.664 300.381C418.413 300.763 419.022 301.372 419.403 302.121C419.61 302.526 419.722 303.01 419.779 303.712C419.837 304.418 419.837 305.313 419.837 306.535V319.114C419.837 320.336 419.837 321.231 419.779 321.937C419.722 322.639 419.61 323.122 419.403 323.527C419.022 324.277 418.413 324.886 417.664 325.267C417.259 325.474 416.775 325.586 416.073 325.643C415.367 325.701 414.472 325.701 413.25 325.701H297.427C296.205 325.701 295.31 325.701 294.604 325.643C293.902 325.586 293.419 325.474 293.014 325.267C292.265 324.886 291.656 324.277 291.274 323.527C291.068 323.122 290.955 322.639 290.898 321.937C290.84 321.231 290.84 320.336 290.84 319.114V306.535Z" fill="#1A1A1A"/>
<path d="M290.84 306.535C290.84 305.313 290.84 304.418 290.898 303.712C290.955 303.01 291.068 302.526 291.274 302.121C291.656 301.372 292.265 300.763 293.014 300.381C293.419 300.175 293.902 300.063 294.604 300.005C295.31 299.948 296.205 299.947 297.427 299.947H413.25C414.472 299.947 415.367 299.948 416.073 300.005C416.775 300.063 417.259 300.175 417.664 300.381C418.413 300.763 419.022 301.372 419.403 302.121C419.61 302.526 419.722 303.01 419.779 303.712C419.837 304.418 419.837 305.313 419.837 306.535V319.114C419.837 320.336 419.837 321.231 419.779 321.937C419.722 322.639 419.61 323.122 419.403 323.527C419.022 324.277 418.413 324.886 417.664 325.267C417.259 325.474 416.775 325.586 416.073 325.643C415.367 325.701 414.472 325.701 413.25 325.701H297.427C296.205 325.701 295.31 325.701 294.604 325.643C293.902 325.586 293.419 325.474 293.014 325.267C292.265 324.886 291.656 324.277 291.274 323.527C291.068 323.122 290.955 322.639 290.898 321.937C290.84 321.231 290.84 320.336 290.84 319.114V306.535Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M336.344 308.454V315.824H335.229V309.569H335.185L333.422 310.721V309.656L335.261 308.454H336.344ZM338.338 315.824V315.018L340.832 312.434C341.098 312.153 341.318 311.907 341.491 311.696C341.666 311.483 341.797 311.28 341.883 311.088C341.969 310.896 342.012 310.692 342.012 310.476C342.012 310.232 341.955 310.02 341.84 309.843C341.725 309.663 341.567 309.525 341.368 309.429C341.169 309.331 340.945 309.281 340.695 309.281C340.431 309.281 340.201 309.335 340.004 309.443C339.808 309.551 339.656 309.704 339.551 309.9C339.445 310.097 339.392 310.328 339.392 310.591H338.331C338.331 310.143 338.434 309.75 338.64 309.415C338.847 309.079 339.13 308.818 339.49 308.634C339.85 308.446 340.259 308.353 340.717 308.353C341.18 308.353 341.588 308.445 341.94 308.63C342.296 308.812 342.573 309.062 342.772 309.379C342.971 309.693 343.071 310.048 343.071 310.444C343.071 310.717 343.019 310.985 342.916 311.246C342.815 311.508 342.639 311.799 342.387 312.121C342.135 312.44 341.785 312.828 341.336 313.283L339.871 314.817V314.871H343.189V315.824H338.338ZM345.277 314.91C345.08 314.91 344.911 314.841 344.769 314.701C344.628 314.56 344.557 314.389 344.557 314.19C344.557 313.994 344.628 313.826 344.769 313.686C344.911 313.545 345.08 313.474 345.277 313.474C345.473 313.474 345.643 313.545 345.784 313.686C345.926 313.826 345.996 313.994 345.996 314.19C345.996 314.322 345.963 314.443 345.896 314.554C345.831 314.662 345.744 314.748 345.637 314.813C345.529 314.878 345.409 314.91 345.277 314.91ZM345.277 311.218C345.08 311.218 344.911 311.148 344.769 311.009C344.628 310.867 344.557 310.697 344.557 310.498C344.557 310.301 344.628 310.133 344.769 309.994C344.911 309.852 345.08 309.782 345.277 309.782C345.473 309.782 345.643 309.852 345.784 309.994C345.926 310.133 345.996 310.301 345.996 310.498C345.996 310.63 345.963 310.751 345.896 310.861C345.831 310.969 345.744 311.056 345.637 311.12C345.529 311.185 345.409 311.218 345.277 311.218ZM349.989 315.925C349.495 315.925 349.054 315.84 348.665 315.669C348.279 315.499 347.973 315.263 347.747 314.96C347.524 314.656 347.404 314.303 347.387 313.902H348.517C348.532 314.121 348.605 314.31 348.737 314.471C348.871 314.629 349.046 314.752 349.262 314.838C349.478 314.924 349.718 314.968 349.982 314.968C350.273 314.968 350.529 314.917 350.752 314.817C350.978 314.716 351.154 314.575 351.281 314.395C351.409 314.213 351.472 314.003 351.472 313.766C351.472 313.519 351.409 313.301 351.281 313.114C351.157 312.925 350.973 312.776 350.731 312.668C350.491 312.56 350.201 312.506 349.86 312.506H349.237V311.599H349.86C350.133 311.599 350.373 311.55 350.58 311.452C350.788 311.353 350.951 311.216 351.069 311.041C351.187 310.864 351.245 310.656 351.245 310.419C351.245 310.191 351.194 309.993 351.091 309.825C350.99 309.655 350.846 309.521 350.659 309.425C350.474 309.329 350.256 309.281 350.004 309.281C349.764 309.281 349.54 309.326 349.331 309.415C349.124 309.501 348.957 309.626 348.827 309.789C348.697 309.95 348.628 310.143 348.618 310.368H347.542C347.554 309.97 347.672 309.62 347.895 309.317C348.12 309.015 348.418 308.779 348.787 308.608C349.157 308.438 349.567 308.353 350.018 308.353C350.491 308.353 350.899 308.445 351.242 308.63C351.587 308.812 351.854 309.056 352.041 309.361C352.23 309.665 352.324 309.999 352.321 310.361C352.324 310.774 352.209 311.124 351.976 311.412C351.746 311.7 351.439 311.893 351.055 311.991V312.049C351.544 312.123 351.923 312.318 352.192 312.632C352.463 312.946 352.597 313.336 352.595 313.802C352.597 314.207 352.485 314.571 352.257 314.892C352.031 315.214 351.723 315.467 351.332 315.651C350.941 315.834 350.493 315.925 349.989 315.925ZM356.521 315.947C355.953 315.944 355.467 315.794 355.064 315.497C354.661 315.199 354.352 314.766 354.139 314.198C353.925 313.629 353.819 312.944 353.819 312.143C353.819 311.344 353.925 310.661 354.139 310.095C354.355 309.529 354.664 309.097 355.067 308.799C355.473 308.502 355.958 308.353 356.521 308.353C357.085 308.353 357.569 308.503 357.972 308.803C358.375 309.1 358.683 309.532 358.897 310.098C359.113 310.662 359.221 311.344 359.221 312.143C359.221 312.946 359.114 313.632 358.9 314.201C358.687 314.767 358.378 315.2 357.975 315.5C357.572 315.798 357.088 315.947 356.521 315.947ZM356.521 314.986C357.02 314.986 357.41 314.742 357.691 314.255C357.974 313.768 358.116 313.064 358.116 312.143C358.116 311.531 358.051 311.014 357.921 310.591C357.794 310.167 357.611 309.845 357.371 309.627C357.133 309.406 356.85 309.296 356.521 309.296C356.025 309.296 355.635 309.541 355.352 310.03C355.069 310.519 354.926 311.224 354.923 312.143C354.923 312.757 354.987 313.276 355.114 313.701C355.244 314.123 355.427 314.443 355.665 314.662C355.902 314.878 356.188 314.986 356.521 314.986ZM363.194 317.897V310.296H364.245V311.192H364.335C364.397 311.077 364.487 310.944 364.604 310.793C364.722 310.642 364.885 310.51 365.094 310.397C365.303 310.282 365.579 310.224 365.922 310.224C366.368 310.224 366.766 310.337 367.117 310.563C367.467 310.788 367.742 311.113 367.941 311.538C368.142 311.963 Line truncated
<path d="M290.84 341.699C290.84 340.477 290.84 339.582 290.898 338.876C290.955 338.174 291.068 337.691 291.274 337.286C291.656 336.537 292.265 335.928 293.014 335.546C293.419 335.34 293.902 335.227 294.604 335.17C295.31 335.112 296.205 335.112 297.427 335.112H413.25C414.472 335.112 415.367 335.112 416.073 335.17C416.775 335.227 417.259 335.34 417.664 335.546C418.413 335.928 419.022 336.537 419.403 337.286C419.61 337.691 419.722 338.174 419.779 338.876C419.837 339.582 419.837 340.477 419.837 341.699V354.278C419.837 355.501 419.837 356.396 419.779 357.102C419.722 357.804 419.61 358.287 419.403 358.692C419.022 359.441 418.413 360.05 417.664 360.432C417.259 360.638 416.775 360.751 416.073 360.808C415.367 360.866 414.472 360.866 413.25 360.866H297.427C296.205 360.866 295.31 360.866 294.604 360.808C293.902 360.751 293.419 360.638 293.014 360.432C292.265 360.05 291.656 359.441 291.274 358.692C291.068 358.287 290.955 357.804 290.898 357.102C290.84 356.396 290.84 355.501 290.84 354.278V341.699Z" fill="#1A1A1A"/>
<path d="M290.84 341.699C290.84 340.477 290.84 339.582 290.898 338.876C290.955 338.174 291.068 337.691 291.274 337.286C291.656 336.537 292.265 335.928 293.014 335.546C293.419 335.34 293.902 335.227 294.604 335.17C295.31 335.112 296.205 335.112 297.427 335.112H413.25C414.472 335.112 415.367 335.112 416.073 335.17C416.775 335.227 417.259 335.34 417.664 335.546C418.413 335.928 419.022 336.537 419.403 337.286C419.61 337.691 419.722 338.174 419.779 338.876C419.837 339.582 419.837 340.477 419.837 341.699V354.278C419.837 355.501 419.837 356.396 419.779 357.102C419.722 357.804 419.61 358.287 419.403 358.692C419.022 359.441 418.413 360.05 417.664 360.432C417.259 360.638 416.775 360.751 416.073 360.808C415.367 360.866 414.472 360.866 413.25 360.866H297.427C296.205 360.866 295.31 360.866 294.604 360.808C293.902 360.751 293.419 360.638 293.014 360.432C292.265 360.05 291.656 359.441 291.274 358.692C291.068 358.287 290.955 357.804 290.898 357.102C290.84 356.396 290.84 355.501 290.84 354.278V341.699Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M337.981 351.09C337.53 351.09 337.124 351.003 336.764 350.83C336.407 350.655 336.121 350.415 335.908 350.111C335.694 349.806 335.58 349.458 335.566 349.067H336.646C336.672 349.384 336.812 349.644 337.067 349.848C337.321 350.052 337.626 350.154 337.981 350.154C338.264 350.154 338.515 350.089 338.733 349.959C338.954 349.828 339.126 349.646 339.251 349.416C339.378 349.186 339.442 348.923 339.442 348.628C339.442 348.328 339.377 348.06 339.248 347.825C339.118 347.59 338.939 347.405 338.711 347.271C338.486 347.137 338.227 347.068 337.934 347.066C337.711 347.066 337.486 347.104 337.261 347.181C337.035 347.258 336.853 347.359 336.714 347.483L335.695 347.332L336.109 343.618H340.162V344.572H337.034L336.8 346.634H336.843C336.987 346.495 337.178 346.379 337.416 346.285C337.656 346.191 337.912 346.145 338.186 346.145C338.634 346.145 339.034 346.251 339.384 346.465C339.737 346.678 340.014 346.97 340.216 347.339C340.42 347.707 340.52 348.129 340.518 348.606C340.52 349.084 340.412 349.51 340.194 349.884C339.978 350.258 339.678 350.553 339.294 350.769C338.913 350.983 338.475 351.09 337.981 351.09ZM342.556 350.075C342.359 350.075 342.19 350.005 342.048 349.866C341.907 349.724 341.836 349.554 341.836 349.355C341.836 349.158 341.907 348.99 342.048 348.851C342.19 348.709 342.359 348.639 342.556 348.639C342.753 348.639 342.922 348.709 343.063 348.851C343.205 348.99 343.276 349.158 343.276 349.355C343.276 349.487 343.242 349.608 343.175 349.718C343.11 349.826 343.024 349.913 342.916 349.977C342.808 350.042 342.688 350.075 342.556 350.075ZM342.556 346.382C342.359 346.382 342.19 346.313 342.048 346.173C341.907 346.032 341.836 345.862 341.836 345.662C341.836 345.466 341.907 345.298 342.048 345.159C342.19 345.017 342.359 344.946 342.556 344.946C342.753 344.946 342.922 345.017 343.063 345.159C343.205 345.298 343.276 345.466 343.276 345.662C343.276 345.794 343.242 345.916 343.175 346.026C343.11 346.134 343.024 346.22 342.916 346.285C342.808 346.35 342.688 346.382 342.556 346.382ZM347.269 351.09C346.774 351.09 346.333 351.004 345.944 350.834C345.558 350.664 345.252 350.427 345.026 350.125C344.803 349.82 344.683 349.468 344.667 349.067H345.797C345.811 349.285 345.884 349.475 346.016 349.636C346.15 349.794 346.326 349.916 346.542 350.003C346.757 350.089 346.997 350.132 347.261 350.132C347.552 350.132 347.808 350.082 348.032 349.981C348.257 349.88 348.433 349.74 348.561 349.56C348.688 349.378 348.751 349.168 348.751 348.93C348.751 348.683 348.688 348.466 348.561 348.279C348.436 348.089 348.252 347.941 348.01 347.833C347.77 347.725 347.48 347.671 347.139 347.671H346.516V346.764H347.139C347.412 346.764 347.652 346.714 347.859 346.616C348.067 346.518 348.231 346.381 348.348 346.206C348.466 346.028 348.525 345.821 348.525 345.583C348.525 345.355 348.473 345.157 348.37 344.989C348.269 344.819 348.125 344.686 347.938 344.59C347.753 344.494 347.535 344.446 347.283 344.446C347.043 344.446 346.819 344.49 346.61 344.579C346.404 344.666 346.236 344.79 346.106 344.953C345.977 345.114 345.907 345.307 345.897 345.533H344.821C344.833 345.135 344.951 344.784 345.174 344.482C345.4 344.18 345.697 343.943 346.067 343.773C346.436 343.603 346.846 343.517 347.297 343.517C347.77 343.517 348.178 343.61 348.521 343.795C348.866 343.977 349.133 344.22 349.32 344.525C349.509 344.83 349.603 345.163 349.601 345.526C349.603 345.938 349.488 346.289 349.255 346.577C349.025 346.864 348.718 347.058 348.334 347.156V347.214C348.823 347.288 349.202 347.482 349.471 347.797C349.742 348.111 349.877 348.501 349.874 348.966C349.877 349.372 349.764 349.735 349.536 350.057C349.31 350.378 349.002 350.631 348.611 350.816C348.22 350.998 347.772 351.09 347.269 351.09ZM353.801 351.111C353.232 351.109 352.746 350.959 352.343 350.661C351.94 350.364 351.632 349.931 351.418 349.362C351.205 348.793 351.098 348.108 351.098 347.307C351.098 346.508 351.205 345.826 351.418 345.259C351.634 344.693 351.944 344.261 352.347 343.964C352.752 343.666 353.237 343.517 353.801 343.517C354.364 343.517 354.848 343.667 355.251 343.967C355.654 344.265 355.962 344.697 356.176 345.263C356.392 345.827 356.5 346.508 356.5 347.307C356.5 348.111 356.393 348.797 356.179 349.366C355.966 349.932 355.658 350.365 355.254 350.665C354.851 350.962 354.367 351.111 353.801 351.111ZM353.801 350.15C354.3 350.15 354.689 349.907 354.97 349.42C355.253 348.933 355.395 348.228 355.395 347.307C355.395 346.695 355.33 346.178 355.201 345.756C355.073 345.331 354.89 345.01 354.65 344.791C354.412 344.571 354.129 344.46 353.801 344.46C353.304 344.46 352.914 344.705 352.631 345.195C352.348 345.684 352.205 346.388 352.203 347.307C352.203 347.921 352.266 348.441 352.393 348.865C352.523 349.288 352.706 349.608 352.944 349.826C353.182 350.042 353.467 350.15 353.801 350.15ZM360.473 353.062V345.461H361.524V346.357H361.614C361.676 346.242 361.766 346.109 361.884 345.958C362.001 345.806 362.164 345.674 362.373 345.562C362.582 345.446 362.858 345.389 363.201 345.389C363.647 345.389 364.045 345.502 364.396 345.727C364.746 345.953 365.021 346Line truncated
<path d="M290.84 376.864C290.84 375.642 290.84 374.747 290.898 374.041C290.955 373.339 291.068 372.855 291.274 372.45C291.656 371.701 292.265 371.092 293.014 370.71C293.419 370.504 293.902 370.392 294.604 370.334C295.31 370.277 296.205 370.276 297.427 370.276H413.25C414.472 370.276 415.367 370.277 416.073 370.334C416.775 370.392 417.259 370.504 417.664 370.71C418.413 371.092 419.022 371.701 419.403 372.45C419.61 372.855 419.722 373.339 419.779 374.041C419.837 374.747 419.837 375.642 419.837 376.864V389.443C419.837 390.665 419.837 391.56 419.779 392.266C419.722 392.968 419.61 393.452 419.403 393.857C419.022 394.606 418.413 395.215 417.664 395.596C417.259 395.803 416.775 395.915 416.073 395.972C415.367 396.03 414.472 396.03 413.25 396.03H297.427C296.205 396.03 295.31 396.03 294.604 395.972C293.902 395.915 293.419 395.803 293.014 395.596C292.265 395.215 291.656 394.606 291.274 393.857C291.068 393.452 290.955 392.968 290.898 392.266C290.84 391.56 290.84 390.665 290.84 389.443V376.864Z" fill="#1A1A1A"/>
<path d="M290.84 376.864C290.84 375.642 290.84 374.747 290.898 374.041C290.955 373.339 291.068 372.855 291.274 372.45C291.656 371.701 292.265 371.092 293.014 370.71C293.419 370.504 293.902 370.392 294.604 370.334C295.31 370.277 296.205 370.276 297.427 370.276H413.25C414.472 370.276 415.367 370.277 416.073 370.334C416.775 370.392 417.259 370.504 417.664 370.71C418.413 371.092 419.022 371.701 419.403 372.45C419.61 372.855 419.722 373.339 419.779 374.041C419.837 374.747 419.837 375.642 419.837 376.864V389.443C419.837 390.665 419.837 391.56 419.779 392.266C419.722 392.968 419.61 393.452 419.403 393.857C419.022 394.606 418.413 395.215 417.664 395.596C417.259 395.803 416.775 395.915 416.073 395.972C415.367 396.03 414.472 396.03 413.25 396.03H297.427C296.205 396.03 295.31 396.03 294.604 395.972C293.902 395.915 293.419 395.803 293.014 395.596C292.265 395.215 291.656 394.606 291.274 393.857C291.068 393.452 290.955 392.968 290.898 392.266C290.84 391.56 290.84 390.665 290.84 389.443V376.864Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M338.121 386.254C337.792 386.249 337.468 386.189 337.149 386.074C336.833 385.959 336.545 385.767 336.286 385.498C336.027 385.23 335.819 384.869 335.663 384.415C335.509 383.962 335.433 383.395 335.433 382.716C335.433 382.073 335.496 381.502 335.623 381.003C335.753 380.504 335.939 380.083 336.181 379.74C336.424 379.395 336.716 379.132 337.059 378.952C337.402 378.772 337.788 378.682 338.215 378.682C338.654 378.682 339.044 378.768 339.384 378.941C339.725 379.114 340.001 379.353 340.212 379.657C340.426 379.962 340.561 380.309 340.619 380.697H339.521C339.447 380.39 339.299 380.14 339.078 379.945C338.858 379.751 338.57 379.654 338.215 379.654C337.675 379.654 337.254 379.889 336.951 380.359C336.652 380.829 336.5 381.483 336.498 382.32H336.552C336.679 382.112 336.835 381.934 337.02 381.788C337.207 381.639 337.416 381.525 337.646 381.446C337.879 381.364 338.123 381.324 338.38 381.324C338.807 381.324 339.194 381.428 339.539 381.637C339.887 381.843 340.164 382.129 340.37 382.493C340.577 382.858 340.68 383.275 340.68 383.746C340.68 384.216 340.573 384.642 340.36 385.023C340.148 385.405 339.851 385.707 339.467 385.93C339.083 386.151 338.634 386.259 338.121 386.254ZM338.117 385.318C338.401 385.318 338.654 385.249 338.877 385.11C339.1 384.97 339.276 384.783 339.406 384.548C339.535 384.313 339.6 384.05 339.6 383.76C339.6 383.477 339.537 383.219 339.409 382.986C339.285 382.754 339.112 382.569 338.891 382.432C338.673 382.295 338.423 382.227 338.143 382.227C337.929 382.227 337.731 382.268 337.549 382.349C337.369 382.431 337.211 382.544 337.074 382.688C336.937 382.832 336.829 382.997 336.75 383.184C336.673 383.369 336.635 383.565 336.635 383.771C336.635 384.047 336.698 384.302 336.825 384.537C336.955 384.773 337.131 384.962 337.354 385.106C337.58 385.248 337.834 385.318 338.117 385.318ZM342.714 385.239C342.517 385.239 342.348 385.17 342.207 385.03C342.065 384.889 341.994 384.719 341.994 384.519C341.994 384.323 342.065 384.155 342.207 384.016C342.348 383.874 342.517 383.803 342.714 383.803C342.911 383.803 343.08 383.874 343.222 384.016C343.363 384.155 343.434 384.323 343.434 384.519C343.434 384.651 343.4 384.773 343.333 384.883C343.268 384.991 343.182 385.077 343.074 385.142C342.966 385.207 342.846 385.239 342.714 385.239ZM342.714 381.547C342.517 381.547 342.348 381.477 342.207 381.338C342.065 381.196 341.994 381.026 341.994 380.827C341.994 380.63 342.065 380.462 342.207 380.323C342.348 380.182 342.517 380.111 342.714 380.111C342.911 380.111 343.08 380.182 343.222 380.323C343.363 380.462 343.434 380.63 343.434 380.827C343.434 380.959 343.4 381.08 343.333 381.19C343.268 381.298 343.182 381.385 343.074 381.45C342.966 381.514 342.846 381.547 342.714 381.547ZM347.427 386.254C346.933 386.254 346.491 386.169 346.103 385.999C345.716 385.828 345.41 385.592 345.185 385.29C344.962 384.985 344.842 384.632 344.825 384.232H345.955C345.969 384.45 346.043 384.639 346.174 384.8C346.309 384.958 346.484 385.081 346.7 385.167C346.916 385.254 347.156 385.297 347.42 385.297C347.71 385.297 347.967 385.246 348.19 385.146C348.415 385.045 348.592 384.905 348.719 384.725C348.846 384.542 348.91 384.332 348.91 384.095C348.91 383.848 348.846 383.63 348.719 383.443C348.594 383.254 348.411 383.105 348.168 382.997C347.928 382.889 347.638 382.835 347.297 382.835H346.675V381.928H347.297C347.571 381.928 347.811 381.879 348.017 381.781C348.226 381.682 348.389 381.546 348.507 381.37C348.624 381.193 348.683 380.985 348.683 380.748C348.683 380.52 348.631 380.322 348.528 380.154C348.427 379.984 348.283 379.85 348.096 379.754C347.912 379.659 347.693 379.611 347.441 379.611C347.201 379.611 346.977 379.655 346.768 379.744C346.562 379.83 346.394 379.955 346.264 380.118C346.135 380.279 346.065 380.472 346.056 380.697H344.98C344.992 380.299 345.109 379.949 345.332 379.647C345.558 379.344 345.855 379.108 346.225 378.938C346.594 378.767 347.005 378.682 347.456 378.682C347.928 378.682 348.336 378.774 348.679 378.959C349.025 379.141 349.291 379.385 349.478 379.69C349.668 379.994 349.761 380.328 349.759 380.69C349.761 381.103 349.646 381.453 349.413 381.741C349.183 382.029 348.876 382.222 348.492 382.32V382.378C348.982 382.452 349.361 382.647 349.629 382.961C349.901 383.275 350.035 383.665 350.032 384.131C350.035 384.536 349.922 384.9 349.694 385.221C349.469 385.543 349.16 385.796 348.769 385.981C348.378 386.163 347.931 386.254 347.427 386.254ZM353.959 386.276C353.39 386.273 352.904 386.123 352.501 385.826C352.098 385.528 351.79 385.095 351.576 384.527C351.363 383.958 351.256 383.273 351.256 382.472C351.256 381.673 351.363 380.99 351.576 380.424C351.792 379.858 352.102 379.426 352.505 379.128C352.91 378.831 353.395 378.682 353.959 378.682C354.523 378.682 355.006 378.832 355.409 379.132C355.812 379.429 356.121 379.861 356.334 380.427C356.55 380.991 356.658 381.673 356.658 382.472C356.658 383.275 356.551 383.962 356.338 384.53C356.124 385.096 355.816 385.53 355.413 385.829C355.01 386.127 354.525 386.276 353.959 386.276ZM353.959 385.315C354.458 385.315 354.848 385.071 355Line truncated
<path d="M482.626 69.2096V68.2503H487.666V69.2096H485.714V74.5679H484.578V69.2096H482.626ZM492.62 68.2503H493.765V72.3777C493.765 72.8301 493.658 73.2281 493.444 73.5715C493.232 73.9149 492.934 74.1833 492.549 74.3766C492.165 74.5679 491.716 74.6635 491.201 74.6635C490.685 74.6635 490.235 74.5679 489.85 74.3766C489.466 74.1833 489.168 73.9149 488.956 73.5715C488.744 73.2281 488.638 72.8301 488.638 72.3777V68.2503H489.782V72.2821C489.782 72.5453 489.84 72.7797 489.955 72.9854C490.072 73.191 490.237 73.3525 490.449 73.4697C490.661 73.5849 490.911 73.6424 491.201 73.6424C491.491 73.6424 491.742 73.5849 491.954 73.4697C492.168 73.3525 492.332 73.191 492.448 72.9854C492.563 72.7797 492.62 72.5453 492.62 72.2821V68.2503ZM495.009 74.5679V68.2503H499.118V69.2096H496.153V70.9248H498.905V71.8841H496.153V73.6085H499.142V74.5679H495.009Z" fill="#A5A5A5"/>
<path d="M505.547 74.5679V73.7412L507.74 71.5911C507.95 71.3793 508.125 71.1911 508.264 71.0266C508.404 70.862 508.509 70.7027 508.579 70.5484C508.649 70.3942 508.684 70.2297 508.684 70.0549C508.684 69.8554 508.639 69.6847 508.548 69.5428C508.458 69.3988 508.333 69.2878 508.175 69.2096C508.017 69.1315 507.837 69.0924 507.635 69.0924C507.427 69.0924 507.245 69.1356 507.089 69.222C506.933 69.3063 506.812 69.4266 506.725 69.5829C506.641 69.7392 506.599 69.9253 506.599 70.1412H505.51C505.51 69.7402 505.601 69.3916 505.784 69.0955C505.967 68.7994 506.219 68.5701 506.54 68.4076C506.863 68.2451 507.233 68.1639 507.651 68.1639C508.074 68.1639 508.446 68.2431 508.767 68.4014C509.088 68.5598 509.337 68.7767 509.514 69.0523C509.693 69.3279 509.782 69.6425 509.782 69.9962C509.782 70.2327 509.737 70.4651 509.646 70.6934C509.556 70.9217 509.397 71.1746 509.168 71.4523C508.942 71.7299 508.624 72.0661 508.215 72.461L507.126 73.5684V73.6116H509.878V74.5679H505.547ZM513.512 68.2503V74.5679H512.367V69.3639H512.33L510.853 70.3078V69.259L512.423 68.2503H513.512Z" fill="#A5A5A5"/>
<path d="M435.039 95.5475C435.039 94.3253 435.039 93.4304 435.097 92.7243C435.155 92.0224 435.267 91.539 435.473 91.134C435.855 90.3848 436.464 89.7757 437.213 89.394C437.618 89.1877 438.101 89.0754 438.803 89.0181C439.509 88.9604 440.404 88.9601 441.627 88.9601H557.449C558.671 88.9601 559.566 88.9604 560.272 89.0181C560.974 89.0754 561.458 89.1877 561.863 89.394C562.612 89.7757 563.221 90.3848 563.603 91.134C563.809 91.539 563.921 92.0224 563.979 92.7243C564.036 93.4304 564.037 94.3253 564.037 95.5475V108.127C564.037 109.349 564.036 110.244 563.979 110.95C563.921 111.652 563.809 112.135 563.603 112.54C563.221 113.289 562.612 113.898 561.863 114.28C561.458 114.486 560.974 114.599 560.272 114.656C559.566 114.714 558.671 114.714 557.449 114.714H441.627C440.404 114.714 439.509 114.714 438.803 114.656C438.101 114.599 437.618 114.486 437.213 114.28C436.464 113.898 435.855 113.289 435.473 112.54C435.267 112.135 435.155 111.652 435.097 110.95C435.039 110.244 435.039 109.349 435.039 108.127V95.5475Z" fill="#1A1A1A"/>
<path d="M435.039 95.5475C435.039 94.3253 435.039 93.4304 435.097 92.7243C435.155 92.0224 435.267 91.539 435.473 91.134C435.855 90.3848 436.464 89.7757 437.213 89.394C437.618 89.1877 438.101 89.0754 438.803 89.0181C439.509 88.9604 440.404 88.9601 441.627 88.9601H557.449C558.671 88.9601 559.566 88.9604 560.272 89.0181C560.974 89.0754 561.458 89.1877 561.863 89.394C562.612 89.7757 563.221 90.3848 563.603 91.134C563.809 91.539 563.921 92.0224 563.979 92.7243C564.036 93.4304 564.037 94.3253 564.037 95.5475V108.127C564.037 109.349 564.036 110.244 563.979 110.95C563.921 111.652 563.809 112.135 563.603 112.54C563.221 113.289 562.612 113.898 561.863 114.28C561.458 114.486 560.974 114.599 560.272 114.656C559.566 114.714 558.671 114.714 557.449 114.714H441.627C440.404 114.714 439.509 114.714 438.803 114.656C438.101 114.599 437.618 114.486 437.213 114.28C436.464 113.898 435.855 113.289 435.473 112.54C435.267 112.135 435.155 111.652 435.097 110.95C435.039 110.244 435.039 109.349 435.039 108.127V95.5475Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M482.698 97.3656C483.027 97.368 483.351 97.428 483.67 97.5456C483.989 97.6631 484.277 97.8551 484.533 98.1214C484.792 98.3877 484.999 98.7476 485.152 99.201C485.308 99.6521 485.387 100.214 485.39 100.885C485.39 101.531 485.325 102.105 485.196 102.609C485.066 103.111 484.88 103.534 484.638 103.88C484.398 104.225 484.106 104.488 483.763 104.668C483.42 104.848 483.034 104.938 482.604 104.938C482.165 104.938 481.775 104.851 481.435 104.679C481.094 104.506 480.817 104.267 480.603 103.962C480.39 103.655 480.257 103.303 480.204 102.904H481.302C481.374 103.221 481.52 103.478 481.741 103.674C481.964 103.869 482.252 103.966 482.604 103.966C483.144 103.966 483.565 103.731 483.868 103.261C484.17 102.788 484.322 102.128 484.325 101.281H484.267C484.142 101.488 483.986 101.665 483.799 101.814C483.614 101.963 483.407 102.078 483.177 102.159C482.946 102.241 482.7 102.282 482.439 102.282C482.014 102.282 481.628 102.177 481.28 101.969C480.932 101.76 480.655 101.473 480.449 101.108C480.242 100.744 480.139 100.328 480.139 99.8596C480.139 99.3942 480.245 98.9719 480.456 98.5928C480.669 98.2138 480.967 97.9138 481.348 97.6931C481.732 97.47 482.182 97.3608 482.698 97.3656ZM482.702 98.3013C482.421 98.3013 482.168 98.3709 481.942 98.5101C481.719 98.6468 481.543 98.8328 481.413 99.0679C481.284 99.3006 481.219 99.5597 481.219 99.8452C481.219 100.131 481.281 100.39 481.406 100.623C481.533 100.853 481.706 101.036 481.924 101.173C482.145 101.308 482.397 101.375 482.68 101.375C482.891 101.375 483.088 101.334 483.27 101.252C483.452 101.171 483.612 101.058 483.749 100.914C483.886 100.768 483.992 100.602 484.069 100.417C484.146 100.233 484.184 100.038 484.184 99.8345C484.184 99.5633 484.119 99.3114 483.99 99.0787C483.863 98.846 483.688 98.6588 483.464 98.5173C483.241 98.3733 482.987 98.3013 482.702 98.3013ZM487.413 103.923C487.217 103.923 487.047 103.853 486.906 103.714C486.764 103.573 486.694 103.402 486.694 103.203C486.694 103.006 486.764 102.838 486.906 102.699C487.047 102.558 487.217 102.487 487.413 102.487C487.61 102.487 487.779 102.558 487.921 102.699C488.062 102.838 488.133 103.006 488.133 103.203C488.133 103.335 488.1 103.456 488.032 103.567C487.968 103.674 487.881 103.761 487.773 103.826C487.665 103.89 487.545 103.923 487.413 103.923ZM487.413 100.23C487.217 100.23 487.047 100.161 486.906 100.022C486.764 99.88 486.694 99.7097 486.694 99.5106C486.694 99.3138 486.764 99.1459 486.906 99.0067C487.047 98.8652 487.217 98.7944 487.413 98.7944C487.61 98.7944 487.779 98.8652 487.921 99.0067C488.062 99.1459 488.133 99.3138 488.133 99.5106C488.133 99.6425 488.1 99.7637 488.032 99.874C487.968 99.982 487.881 100.068 487.773 100.133C487.665 100.198 487.545 100.23 487.413 100.23ZM492.126 104.938C491.632 104.938 491.19 104.853 490.802 104.682C490.415 104.512 490.11 104.275 489.884 103.973C489.661 103.668 489.541 103.316 489.524 102.915H490.654C490.669 103.133 490.742 103.323 490.874 103.484C491.008 103.642 491.183 103.764 491.399 103.851C491.615 103.937 491.855 103.98 492.119 103.98C492.409 103.98 492.666 103.93 492.889 103.829C493.115 103.728 493.291 103.588 493.418 103.408C493.545 103.226 493.609 103.016 493.609 102.778C493.609 102.531 493.545 102.314 493.418 102.127C493.293 101.937 493.11 101.789 492.867 101.681C492.628 101.573 492.337 101.519 491.997 101.519H491.374V100.612H491.997C492.27 100.612 492.51 100.563 492.716 100.464C492.925 100.366 493.088 100.229 493.206 100.054C493.323 99.8764 493.382 99.6689 493.382 99.4314C493.382 99.2034 493.331 99.0055 493.227 98.8376C493.127 98.6672 492.983 98.5341 492.796 98.4381C492.611 98.3421 492.392 98.2941 492.141 98.2941C491.901 98.2941 491.676 98.3385 491.468 98.4273C491.261 98.5137 491.093 98.6384 490.964 98.8016C490.834 98.9623 490.765 99.1555 490.755 99.381H489.679C489.691 98.9827 489.808 98.6324 490.032 98.3301C490.257 98.0278 490.555 97.7915 490.924 97.6211C491.294 97.4508 491.704 97.3656 492.155 97.3656C492.628 97.3656 493.035 97.458 493.379 97.6427C493.724 97.8251 493.99 98.0686 494.177 98.3733C494.367 98.678 494.461 99.0115 494.458 99.3738C494.461 99.7865 494.345 100.137 494.113 100.425C493.882 100.713 493.575 100.906 493.191 101.004V101.062C493.681 101.136 494.06 101.33 494.329 101.645C494.6 101.959 494.734 102.349 494.732 102.814C494.734 103.22 494.621 103.583 494.393 103.905C494.168 104.226 493.86 104.479 493.468 104.664C493.077 104.847 492.63 104.938 492.126 104.938ZM498.658 104.959C498.089 104.957 497.604 104.807 497.201 104.509C496.797 104.212 496.489 103.779 496.276 103.21C496.062 102.642 495.955 101.957 495.955 101.155C495.955 100.356 496.062 99.6737 496.276 99.1075C496.492 98.5413 496.801 98.1094 497.204 97.8119C497.61 97.5144 498.094 97.3656 498.658 97.3656C499.222 97.3656 499.705 97.5156 500.108 97.8155C500.512 98.113 500.82 98.5449 501.033 99.1111C501.249 99.6749 501.357 100.356 501.357 101.155C501.357 101.959 501.251 102.645 501.037 103.214C500.823 103.78 500.515 104.213 500.112 104.513C499.709 104.811 499.224 104.959 498.658 104.959ZM498.658 103.998C499.157 103.998 499.54Line truncated
<path d="M435.039 130.712C435.039 129.49 435.039 128.595 435.097 127.889C435.155 127.187 435.267 126.704 435.473 126.299C435.855 125.549 436.464 124.94 437.213 124.559C437.618 124.352 438.101 124.24 438.803 124.183C439.509 124.125 440.404 124.125 441.627 124.125H557.449C558.671 124.125 559.566 124.125 560.272 124.183C560.974 124.24 561.458 124.352 561.863 124.559C562.612 124.94 563.221 125.549 563.603 126.299C563.809 126.704 563.921 127.187 563.979 127.889C564.036 128.595 564.037 129.49 564.037 130.712V143.291C564.037 144.513 564.036 145.408 563.979 146.114C563.921 146.816 563.809 147.3 563.603 147.705C563.221 148.454 562.612 149.063 561.863 149.445C561.458 149.651 560.974 149.763 560.272 149.821C559.566 149.878 558.671 149.879 557.449 149.879H441.627C440.404 149.879 439.509 149.878 438.803 149.821C438.101 149.763 437.618 149.651 437.213 149.445C436.464 149.063 435.855 148.454 435.473 147.705C435.267 147.3 435.155 146.816 435.097 146.114C435.039 145.408 435.039 144.513 435.039 143.291V130.712Z" fill="#1A1A1A"/>
<path d="M435.039 130.712C435.039 129.49 435.039 128.595 435.097 127.889C435.155 127.187 435.267 126.704 435.473 126.299C435.855 125.549 436.464 124.94 437.213 124.559C437.618 124.352 438.101 124.24 438.803 124.183C439.509 124.125 440.404 124.125 441.627 124.125H557.449C558.671 124.125 559.566 124.125 560.272 124.183C560.974 124.24 561.458 124.352 561.863 124.559C562.612 124.94 563.221 125.549 563.603 126.299C563.809 126.704 563.921 127.187 563.979 127.889C564.036 128.595 564.037 129.49 564.037 130.712V143.291C564.037 144.513 564.036 145.408 563.979 146.114C563.921 146.816 563.809 147.3 563.603 147.705C563.221 148.454 562.612 149.063 561.863 149.445C561.458 149.651 560.974 149.763 560.272 149.821C559.566 149.878 558.671 149.879 557.449 149.879H441.627C440.404 149.879 439.509 149.878 438.803 149.821C438.101 149.763 437.618 149.651 437.213 149.445C436.464 149.063 435.855 148.454 435.473 147.705C435.267 147.3 435.155 146.816 435.097 146.114C435.039 145.408 435.039 144.513 435.039 143.291V130.712Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M480.543 132.631V140.001H479.428V133.747H479.385L477.621 134.898V133.833L479.46 132.631H480.543ZM485.103 140.124C484.535 140.121 484.049 139.971 483.646 139.674C483.243 139.376 482.934 138.943 482.721 138.375C482.507 137.806 482.4 137.121 482.4 136.32C482.4 135.521 482.507 134.838 482.721 134.272C482.937 133.706 483.246 133.274 483.649 132.976C484.055 132.679 484.539 132.53 485.103 132.53C485.667 132.53 486.15 132.68 486.554 132.98C486.957 133.278 487.265 133.709 487.478 134.276C487.694 134.839 487.802 135.521 487.802 136.32C487.802 137.124 487.696 137.81 487.482 138.378C487.269 138.945 486.96 139.378 486.557 139.678C486.154 139.975 485.669 140.124 485.103 140.124ZM485.103 139.163C485.602 139.163 485.992 138.919 486.273 138.432C486.556 137.945 486.698 137.241 486.698 136.32C486.698 135.708 486.633 135.191 486.503 134.769C486.376 134.344 486.192 134.023 485.953 133.804C485.715 133.583 485.432 133.473 485.103 133.473C484.607 133.473 484.217 133.718 483.934 134.207C483.65 134.697 483.508 135.401 483.505 136.32C483.505 136.934 483.569 137.453 483.696 137.878C483.826 138.3 484.009 138.621 484.247 138.839C484.484 139.055 484.77 139.163 485.103 139.163ZM489.802 139.087C489.606 139.087 489.437 139.018 489.295 138.879C489.153 138.737 489.083 138.567 489.083 138.368C489.083 138.171 489.153 138.003 489.295 137.864C489.437 137.722 489.606 137.651 489.802 137.651C489.999 137.651 490.168 137.722 490.31 137.864C490.451 138.003 490.522 138.171 490.522 138.368C490.522 138.5 490.489 138.621 490.421 138.731C490.357 138.839 490.27 138.925 490.162 138.99C490.054 139.055 489.934 139.087 489.802 139.087ZM489.802 135.395C489.606 135.395 489.437 135.325 489.295 135.186C489.153 135.045 489.083 134.874 489.083 134.675C489.083 134.478 489.153 134.31 489.295 134.171C489.437 134.03 489.606 133.959 489.802 133.959C489.999 133.959 490.168 134.03 490.31 134.171C490.451 134.31 490.522 134.478 490.522 134.675C490.522 134.807 490.489 134.928 490.421 135.039C490.357 135.147 490.27 135.233 490.162 135.298C490.054 135.362 489.934 135.395 489.802 135.395ZM494.515 140.124C493.947 140.121 493.461 139.971 493.058 139.674C492.655 139.376 492.346 138.943 492.133 138.375C491.919 137.806 491.812 137.121 491.812 136.32C491.812 135.521 491.919 134.838 492.133 134.272C492.349 133.706 492.658 133.274 493.061 132.976C493.467 132.679 493.951 132.53 494.515 132.53C495.079 132.53 495.562 132.68 495.966 132.98C496.369 133.278 496.677 133.709 496.89 134.276C497.106 134.839 497.214 135.521 497.214 136.32C497.214 137.124 497.108 137.81 496.894 138.378C496.681 138.945 496.372 139.378 495.969 139.678C495.566 139.975 495.081 140.124 494.515 140.124ZM494.515 139.163C495.014 139.163 495.404 138.919 495.685 138.432C495.968 137.945 496.11 137.241 496.11 136.32C496.11 135.708 496.045 135.191 495.915 134.769C495.788 134.344 495.604 134.023 495.365 133.804C495.127 133.583 494.844 133.473 494.515 133.473C494.019 133.473 493.629 133.718 493.346 134.207C493.062 134.697 492.92 135.401 492.917 136.32C492.917 136.934 492.981 137.453 493.108 137.878C493.238 138.3 493.421 138.621 493.659 138.839C493.896 139.055 494.182 139.163 494.515 139.163ZM501.057 140.124C500.488 140.121 500.003 139.971 499.6 139.674C499.196 139.376 498.888 138.943 498.675 138.375C498.461 137.806 498.354 137.121 498.354 136.32C498.354 135.521 498.461 134.838 498.675 134.272C498.891 133.706 499.2 133.274 499.603 132.976C500.009 132.679 500.493 132.53 501.057 132.53C501.621 132.53 502.104 132.68 502.507 132.98C502.911 133.278 503.219 133.709 503.432 134.276C503.648 134.839 503.756 135.521 503.756 136.32C503.756 137.124 503.649 137.81 503.436 138.378C503.222 138.945 502.914 139.378 502.511 139.678C502.108 139.975 501.623 140.124 501.057 140.124ZM501.057 139.163C501.556 139.163 501.946 138.919 502.227 138.432C502.51 137.945 502.651 137.241 502.651 136.32C502.651 135.708 502.587 135.191 502.457 134.769C502.33 134.344 502.146 134.023 501.906 133.804C501.669 133.583 501.386 133.473 501.057 133.473C500.56 133.473 500.171 133.718 499.887 134.207C499.604 134.697 499.462 135.401 499.459 136.32C499.459 136.934 499.523 137.453 499.65 137.878C499.779 138.3 499.963 138.621 500.201 138.839C500.438 139.055 500.724 139.163 501.057 139.163ZM509.338 140.124C508.988 140.124 508.671 140.059 508.388 139.929C508.105 139.798 507.881 139.607 507.715 139.357C507.552 139.108 507.47 138.802 507.47 138.44C507.47 138.128 507.53 137.871 507.65 137.669C507.77 137.468 507.932 137.308 508.136 137.191C508.34 137.073 508.568 136.984 508.82 136.924C509.072 136.864 509.329 136.819 509.59 136.788C509.921 136.749 510.19 136.718 510.396 136.694C510.603 136.668 510.753 136.626 510.846 136.568C510.94 136.511 510.986 136.417 510.986 136.287V136.262C510.986 135.948 510.898 135.704 510.72 135.532C510.545 135.359 510.283 135.273 509.936 135.273C509.573 135.273 509.288 135.353 509.079 135.514C508.873 135.672 508.73 135.848 508.651 136.043L507.639 135.812C507.759 135.476 507.935 135.205 508.165 134.999C508.398 134.79 508.665 134.639 508.967 134.546C50Line truncated
<path d="M435.039 165.877C435.039 164.654 435.039 163.759 435.097 163.053C435.155 162.351 435.267 161.868 435.473 161.463C435.855 160.714 436.464 160.105 437.213 159.723C437.618 159.517 438.101 159.405 438.803 159.347C439.509 159.289 440.404 159.289 441.627 159.289H557.449C558.671 159.289 559.566 159.289 560.272 159.347C560.974 159.405 561.458 159.517 561.863 159.723C562.612 160.105 563.221 160.714 563.603 161.463C563.809 161.868 563.921 162.351 563.979 163.053C564.036 163.759 564.037 164.654 564.037 165.877V178.456C564.037 179.678 564.036 180.573 563.979 181.279C563.921 181.981 563.809 182.464 563.603 182.869C563.221 183.618 562.612 184.227 561.863 184.609C561.458 184.816 560.974 184.928 560.272 184.985C559.566 185.043 558.671 185.043 557.449 185.043H441.627C440.404 185.043 439.509 185.043 438.803 184.985C438.101 184.928 437.618 184.816 437.213 184.609C436.464 184.227 435.855 183.618 435.473 182.869C435.267 182.464 435.155 181.981 435.097 181.279C435.039 180.573 435.039 179.678 435.039 178.456V165.877Z" fill="#1A1A1A"/>
<path d="M435.039 165.877C435.039 164.654 435.039 163.759 435.097 163.053C435.155 162.351 435.267 161.868 435.473 161.463C435.855 160.714 436.464 160.105 437.213 159.723C437.618 159.517 438.101 159.405 438.803 159.347C439.509 159.289 440.404 159.289 441.627 159.289H557.449C558.671 159.289 559.566 159.289 560.272 159.347C560.974 159.405 561.458 159.517 561.863 159.723C562.612 160.105 563.221 160.714 563.603 161.463C563.809 161.868 563.921 162.351 563.979 163.053C564.036 163.759 564.037 164.654 564.037 165.877V178.456C564.037 179.678 564.036 180.573 563.979 181.279C563.921 181.981 563.809 182.464 563.603 182.869C563.221 183.618 562.612 184.227 561.863 184.609C561.458 184.816 560.974 184.928 560.272 184.985C559.566 185.043 558.671 185.043 557.449 185.043H441.627C440.404 185.043 439.509 185.043 438.803 184.985C438.101 184.928 437.618 184.816 437.213 184.609C436.464 184.227 435.855 183.618 435.473 182.869C435.267 182.464 435.155 181.981 435.097 181.279C435.039 180.573 435.039 179.678 435.039 178.456V165.877Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M480.543 167.795V175.166H479.428V168.911H479.385L477.621 170.063V168.998L479.46 167.795H480.543ZM485.103 175.288C484.535 175.286 484.049 175.136 483.646 174.839C483.243 174.541 482.934 174.108 482.721 173.539C482.507 172.971 482.4 172.286 482.4 171.484C482.4 170.685 482.507 170.003 482.721 169.437C482.937 168.87 483.246 168.438 483.649 168.141C484.055 167.843 484.539 167.695 485.103 167.695C485.667 167.695 486.15 167.845 486.554 168.145C486.957 168.442 487.265 168.874 487.478 169.44C487.694 170.004 487.802 170.685 487.802 171.484C487.802 172.288 487.696 172.974 487.482 173.543C487.269 174.109 486.96 174.542 486.557 174.842C486.154 175.14 485.669 175.288 485.103 175.288ZM485.103 174.327C485.602 174.327 485.992 174.084 486.273 173.597C486.556 173.11 486.698 172.406 486.698 171.484C486.698 170.873 486.633 170.355 486.503 169.933C486.376 169.509 486.192 169.187 485.953 168.969C485.715 168.748 485.432 168.638 485.103 168.638C484.607 168.638 484.217 168.882 483.934 169.372C483.65 169.861 483.508 170.565 483.505 171.484C483.505 172.099 483.569 172.618 483.696 173.043C483.826 173.465 484.009 173.785 484.247 174.004C484.484 174.22 484.77 174.327 485.103 174.327ZM489.802 174.252C489.606 174.252 489.437 174.182 489.295 174.043C489.153 173.902 489.083 173.731 489.083 173.532C489.083 173.335 489.153 173.167 489.295 173.028C489.437 172.887 489.606 172.816 489.802 172.816C489.999 172.816 490.168 172.887 490.31 173.028C490.451 173.167 490.522 173.335 490.522 173.532C490.522 173.664 490.489 173.785 490.421 173.896C490.357 174.004 490.27 174.09 490.162 174.155C490.054 174.22 489.934 174.252 489.802 174.252ZM489.802 170.559C489.606 170.559 489.437 170.49 489.295 170.351C489.153 170.209 489.083 170.039 489.083 169.84C489.083 169.643 489.153 169.475 489.295 169.336C489.437 169.194 489.606 169.123 489.802 169.123C489.999 169.123 490.168 169.194 490.31 169.336C490.451 169.475 490.522 169.643 490.522 169.84C490.522 169.972 490.489 170.093 490.421 170.203C490.357 170.311 490.27 170.397 490.162 170.462C490.054 170.527 489.934 170.559 489.802 170.559ZM494.515 175.267C494.021 175.267 493.579 175.182 493.191 175.011C492.805 174.841 492.499 174.605 492.273 174.302C492.05 173.998 491.93 173.645 491.913 173.244H493.043C493.058 173.463 493.131 173.652 493.263 173.813C493.397 173.971 493.572 174.094 493.788 174.18C494.004 174.266 494.244 174.309 494.508 174.309C494.798 174.309 495.055 174.259 495.278 174.158C495.504 174.058 495.68 173.917 495.807 173.737C495.934 173.555 495.998 173.345 495.998 173.107C495.998 172.86 495.934 172.643 495.807 172.456C495.682 172.267 495.499 172.118 495.257 172.01C495.017 171.902 494.726 171.848 494.386 171.848H493.763V170.941H494.386C494.659 170.941 494.899 170.892 495.105 170.793C495.314 170.695 495.477 170.558 495.595 170.383C495.712 170.206 495.771 169.998 495.771 169.76C495.771 169.533 495.72 169.335 495.616 169.167C495.516 168.996 495.372 168.863 495.185 168.767C495 168.671 494.782 168.623 494.53 168.623C494.29 168.623 494.065 168.668 493.857 168.756C493.65 168.843 493.482 168.968 493.353 169.131C493.223 169.291 493.154 169.485 493.144 169.71H492.068C492.08 169.312 492.198 168.962 492.421 168.659C492.646 168.357 492.944 168.121 493.313 167.95C493.683 167.78 494.093 167.695 494.544 167.695C495.017 167.695 495.425 167.787 495.768 167.972C496.113 168.154 496.379 168.398 496.567 168.702C496.756 169.007 496.85 169.341 496.847 169.703C496.85 170.116 496.735 170.466 496.502 170.754C496.271 171.042 495.964 171.235 495.58 171.333V171.391C496.07 171.465 496.449 171.659 496.718 171.974C496.989 172.288 497.123 172.678 497.121 173.143C497.123 173.549 497.01 173.912 496.783 174.234C496.557 174.555 496.249 174.809 495.858 174.993C495.467 175.176 495.019 175.267 494.515 175.267ZM501.047 175.288C500.479 175.286 499.993 175.136 499.59 174.839C499.187 174.541 498.878 174.108 498.665 173.539C498.451 172.971 498.344 172.286 498.344 171.484C498.344 170.685 498.451 170.003 498.665 169.437C498.881 168.87 499.19 168.438 499.593 168.141C499.999 167.843 500.483 167.695 501.047 167.695C501.611 167.695 502.094 167.845 502.498 168.145C502.901 168.442 503.209 168.874 503.422 169.44C503.638 170.004 503.746 170.685 503.746 171.484C503.746 172.288 503.64 172.974 503.426 173.543C503.213 174.109 502.904 174.542 502.501 174.842C502.098 175.14 501.613 175.288 501.047 175.288ZM501.047 174.327C501.546 174.327 501.936 174.084 502.217 173.597C502.5 173.11 502.642 172.406 502.642 171.484C502.642 170.873 502.577 170.355 502.447 169.933C502.32 169.509 502.136 169.187 501.897 168.969C501.659 168.748 501.376 168.638 501.047 168.638C500.551 168.638 500.161 168.882 499.878 169.372C499.594 169.861 499.452 170.565 499.449 171.484C499.449 172.099 499.513 172.618 499.64 173.043C499.77 173.465 499.953 173.785 500.191 174.004C500.428 174.22 500.714 174.327 501.047 174.327ZM509.328 175.288C508.978 175.288 508.661 175.224 508.378 175.094C508.095 174.962 507.871 174.771 507.705 174.522C507.542 174.272 507.46 173.966 507.46 173.604C507.46 173.292 507.52Line truncated
<path d="M435.039 201.041C435.039 199.819 435.039 198.924 435.097 198.218C435.155 197.516 435.267 197.033 435.473 196.628C435.855 195.878 436.464 195.269 437.213 194.888C437.618 194.681 438.101 194.569 438.803 194.512C439.509 194.454 440.404 194.454 441.627 194.454H557.449C558.671 194.454 559.566 194.454 560.272 194.512C560.974 194.569 561.458 194.681 561.863 194.888C562.612 195.269 563.221 195.878 563.603 196.628C563.809 197.033 563.921 197.516 563.979 198.218C564.036 198.924 564.037 199.819 564.037 201.041V213.62C564.037 214.842 564.036 215.737 563.979 216.443C563.921 217.145 563.809 217.629 563.603 218.034C563.221 218.783 562.612 219.392 561.863 219.774C561.458 219.98 560.974 220.092 560.272 220.15C559.566 220.207 558.671 220.208 557.449 220.208H441.627C440.404 220.208 439.509 220.207 438.803 220.15C438.101 220.092 437.618 219.98 437.213 219.774C436.464 219.392 435.855 218.783 435.473 218.034C435.267 217.629 435.155 217.145 435.097 216.443C435.039 215.737 435.039 214.842 435.039 213.62V201.041Z" fill="#1A1A1A"/>
<path d="M435.039 201.041C435.039 199.819 435.039 198.924 435.097 198.218C435.155 197.516 435.267 197.033 435.473 196.628C435.855 195.878 436.464 195.269 437.213 194.888C437.618 194.681 438.101 194.569 438.803 194.512C439.509 194.454 440.404 194.454 441.627 194.454H557.449C558.671 194.454 559.566 194.454 560.272 194.512C560.974 194.569 561.458 194.681 561.863 194.888C562.612 195.269 563.221 195.878 563.603 196.628C563.809 197.033 563.921 197.516 563.979 198.218C564.036 198.924 564.037 199.819 564.037 201.041V213.62C564.037 214.842 564.036 215.737 563.979 216.443C563.921 217.145 563.809 217.629 563.603 218.034C563.221 218.783 562.612 219.392 561.863 219.774C561.458 219.98 560.974 220.092 560.272 220.15C559.566 220.207 558.671 220.208 557.449 220.208H441.627C440.404 220.208 439.509 220.207 438.803 220.15C438.101 220.092 437.618 219.98 437.213 219.774C436.464 219.392 435.855 218.783 435.473 218.034C435.267 217.629 435.155 217.145 435.097 216.443C435.039 215.737 435.039 214.842 435.039 213.62V201.041Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M481.543 202.96V210.331H480.428V204.076H480.385L478.621 205.227V204.162L480.46 202.96H481.543ZM486.334 202.96V210.331H485.218V204.076H485.175L483.411 205.227V204.162L485.25 202.96H486.334ZM489.051 209.416C488.854 209.416 488.685 209.347 488.543 209.208C488.402 209.066 488.331 208.896 488.331 208.697C488.331 208.5 488.402 208.332 488.543 208.193C488.685 208.051 488.854 207.98 489.051 207.98C489.247 207.98 489.417 208.051 489.558 208.193C489.7 208.332 489.77 208.5 489.77 208.697C489.77 208.829 489.737 208.95 489.67 209.06C489.605 209.168 489.519 209.254 489.411 209.319C489.303 209.384 489.183 209.416 489.051 209.416ZM489.051 205.724C488.854 205.724 488.685 205.654 488.543 205.515C488.402 205.374 488.331 205.203 488.331 205.004C488.331 204.807 488.402 204.64 488.543 204.5C488.685 204.359 488.854 204.288 489.051 204.288C489.247 204.288 489.417 204.359 489.558 204.5C489.7 204.64 489.77 204.807 489.77 205.004C489.77 205.136 489.737 205.257 489.67 205.368C489.605 205.476 489.519 205.562 489.411 205.627C489.303 205.692 489.183 205.724 489.051 205.724ZM493.763 210.453C493.195 210.451 492.709 210.301 492.306 210.003C491.903 209.706 491.595 209.272 491.381 208.704C491.167 208.135 491.061 207.45 491.061 206.649C491.061 205.85 491.167 205.167 491.381 204.601C491.597 204.035 491.906 203.603 492.309 203.306C492.715 203.008 493.2 202.859 493.763 202.859C494.327 202.859 494.811 203.009 495.214 203.309C495.617 203.607 495.925 204.039 496.139 204.605C496.355 205.169 496.463 205.85 496.463 206.649C496.463 207.453 496.356 208.139 496.142 208.707C495.929 209.274 495.62 209.707 495.217 210.007C494.814 210.304 494.33 210.453 493.763 210.453ZM493.763 209.492C494.262 209.492 494.652 209.248 494.933 208.761C495.216 208.274 495.358 207.57 495.358 206.649C495.358 206.037 495.293 205.52 495.163 205.098C495.036 204.673 494.853 204.352 494.613 204.133C494.375 203.913 494.092 203.802 493.763 203.802C493.267 203.802 492.877 204.047 492.594 204.536C492.311 205.026 492.168 205.73 492.166 206.649C492.166 207.263 492.229 207.783 492.356 208.207C492.486 208.629 492.669 208.95 492.907 209.168C493.144 209.384 493.43 209.492 493.763 209.492ZM500.305 210.453C499.737 210.451 499.251 210.301 498.848 210.003C498.445 209.706 498.136 209.272 497.923 208.704C497.709 208.135 497.603 207.45 497.603 206.649C497.603 205.85 497.709 205.167 497.923 204.601C498.139 204.035 498.448 203.603 498.851 203.306C499.257 203.008 499.742 202.859 500.305 202.859C500.869 202.859 501.353 203.009 501.756 203.309C502.159 203.607 502.467 204.039 502.681 204.605C502.897 205.169 503.005 205.85 503.005 206.649C503.005 207.453 502.898 208.139 502.684 208.707C502.471 209.274 502.162 209.707 501.759 210.007C501.356 210.304 500.872 210.453 500.305 210.453ZM500.305 209.492C500.804 209.492 501.194 209.248 501.475 208.761C501.758 208.274 501.9 207.57 501.9 206.649C501.9 206.037 501.835 205.52 501.705 205.098C501.578 204.673 501.395 204.352 501.155 204.133C500.917 203.913 500.634 203.802 500.305 203.802C499.809 203.802 499.419 204.047 499.136 204.536C498.853 205.026 498.71 205.73 498.707 206.649C498.707 207.263 498.771 207.783 498.898 208.207C499.028 208.629 499.211 208.95 499.449 209.168C499.686 209.384 499.972 209.492 500.305 209.492ZM508.586 210.453C508.236 210.453 507.919 210.388 507.636 210.259C507.353 210.127 507.129 209.936 506.963 209.686C506.8 209.437 506.719 209.131 506.719 208.769C506.719 208.457 506.779 208.2 506.899 207.998C507.018 207.797 507.18 207.637 507.384 207.52C507.588 207.402 507.816 207.313 508.068 207.254C508.32 207.194 508.577 207.148 508.838 207.117C509.169 207.078 509.438 207.047 509.644 207.023C509.851 206.997 510.001 206.955 510.094 206.897C510.188 206.84 510.235 206.746 510.235 206.617V206.591C510.235 206.277 510.146 206.033 509.968 205.861C509.793 205.688 509.532 205.602 509.184 205.602C508.822 205.602 508.536 205.682 508.327 205.843C508.121 206.001 507.978 206.177 507.899 206.372L506.888 206.141C507.008 205.806 507.183 205.534 507.413 205.328C507.646 205.119 507.913 204.968 508.216 204.875C508.518 204.779 508.836 204.731 509.169 204.731C509.39 204.731 509.624 204.757 509.871 204.81C510.121 204.86 510.353 204.954 510.569 205.091C510.788 205.227 510.966 205.423 511.106 205.677C511.245 205.929 511.314 206.257 511.314 206.66V210.331H510.263V209.575H510.22C510.151 209.714 510.046 209.851 509.907 209.985C509.768 210.119 509.589 210.231 509.371 210.32C509.153 210.409 508.891 210.453 508.586 210.453ZM508.82 209.589C509.118 209.589 509.372 209.53 509.583 209.413C509.797 209.295 509.959 209.142 510.069 208.952C510.182 208.76 510.238 208.555 510.238 208.337V207.624C510.2 207.663 510.126 207.699 510.015 207.732C509.907 207.763 509.784 207.791 509.644 207.815C509.505 207.837 509.37 207.857 509.238 207.876C509.106 207.893 508.995 207.907 508.907 207.919C508.698 207.946 508.507 207.99 508.334 208.052C508.164 208.115 508.027 208.205 507.924 208.322C507.823 208.438 507.773 208.591 507.773 208.783C507.773 209.049 507.871 209.251 508.068 209.388C508.265 209.522 508.Line truncated
<path d="M435.039 236.206C435.039 234.983 435.039 234.089 435.097 233.382C435.155 232.681 435.267 232.197 435.473 231.792C435.855 231.043 436.464 230.434 437.213 230.052C437.618 229.846 438.101 229.734 438.803 229.676C439.509 229.619 440.404 229.618 441.627 229.618H557.449C558.671 229.618 559.566 229.619 560.272 229.676C560.974 229.734 561.458 229.846 561.863 230.052C562.612 230.434 563.221 231.043 563.603 231.792C563.809 232.197 563.921 232.681 563.979 233.382C564.036 234.089 564.037 234.983 564.037 236.206V248.785C564.037 250.007 564.036 250.902 563.979 251.608C563.921 252.31 563.809 252.793 563.603 253.198C563.221 253.947 562.612 254.557 561.863 254.938C561.458 255.145 560.974 255.257 560.272 255.314C559.566 255.372 558.671 255.372 557.449 255.372H441.627C440.404 255.372 439.509 255.372 438.803 255.314C438.101 255.257 437.618 255.145 437.213 254.938C436.464 254.557 435.855 253.947 435.473 253.198C435.267 252.793 435.155 252.31 435.097 251.608C435.039 250.902 435.039 250.007 435.039 248.785V236.206Z" fill="#1A1A1A"/>
<path d="M435.039 236.206C435.039 234.983 435.039 234.089 435.097 233.382C435.155 232.681 435.267 232.197 435.473 231.792C435.855 231.043 436.464 230.434 437.213 230.052C437.618 229.846 438.101 229.734 438.803 229.676C439.509 229.619 440.404 229.618 441.627 229.618H557.449C558.671 229.618 559.566 229.619 560.272 229.676C560.974 229.734 561.458 229.846 561.863 230.052C562.612 230.434 563.221 231.043 563.603 231.792C563.809 232.197 563.921 232.681 563.979 233.382C564.036 234.089 564.037 234.983 564.037 236.206V248.785C564.037 250.007 564.036 250.902 563.979 251.608C563.921 252.31 563.809 252.793 563.603 253.198C563.221 253.947 562.612 254.557 561.863 254.938C561.458 255.145 560.974 255.257 560.272 255.314C559.566 255.372 558.671 255.372 557.449 255.372H441.627C440.404 255.372 439.509 255.372 438.803 255.314C438.101 255.257 437.618 255.145 437.213 254.938C436.464 254.557 435.855 253.947 435.473 253.198C435.267 252.793 435.155 252.31 435.097 251.608C435.039 250.902 435.039 250.007 435.039 248.785V236.206Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M481.543 238.125V245.495H480.428V239.24H480.385L478.621 240.392V239.327L480.46 238.125H481.543ZM486.334 238.125V245.495H485.218V239.24H485.175L483.411 240.392V239.327L485.25 238.125H486.334ZM489.051 244.581C488.854 244.581 488.685 244.511 488.543 244.372C488.402 244.231 488.331 244.06 488.331 243.861C488.331 243.664 488.402 243.497 488.543 243.357C488.685 243.216 488.854 243.145 489.051 243.145C489.247 243.145 489.417 243.216 489.558 243.357C489.7 243.497 489.77 243.664 489.77 243.861C489.77 243.993 489.737 244.114 489.67 244.225C489.605 244.333 489.519 244.419 489.411 244.484C489.303 244.549 489.183 244.581 489.051 244.581ZM489.051 240.889C488.854 240.889 488.685 240.819 488.543 240.68C488.402 240.538 488.331 240.368 488.331 240.169C488.331 239.972 488.402 239.804 488.543 239.665C488.685 239.523 488.854 239.453 489.051 239.453C489.247 239.453 489.417 239.523 489.558 239.665C489.7 239.804 489.77 239.972 489.77 240.169C489.77 240.301 489.737 240.422 489.67 240.532C489.605 240.64 489.519 240.727 489.411 240.791C489.303 240.856 489.183 240.889 489.051 240.889ZM493.763 245.596C493.269 245.596 492.828 245.511 492.439 245.34C492.053 245.17 491.747 244.934 491.521 244.631C491.298 244.327 491.178 243.974 491.161 243.573H492.292C492.306 243.792 492.379 243.981 492.511 244.142C492.645 244.3 492.821 244.423 493.036 244.509C493.252 244.595 493.492 244.639 493.756 244.639C494.047 244.639 494.303 244.588 494.526 244.487C494.752 244.387 494.928 244.246 495.055 244.066C495.183 243.884 495.246 243.674 495.246 243.437C495.246 243.189 495.183 242.972 495.055 242.785C494.931 242.596 494.747 242.447 494.505 242.339C494.265 242.231 493.975 242.177 493.634 242.177H493.011V241.27H493.634C493.907 241.27 494.147 241.221 494.354 241.122C494.562 241.024 494.726 240.887 494.843 240.712C494.961 240.535 495.019 240.327 495.019 240.09C495.019 239.862 494.968 239.664 494.865 239.496C494.764 239.325 494.62 239.192 494.433 239.096C494.248 239 494.03 238.952 493.778 238.952C493.538 238.952 493.314 238.997 493.105 239.085C492.899 239.172 492.731 239.297 492.601 239.46C492.471 239.621 492.402 239.814 492.392 240.039H491.316C491.328 239.641 491.446 239.291 491.669 238.988C491.894 238.686 492.192 238.45 492.561 238.279C492.931 238.109 493.341 238.024 493.792 238.024C494.265 238.024 494.673 238.116 495.016 238.301C495.361 238.483 495.628 238.727 495.815 239.032C496.004 239.336 496.098 239.67 496.096 240.032C496.098 240.445 495.983 240.795 495.75 241.083C495.52 241.371 495.213 241.564 494.829 241.662V241.72C495.318 241.794 495.697 241.989 495.966 242.303C496.237 242.617 496.371 243.007 496.369 243.473C496.371 243.878 496.259 244.242 496.031 244.563C495.805 244.885 495.497 245.138 495.106 245.322C494.715 245.505 494.267 245.596 493.763 245.596ZM500.295 245.617C499.727 245.615 499.241 245.465 498.838 245.168C498.435 244.87 498.127 244.437 497.913 243.868C497.699 243.3 497.593 242.615 497.593 241.813C497.593 241.014 497.699 240.332 497.913 239.766C498.129 239.199 498.438 238.768 498.841 238.47C499.247 238.173 499.732 238.024 500.295 238.024C500.859 238.024 501.343 238.174 501.746 238.474C502.149 238.771 502.457 239.203 502.671 239.769C502.887 240.333 502.995 241.014 502.995 241.813C502.995 242.617 502.888 243.303 502.674 243.872C502.461 244.438 502.152 244.871 501.749 245.171C501.346 245.469 500.862 245.617 500.295 245.617ZM500.295 244.657C500.794 244.657 501.184 244.413 501.465 243.926C501.748 243.439 501.89 242.735 501.89 241.813C501.89 241.202 501.825 240.685 501.695 240.262C501.568 239.838 501.385 239.516 501.145 239.298C500.907 239.077 500.624 238.967 500.295 238.967C499.799 238.967 499.409 239.211 499.126 239.701C498.843 240.19 498.7 240.895 498.698 241.813C498.698 242.428 498.761 242.947 498.888 243.372C499.018 243.794 499.201 244.114 499.439 244.333C499.676 244.549 499.962 244.657 500.295 244.657ZM508.576 245.617C508.226 245.617 507.909 245.553 507.626 245.423C507.343 245.291 507.119 245.1 506.953 244.851C506.79 244.601 506.709 244.295 506.709 243.933C506.709 243.621 506.769 243.365 506.889 243.163C507.009 242.961 507.171 242.802 507.374 242.684C507.578 242.567 507.806 242.478 508.058 242.418C508.31 242.358 508.567 242.312 508.828 242.281C509.16 242.243 509.428 242.212 509.635 242.188C509.841 242.161 509.991 242.119 510.084 242.062C510.178 242.004 510.225 241.911 510.225 241.781V241.756C510.225 241.442 510.136 241.198 509.958 241.025C509.783 240.853 509.522 240.766 509.174 240.766C508.812 240.766 508.526 240.847 508.317 241.007C508.111 241.166 507.968 241.342 507.889 241.536L506.878 241.306C506.998 240.97 507.173 240.699 507.403 240.493C507.636 240.284 507.903 240.133 508.206 240.039C508.508 239.943 508.826 239.895 509.16 239.895C509.38 239.895 509.614 239.922 509.861 239.974C510.111 240.025 510.344 240.118 510.559 240.255C510.778 240.392 510.957 240.587 511.096 240.842C511.235 241.094 511.304 241.421 511.304 241.824V245.495H510.254V244.739H510.21C510.141 244.879 510.036 245.015 509.897 245.15C509.758 245.284 509.579 245.396 509.361 Line truncated
<path d="M435.039 271.37C435.039 270.148 435.039 269.253 435.097 268.547C435.155 267.845 435.267 267.362 435.473 266.957C435.855 266.208 436.464 265.599 437.213 265.217C437.618 265.01 438.101 264.898 438.803 264.841C439.509 264.783 440.404 264.783 441.627 264.783H557.449C558.671 264.783 559.566 264.783 560.272 264.841C560.974 264.898 561.458 265.01 561.863 265.217C562.612 265.599 563.221 266.208 563.603 266.957C563.809 267.362 563.921 267.845 563.979 268.547C564.036 269.253 564.037 270.148 564.037 271.37V283.949C564.037 285.172 564.036 286.066 563.979 286.773C563.921 287.474 563.809 287.958 563.603 288.363C563.221 289.112 562.612 289.721 561.863 290.103C561.458 290.309 560.974 290.421 560.272 290.479C559.566 290.537 558.671 290.537 557.449 290.537H441.627C440.404 290.537 439.509 290.537 438.803 290.479C438.101 290.421 437.618 290.309 437.213 290.103C436.464 289.721 435.855 289.112 435.473 288.363C435.267 287.958 435.155 287.474 435.097 286.773C435.039 286.066 435.039 285.172 435.039 283.949V271.37Z" fill="#1A1A1A"/>
<path d="M435.039 271.37C435.039 270.148 435.039 269.253 435.097 268.547C435.155 267.845 435.267 267.362 435.473 266.957C435.855 266.208 436.464 265.599 437.213 265.217C437.618 265.01 438.101 264.898 438.803 264.841C439.509 264.783 440.404 264.783 441.627 264.783H557.449C558.671 264.783 559.566 264.783 560.272 264.841C560.974 264.898 561.458 265.01 561.863 265.217C562.612 265.599 563.221 266.208 563.603 266.957C563.809 267.362 563.921 267.845 563.979 268.547C564.036 269.253 564.037 270.148 564.037 271.37V283.949C564.037 285.172 564.036 286.066 563.979 286.773C563.921 287.474 563.809 287.958 563.603 288.363C563.221 289.112 562.612 289.721 561.863 290.103C561.458 290.309 560.974 290.421 560.272 290.479C559.566 290.537 558.671 290.537 557.449 290.537H441.627C440.404 290.537 439.509 290.537 438.803 290.479C438.101 290.421 437.618 290.309 437.213 290.103C436.464 289.721 435.855 289.112 435.473 288.363C435.267 287.958 435.155 287.474 435.097 286.773C435.039 286.066 435.039 285.172 435.039 283.949V271.37Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M480.543 273.289V280.66H479.428V274.405H479.385L477.621 275.556V274.491L479.46 273.289H480.543ZM482.537 280.66V279.854L485.031 277.27C485.298 276.989 485.517 276.743 485.69 276.532C485.865 276.318 485.996 276.115 486.082 275.924C486.168 275.732 486.212 275.528 486.212 275.312C486.212 275.067 486.154 274.856 486.039 274.678C485.924 274.498 485.767 274.36 485.567 274.264C485.368 274.166 485.144 274.117 484.894 274.117C484.631 274.117 484.4 274.171 484.203 274.279C484.007 274.387 483.856 274.539 483.75 274.736C483.644 274.933 483.592 275.163 483.592 275.427H482.53C482.53 274.978 482.633 274.586 482.84 274.25C483.046 273.914 483.329 273.654 483.689 273.469C484.049 273.282 484.458 273.188 484.916 273.188C485.379 273.188 485.787 273.281 486.14 273.465C486.495 273.648 486.772 273.897 486.971 274.214C487.17 274.528 487.27 274.883 487.27 275.279C487.27 275.553 487.218 275.82 487.115 276.082C487.014 276.343 486.838 276.635 486.586 276.956C486.334 277.276 485.984 277.663 485.535 278.119L484.07 279.652V279.706H487.389V280.66H482.537ZM489.476 279.746C489.279 279.746 489.11 279.676 488.968 279.537C488.827 279.395 488.756 279.225 488.756 279.026C488.756 278.829 488.827 278.661 488.968 278.522C489.11 278.38 489.279 278.31 489.476 278.31C489.673 278.31 489.842 278.38 489.983 278.522C490.125 278.661 490.196 278.829 490.196 279.026C490.196 279.158 490.162 279.279 490.095 279.389C490.03 279.497 489.944 279.584 489.836 279.648C489.728 279.713 489.608 279.746 489.476 279.746ZM489.476 276.053C489.279 276.053 489.11 275.984 488.968 275.844C488.827 275.703 488.756 275.532 488.756 275.333C488.756 275.137 488.827 274.969 488.968 274.829C489.11 274.688 489.279 274.617 489.476 274.617C489.673 274.617 489.842 274.688 489.983 274.829C490.125 274.969 490.196 275.137 490.196 275.333C490.196 275.465 490.162 275.586 490.095 275.697C490.03 275.805 489.944 275.891 489.836 275.956C489.728 276.021 489.608 276.053 489.476 276.053ZM494.189 280.782C493.62 280.78 493.134 280.63 492.731 280.332C492.328 280.035 492.02 279.602 491.806 279.033C491.593 278.464 491.486 277.779 491.486 276.978C491.486 276.179 491.593 275.496 491.806 274.93C492.022 274.364 492.332 273.932 492.735 273.635C493.14 273.337 493.625 273.188 494.189 273.188C494.752 273.188 495.236 273.338 495.639 273.638C496.042 273.936 496.35 274.368 496.564 274.934C496.78 275.498 496.888 276.179 496.888 276.978C496.888 277.782 496.781 278.468 496.567 279.037C496.354 279.603 496.046 280.036 495.643 280.336C495.239 280.633 494.755 280.782 494.189 280.782ZM494.189 279.821C494.688 279.821 495.078 279.578 495.358 279.091C495.641 278.604 495.783 277.899 495.783 276.978C495.783 276.366 495.718 275.849 495.589 275.427C495.461 275.002 495.278 274.681 495.038 274.462C494.8 274.242 494.517 274.131 494.189 274.131C493.692 274.131 493.302 274.376 493.019 274.865C492.736 275.355 492.593 276.059 492.591 276.978C492.591 277.592 492.654 278.112 492.781 278.536C492.911 278.959 493.095 279.279 493.332 279.497C493.57 279.713 493.855 279.821 494.189 279.821ZM500.73 280.782C500.162 280.78 499.676 280.63 499.273 280.332C498.87 280.035 498.562 279.602 498.348 279.033C498.134 278.464 498.028 277.779 498.028 276.978C498.028 276.179 498.134 275.496 498.348 274.93C498.564 274.364 498.873 273.932 499.277 273.635C499.682 273.337 500.167 273.188 500.73 273.188C501.294 273.188 501.778 273.338 502.181 273.638C502.584 273.936 502.892 274.368 503.106 274.934C503.322 275.498 503.43 276.179 503.43 276.978C503.43 277.782 503.323 278.468 503.109 279.037C502.896 279.603 502.588 280.036 502.184 280.336C501.781 280.633 501.297 280.782 500.73 280.782ZM500.73 279.821C501.23 279.821 501.619 279.578 501.9 279.091C502.183 278.604 502.325 277.899 502.325 276.978C502.325 276.366 502.26 275.849 502.13 275.427C502.003 275.002 501.82 274.681 501.58 274.462C501.342 274.242 501.059 274.131 500.73 274.131C500.234 274.131 499.844 274.376 499.561 274.865C499.278 275.355 499.135 276.059 499.133 276.978C499.133 277.592 499.196 278.112 499.323 278.536C499.453 278.959 499.636 279.279 499.874 279.497C500.111 279.713 500.397 279.821 500.73 279.821ZM507.403 282.733V275.132H508.454V276.028H508.544C508.606 275.913 508.696 275.78 508.814 275.628C508.931 275.477 509.094 275.345 509.303 275.233C509.512 275.117 509.788 275.06 510.131 275.06C510.577 275.06 510.975 275.173 511.326 275.398C511.676 275.624 511.951 275.949 512.15 276.373C512.351 276.798 512.452 277.309 512.452 277.907C512.452 278.504 512.353 279.016 512.153 279.443C511.954 279.868 511.681 280.195 511.333 280.426C510.985 280.654 510.588 280.768 510.142 280.768C509.806 280.768 509.531 280.711 509.317 280.598C509.106 280.486 508.941 280.354 508.821 280.203C508.701 280.051 508.608 279.917 508.544 279.8H508.479V282.733H507.403ZM508.457 277.896C508.457 278.284 508.514 278.625 508.626 278.918C508.739 279.211 508.902 279.44 509.116 279.605C509.329 279.768 509.591 279.85 509.9 279.85C510.222 279.85 510.491 279.765 510.707 279.594C510.923 279.422 511.086 279.188 511.196 278.893C511.309 278.598 511.365 Line truncated
<path d="M435.039 306.535C435.039 305.313 435.039 304.418 435.097 303.712C435.155 303.01 435.267 302.526 435.473 302.121C435.855 301.372 436.464 300.763 437.213 300.381C437.618 300.175 438.101 300.063 438.803 300.005C439.509 299.948 440.404 299.947 441.627 299.947H557.449C558.671 299.947 559.566 299.948 560.272 300.005C560.974 300.063 561.458 300.175 561.863 300.381C562.612 300.763 563.221 301.372 563.603 302.121C563.809 302.526 563.921 303.01 563.979 303.712C564.036 304.418 564.037 305.313 564.037 306.535V319.114C564.037 320.336 564.036 321.231 563.979 321.937C563.921 322.639 563.809 323.122 563.603 323.527C563.221 324.277 562.612 324.886 561.863 325.267C561.458 325.474 560.974 325.586 560.272 325.643C559.566 325.701 558.671 325.701 557.449 325.701H441.627C440.404 325.701 439.509 325.701 438.803 325.643C438.101 325.586 437.618 325.474 437.213 325.267C436.464 324.886 435.855 324.277 435.473 323.527C435.267 323.122 435.155 322.639 435.097 321.937C435.039 321.231 435.039 320.336 435.039 319.114V306.535Z" fill="#1A1A1A"/>
<path d="M435.039 306.535C435.039 305.313 435.039 304.418 435.097 303.712C435.155 303.01 435.267 302.526 435.473 302.121C435.855 301.372 436.464 300.763 437.213 300.381C437.618 300.175 438.101 300.063 438.803 300.005C439.509 299.948 440.404 299.947 441.627 299.947H557.449C558.671 299.947 559.566 299.948 560.272 300.005C560.974 300.063 561.458 300.175 561.863 300.381C562.612 300.763 563.221 301.372 563.603 302.121C563.809 302.526 563.921 303.01 563.979 303.712C564.036 304.418 564.037 305.313 564.037 306.535V319.114C564.037 320.336 564.036 321.231 563.979 321.937C563.921 322.639 563.809 323.122 563.603 323.527C563.221 324.277 562.612 324.886 561.863 325.267C561.458 325.474 560.974 325.586 560.272 325.643C559.566 325.701 558.671 325.701 557.449 325.701H441.627C440.404 325.701 439.509 325.701 438.803 325.643C438.101 325.586 437.618 325.474 437.213 325.267C436.464 324.886 435.855 324.277 435.473 323.527C435.267 323.122 435.155 322.639 435.097 321.937C435.039 321.231 435.039 320.336 435.039 319.114V306.535Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M480.543 308.454V315.824H479.428V309.569H479.385L477.621 310.721V309.656L479.46 308.454H480.543ZM482.537 315.824V315.018L485.031 312.434C485.298 312.153 485.517 311.907 485.69 311.696C485.865 311.483 485.996 311.28 486.082 311.088C486.168 310.896 486.212 310.692 486.212 310.476C486.212 310.232 486.154 310.02 486.039 309.843C485.924 309.663 485.767 309.525 485.567 309.429C485.368 309.331 485.144 309.281 484.894 309.281C484.631 309.281 484.4 309.335 484.203 309.443C484.007 309.551 483.856 309.704 483.75 309.9C483.644 310.097 483.592 310.328 483.592 310.591H482.53C482.53 310.143 482.633 309.75 482.84 309.415C483.046 309.079 483.329 308.818 483.689 308.634C484.049 308.446 484.458 308.353 484.916 308.353C485.379 308.353 485.787 308.445 486.14 308.63C486.495 308.812 486.772 309.062 486.971 309.379C487.17 309.693 487.27 310.048 487.27 310.444C487.27 310.717 487.218 310.985 487.115 311.246C487.014 311.508 486.838 311.799 486.586 312.121C486.334 312.44 485.984 312.828 485.535 313.283L484.07 314.817V314.871H487.389V315.824H482.537ZM489.476 314.91C489.279 314.91 489.11 314.841 488.968 314.701C488.827 314.56 488.756 314.389 488.756 314.19C488.756 313.994 488.827 313.826 488.968 313.686C489.11 313.545 489.279 313.474 489.476 313.474C489.673 313.474 489.842 313.545 489.983 313.686C490.125 313.826 490.196 313.994 490.196 314.19C490.196 314.322 490.162 314.443 490.095 314.554C490.03 314.662 489.944 314.748 489.836 314.813C489.728 314.878 489.608 314.91 489.476 314.91ZM489.476 311.218C489.279 311.218 489.11 311.148 488.968 311.009C488.827 310.867 488.756 310.697 488.756 310.498C488.756 310.301 488.827 310.133 488.968 309.994C489.11 309.852 489.279 309.782 489.476 309.782C489.673 309.782 489.842 309.852 489.983 309.994C490.125 310.133 490.196 310.301 490.196 310.498C490.196 310.63 490.162 310.751 490.095 310.861C490.03 310.969 489.944 311.056 489.836 311.12C489.728 311.185 489.608 311.218 489.476 311.218ZM494.189 315.925C493.694 315.925 493.253 315.84 492.864 315.669C492.478 315.499 492.172 315.263 491.946 314.96C491.723 314.656 491.603 314.303 491.587 313.902H492.717C492.731 314.121 492.804 314.31 492.936 314.471C493.071 314.629 493.246 314.752 493.462 314.838C493.678 314.924 493.917 314.968 494.181 314.968C494.472 314.968 494.728 314.917 494.952 314.817C495.177 314.716 495.353 314.575 495.481 314.395C495.608 314.213 495.671 314.003 495.671 313.766C495.671 313.519 495.608 313.301 495.481 313.114C495.356 312.925 495.172 312.776 494.93 312.668C494.69 312.56 494.4 312.506 494.059 312.506H493.436V311.599H494.059C494.333 311.599 494.572 311.55 494.779 311.452C494.988 311.353 495.151 311.216 495.268 311.041C495.386 310.864 495.445 310.656 495.445 310.419C495.445 310.191 495.393 309.993 495.29 309.825C495.189 309.655 495.045 309.521 494.858 309.425C494.673 309.329 494.455 309.281 494.203 309.281C493.963 309.281 493.739 309.326 493.53 309.415C493.324 309.501 493.156 309.626 493.026 309.789C492.897 309.95 492.827 310.143 492.817 310.368H491.741C491.753 309.97 491.871 309.62 492.094 309.317C492.32 309.015 492.617 308.779 492.987 308.608C493.356 308.438 493.766 308.353 494.217 308.353C494.69 308.353 495.098 308.445 495.441 308.63C495.787 308.812 496.053 309.056 496.24 309.361C496.43 309.665 496.523 309.999 496.521 310.361C496.523 310.774 496.408 311.124 496.175 311.412C495.945 311.7 495.638 311.893 495.254 311.991V312.049C495.743 312.123 496.122 312.318 496.391 312.632C496.662 312.946 496.797 313.336 496.794 313.802C496.797 314.207 496.684 314.571 496.456 314.892C496.23 315.214 495.922 315.467 495.531 315.651C495.14 315.834 494.692 315.925 494.189 315.925ZM500.721 315.947C500.152 315.944 499.666 315.794 499.263 315.497C498.86 315.199 498.552 314.766 498.338 314.198C498.125 313.629 498.018 312.944 498.018 312.143C498.018 311.344 498.125 310.661 498.338 310.095C498.554 309.529 498.864 309.097 499.267 308.799C499.672 308.502 500.157 308.353 500.721 308.353C501.284 308.353 501.768 308.503 502.171 308.803C502.574 309.1 502.882 309.532 503.096 310.098C503.312 310.662 503.42 311.344 503.42 312.143C503.42 312.946 503.313 313.632 503.099 314.201C502.886 314.767 502.578 315.2 502.175 315.5C501.771 315.798 501.287 315.947 500.721 315.947ZM500.721 314.986C501.22 314.986 501.61 314.742 501.89 314.255C502.173 313.768 502.315 313.064 502.315 312.143C502.315 311.531 502.25 311.014 502.121 310.591C501.993 310.167 501.81 309.845 501.57 309.627C501.332 309.406 501.049 309.296 500.721 309.296C500.224 309.296 499.834 309.541 499.551 310.03C499.268 310.519 499.125 311.224 499.123 312.143C499.123 312.757 499.186 313.276 499.313 313.701C499.443 314.123 499.627 314.443 499.864 314.662C500.102 314.878 500.387 314.986 500.721 314.986ZM507.393 317.897V310.296H508.444V311.192H508.534C508.596 311.077 508.686 310.944 508.804 310.793C508.921 310.642 509.084 310.51 509.293 310.397C509.502 310.282 509.778 310.224 510.121 310.224C510.567 310.224 510.965 310.337 511.316 310.563C511.666 310.788 511.941 311.113 512.14 311.538C512.341 311.963 512.442 312.474 512.Line truncated
<g clip-path="url(#clip6_16843_34774)">
<path d="M626.432 74.5679L624.649 68.2503H625.879L627.018 72.8928H627.076L628.292 68.2503H629.412L630.63 72.8959H630.686L631.824 68.2503H633.055L631.272 74.5679H630.143L628.878 70.1351H628.829L627.561 74.5679H626.432ZM633.848 74.5679V68.2503H637.957V69.2096H634.993V70.9248H637.744V71.8841H634.993V73.6085H637.982V74.5679H633.848ZM641.266 74.5679H639.125V68.2503H641.309C641.936 68.2503 642.475 68.3767 642.925 68.6297C643.378 68.8806 643.725 69.2415 643.968 69.7125C644.21 70.1834 644.332 70.7469 644.332 71.4029C644.332 72.061 644.209 72.6265 643.965 73.0995C643.722 73.5725 643.371 73.9355 642.913 74.1884C642.456 74.4414 641.907 74.5679 641.266 74.5679ZM640.269 73.5777H641.21C641.65 73.5777 642.017 73.4975 642.311 73.337C642.605 73.1746 642.826 72.9329 642.974 72.6121C643.123 72.2893 643.197 71.8862 643.197 71.4029C643.197 70.9196 643.123 70.5186 642.974 70.1998C642.826 69.879 642.607 69.6394 642.317 69.4811C642.03 69.3207 641.672 69.2405 641.244 69.2405H640.269V73.5777Z" fill="#A5A5A5"/>
<path d="M650.746 74.5679V73.7412L652.939 71.5911C653.149 71.3793 653.323 71.1911 653.463 71.0266C653.603 70.862 653.708 70.7027 653.778 70.5484C653.848 70.3942 653.883 70.2297 653.883 70.0549C653.883 69.8554 653.837 69.6847 653.747 69.5428C653.656 69.3988 653.532 69.2878 653.374 69.2096C653.215 69.1315 653.035 69.0924 652.834 69.0924C652.626 69.0924 652.444 69.1356 652.288 69.222C652.132 69.3063 652.01 69.4266 651.924 69.5829C651.84 69.7392 651.797 69.9253 651.797 70.1412H650.708C650.708 69.7402 650.8 69.3916 650.983 69.0955C651.166 68.7994 651.418 68.5701 651.739 68.4076C652.062 68.2451 652.432 68.1639 652.849 68.1639C653.273 68.1639 653.645 68.2431 653.966 68.4014C654.287 68.5598 654.536 68.7767 654.713 69.0523C654.891 69.3279 654.981 69.6425 654.981 69.9962C654.981 70.2327 654.936 70.4651 654.845 70.6934C654.755 70.9217 654.595 71.1746 654.367 71.4523C654.141 71.7299 653.823 72.0661 653.414 72.461L652.325 73.5684V73.6116H655.077V74.5679H650.746ZM656.141 74.5679V73.7412L658.334 71.5911C658.544 71.3793 658.719 71.1911 658.858 71.0266C658.998 70.862 659.103 70.7027 659.173 70.5484C659.243 70.3942 659.278 70.2297 659.278 70.0549C659.278 69.8554 659.233 69.6847 659.142 69.5428C659.052 69.3988 658.927 69.2878 658.769 69.2096C658.611 69.1315 658.431 69.0924 658.229 69.0924C658.021 69.0924 657.839 69.1356 657.683 69.222C657.527 69.3063 657.406 69.4266 657.319 69.5829C657.235 69.7392 657.193 69.9253 657.193 70.1412H656.104C656.104 69.7402 656.195 69.3916 656.378 69.0955C656.561 68.7994 656.813 68.5701 657.134 68.4076C657.457 68.2451 657.827 68.1639 658.245 68.1639C658.668 68.1639 659.04 68.2431 659.361 68.4014C659.682 68.5598 659.931 68.7767 660.108 69.0523C660.287 69.3279 660.376 69.6425 660.376 69.9962C660.376 70.2327 660.331 70.4651 660.24 70.6934C660.15 70.9217 659.991 71.1746 659.762 71.4523C659.536 71.7299 659.218 72.0661 658.809 72.461L657.72 73.5684V73.6116H660.472V74.5679H656.141Z" fill="#A5A5A5"/>
<path d="M579.238 95.5475C579.238 94.3253 579.238 93.4304 579.296 92.7243C579.353 92.0224 579.466 91.539 579.672 91.134C580.054 90.3848 580.663 89.7757 581.412 89.394C581.817 89.1877 582.3 89.0754 583.002 89.0181C583.708 88.9604 584.603 88.9601 585.825 88.9601H701.648C702.87 88.9601 703.765 88.9604 704.471 89.0181C705.173 89.0754 705.656 89.1877 706.062 89.394C706.811 89.7757 707.42 90.3848 707.801 91.134C708.008 91.539 708.12 92.0224 708.177 92.7243C708.235 93.4304 708.235 94.3253 708.235 95.5475V108.127C708.235 109.349 708.235 110.244 708.177 110.95C708.12 111.652 708.008 112.135 707.801 112.54C707.42 113.289 706.811 113.898 706.062 114.28C705.656 114.486 705.173 114.599 704.471 114.656C703.765 114.714 702.87 114.714 701.648 114.714H585.825C584.603 114.714 583.708 114.714 583.002 114.656C582.3 114.599 581.817 114.486 581.412 114.28C580.663 113.898 580.054 113.289 579.672 112.54C579.466 112.135 579.353 111.652 579.296 110.95C579.238 110.244 579.238 109.349 579.238 108.127V95.5475Z" fill="#1A1A1A"/>
<path d="M579.238 95.5475C579.238 94.3253 579.238 93.4304 579.296 92.7243C579.353 92.0224 579.466 91.539 579.672 91.134C580.054 90.3848 580.663 89.7757 581.412 89.394C581.817 89.1877 582.3 89.0754 583.002 89.0181C583.708 88.9604 584.603 88.9601 585.825 88.9601H701.648C702.87 88.9601 703.765 88.9604 704.471 89.0181C705.173 89.0754 705.656 89.1877 706.062 89.394C706.811 89.7757 707.42 90.3848 707.801 91.134C708.008 91.539 708.12 92.0224 708.177 92.7243C708.235 93.4304 708.235 94.3253 708.235 95.5475V108.127C708.235 109.349 708.235 110.244 708.177 110.95C708.12 111.652 708.008 112.135 707.801 112.54C707.42 113.289 706.811 113.898 706.062 114.28C705.656 114.486 705.173 114.599 704.471 114.656C703.765 114.714 702.87 114.714 701.648 114.714H585.825C584.603 114.714 583.708 114.714 583.002 114.656C582.3 114.599 581.817 114.486 581.412 114.28C580.663 113.898 580.054 113.289 579.672 112.54C579.466 112.135 579.353 111.652 579.296 110.95C579.238 110.244 579.238 109.349 579.238 108.127V95.5475Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M626.897 97.3656C627.225 97.368 627.549 97.428 627.868 97.5456C628.187 97.6631 628.475 97.8551 628.732 98.1214C628.991 98.3877 629.198 98.7476 629.351 99.201C629.507 99.6521 629.586 100.214 629.589 100.885C629.589 101.531 629.524 102.105 629.394 102.609C629.265 103.111 629.079 103.534 628.836 103.88C628.597 104.225 628.305 104.488 627.962 104.668C627.619 104.848 627.233 104.938 626.803 104.938C626.364 104.938 625.974 104.851 625.633 104.679C625.293 104.506 625.016 104.267 624.802 103.962C624.589 103.655 624.455 103.303 624.403 102.904H625.5C625.572 103.221 625.719 103.478 625.939 103.674C626.162 103.869 626.45 103.966 626.803 103.966C627.343 103.966 627.764 103.731 628.066 103.261C628.369 102.788 628.521 102.128 628.523 101.281H628.466C628.341 101.488 628.185 101.665 627.998 101.814C627.813 101.963 627.606 102.078 627.375 102.159C627.145 102.241 626.899 102.282 626.638 102.282C626.213 102.282 625.827 102.177 625.479 101.969C625.131 101.76 624.854 101.473 624.647 101.108C624.441 100.744 624.338 100.328 624.338 99.8596C624.338 99.3942 624.443 98.9719 624.655 98.5928C624.868 98.2138 625.166 97.9138 625.547 97.6931C625.931 97.47 626.381 97.3608 626.897 97.3656ZM626.9 98.3013C626.62 98.3013 626.366 98.3709 626.141 98.5101C625.918 98.6468 625.741 98.8328 625.612 99.0679C625.482 99.3006 625.417 99.5597 625.417 99.8452C625.417 100.131 625.48 100.39 625.605 100.623C625.732 100.853 625.905 101.036 626.123 101.173C626.344 101.308 626.596 101.375 626.879 101.375C627.09 101.375 627.287 101.334 627.469 101.252C627.651 101.171 627.811 101.058 627.948 100.914C628.084 100.768 628.191 100.602 628.268 100.417C628.345 100.233 628.383 100.038 628.383 99.8345C628.383 99.5633 628.318 99.3114 628.189 99.0787C628.061 98.846 627.886 98.6588 627.663 98.5173C627.44 98.3733 627.186 98.3013 626.9 98.3013ZM631.612 103.923C631.415 103.923 631.246 103.853 631.105 103.714C630.963 103.573 630.892 103.402 630.892 103.203C630.892 103.006 630.963 102.838 631.105 102.699C631.246 102.558 631.415 102.487 631.612 102.487C631.809 102.487 631.978 102.558 632.12 102.699C632.261 102.838 632.332 103.006 632.332 103.203C632.332 103.335 632.298 103.456 632.231 103.567C632.166 103.674 632.08 103.761 631.972 103.826C631.864 103.89 631.744 103.923 631.612 103.923ZM631.612 100.23C631.415 100.23 631.246 100.161 631.105 100.022C630.963 99.88 630.892 99.7097 630.892 99.5106C630.892 99.3138 630.963 99.1459 631.105 99.0067C631.246 98.8652 631.415 98.7944 631.612 98.7944C631.809 98.7944 631.978 98.8652 632.12 99.0067C632.261 99.1459 632.332 99.3138 632.332 99.5106C632.332 99.6425 632.298 99.7637 632.231 99.874C632.166 99.982 632.08 100.068 631.972 100.133C631.864 100.198 631.744 100.23 631.612 100.23ZM636.325 104.938C635.831 104.938 635.389 104.853 635 104.682C634.614 104.512 634.308 104.275 634.083 103.973C633.86 103.668 633.74 103.316 633.723 102.915H634.853C634.867 103.133 634.94 103.323 635.072 103.484C635.207 103.642 635.382 103.764 635.598 103.851C635.814 103.937 636.054 103.98 636.318 103.98C636.608 103.98 636.865 103.93 637.088 103.829C637.313 103.728 637.49 103.588 637.617 103.408C637.744 103.226 637.808 103.016 637.808 102.778C637.808 102.531 637.744 102.314 637.617 102.127C637.492 101.937 637.309 101.789 637.066 101.681C636.826 101.573 636.536 101.519 636.195 101.519H635.573V100.612H636.195C636.469 100.612 636.709 100.563 636.915 100.464C637.124 100.366 637.287 100.229 637.405 100.054C637.522 99.8764 637.581 99.6689 637.581 99.4314C637.581 99.2034 637.529 99.0055 637.426 98.8376C637.325 98.6672 637.181 98.5341 636.994 98.4381C636.809 98.3421 636.591 98.2941 636.339 98.2941C636.099 98.2941 635.875 98.3385 635.666 98.4273C635.46 98.5137 635.292 98.6384 635.162 98.8016C635.033 98.9623 634.963 99.1555 634.954 99.381H633.878C633.89 98.9827 634.007 98.6324 634.23 98.3301C634.456 98.0278 634.753 97.7915 635.123 97.6211C635.492 97.4508 635.903 97.3656 636.354 97.3656C636.826 97.3656 637.234 97.458 637.577 97.6427C637.923 97.8251 638.189 98.0686 638.376 98.3733C638.566 98.678 638.659 99.0115 638.657 99.3738C638.659 99.7865 638.544 100.137 638.311 100.425C638.081 100.713 637.774 100.906 637.39 101.004V101.062C637.88 101.136 638.259 101.33 638.527 101.645C638.798 101.959 638.933 102.349 638.93 102.814C638.933 103.22 638.82 103.583 638.592 103.905C638.367 104.226 638.058 104.479 637.667 104.664C637.276 104.847 636.829 104.938 636.325 104.938ZM642.857 104.959C642.288 104.957 641.802 104.807 641.399 104.509C640.996 104.212 640.688 103.779 640.474 103.21C640.261 102.642 640.154 101.957 640.154 101.155C640.154 100.356 640.261 99.6737 640.474 99.1075C640.69 98.5413 641 98.1094 641.403 97.8119C641.808 97.5144 642.293 97.3656 642.857 97.3656C643.421 97.3656 643.904 97.5156 644.307 97.8155C644.71 98.113 645.019 98.5449 645.232 99.1111C645.448 99.6749 645.556 100.356 645.556 101.155C645.556 101.959 645.449 102.645 645.236 103.214C645.022 103.78 644.714 104.213 644.311 104.513C643.908 104.811 643.423 104.959 642.857 104.959ZM642.857 103.998C643.356 103.998 643.746 1Line truncated
<path d="M579.238 130.712C579.238 129.49 579.238 128.595 579.296 127.889C579.353 127.187 579.466 126.704 579.672 126.299C580.054 125.549 580.663 124.94 581.412 124.559C581.817 124.352 582.3 124.24 583.002 124.183C583.708 124.125 584.603 124.125 585.825 124.125H701.648C702.87 124.125 703.765 124.125 704.471 124.183C705.173 124.24 705.656 124.352 706.062 124.559C706.811 124.94 707.42 125.549 707.801 126.299C708.008 126.704 708.12 127.187 708.177 127.889C708.235 128.595 708.235 129.49 708.235 130.712V143.291C708.235 144.513 708.235 145.408 708.177 146.114C708.12 146.816 708.008 147.3 707.801 147.705C707.42 148.454 706.811 149.063 706.062 149.445C705.656 149.651 705.173 149.763 704.471 149.821C703.765 149.878 702.87 149.879 701.648 149.879H585.825C584.603 149.879 583.708 149.878 583.002 149.821C582.3 149.763 581.817 149.651 581.412 149.445C580.663 149.063 580.054 148.454 579.672 147.705C579.466 147.3 579.353 146.816 579.296 146.114C579.238 145.408 579.238 144.513 579.238 143.291V130.712Z" fill="#1A1A1A"/>
<path d="M579.238 130.712C579.238 129.49 579.238 128.595 579.296 127.889C579.353 127.187 579.466 126.704 579.672 126.299C580.054 125.549 580.663 124.94 581.412 124.559C581.817 124.352 582.3 124.24 583.002 124.183C583.708 124.125 584.603 124.125 585.825 124.125H701.648C702.87 124.125 703.765 124.125 704.471 124.183C705.173 124.24 705.656 124.352 706.062 124.559C706.811 124.94 707.42 125.549 707.801 126.299C708.008 126.704 708.12 127.187 708.177 127.889C708.235 128.595 708.235 129.49 708.235 130.712V143.291C708.235 144.513 708.235 145.408 708.177 146.114C708.12 146.816 708.008 147.3 707.801 147.705C707.42 148.454 706.811 149.063 706.062 149.445C705.656 149.651 705.173 149.763 704.471 149.821C703.765 149.878 702.87 149.879 701.648 149.879H585.825C584.603 149.879 583.708 149.878 583.002 149.821C582.3 149.763 581.817 149.651 581.412 149.445C580.663 149.063 580.054 148.454 579.672 147.705C579.466 147.3 579.353 146.816 579.296 146.114C579.238 145.408 579.238 144.513 579.238 143.291V130.712Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M624.742 132.631V140.001H623.626V133.747H623.583L621.82 134.898V133.833L623.659 132.631H624.742ZM629.302 140.124C628.733 140.121 628.247 139.971 627.844 139.674C627.441 139.376 627.133 138.943 626.919 138.375C626.706 137.806 626.599 137.121 626.599 136.32C626.599 135.521 626.706 134.838 626.919 134.272C627.135 133.706 627.445 133.274 627.848 132.976C628.253 132.679 628.738 132.53 629.302 132.53C629.866 132.53 630.349 132.68 630.752 132.98C631.155 133.278 631.464 133.709 631.677 134.276C631.893 134.839 632.001 135.521 632.001 136.32C632.001 137.124 631.894 137.81 631.681 138.378C631.467 138.945 631.159 139.378 630.756 139.678C630.353 139.975 629.868 140.124 629.302 140.124ZM629.302 139.163C629.801 139.163 630.191 138.919 630.472 138.432C630.755 137.945 630.896 137.241 630.896 136.32C630.896 135.708 630.831 135.191 630.702 134.769C630.575 134.344 630.391 134.023 630.151 133.804C629.914 133.583 629.631 133.473 629.302 133.473C628.805 133.473 628.415 133.718 628.132 134.207C627.849 134.697 627.706 135.401 627.704 136.32C627.704 136.934 627.768 137.453 627.895 137.878C628.024 138.3 628.208 138.621 628.445 138.839C628.683 139.055 628.968 139.163 629.302 139.163ZM634.001 139.087C633.804 139.087 633.635 139.018 633.494 138.879C633.352 138.737 633.281 138.567 633.281 138.368C633.281 138.171 633.352 138.003 633.494 137.864C633.635 137.722 633.804 137.651 634.001 137.651C634.198 137.651 634.367 137.722 634.509 137.864C634.65 138.003 634.721 138.171 634.721 138.368C634.721 138.5 634.687 138.621 634.62 138.731C634.555 138.839 634.469 138.925 634.361 138.99C634.253 139.055 634.133 139.087 634.001 139.087ZM634.001 135.395C633.804 135.395 633.635 135.325 633.494 135.186C633.352 135.045 633.281 134.874 633.281 134.675C633.281 134.478 633.352 134.31 633.494 134.171C633.635 134.03 633.804 133.959 634.001 133.959C634.198 133.959 634.367 134.03 634.509 134.171C634.65 134.31 634.721 134.478 634.721 134.675C634.721 134.807 634.687 134.928 634.62 135.039C634.555 135.147 634.469 135.233 634.361 135.298C634.253 135.362 634.133 135.395 634.001 135.395ZM638.714 140.124C638.145 140.121 637.659 139.971 637.256 139.674C636.853 139.376 636.545 138.943 636.331 138.375C636.118 137.806 636.011 137.121 636.011 136.32C636.011 135.521 636.118 134.838 636.331 134.272C636.547 133.706 636.857 133.274 637.26 132.976C637.665 132.679 638.15 132.53 638.714 132.53C639.278 132.53 639.761 132.68 640.164 132.98C640.567 133.278 640.876 133.709 641.089 134.276C641.305 134.839 641.413 135.521 641.413 136.32C641.413 137.124 641.306 137.81 641.093 138.378C640.879 138.945 640.571 139.378 640.168 139.678C639.765 139.975 639.28 140.124 638.714 140.124ZM638.714 139.163C639.213 139.163 639.603 138.919 639.884 138.432C640.167 137.945 640.308 137.241 640.308 136.32C640.308 135.708 640.243 135.191 640.114 134.769C639.987 134.344 639.803 134.023 639.563 133.804C639.326 133.583 639.043 133.473 638.714 133.473C638.217 133.473 637.827 133.718 637.544 134.207C637.261 134.697 637.118 135.401 637.116 136.32C637.116 136.934 637.18 137.453 637.307 137.878C637.436 138.3 637.62 138.621 637.857 138.839C638.095 139.055 638.38 139.163 638.714 139.163ZM645.256 140.124C644.687 140.121 644.201 139.971 643.798 139.674C643.395 139.376 643.087 138.943 642.873 138.375C642.66 137.806 642.553 137.121 642.553 136.32C642.553 135.521 642.66 134.838 642.873 134.272C643.089 133.706 643.399 133.274 643.802 132.976C644.207 132.679 644.692 132.53 645.256 132.53C645.82 132.53 646.303 132.68 646.706 132.98C647.109 133.278 647.418 133.709 647.631 134.276C647.847 134.839 647.955 135.521 647.955 136.32C647.955 137.124 647.848 137.81 647.635 138.378C647.421 138.945 647.113 139.378 646.71 139.678C646.307 139.975 645.822 140.124 645.256 140.124ZM645.256 139.163C645.755 139.163 646.145 138.919 646.425 138.432C646.709 137.945 646.85 137.241 646.85 136.32C646.85 135.708 646.785 135.191 646.656 134.769C646.529 134.344 646.345 134.023 646.105 133.804C645.868 133.583 645.585 133.473 645.256 133.473C644.759 133.473 644.369 133.718 644.086 134.207C643.803 134.697 643.66 135.401 643.658 136.32C643.658 136.934 643.721 137.453 643.849 137.878C643.978 138.3 644.162 138.621 644.399 138.839C644.637 139.055 644.922 139.163 645.256 139.163ZM653.537 140.124C653.187 140.124 652.87 140.059 652.587 139.929C652.304 139.798 652.079 139.607 651.914 139.357C651.751 139.108 651.669 138.802 651.669 138.44C651.669 138.128 651.729 137.871 651.849 137.669C651.969 137.468 652.131 137.308 652.335 137.191C652.539 137.073 652.767 136.984 653.019 136.924C653.271 136.864 653.527 136.819 653.789 136.788C654.12 136.749 654.389 136.718 654.595 136.694C654.801 136.668 654.951 136.626 655.045 136.568C655.138 136.511 655.185 136.417 655.185 136.287V136.262C655.185 135.948 655.096 135.704 654.919 135.532C654.744 135.359 654.482 135.273 654.134 135.273C653.772 135.273 653.486 135.353 653.278 135.514C653.071 135.672 652.929 135.848 652.849 136.043L651.838 135.812C651.958 135.476 652.133 135.205 652.364 134.999C652.596 134.79 652.864 134.63Line truncated
<path d="M579.238 165.877C579.238 164.654 579.238 163.759 579.296 163.053C579.353 162.351 579.466 161.868 579.672 161.463C580.054 160.714 580.663 160.105 581.412 159.723C581.817 159.517 582.3 159.405 583.002 159.347C583.708 159.289 584.603 159.289 585.825 159.289H701.648C702.87 159.289 703.765 159.289 704.471 159.347C705.173 159.405 705.656 159.517 706.062 159.723C706.811 160.105 707.42 160.714 707.801 161.463C708.008 161.868 708.12 162.351 708.177 163.053C708.235 163.759 708.235 164.654 708.235 165.877V178.456C708.235 179.678 708.235 180.573 708.177 181.279C708.12 181.981 708.008 182.464 707.801 182.869C707.42 183.618 706.811 184.227 706.062 184.609C705.656 184.816 705.173 184.928 704.471 184.985C703.765 185.043 702.87 185.043 701.648 185.043H585.825C584.603 185.043 583.708 185.043 583.002 184.985C582.3 184.928 581.817 184.816 581.412 184.609C580.663 184.227 580.054 183.618 579.672 182.869C579.466 182.464 579.353 181.981 579.296 181.279C579.238 180.573 579.238 179.678 579.238 178.456V165.877Z" fill="#1A1A1A"/>
<path d="M579.238 165.877C579.238 164.654 579.238 163.759 579.296 163.053C579.353 162.351 579.466 161.868 579.672 161.463C580.054 160.714 580.663 160.105 581.412 159.723C581.817 159.517 582.3 159.405 583.002 159.347C583.708 159.289 584.603 159.289 585.825 159.289H701.648C702.87 159.289 703.765 159.289 704.471 159.347C705.173 159.405 705.656 159.517 706.062 159.723C706.811 160.105 707.42 160.714 707.801 161.463C708.008 161.868 708.12 162.351 708.177 163.053C708.235 163.759 708.235 164.654 708.235 165.877V178.456C708.235 179.678 708.235 180.573 708.177 181.279C708.12 181.981 708.008 182.464 707.801 182.869C707.42 183.618 706.811 184.227 706.062 184.609C705.656 184.816 705.173 184.928 704.471 184.985C703.765 185.043 702.87 185.043 701.648 185.043H585.825C584.603 185.043 583.708 185.043 583.002 184.985C582.3 184.928 581.817 184.816 581.412 184.609C580.663 184.227 580.054 183.618 579.672 182.869C579.466 182.464 579.353 181.981 579.296 181.279C579.238 180.573 579.238 179.678 579.238 178.456V165.877Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M625.742 167.795V175.166H624.626V168.911H624.583L622.82 170.063V168.998L624.659 167.795H625.742ZM630.532 167.795V175.166H629.417V168.911H629.373L627.61 170.063V168.998L629.449 167.795H630.532ZM633.249 174.252C633.053 174.252 632.884 174.182 632.742 174.043C632.6 173.902 632.53 173.731 632.53 173.532C632.53 173.335 632.6 173.167 632.742 173.028C632.884 172.887 633.053 172.816 633.249 172.816C633.446 172.816 633.615 172.887 633.757 173.028C633.898 173.167 633.969 173.335 633.969 173.532C633.969 173.664 633.936 173.785 633.868 173.896C633.804 174.004 633.717 174.09 633.609 174.155C633.501 174.22 633.381 174.252 633.249 174.252ZM633.249 170.559C633.053 170.559 632.884 170.49 632.742 170.351C632.6 170.209 632.53 170.039 632.53 169.84C632.53 169.643 632.6 169.475 632.742 169.336C632.884 169.194 633.053 169.123 633.249 169.123C633.446 169.123 633.615 169.194 633.757 169.336C633.898 169.475 633.969 169.643 633.969 169.84C633.969 169.972 633.936 170.093 633.868 170.203C633.804 170.311 633.717 170.397 633.609 170.462C633.501 170.527 633.381 170.559 633.249 170.559ZM637.962 175.267C637.468 175.267 637.026 175.182 636.638 175.011C636.252 174.841 635.946 174.605 635.72 174.302C635.497 173.998 635.377 173.645 635.36 173.244H636.49C636.505 173.463 636.578 173.652 636.71 173.813C636.844 173.971 637.019 174.094 637.235 174.18C637.451 174.266 637.691 174.309 637.955 174.309C638.245 174.309 638.502 174.259 638.725 174.158C638.951 174.058 639.127 173.917 639.254 173.737C639.381 173.555 639.445 173.345 639.445 173.107C639.445 172.86 639.381 172.643 639.254 172.456C639.129 172.267 638.946 172.118 638.704 172.01C638.464 171.902 638.173 171.848 637.833 171.848H637.21V170.941H637.833C638.106 170.941 638.346 170.892 638.552 170.793C638.761 170.695 638.924 170.558 639.042 170.383C639.159 170.206 639.218 169.998 639.218 169.76C639.218 169.533 639.167 169.335 639.063 169.167C638.963 168.996 638.819 168.863 638.632 168.767C638.447 168.671 638.228 168.623 637.977 168.623C637.737 168.623 637.512 168.668 637.304 168.756C637.097 168.843 636.929 168.968 636.8 169.131C636.67 169.291 636.601 169.485 636.591 169.71H635.515C635.527 169.312 635.644 168.962 635.868 168.659C636.093 168.357 636.391 168.121 636.76 167.95C637.13 167.78 637.54 167.695 637.991 167.695C638.464 167.695 638.871 167.787 639.215 167.972C639.56 168.154 639.826 168.398 640.014 168.702C640.203 169.007 640.297 169.341 640.294 169.703C640.297 170.116 640.181 170.466 639.949 170.754C639.718 171.042 639.411 171.235 639.027 171.333V171.391C639.517 171.465 639.896 171.659 640.165 171.974C640.436 172.288 640.57 172.678 640.568 173.143C640.57 173.549 640.457 173.912 640.229 174.234C640.004 174.555 639.696 174.809 639.305 174.993C638.913 175.176 638.466 175.267 637.962 175.267ZM644.494 175.288C643.926 175.286 643.44 175.136 643.037 174.839C642.634 174.541 642.325 174.108 642.112 173.539C641.898 172.971 641.791 172.286 641.791 171.484C641.791 170.685 641.898 170.003 642.112 169.437C642.328 168.87 642.637 168.438 643.04 168.141C643.446 167.843 643.93 167.695 644.494 167.695C645.058 167.695 645.541 167.845 645.945 168.145C646.348 168.442 646.656 168.874 646.869 169.44C647.085 170.004 647.193 170.685 647.193 171.484C647.193 172.288 647.087 172.974 646.873 173.543C646.66 174.109 646.351 174.542 645.948 174.842C645.545 175.14 645.06 175.288 644.494 175.288ZM644.494 174.327C644.993 174.327 645.383 174.084 645.664 173.597C645.947 173.11 646.088 172.406 646.088 171.484C646.088 170.873 646.024 170.355 645.894 169.933C645.767 169.509 645.583 169.187 645.344 168.969C645.106 168.748 644.823 168.638 644.494 168.638C643.998 168.638 643.608 168.882 643.325 169.372C643.041 169.861 642.899 170.565 642.896 171.484C642.896 172.099 642.96 172.618 643.087 173.043C643.217 173.465 643.4 173.785 643.638 174.004C643.875 174.22 644.161 174.327 644.494 174.327ZM652.775 175.288C652.425 175.288 652.108 175.224 651.825 175.094C651.542 174.962 651.318 174.771 651.152 174.522C650.989 174.272 650.907 173.966 650.907 173.604C650.907 173.292 650.967 173.035 651.087 172.834C651.207 172.632 651.369 172.473 651.573 172.355C651.777 172.238 652.005 172.149 652.257 172.089C652.509 172.029 652.766 171.983 653.027 171.952C653.358 171.914 653.627 171.883 653.833 171.859C654.04 171.832 654.19 171.79 654.283 171.733C654.377 171.675 654.424 171.582 654.424 171.452V171.427C654.424 171.112 654.335 170.869 654.157 170.696C653.982 170.523 653.721 170.437 653.373 170.437C653.01 170.437 652.725 170.517 652.516 170.678C652.31 170.837 652.167 171.013 652.088 171.207L651.077 170.977C651.197 170.641 651.372 170.37 651.602 170.164C651.835 169.955 652.102 169.804 652.405 169.71C652.707 169.614 653.025 169.566 653.358 169.566C653.579 169.566 653.813 169.593 654.06 169.645C654.31 169.696 654.542 169.789 654.758 169.926C654.977 170.063 655.155 170.258 655.294 170.513C655.434 170.765 655.503 171.092 655.503 171.495V175.166H654.452V174.41H654.409C654.34 174.549 654.235 174.686 654.096 174.821C653.957 174.955 653.778 175.066 653.56 175.155C6Line truncated
<path d="M579.238 201.041C579.238 199.819 579.238 198.924 579.296 198.218C579.353 197.516 579.466 197.033 579.672 196.628C580.054 195.878 580.663 195.269 581.412 194.888C581.817 194.681 582.3 194.569 583.002 194.512C583.708 194.454 584.603 194.454 585.825 194.454H701.648C702.87 194.454 703.765 194.454 704.471 194.512C705.173 194.569 705.656 194.681 706.062 194.888C706.811 195.269 707.42 195.878 707.801 196.628C708.008 197.033 708.12 197.516 708.177 198.218C708.235 198.924 708.235 199.819 708.235 201.041V213.62C708.235 214.842 708.235 215.737 708.177 216.443C708.12 217.145 708.008 217.629 707.801 218.034C707.42 218.783 706.811 219.392 706.062 219.774C705.656 219.98 705.173 220.092 704.471 220.15C703.765 220.207 702.87 220.208 701.648 220.208H585.825C584.603 220.208 583.708 220.207 583.002 220.15C582.3 220.092 581.817 219.98 581.412 219.774C580.663 219.392 580.054 218.783 579.672 218.034C579.466 217.629 579.353 217.145 579.296 216.443C579.238 215.737 579.238 214.842 579.238 213.62V201.041Z" fill="#1A1A1A"/>
<path d="M579.238 201.041C579.238 199.819 579.238 198.924 579.296 198.218C579.353 197.516 579.466 197.033 579.672 196.628C580.054 195.878 580.663 195.269 581.412 194.888C581.817 194.681 582.3 194.569 583.002 194.512C583.708 194.454 584.603 194.454 585.825 194.454H701.648C702.87 194.454 703.765 194.454 704.471 194.512C705.173 194.569 705.656 194.681 706.062 194.888C706.811 195.269 707.42 195.878 707.801 196.628C708.008 197.033 708.12 197.516 708.177 198.218C708.235 198.924 708.235 199.819 708.235 201.041V213.62C708.235 214.842 708.235 215.737 708.177 216.443C708.12 217.145 708.008 217.629 707.801 218.034C707.42 218.783 706.811 219.392 706.062 219.774C705.656 219.98 705.173 220.092 704.471 220.15C703.765 220.207 702.87 220.208 701.648 220.208H585.825C584.603 220.208 583.708 220.207 583.002 220.15C582.3 220.092 581.817 219.98 581.412 219.774C580.663 219.392 580.054 218.783 579.672 218.034C579.466 217.629 579.353 217.145 579.296 216.443C579.238 215.737 579.238 214.842 579.238 213.62V201.041Z" stroke="#575757" stroke-width="0.723892"/>
<path d="M626.519 210.431C626.19 210.427 625.866 210.367 625.547 210.251C625.231 210.136 624.943 209.944 624.684 209.676C624.424 209.407 624.217 209.046 624.061 208.592C623.907 208.139 623.831 207.573 623.831 206.894C623.831 206.251 623.894 205.68 624.021 205.181C624.151 204.682 624.337 204.26 624.579 203.917C624.822 203.572 625.114 203.309 625.457 203.129C625.8 202.949 626.186 202.859 626.613 202.859C627.052 202.859 627.442 202.946 627.782 203.118C628.123 203.291 628.399 203.53 628.61 203.835C628.823 204.139 628.959 204.486 629.017 204.875H627.919C627.845 204.568 627.697 204.317 627.476 204.122C627.256 203.928 626.968 203.831 626.613 203.831C626.073 203.831 625.652 204.066 625.349 204.536C625.049 205.007 624.898 205.66 624.896 206.498H624.95C625.077 206.289 625.233 206.111 625.418 205.965C625.605 205.816 625.814 205.702 626.044 205.623C626.277 205.542 626.521 205.501 626.778 205.501C627.205 205.501 627.591 205.605 627.937 205.814C628.285 206.02 628.562 206.306 628.768 206.67C628.975 207.035 629.078 207.453 629.078 207.923C629.078 208.393 628.971 208.819 628.758 209.201C628.546 209.582 628.249 209.884 627.865 210.107C627.481 210.328 627.032 210.436 626.519 210.431ZM626.515 209.496C626.799 209.496 627.052 209.426 627.275 209.287C627.498 209.148 627.674 208.961 627.804 208.725C627.933 208.49 627.998 208.228 627.998 207.937C627.998 207.654 627.935 207.396 627.807 207.164C627.683 206.931 627.51 206.746 627.289 206.609C627.071 206.473 626.821 206.404 626.541 206.404C626.327 206.404 626.129 206.445 625.947 206.527C625.767 206.608 625.608 206.721 625.472 206.865C625.335 207.009 625.227 207.174 625.148 207.361C625.071 207.546 625.033 207.742 625.033 207.948C625.033 208.224 625.096 208.48 625.223 208.715C625.353 208.95 625.529 209.139 625.752 209.283C625.978 209.425 626.232 209.496 626.515 209.496ZM631.112 209.416C630.915 209.416 630.746 209.347 630.605 209.208C630.463 209.066 630.392 208.896 630.392 208.697C630.392 208.5 630.463 208.332 630.605 208.193C630.746 208.051 630.915 207.98 631.112 207.98C631.309 207.98 631.478 208.051 631.62 208.193C631.761 208.332 631.832 208.5 631.832 208.697C631.832 208.829 631.798 208.95 631.731 209.06C631.666 209.168 631.58 209.254 631.472 209.319C631.364 209.384 631.244 209.416 631.112 209.416ZM631.112 205.724C630.915 205.724 630.746 205.654 630.605 205.515C630.463 205.374 630.392 205.203 630.392 205.004C630.392 204.807 630.463 204.64 630.605 204.5C630.746 204.359 630.915 204.288 631.112 204.288C631.309 204.288 631.478 204.359 631.62 204.5C631.761 204.64 631.832 204.807 631.832 205.004C631.832 205.136 631.798 205.257 631.731 205.368C631.666 205.476 631.58 205.562 631.472 205.627C631.364 205.692 631.244 205.724 631.112 205.724ZM635.825 210.431C635.331 210.431 634.889 210.346 634.5 210.176C634.114 210.005 633.808 209.769 633.583 209.467C633.36 209.162 633.24 208.809 633.223 208.409H634.353C634.367 208.627 634.44 208.817 634.572 208.977C634.707 209.136 634.882 209.258 635.098 209.344C635.314 209.431 635.554 209.474 635.818 209.474C636.108 209.474 636.365 209.424 636.588 209.323C636.813 209.222 636.99 209.082 637.117 208.902C637.244 208.719 637.308 208.51 637.308 208.272C637.308 208.025 637.244 207.808 637.117 207.621C636.992 207.431 636.809 207.282 636.566 207.174C636.326 207.066 636.036 207.012 635.695 207.012H635.073V206.105H635.695C635.969 206.105 636.209 206.056 636.415 205.958C636.624 205.86 636.787 205.723 636.905 205.548C637.022 205.37 637.081 205.163 637.081 204.925C637.081 204.697 637.029 204.499 636.926 204.331C636.825 204.161 636.681 204.028 636.494 203.932C636.309 203.836 636.091 203.788 635.839 203.788C635.599 203.788 635.375 203.832 635.166 203.921C634.96 204.007 634.792 204.132 634.662 204.295C634.533 204.456 634.463 204.649 634.454 204.875H633.378C633.39 204.476 633.507 204.126 633.73 203.824C633.956 203.521 634.253 203.285 634.623 203.115C634.992 202.944 635.403 202.859 635.854 202.859C636.326 202.859 636.734 202.952 637.077 203.136C637.423 203.319 637.689 203.562 637.876 203.867C638.066 204.172 638.159 204.505 638.157 204.867C638.159 205.28 638.044 205.63 637.811 205.918C637.581 206.206 637.274 206.399 636.89 206.498V206.555C637.38 206.63 637.759 206.824 638.027 207.138C638.298 207.453 638.433 207.843 638.43 208.308C638.433 208.713 638.32 209.077 638.092 209.398C637.867 209.72 637.558 209.973 637.167 210.158C636.776 210.34 636.329 210.431 635.825 210.431ZM642.357 210.453C641.788 210.451 641.302 210.301 640.899 210.003C640.496 209.706 640.188 209.272 639.974 208.704C639.761 208.135 639.654 207.45 639.654 206.649C639.654 205.85 639.761 205.167 639.974 204.601C640.19 204.035 640.5 203.603 640.903 203.306C641.308 203.008 641.793 202.859 642.357 202.859C642.921 202.859 643.404 203.009 643.807 203.309C644.21 203.607 644.519 204.039 644.732 204.605C644.948 205.169 645.056 205.85 645.056 206.649C645.056 207.453 644.949 208.139 644.736 208.707C644.522 209.274 644.214 209.707 643.811 210.007C643.408 210.304 642.923 210.453 642.357 210.453ZM642.357 209.492C642.856 209.492 643.2Line truncated
</g>
</g>
<rect x="0.5" y="0.5" width="879" height="519" rx="12.5" stroke="#575757"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_16843_34774" transform="scale(0.00166667)"/>
</pattern>
<pattern id="pattern1" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image1_16843_34774" transform="scale(0.00390625)"/>
</pattern>
<clipPath id="clip0_16843_34774">
<rect x="1" y="1" width="878" height="518" rx="12" fill="white"/>
</clipPath>
<clipPath id="clip1_16843_34774">
<rect width="11.5823" height="11.5823" fill="white" transform="translate(26.7842 89.9414)"/>
</clipPath>
<clipPath id="clip2_16843_34774">
<rect width="11.5823" height="11.5823" fill="white" transform="translate(26.7842 116.21)"/>
</clipPath>
<clipPath id="clip3_16843_34774">
<rect width="11.5823" height="11.5823" fill="white" transform="translate(26.7842 142.688)"/>
</clipPath>
<clipPath id="clip4_16843_34774">
<rect width="11.5823" height="11.5823" fill="white" transform="translate(26.7842 169.166)"/>
</clipPath>
<clipPath id="clip5_16843_34774">
<rect x="44.1577" y="168.957" width="83.4778" height="12" rx="2.89557" fill="white"/>
</clipPath>
<clipPath id="clip6_16843_34774">
<rect width="129.721" height="451.984" fill="white" transform="translate(578.876 59.3291)"/>
</clipPath>
<image id="image0_16843_34774" width="600" height="600" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAIAAAAxBA+LAAAMbWlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnluSkJCEEkBASuhNEKkBpITQAkgvgo2QBBJKjAlBxV4WFVy7iIANXRVRbCsgduzKotj7YkFFWRd1saHyJiSg677yvfN9c++fM2f+U+5M7j0AaH3gSaV5qDYA+ZICWUJ4MHN0WjqT9AwgQAfQAQt48fhyKTsuLhpAGbj/Xd7dgNZQrjoruf45/19FVyCU8wFAxkKcKZDz8yE+DgBexZfKCgAgKvVWkwukSjwbYj0ZDBDiVUqcrcLblThThQ/32yQlcCC+DIAGlceTZQNAvwf1zEJ+NuShf4bYVSIQSwDQGgZxAF/EE0CsjH1Yfv5EJS6H2B7aSyGG8QBW5nec2X/jzxzk5/GyB7Eqr37RCBHLpXm8qf9naf635OcpBnzYwkEVySISlPnDGt7KnRilxFSIuySZMbHKWkP8QSxQ1R0AlCJSRCSr7FETvpwD6wcMIHYV8EKiIDaBOEySFxOt1mdmicO4EMPdgk4RF3CTIDaEeKFQHpqottkom5ig9oXWZ8k4bLX+HE/W71fp64EiN5mt5n8jEnLV/Bi9SJSUCjEFYutCcUoMxHSIXeS5iVFqm5FFIk7MgI1MkaCM3xriBKEkPFjFjxVmycIS1PYl+fKBfLGNIjE3Ro33FYiSIlT1wU7xef3xw1ywy0IJO3mARygfHT2Qi0AYEqrKHXsulCQnqnk+SAuCE1RrcYo0L05tj1sK88KVekuIPeSFieq1eEoB3JwqfjxLWhCXpIoTL8rhRcap4sGXgWjAASGACRRwZIKJIAeIW7sauuAv1UwY4AEZyAZC4KzWDKxI7Z+RwGsiKAJ/QCQE8sF1wf2zQlAI9V8GtaqrM8jqny3sX5ELnkKcD6JAHvyt6F8lGfSWAp5Ajfgf3nlw8GG8eXAo5/+9fkD7TcOGmmi1RjHgkak1YEkMJYYQI4hhRAfcGA/A/fBoeA2Cww1n4T4DeXyzJzwltBEeEa4T2gm3J4jnyn6IchRoh/xh6lpkfl8L3BZyeuLBuD9kh8y4AW4MnHEP6IeNB0LPnlDLUcetrArzB+6/ZfDd01DbkV3JKHkIOYhs/+NKuiPdc5BFWevv66OKNXOw3pzBmR/9c76rvgDeo360xBZi+7Gz2AnsPHYYawBM7BjWiLVgR5R4cHc96d9dA94S+uPJhTzif/gbeLLKSspda107XT+r5gqEUwqUB48zUTpVJs4WFTDZ8O0gZHIlfJdhTDdXN3cAlO8a1d/X2/j+dwhi0PJNN+93APyP9fX1HfqmizwGwF5vePwPftPZswDQ0QTg3EG+Qlao0uHKCwH+S2jBk2YEzIAVsIf5uAEv4AeCQCiIBLEgCaSB8TB6EdznMjAZTAdzQDEoBcvAalABNoDNYDvYBfaBBnAYnABnwEVwGVwHd+Hu6QAvQTd4B3oRBCEhNISBGCHmiA3ihLghLCQACUWikQQkDclAshEJokCmI/OQUmQFUoFsQmqQvchB5ARyHmlDbiMPkU7kDfIJxVAqqoeaorbocJSFstEoNAkdh2ajk9AidD66BC1Hq9GdaD16Ar2IXkfb0ZdoDwYwTcwAs8CcMRbGwWKxdCwLk2EzsRKsDKvG6rAm+JyvYu1YF/YRJ+IMnIk7wx0cgSfjfHwSPhNfjFfg2/F6/BR+FX+Id+NfCTSCCcGJ4EvgEkYTsgmTCcWEMsJWwgHCaXiWOgjviESiAdGO6A3PYhoxhziNuJi4jribeJzYRnxM7CGRSEYkJ5I/KZbEIxWQiklrSTtJx0hXSB2kDxqaGuYabhphGukaEo25GmUaOzSOalzReKbRS9Ym25B9ybFkAXkqeSl5C7mJfIncQe6l6FDsKP6UJEoOZQ6lnFJHOU25R3mrqalpqemjGa8p1pytWa65R/Oc5kPNj1RdqiOVQx1LVVCXULdRj1NvU9/SaDRbWhAtnVZAW0KroZ2kPaB9oDPoLnQuXUCfRa+k19Ov0F9pkbVstNha47WKtMq09mtd0urSJmvbanO0edoztSu1D2rf1O7RYeiM0InVyddZrLND57zOc12Srq1uqK5Ad77uZt2Tuo8ZGMOKwWHwGfMYWxinGR16RD07Pa5ejl6p3i69Vr1ufV19D/0U/Sn6lfpH9NsNMANbA65BnsFSg30GNww+DTEdwh4iHLJoSN2QK0PeGw41DDIUGpYY7ja8bvjJiGkUapRrtNyowei+MW7saBxvPNl4vfFp466hekP9hvKHlgzdN/SOCWriaJJgMs1ks0mLSY+pmWm4qdR0relJ0y4zA7MgsxyzVWZHzTrNGeYB5mLzVebHzF8w9ZlsZh6znHmK2W1hYhFhobDYZNFq0WtpZ5lsOddyt+V9K4oVyyrLapVVs1W3tbn1KOvp1rXWd2zINiwbkc0am7M2723tbFNtF9g22D63M7Tj2hXZ1drds6fZB9pPsq+2v+ZAdGA55Dqsc7jsiDp6OoocKx0vOaFOXk5ip3VObcMIw3yGSYZVD7vpTHVmOxc61zo/dDFwiXaZ69Lg8mq49fD04cuHnx3+1dXTNc91i+vdEbojIkfMHdE04o2boxvfrdLtmjvNPcx9lnuj+2sPJw+hx3qPW54Mz1GeCzybPb94eXvJvOq8Or2tvTO8q7xvsvRYcazFrHM+BJ9gn1k+h30++nr5Fvju8/3Tz9kv12+H3/ORdiOFI7eMfOxv6c/z3+TfHsAMyAjYGNAeaBHIC6wOfBRkFSQI2hr0jO3AzmHvZL8Kdg2WBR8Ifs/x5czgHA/BQsJDSkJaQ3VDk0MrQh+EWYZlh9WGdYd7hk8LPx5BiIiKWB5xk2vK5XNruN2R3pEzIk9FUaMSoyqiHkU7Rsuim0ahoyJHrRx1L8YmRhLTEAtiubErY+/H2cVNijsUT4yPi6+Mf5owImF6wtlERuKExB2J75KCk5Ym3U22T1YkN6dopYxNqUl5nxqSuiK1ffTw0TNGX0wzThOnNaaT0lPSt6b3jAkds3pMx1jPscVjb4yzGzdl3PnxxuPzxh+ZoDWBN2F/BiEjNWNHxmdeLK+a15PJzazK7OZz+Gv4LwVBglWCTqG/cIXwWZZ/1oqs59n+2SuzO0WBojJRl5gjrhC/zonI2ZDzPjc2d1tuX15q3u58jfyM/IMSXUmu5NREs4lTJrZJnaTF0vZJvpNWT+qWRcm2yhH5OHljgR78qG9R2Ct+UjwsDCisLPwwOWXy/ik6UyRTWqY6Tl009VlRWNEv0/Bp/GnN0y2mz5n+cAZ7xqaZyMzMmc2zrGbNn9UxO3z29jmUOblzfpvrOnfF3L/mpc5rmm86f/b8xz+F/1RbTC+WFd9c4Ldgw0J8oXhh6yL3RWsXfS0RlFwodS0tK/28mL/4ws8jfi7/uW9J1pLWpV5L1y8jLpMsu7E8cPn2FTorilY8XjlqZf0q5qqSVX+tnrD6fJlH2YY1lDWKNe3l0eWNa63XLlv7uUJUcb0yuHJ3lUnVoqr36wTrrqwPWl+3wXRD6YZPG8Ubb20K31RfbVtdtpm4uXDz0y0pW87+wvqlZqvx1tKtX7ZJtrVvT9h+qsa7pmaHyY6ltWitorZz59idl3eF7Gqsc67btNtgd+kesEex58XejL039kXta97P2l/3q82vVQcYB0rqkfqp9d0Noob2xrTGtoORB5ub/JoOHHI5tO2wxeHKI/pHlh6lHJ1/tO9Y0bGe49LjXSeyTzxuntB89+Tok9dOxZ9qPR11+tyZsDMnz7LPHjvnf+7wed/zBy+wLjRc9LpY3+LZcuA3z98OtHq11l/yvtR42edyU9vItqNXAq+cuBpy9cw17rWL12Out91IvnHr5tib7bcEt57fzrv9+k7hnd67s+8R7pXc175f9sDkQfXvDr/vbvdqP/Iw5GHLo8RHdx/zH798In/yuWP+U9rTsmfmz2qeuz0/3BnWefnFmBcdL6Uve7uK/9D5o+qV/atf/wz6s6V7dHfHa9nrvjeL3xq93faXx1/NPXE9D97lv+t9X/LB6MP2j6yPZz+lfnrWO/kz6XP5F4cvTV+jvt7ry+/rk/JkvP5PAQwONCsLgDfbAKClAcCAfRtljKoX7BdE1b/2I/CfsKpf7BcvAOrg93t8F/y6uQnAni2w/YL8WrBXjaMBkOQDUHf3waEWeZa7m4qLCvsUwoO+vrewZyOtBODLsr6+3uq+vi+bYbCwdzwuUfWgSiHCnmEj90tmfib4N6LqT7/L8cc7UEbgAX68/wvvK5EZ+pqtigAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAACWKADAAQAAAABAAACWAAAAACMVKCiAAAWiklEQVR4Ae3VQQ0AIBADQUA2ghDJi6Bj5xx0eknnuXs4AgQIECBQFVjV4HITIECAAIEvYAj9AQECBAikBQxhun7hCRAgQMAQ+gECBAgQSAsYwnT9whMgQICAIfQDBAgQIJAWMITp+oUnQIAAAUPoBwgQIEAgLWAI0/ULT4AAAQKG0A8QIECAQFrAEKbrF54AAQIEDKEfIECAAIG0gCFM1y88AQIECBhCP0CAAAECaQFDmK5feAIECBAwhH6AAAECBNIChjBdv/AECBAgYAj9AAECBAikBQxhun7hCRAgQMAQ+gECBAgQSAsYwnT9whMgQICAIfQDBAgQIJAWMITp+oUnQIAAAUPoBwgQIEAgLWAI0/ULT4AAAQKG0A8QIECAQFrAEKbrF54AAQIEDKEfIECAAIG0gCFM1y88AQIECBhCP0CAAAECaQFDmK5feAIECBAwhH6AAAECBNIChjBdv/AECBAgYAj9AAECBAikBQxhun7hCRAgQMAQ+gECBAgQSLine truncated
<image id="image1_16843_34774" width="256" height="256" xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdCIFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAACVAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAAABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxiVFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQgQ29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAAABOk/gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJnAnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t////2wCEAAMEBAQGBAYHBwYICQgJCAwLCgoLDBINDg0ODRIcERQRERQRHBgdGBYYHRgsIh4eIiwyKigqMj02Nj1MSUxkZIYBAwQEBAYEBgcHBggJCAkIDAsKCgsMEg0ODQ4NEhwRFBERFBEcGB0YFhgdGCwiHh4iLDIqKCoyPTY2PUxJTGRkhv/CABEIAQABAAMBIgACEQEDEQH/xAAdAAABBQEBAQEAAAAAAAAAAAABAAQFBgcCAwgJ/9oACAEBAAAAANmSAC54gvn11CGmVVhAVi0T2zfVvqEkAuZFIAAVf5z9qdRLnL55CdMPGRvX0rrSQQQEikFyOPmFvU8rrM3YKrqVpz3NHzDfd71BIBBSKCADD5QY+dFUe1kJWOlozH2ukaz9guyAEJJIADL/AJ3g/HS38p5TTbO6zVJDN3+qfYE+kgFIlcocYDjwt23phH+PlT2dUjqFFaP9f2gJACTSAHPzXlsxst3beTKL4rnnEQjTH9C+xZZJcoSaS5QwD5+1PWbH4iGYM41nBR09kVk+xekEAJNJACo/F2o6vZ+2kPCRsuwp/Mvldr+rSAgBJpIBNPhnStgl/GKpMdJTtNpElds12vVwkABJkIBeXxXdtzjs/wAtidguVBxzNnGi3Xd74gAgJNJBCu51lX0qs4yitbppLine truncated
</defs>
</svg>

After

Width:  |  Height:  |  Size: 342 KiB

@@ -1287,6 +1287,7 @@
"default_calendar_selected": "Default calendar", "default_calendar_selected": "Default calendar",
"hide_from_profile": "Hide from profile", "hide_from_profile": "Hide from profile",
"event_setup_tab_title": "Event Setup", "event_setup_tab_title": "Event Setup",
"availability_not_found_in_schedule_error":"No availability found in schedule",
"event_limit_tab_title": "Limits", "event_limit_tab_title": "Limits",
"event_limit_tab_description": "How often you can be booked", "event_limit_tab_description": "How often you can be booked",
"event_advanced_tab_description": "Calendar settings & more...", "event_advanced_tab_description": "Calendar settings & more...",
@@ -2193,6 +2194,11 @@
"uprade_to_create_instant_bookings": "Upgrade to Enterprise and let guests join an instant call that attendees can jump straight into. This is only for team event types", "uprade_to_create_instant_bookings": "Upgrade to Enterprise and let guests join an instant call that attendees can jump straight into. This is only for team event types",
"dont_want_to_wait": "Don't want to wait?", "dont_want_to_wait": "Don't want to wait?",
"meeting_started": "Meeting Started", "meeting_started": "Meeting Started",
"booking_not_found_error": "Could not find booking",
"booking_seats_full_error": "Booking seats are full",
"missing_payment_credential_error": "Missing payment credentials",
"missing_payment_app_id_error": "Missing payment app id",
"not_enough_available_seats_error": "Booking does not have enough available seats",
"user_redirect_title": "{{username}} is currently away for a brief period of time.", "user_redirect_title": "{{username}} is currently away for a brief period of time.",
"user_redirect_description": "In the meantime, {{profile.username}} will be in charge of all the new scheduled meetings on behalf of {{username}}.", "user_redirect_description": "In the meantime, {{profile.username}} will be in charge of all the new scheduled meetings on behalf of {{username}}.",
"out_of_office": "Out of office", "out_of_office": "Out of office",
@@ -891,6 +891,7 @@
"toggle_calendars_conflict": "סמן/י את לוחות השנה שבהם ברצונך לבדוק אם יש התנגשויות כדי למנוע כפל הזמנות.", "toggle_calendars_conflict": "סמן/י את לוחות השנה שבהם ברצונך לבדוק אם יש התנגשויות כדי למנוע כפל הזמנות.",
"connect_additional_calendar": "חיבור לוח שנה נוסף", "connect_additional_calendar": "חיבור לוח שנה נוסף",
"calendar_updated_successfully": "עדכון לוח השנה בוצע בהצלחה", "calendar_updated_successfully": "עדכון לוח השנה בוצע בהצלחה",
"check_here": "נא לבדוק כאן",
"conferencing": "שיחות ועידה", "conferencing": "שיחות ועידה",
"calendar": "לוח שנה", "calendar": "לוח שנה",
"payments": "תשלומים", "payments": "תשלומים",
@@ -1286,6 +1287,7 @@
"default_calendar_selected": "לוח השנה המוגדר כברירת מחדל", "default_calendar_selected": "לוח השנה המוגדר כברירת מחדל",
"hide_from_profile": "הסתר מהפרופיל", "hide_from_profile": "הסתר מהפרופיל",
"event_setup_tab_title": "הגדרת ארוע", "event_setup_tab_title": "הגדרת ארוע",
"availability_not_found_in_schedule_error": "לא נמצא חלון זמינות בלוח הזמנים",
"event_limit_tab_title": "מגבלות", "event_limit_tab_title": "מגבלות",
"event_limit_tab_description": "באיזו תדירות ניתן להזמין אותך", "event_limit_tab_description": "באיזו תדירות ניתן להזמין אותך",
"event_advanced_tab_description": "הגדרות לוח שנה ועוד...", "event_advanced_tab_description": "הגדרות לוח שנה ועוד...",
@@ -1306,6 +1308,7 @@
"do_this": "עשה את זה", "do_this": "עשה את זה",
"turn_off": "כבה", "turn_off": "כבה",
"turn_on": "הפעלה", "turn_on": "הפעלה",
"cancelled_bookings_cannot_be_rescheduled": "אי אפשר לתזמן מחדש הזמנה שבוטלה",
"settings_updated_successfully": "עדכון ההגדרות בוצע בהצלחה", "settings_updated_successfully": "עדכון ההגדרות בוצע בהצלחה",
"error_updating_settings": "שגיאה בעדכון ההגדרות", "error_updating_settings": "שגיאה בעדכון ההגדרות",
"personal_cal_url": "כתובת ה-URL האישית שלי של {{appName}}", "personal_cal_url": "כתובת ה-URL האישית שלי של {{appName}}",
@@ -2116,6 +2119,7 @@
"workflow_automation": "אוטומצית תהליך עבודה", "workflow_automation": "אוטומצית תהליך עבודה",
"workflow_automation_description": "אפשר לכוון את חוויית התזמון שלך עם תהליכי עבודה", "workflow_automation_description": "אפשר לכוון את חוויית התזמון שלך עם תהליכי עבודה",
"scheduling_for_your_team": "אוטומצית תהליך עבודה", "scheduling_for_your_team": "אוטומצית תהליך עבודה",
"scheduling_for_your_team_description": "עריכת תזמון לצוות שלך באופן שיתופי ולפי התור",
"no_members_found": "לא נמצא אף חבר", "no_members_found": "לא נמצא אף חבר",
"event_setup_length_error": "הגדרת אירוע: משך הזמן חייב להיות לפחות דקה אחת.", "event_setup_length_error": "הגדרת אירוע: משך הזמן חייב להיות לפחות דקה אחת.",
"availability_schedules": "לוחות זמנים לזמינוּת", "availability_schedules": "לוחות זמנים לזמינוּת",
@@ -2156,11 +2160,13 @@
"manage_availability_schedules": "ניהול לוחות זמנים לזמינות", "manage_availability_schedules": "ניהול לוחות זמנים לזמינות",
"lock_timezone_toggle_on_booking_page": "נעילת אזור הזמן בדף ההזמנות", "lock_timezone_toggle_on_booking_page": "נעילת אזור הזמן בדף ההזמנות",
"description_lock_timezone_toggle_on_booking_page": "כדי לנעול את אזור הזמן בדף ההזמנות – שימושי לאירועים אישיים.", "description_lock_timezone_toggle_on_booking_page": "כדי לנעול את אזור הזמן בדף ההזמנות – שימושי לאירועים אישיים.",
"event_setup_multiple_payment_apps_error": "יכול להיות רק יישומון תשלום אחד פעיל לכל סוג אירוע.",
"number_in_international_format": "נא למלא מספר בתבנית בינלאומית.", "number_in_international_format": "נא למלא מספר בתבנית בינלאומית.",
"install_calendar": "התקנת לוח שנה", "install_calendar": "התקנת לוח שנה",
"branded_subdomain": "תת־תחום ממותג", "branded_subdomain": "תת־תחום ממותג",
"branded_subdomain_description": "קבלת תת־תחום ממותג משלך, כגון acme.cal.com", "branded_subdomain_description": "קבלת תת־תחום ממותג משלך, כגון acme.cal.com",
"org_insights": "תובנות כלל־ארגוניות", "org_insights": "תובנות כלל־ארגוניות",
"org_insights_description": "הפקת תובנות בנוגע לאופן בו הארגון שלך משקיע את זמנו",
"extensive_whitelabeling": "תהליך הטמעה והנדסת תמיכה אישי", "extensive_whitelabeling": "תהליך הטמעה והנדסת תמיכה אישי",
"extensive_whitelabeling_description": "אפשר לעצב את חוויית הזימון שלך עם לוגו, צבעים ופריטי עיצוב נוספים משלך", "extensive_whitelabeling_description": "אפשר לעצב את חוויית הזימון שלך עם לוגו, צבעים ופריטי עיצוב נוספים משלך",
"unlimited_teams": "כמות בלתי מוגבלת של צוותים", "unlimited_teams": "כמות בלתי מוגבלת של צוותים",
@@ -2173,6 +2179,7 @@
"create_your_org": "יצירת הארגון שלך", "create_your_org": "יצירת הארגון שלך",
"create_your_org_description": "אפשר לשדרג לרישיון תאגידי ולקבל תת־תחום, חיוב מאוחד, תובנות, שינוי מיתוג נרחב ועוד", "create_your_org_description": "אפשר לשדרג לרישיון תאגידי ולקבל תת־תחום, חיוב מאוחד, תובנות, שינוי מיתוג נרחב ועוד",
"other_payment_app_enabled": "אפשר להפעיל רק יישומון תשלום אחד לכל סוג אירוע", "other_payment_app_enabled": "אפשר להפעיל רק יישומון תשלום אחד לכל סוג אירוע",
"admin_delete_organization_description": "<ul><li>צוותים שחברים בארגון הזה יימחקו יחד עם סוגי האירועים שלהם</li><li>משתמשים שהיו חלק מהארגון לא יימחקו וסוג האירועים שלהם יישארו ללא שינוי.</li><li>שמות המשתמשים ישתנו כדי לאפשר להם להתקיים מחוץ לארגון שהיו חברים בו</li></ul>",
"admin_delete_organization_title": "למחוק את הארגון?", "admin_delete_organization_title": "למחוק את הארגון?",
"published": "מפורסם", "published": "מפורסם",
"unpublished": "לא מפורסם", "unpublished": "לא מפורסם",
@@ -2187,6 +2194,12 @@
"uprade_to_create_instant_bookings": "ניתן לשדג לרישיון התאגידי ולאפשר למשתמשים להצטרף לשיחה מיידית שמשתתפים יכולים לקפוץ ישירות אליה. זה מיועד רק לסוגי אירועים של צוותים", "uprade_to_create_instant_bookings": "ניתן לשדג לרישיון התאגידי ולאפשר למשתמשים להצטרף לשיחה מיידית שמשתתפים יכולים לקפוץ ישירות אליה. זה מיועד רק לסוגי אירועים של צוותים",
"dont_want_to_wait": "לא רוצה להמתין?", "dont_want_to_wait": "לא רוצה להמתין?",
"meeting_started": "הפגישה החלה", "meeting_started": "הפגישה החלה",
"booking_not_found_error": "לא ניתן למצוא הזמנה",
"booking_seats_full_error": "חלונות ההזמנה מלאים",
"missing_payment_credential_error": "פרטי התשלום חסרים",
"missing_payment_app_id_error": "מזהה יישום תשלום חסר",
"not_enough_available_seats_error": "להזמנה אין מספיק חלונות פנויים",
"user_redirect_title": "{{username}} לא כאן כרגע לזמן קצר.",
"user_redirect_description": "בינתיים, האחריות על כל ההזמנות המתוזמנות החדשות בשם {{username}} תהיה על כתפי {{profile.username}}.", "user_redirect_description": "בינתיים, האחריות על כל ההזמנות המתוזמנות החדשות בשם {{username}} תהיה על כתפי {{profile.username}}.",
"out_of_office": "מחוץ למשרד", "out_of_office": "מחוץ למשרד",
"out_of_office_description": "הגדרת פעולות בפרופיל שלך כשאינך במשרד.", "out_of_office_description": "הגדרת פעולות בפרופיל שלך כשאינך במשרד.",
@@ -19,6 +19,7 @@ import { safeStringify } from "@calcom/lib/safeStringify";
import type { WorkflowActions, WorkflowTemplates, WorkflowTriggerEvents } from "@calcom/prisma/client"; import type { WorkflowActions, WorkflowTemplates, WorkflowTriggerEvents } from "@calcom/prisma/client";
import type { SchedulingType } from "@calcom/prisma/enums"; import type { SchedulingType } from "@calcom/prisma/enums";
import type { BookingStatus } from "@calcom/prisma/enums"; import type { BookingStatus } from "@calcom/prisma/enums";
import type { userMetadataType } from "@calcom/prisma/zod-utils";
import type { AppMeta } from "@calcom/types/App"; import type { AppMeta } from "@calcom/types/App";
import type { NewCalendarEventType } from "@calcom/types/Calendar"; import type { NewCalendarEventType } from "@calcom/types/Calendar";
import type { EventBusyDate, IntervalLimit } from "@calcom/types/Calendar"; import type { EventBusyDate, IntervalLimit } from "@calcom/types/Calendar";
@@ -128,6 +129,8 @@ export type InputEventType = {
durationLimits?: IntervalLimit; durationLimits?: IntervalLimit;
} & Partial<Omit<Prisma.EventTypeCreateInput, "users" | "schedule" | "bookingLimits" | "durationLimits">>; } & Partial<Omit<Prisma.EventTypeCreateInput, "users" | "schedule" | "bookingLimits" | "durationLimits">>;
type AttendeeBookingSeatInput = Pick<Prisma.BookingSeatCreateInput, "referenceUid" | "data">;
type WhiteListedBookingProps = { type WhiteListedBookingProps = {
id?: number; id?: number;
uid?: string; uid?: string;
@@ -137,11 +140,15 @@ type WhiteListedBookingProps = {
endTime: string; endTime: string;
title?: string; title?: string;
status: BookingStatus; status: BookingStatus;
attendees?: { email: string }[]; attendees?: {
email: string;
bookingSeat?: AttendeeBookingSeatInput | null;
}[];
references?: (Omit<ReturnType<typeof getMockBookingReference>, "credentialId"> & { references?: (Omit<ReturnType<typeof getMockBookingReference>, "credentialId"> & {
// TODO: Make sure that all references start providing credentialId and then remove this intersection of optional credentialId // TODO: Make sure that all references start providing credentialId and then remove this intersection of optional credentialId
credentialId?: number | null; credentialId?: number | null;
})[]; })[];
bookingSeat?: Prisma.BookingSeatCreateInput[];
}; };
type InputBooking = Partial<Omit<Booking, keyof WhiteListedBookingProps>> & WhiteListedBookingProps; type InputBooking = Partial<Omit<Booking, keyof WhiteListedBookingProps>> & WhiteListedBookingProps;
@@ -334,7 +341,7 @@ async function addBookings(bookings: InputBooking[]) {
); );
} }
return { return {
uid: uuidv4(), uid: booking.uid || uuidv4(),
workflowReminders: [], workflowReminders: [],
references: [], references: [],
title: "Test Booking Title", title: "Test Booking Title",
@@ -361,10 +368,23 @@ async function addBookings(bookings: InputBooking[]) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore //@ts-ignore
createMany: { createMany: {
data: booking.attendees, data: booking.attendees.map((attendee) => {
if (attendee.bookingSeat) {
const { bookingSeat, ...attendeeWithoutBookingSeat } = attendee;
return {
...attendeeWithoutBookingSeat,
bookingSeat: {
create: { ...bookingSeat, bookingId: booking.id },
},
};
} else {
return attendee;
}
}),
}, },
}; };
} }
return bookingCreate; return bookingCreate;
}) })
); );
@@ -887,6 +907,7 @@ export function getOrganizer({
weekStart = "Sunday", weekStart = "Sunday",
teams, teams,
organizationId, organizationId,
metadata,
}: { }: {
name: string; name: string;
email: string; email: string;
@@ -899,6 +920,7 @@ export function getOrganizer({
destinationCalendar?: Prisma.DestinationCalendarCreateInput; destinationCalendar?: Prisma.DestinationCalendarCreateInput;
weekStart?: WeekDays; weekStart?: WeekDays;
teams?: InputUser["teams"]; teams?: InputUser["teams"];
metadata?: userMetadataType;
}) { }) {
return { return {
...TestData.users.example, ...TestData.users.example,
@@ -913,6 +935,7 @@ export function getOrganizer({
weekStart, weekStart,
teams, teams,
organizationId, organizationId,
metadata,
}; };
} }
@@ -1394,13 +1417,18 @@ export function getMockBookingReference(
}; };
} }
export function getMockBookingAttendee(attendee: Omit<Attendee, "bookingId">) { export function getMockBookingAttendee(
attendee: Omit<Attendee, "bookingId"> & {
bookingSeat?: AttendeeBookingSeatInput;
}
) {
return { return {
id: attendee.id, id: attendee.id,
timeZone: attendee.timeZone, timeZone: attendee.timeZone,
name: attendee.name, name: attendee.name,
email: attendee.email, email: attendee.email,
locale: attendee.locale, locale: attendee.locale,
bookingSeat: attendee.bookingSeat || null,
}; };
} }
+1 -1
View File
@@ -1,2 +1,2 @@
export { default as add } from "./add"; export { default as add } from "./add";
export { default as webhook, config } from "@calcom/web/pages/api/integrations/alby/webhook"; export { default as webhook, config } from "./webhook";
@@ -2,6 +2,7 @@
import type { Prisma } from "@prisma/client"; import type { Prisma } from "@prisma/client";
import type { calendar_v3 } from "googleapis"; import type { calendar_v3 } from "googleapis";
import { google } from "googleapis"; import { google } from "googleapis";
import { RRule } from "rrule";
import { MeetLocationType } from "@calcom/app-store/locations"; import { MeetLocationType } from "@calcom/app-store/locations";
import dayjs from "@calcom/dayjs"; import dayjs from "@calcom/dayjs";
@@ -209,6 +210,16 @@ export default class GoogleCalendarService implements Calendar {
payload["location"] = getLocation(calEventRaw); payload["location"] = getLocation(calEventRaw);
} }
if (calEventRaw.recurringEvent) {
const rule = new RRule({
freq: calEventRaw.recurringEvent.freq,
interval: calEventRaw.recurringEvent.interval,
count: calEventRaw.recurringEvent.count,
});
payload["recurrence"] = [rule.toString()];
}
if (calEventRaw.conferenceData && calEventRaw.location === MeetLocationType) { if (calEventRaw.conferenceData && calEventRaw.location === MeetLocationType) {
payload["conferenceData"] = calEventRaw.conferenceData; payload["conferenceData"] = calEventRaw.conferenceData;
} }
@@ -220,22 +231,70 @@ export default class GoogleCalendarService implements Calendar {
"primary"; "primary";
try { try {
const event = await calendar.events.insert({ let event;
calendarId: selectedCalendar, let recurringEventId = null;
requestBody: payload, if (calEventRaw.existingRecurringEvent) {
conferenceDataVersion: 1, recurringEventId = calEventRaw.existingRecurringEvent.recurringEventId;
sendUpdates: "none", const recurringEventInstances = await calendar.events.instances({
}); calendarId: selectedCalendar,
eventId: calEventRaw.existingRecurringEvent.recurringEventId,
});
if (recurringEventInstances.data.items) {
const calComEventStartTime = dayjs(calEventRaw.startTime).format();
for (let i = 0; i < recurringEventInstances.data.items.length; i++) {
const instance = recurringEventInstances.data.items[i];
const instanceStartTime = dayjs(instance.start?.dateTime)
.tz(instance.start?.timeZone == null ? undefined : instance.start?.timeZone)
.format();
if (event && event.data.id && event.data.hangoutLink) { if (instanceStartTime === calComEventStartTime) {
event = instance;
break;
}
}
if (!event) {
event = recurringEventInstances.data.items[0];
this.log.error(
"Unable to find matching event amongst recurring event instances",
safeStringify({ selectedCalendar, credentialId })
);
}
await calendar.events.patch({
calendarId: selectedCalendar,
eventId: event.id || "",
requestBody: {
description: getRichDescription({
...calEventRaw,
}),
},
});
}
} else {
const eventResponse = await calendar.events.insert({
calendarId: selectedCalendar,
requestBody: payload,
conferenceDataVersion: 1,
sendUpdates: "none",
});
event = eventResponse.data;
if (event.recurrence) {
if (event.recurrence.length > 0) {
recurringEventId = event.id;
event = await this.getFirstEventInRecurrence(recurringEventId, selectedCalendar, calendar);
}
}
}
if (event && event.id && event.hangoutLink) {
await calendar.events.patch({ await calendar.events.patch({
// Update the same event but this time we know the hangout link // Update the same event but this time we know the hangout link
calendarId: selectedCalendar, calendarId: selectedCalendar,
eventId: event.data.id || "", eventId: event.id || "",
requestBody: { requestBody: {
description: getRichDescription({ description: getRichDescription({
...calEventRaw, ...calEventRaw,
additionalInformation: { hangoutLink: event.data.hangoutLink }, additionalInformation: { hangoutLink: event.hangoutLink },
}), }),
}, },
}); });
@@ -243,14 +302,16 @@ export default class GoogleCalendarService implements Calendar {
return { return {
uid: "", uid: "",
...event.data, ...event,
id: event.data.id || "", id: event?.id || "",
thirdPartyRecurringEventId: recurringEventId,
additionalInfo: { additionalInfo: {
hangoutLink: event.data.hangoutLink || "", hangoutLink: event?.hangoutLink || "",
}, },
type: "google_calendar", type: "google_calendar",
password: "", password: "",
url: "", url: "",
iCalUID: event?.iCalUID,
}; };
} catch (error) { } catch (error) {
this.log.error( this.log.error(
@@ -260,6 +321,22 @@ export default class GoogleCalendarService implements Calendar {
throw error; throw error;
} }
} }
async getFirstEventInRecurrence(
recurringEventId: string | null | undefined,
selectedCalendar: string,
calendar: calendar_v3.Calendar
): Promise<calendar_v3.Schema$Event> {
const recurringEventInstances = await calendar.events.instances({
calendarId: selectedCalendar,
eventId: recurringEventId || "",
});
if (recurringEventInstances.data.items) {
return recurringEventInstances.data.items[0];
} else {
return {} as calendar_v3.Schema$Event;
}
}
async updateEvent(uid: string, event: CalendarEvent, externalCalendarId: string): Promise<any> { async updateEvent(uid: string, event: CalendarEvent, externalCalendarId: string): Promise<any> {
const payload: calendar_v3.Schema$Event = { const payload: calendar_v3.Schema$Event = {
+1 -1
View File
@@ -1,3 +1,3 @@
export { default as add } from "./add"; export { default as add } from "./add";
export { default as webhook, config } from "@calcom/web/pages/api/integrations/paypal/webhook"; export { default as webhook, config } from "./webhook";
export { default as capture } from "./capture"; export { default as capture } from "./capture";
+3
View File
@@ -173,6 +173,7 @@ export default class EventManager {
// References can be any type: calendar/video // References can be any type: calendar/video
const referencesToCreate = results.map((result) => { const referencesToCreate = results.map((result) => {
let thirdPartyRecurringEventId;
let createdEventObj: createdEventSchema | null = null; let createdEventObj: createdEventSchema | null = null;
if (typeof result?.createdEvent === "string") { if (typeof result?.createdEvent === "string") {
createdEventObj = createdEventSchema.parse(JSON.parse(result.createdEvent)); createdEventObj = createdEventSchema.parse(JSON.parse(result.createdEvent));
@@ -180,11 +181,13 @@ export default class EventManager {
const isCalendarType = isCalendarResult(result); const isCalendarType = isCalendarResult(result);
if (isCalendarType) { if (isCalendarType) {
evt.iCalUID = result.iCalUID || event.iCalUID || undefined; evt.iCalUID = result.iCalUID || event.iCalUID || undefined;
thirdPartyRecurringEventId = result.createdEvent?.thirdPartyRecurringEventId;
} }
return { return {
type: result.type, type: result.type,
uid: createdEventObj ? createdEventObj.id : result.createdEvent?.id?.toString() ?? "", uid: createdEventObj ? createdEventObj.id : result.createdEvent?.id?.toString() ?? "",
thirdPartyRecurringEventId: isCalendarType ? thirdPartyRecurringEventId : undefined,
meetingId: createdEventObj ? createdEventObj.id : result.createdEvent?.id?.toString(), meetingId: createdEventObj ? createdEventObj.id : result.createdEvent?.id?.toString(),
meetingPassword: createdEventObj ? createdEventObj.password : result.createdEvent?.password, meetingPassword: createdEventObj ? createdEventObj.password : result.createdEvent?.password,
meetingUrl: createdEventObj ? createdEventObj.onlineMeetingUrl : result.createdEvent?.url, meetingUrl: createdEventObj ? createdEventObj.onlineMeetingUrl : result.createdEvent?.url,
@@ -4,6 +4,7 @@ import type {
AdditionalInformation, AdditionalInformation,
CalendarEvent, CalendarEvent,
ConferenceData, ConferenceData,
ExistingRecurringEvent,
Person, Person,
VideoCallData, VideoCallData,
} from "@calcom/types/Calendar"; } from "@calcom/types/Calendar";
@@ -22,6 +23,7 @@ class CalendarEventClass implements CalendarEvent {
conferenceData?: ConferenceData; conferenceData?: ConferenceData;
additionalInformation?: AdditionalInformation; additionalInformation?: AdditionalInformation;
uid?: string | null; uid?: string | null;
existingRecurringEvent?: ExistingRecurringEvent | null;
videoCallData?: VideoCallData; videoCallData?: VideoCallData;
paymentInfo?: any; paymentInfo?: any;
destinationCalendar?: DestinationCalendar[] | null; destinationCalendar?: DestinationCalendar[] | null;
+9 -1
View File
@@ -6,6 +6,7 @@ import dayjs from "@calcom/dayjs";
import { parseBookingLimit, parseDurationLimit } from "@calcom/lib"; import { parseBookingLimit, parseDurationLimit } from "@calcom/lib";
import { getWorkingHours } from "@calcom/lib/availability"; import { getWorkingHours } from "@calcom/lib/availability";
import { buildDateRanges, subtract } from "@calcom/lib/date-ranges"; import { buildDateRanges, subtract } from "@calcom/lib/date-ranges";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { HttpError } from "@calcom/lib/http-error"; import { HttpError } from "@calcom/lib/http-error";
import { descendingLimitKeys, intervalLimitKeyToUnit } from "@calcom/lib/intervalLimit"; import { descendingLimitKeys, intervalLimitKeyToUnit } from "@calcom/lib/intervalLimit";
import logger from "@calcom/lib/logger"; import logger from "@calcom/lib/logger";
@@ -258,6 +259,7 @@ const _getUserAvailability = async function getUsersWorkingHoursLifeTheUniverseA
const useHostSchedulesForTeamEvent = eventType?.metadata?.config?.useHostSchedulesForTeamEvent; const useHostSchedulesForTeamEvent = eventType?.metadata?.config?.useHostSchedulesForTeamEvent;
const schedule = !useHostSchedulesForTeamEvent && eventType?.schedule ? eventType.schedule : userSchedule; const schedule = !useHostSchedulesForTeamEvent && eventType?.schedule ? eventType.schedule : userSchedule;
log.debug( log.debug(
"Using schedule:", "Using schedule:",
safeStringify({ safeStringify({
@@ -272,8 +274,14 @@ const _getUserAvailability = async function getUsersWorkingHoursLifeTheUniverseA
const timeZone = schedule?.timeZone || eventType?.timeZone || user.timeZone; const timeZone = schedule?.timeZone || eventType?.timeZone || user.timeZone;
if (
!(schedule?.availability || (eventType?.availability.length ? eventType.availability : user.availability))
) {
throw new HttpError({ statusCode: 400, message: ErrorCode.AvailabilityNotFoundInSchedule });
}
const availability = ( const availability = (
schedule.availability || (eventType?.availability.length ? eventType.availability : user.availability) schedule?.availability || (eventType?.availability.length ? eventType.availability : user.availability)
).map((a) => ({ ).map((a) => ({
...a, ...a,
userId: user.id, userId: user.id,
@@ -41,7 +41,7 @@ export default class AttendeeScheduledEmail extends BaseEmail {
? this.t("your_event_has_been_scheduled_recurring") ? this.t("your_event_has_been_scheduled_recurring")
: this.t("your_event_has_been_scheduled"), : this.t("your_event_has_been_scheduled"),
role: "attendee", role: "attendee",
subtitle: "emailed_you_and_any_other_attendees", subtitle: this.t("emailed_you_and_any_other_attendees"),
status: "CONFIRMED", status: "CONFIRMED",
}), }),
method: "REQUEST", method: "REQUEST",
+18 -1
View File
@@ -72,13 +72,30 @@ export async function getServerSession(options: {
email_verified: user.emailVerified !== null, email_verified: user.emailVerified !== null,
role: user.role, role: user.role,
image: `${CAL_URL}/${user.username}/avatar.png`, image: `${CAL_URL}/${user.username}/avatar.png`,
impersonatedByUID: token.impersonatedByUID ?? undefined,
belongsToActiveTeam: token.belongsToActiveTeam, belongsToActiveTeam: token.belongsToActiveTeam,
org: token.org, org: token.org,
locale: user.locale ?? undefined, locale: user.locale ?? undefined,
}, },
}; };
if (token?.impersonatedBy?.id) {
const impersonatedByUser = await prisma.user.findUnique({
where: {
id: token.impersonatedBy.id,
},
select: {
id: true,
role: true,
},
});
if (impersonatedByUser) {
session.user.impersonatedBy = {
id: impersonatedByUser?.id,
role: impersonatedByUser.role,
};
}
}
CACHE.set(JSON.stringify(token), session); CACHE.set(JSON.stringify(token), session);
return session; return session;
@@ -508,7 +508,7 @@ export const AUTH_OPTIONS: AuthOptions = {
username: user.username, username: user.username,
email: user.email, email: user.email,
role: user.role, role: user.role,
impersonatedByUID: user?.impersonatedByUID, impersonatedBy: user.impersonatedBy,
belongsToActiveTeam: user?.belongsToActiveTeam, belongsToActiveTeam: user?.belongsToActiveTeam,
org: user?.org, org: user?.org,
locale: user?.locale, locale: user?.locale,
@@ -547,7 +547,7 @@ export const AUTH_OPTIONS: AuthOptions = {
username: existingUser.username, username: existingUser.username,
email: existingUser.email, email: existingUser.email,
role: existingUser.role, role: existingUser.role,
impersonatedByUID: token.impersonatedByUID as number, impersonatedBy: token.impersonatedBy,
belongsToActiveTeam: token?.belongsToActiveTeam as boolean, belongsToActiveTeam: token?.belongsToActiveTeam as boolean,
org: token?.org, org: token?.org,
locale: existingUser.locale, locale: existingUser.locale,
@@ -567,7 +567,7 @@ export const AUTH_OPTIONS: AuthOptions = {
name: token.name, name: token.name,
username: token.username as string, username: token.username as string,
role: token.role as UserPermissionRole, role: token.role as UserPermissionRole,
impersonatedByUID: token.impersonatedByUID as number, impersonatedBy: token.impersonatedBy,
belongsToActiveTeam: token?.belongsToActiveTeam as boolean, belongsToActiveTeam: token?.belongsToActiveTeam as boolean,
org: token?.org, org: token?.org,
locale: token.locale, locale: token.locale,
@@ -59,6 +59,7 @@ async function getBookingToDelete(id: number | undefined, uid: string | undefine
type: true, type: true,
externalCalendarId: true, externalCalendarId: true,
credentialId: true, credentialId: true,
thirdPartyRecurringEventId: true,
}, },
}, },
payment: true, payment: true,
@@ -458,24 +459,37 @@ async function handler(req: CustomRequest) {
bookingToDelete.recurringEventId && bookingToDelete.recurringEventId &&
allRemainingBookings allRemainingBookings
) { ) {
const promises = bookingToDelete.user.credentials let thirdPartyRecurringEventId;
.filter((credential) => credential.type.endsWith("_calendar")) for (const reference of bookingToDelete.references) {
.map(async (credential) => { if (reference.thirdPartyRecurringEventId) {
const calendar = await getCalendar(credential); thirdPartyRecurringEventId = reference.thirdPartyRecurringEventId;
for (const updBooking of updatedBookings) { break;
const bookingRef = updBooking.references.find((ref) => ref.type.includes("_calendar")); }
if (bookingRef) { }
const { uid, externalCalendarId } = bookingRef; if (thirdPartyRecurringEventId) {
const deletedEvent = await calendar?.deleteEvent(uid, evt, externalCalendarId); apiDeletes.push(
apiDeletes.push(deletedEvent); calendar?.deleteEvent(thirdPartyRecurringEventId, evt, externalCalendarId) as Promise<unknown>
);
} else {
const promises = bookingToDelete.user.credentials
.filter((credential) => credential.type.endsWith("_calendar"))
.map(async (credential) => {
const calendar = await getCalendar(credential);
for (const updBooking of updatedBookings) {
const bookingRef = updBooking.references.find((ref) => ref.type.includes("_calendar"));
if (bookingRef) {
const { uid, externalCalendarId } = bookingRef;
const deletedEvent = await calendar?.deleteEvent(uid, evt, externalCalendarId);
apiDeletes.push(deletedEvent);
}
} }
});
try {
await Promise.all(promises);
} catch (error) {
if (error instanceof Error) {
logger.error(error.message);
} }
});
try {
await Promise.all(promises);
} catch (error) {
if (error instanceof Error) {
logger.error(error.message);
} }
} }
} else { } else {
@@ -666,7 +680,8 @@ async function handler(req: CustomRequest) {
const prismaPromises: Promise<unknown>[] = [bookingReferenceDeletes]; const prismaPromises: Promise<unknown>[] = [bookingReferenceDeletes];
try { try {
const settled = await Promise.allSettled(prismaPromises.concat(apiDeletes)); const temp = prismaPromises.concat(apiDeletes);
const settled = await Promise.allSettled(temp);
const rejected = settled.filter(({ status }) => status === "rejected") as PromiseRejectedResult[]; const rejected = settled.filter(({ status }) => status === "rejected") as PromiseRejectedResult[];
if (rejected.length) { if (rejected.length) {
throw new Error(`Reasons: ${rejected.map(({ reason }) => reason)}`); throw new Error(`Reasons: ${rejected.map(({ reason }) => reason)}`);
+125 -749
View File
@@ -1,4 +1,4 @@
import type { App, Attendee, DestinationCalendar, EventTypeCustomInput } from "@prisma/client"; import type { App, DestinationCalendar, EventTypeCustomInput } from "@prisma/client";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import async from "async"; import async from "async";
import type { IncomingMessage } from "http"; import type { IncomingMessage } from "http";
@@ -12,7 +12,6 @@ import type { Logger } from "tslog";
import { v5 as uuidv5 } from "uuid"; import { v5 as uuidv5 } from "uuid";
import z from "zod"; import z from "zod";
import { getCalendar } from "@calcom/app-store/_utils/getCalendar";
import { metadata as GoogleMeetMetadata } from "@calcom/app-store/googlevideo/_metadata"; import { metadata as GoogleMeetMetadata } from "@calcom/app-store/googlevideo/_metadata";
import type { LocationObject } from "@calcom/app-store/locations"; import type { LocationObject } from "@calcom/app-store/locations";
import { import {
@@ -25,19 +24,16 @@ import { getAppFromSlug } from "@calcom/app-store/utils";
import EventManager from "@calcom/core/EventManager"; import EventManager from "@calcom/core/EventManager";
import { getEventName } from "@calcom/core/event"; import { getEventName } from "@calcom/core/event";
import { getUserAvailability } from "@calcom/core/getUserAvailability"; import { getUserAvailability } from "@calcom/core/getUserAvailability";
import { deleteMeeting } from "@calcom/core/videoClient";
import dayjs from "@calcom/dayjs"; import dayjs from "@calcom/dayjs";
import { scheduleMandatoryReminder } from "@calcom/ee/workflows/lib/reminders/scheduleMandatoryReminder"; import { scheduleMandatoryReminder } from "@calcom/ee/workflows/lib/reminders/scheduleMandatoryReminder";
import { import {
sendAttendeeRequestEmail, sendAttendeeRequestEmail,
sendOrganizerRequestEmail, sendOrganizerRequestEmail,
sendRescheduledEmails, sendRescheduledEmails,
sendRescheduledSeatEmail,
sendRoundRobinCancelledEmails, sendRoundRobinCancelledEmails,
sendRoundRobinRescheduledEmails, sendRoundRobinRescheduledEmails,
sendRoundRobinScheduledEmails, sendRoundRobinScheduledEmails,
sendScheduledEmails, sendScheduledEmails,
sendScheduledSeatsEmails,
} from "@calcom/emails"; } from "@calcom/emails";
import getICalUID from "@calcom/emails/lib/getICalUID"; import getICalUID from "@calcom/emails/lib/getICalUID";
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields"; import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
@@ -99,7 +95,8 @@ import type { EventResult, PartialReference } from "@calcom/types/EventManager";
import type { EventTypeInfo } from "../../webhooks/lib/sendPayload"; import type { EventTypeInfo } from "../../webhooks/lib/sendPayload";
import getBookingDataSchema from "./getBookingDataSchema"; import getBookingDataSchema from "./getBookingDataSchema";
import type { BookingSeat } from "./handleSeats"; import handleSeats from "./handleSeats/handleSeats";
import type { BookingSeat } from "./handleSeats/types";
const translator = short(); const translator = short();
const log = logger.getSubLogger({ prefix: ["[api] book:user"] }); const log = logger.getSubLogger({ prefix: ["[api] book:user"] });
@@ -861,7 +858,7 @@ export function getCustomInputsResponses(
* *
* @returns updated evt with video call data * @returns updated evt with video call data
*/ */
export const addVideoCallDataToEvt = (bookingReferences: BookingReference[], evt: CalendarEvent) => { export const addVideoCallDataToEvent = (bookingReferences: BookingReference[], evt: CalendarEvent) => {
const videoCallReference = bookingReferences.find((reference) => reference.type.includes("_video")); const videoCallReference = bookingReferences.find((reference) => reference.type.includes("_video"));
if (videoCallReference) { if (videoCallReference) {
@@ -892,7 +889,7 @@ export function handleAppsStatus(
reqAppsStatus: ReqAppsStatus reqAppsStatus: ReqAppsStatus
) { ) {
// Taking care of apps status // Taking care of apps status
let resultStatus: AppsStatus[] = results.map((app) => ({ const resultStatus: AppsStatus[] = results.map((app) => ({
appName: app.appName, appName: app.appName,
type: app.type, type: app.type,
success: app.success ? 1 : 0, success: app.success ? 1 : 0,
@@ -920,8 +917,7 @@ export function handleAppsStatus(
} }
return prev; return prev;
}, {} as { [key: string]: AppsStatus }); }, {} as { [key: string]: AppsStatus });
resultStatus = Object.values(calcAppsStatus); return Object.values(calcAppsStatus);
return resultStatus;
} }
function getICalSequence(originalRescheduledBooking: BookingType | null) { function getICalSequence(originalRescheduledBooking: BookingType | null) {
@@ -989,13 +985,10 @@ async function handler(
req: NextApiRequest & { userId?: number | undefined }, req: NextApiRequest & { userId?: number | undefined },
{ {
isNotAnApiCall = false, isNotAnApiCall = false,
skipAvailabilityCheck = false,
}: { }: {
isNotAnApiCall?: boolean; isNotAnApiCall?: boolean;
skipAvailabilityCheck?: boolean;
} = { } = {
isNotAnApiCall: false, isNotAnApiCall: false,
skipAvailabilityCheck: false,
} }
) { ) {
const { userId } = req; const { userId } = req;
@@ -1065,7 +1058,6 @@ async function handler(
isTeamEventType, isTeamEventType,
eventType: getPiiFreeEventType(eventType), eventType: getPiiFreeEventType(eventType),
dynamicUserList, dynamicUserList,
skipAvailabilityCheck,
paymentAppData: { paymentAppData: {
enabled: paymentAppData.enabled, enabled: paymentAppData.enabled,
price: paymentAppData.price, price: paymentAppData.price,
@@ -1202,7 +1194,7 @@ async function handler(
let originalRescheduledBooking: BookingType = null; let originalRescheduledBooking: BookingType = null;
//this gets the orginal rescheduled booking //this gets the original rescheduled booking
if (rescheduleUid) { if (rescheduleUid) {
// rescheduleUid can be bookingUid and bookingSeatUid // rescheduleUid can be bookingUid and bookingSeatUid
bookingSeat = await prisma.bookingSeat.findUnique({ bookingSeat = await prisma.bookingSeat.findUnique({
@@ -1234,58 +1226,85 @@ async function handler(
} }
//checks what users are available //checks what users are available
if (!eventType.seatsPerTimeSlot && !skipAvailabilityCheck) { if (!eventType.seatsPerTimeSlot) {
const availableUsers = await ensureAvailableUsers( const eventTypeWithUsers: Awaited<ReturnType<typeof getEventTypesFromDB>> & {
{ users: IsFixedAwareUser[];
...eventType, } = {
users: users as IsFixedAwareUser[], ...eventType,
...(eventType.recurringEvent && { users: users as IsFixedAwareUser[],
recurringEvent: { ...(eventType.recurringEvent && {
...eventType.recurringEvent, recurringEvent: {
count: recurringCount || eventType.recurringEvent.count, ...eventType.recurringEvent,
}, count: recurringCount || eventType.recurringEvent.count,
}), },
}, }),
{ };
dateFrom: dayjs(reqBody.start).tz(reqBody.timeZone).format(), if (req.body.allRecurringDates) {
dateTo: dayjs(reqBody.end).tz(reqBody.timeZone).format(), if (req.body.isFirstRecurringSlot) {
timeZone: reqBody.timeZone, for (
originalRescheduledBooking, let i = 0;
}, i < req.body.allRecurringDates.length && i < req.body.numSlotsToCheckForAvailability;
loggerWithEventDetails i++
); ) {
const start = req.body.allRecurringDates[i].start;
const luckyUsers: typeof users = []; const end = req.body.allRecurringDates[i].end;
const luckyUserPool = availableUsers.filter((user) => !user.isFixed); await ensureAvailableUsers(
loggerWithEventDetails.debug( eventTypeWithUsers,
"Computed available users", {
safeStringify({ dateFrom: dayjs(start).tz(reqBody.timeZone).format(),
availableUsers: availableUsers.map((user) => user.id), dateTo: dayjs(end).tz(reqBody.timeZone).format(),
luckyUserPool: luckyUserPool.map((user) => user.id), timeZone: reqBody.timeZone,
}) originalRescheduledBooking,
); },
// loop through all non-fixed hosts and get the lucky users loggerWithEventDetails
while (luckyUserPool.length > 0 && luckyUsers.length < 1 /* TODO: Add variable */) { );
const newLuckyUser = await getLuckyUser("MAXIMIZE_AVAILABILITY", { }
// find a lucky user that is not already in the luckyUsers array
availableUsers: luckyUserPool.filter(
(user) => !luckyUsers.find((existing) => existing.id === user.id)
),
eventTypeId: eventType.id,
});
if (!newLuckyUser) {
break; // prevent infinite loop
} }
luckyUsers.push(newLuckyUser);
} }
// ALL fixed users must be available if (!req.body.allRecurringDates || req.body.isFirstRecurringSlot) {
if ( const availableUsers = await ensureAvailableUsers(
availableUsers.filter((user) => user.isFixed).length !== users.filter((user) => user.isFixed).length eventTypeWithUsers,
) { {
throw new Error(ErrorCode.HostsUnavailableForBooking); dateFrom: dayjs(reqBody.start).tz(reqBody.timeZone).format(),
dateTo: dayjs(reqBody.end).tz(reqBody.timeZone).format(),
timeZone: reqBody.timeZone,
originalRescheduledBooking,
},
loggerWithEventDetails
);
const luckyUsers: typeof users = [];
const luckyUserPool = availableUsers.filter((user) => !user.isFixed);
loggerWithEventDetails.debug(
"Computed available users",
safeStringify({
availableUsers: availableUsers.map((user) => user.id),
luckyUserPool: luckyUserPool.map((user) => user.id),
})
);
// loop through all non-fixed hosts and get the lucky users
while (luckyUserPool.length > 0 && luckyUsers.length < 1 /* TODO: Add variable */) {
const newLuckyUser = await getLuckyUser("MAXIMIZE_AVAILABILITY", {
// find a lucky user that is not already in the luckyUsers array
availableUsers: luckyUserPool.filter(
(user) => !luckyUsers.find((existing) => existing.id === user.id)
),
eventTypeId: eventType.id,
});
if (!newLuckyUser) {
break; // prevent infinite loop
}
luckyUsers.push(newLuckyUser);
}
// ALL fixed users must be available
if (
availableUsers.filter((user) => user.isFixed).length !== users.filter((user) => user.isFixed).length
) {
throw new Error(ErrorCode.HostsUnavailableForBooking);
}
// Pushing fixed user before the luckyUser guarantees the (first) fixed user as the organizer.
users = [...availableUsers.filter((user) => user.isFixed), ...luckyUsers];
} }
// Pushing fixed user before the luckyUser guarantees the (first) fixed user as the organizer.
users = [...availableUsers.filter((user) => user.isFixed), ...luckyUsers];
} }
const [organizerUser] = users; const [organizerUser] = users;
@@ -1325,7 +1344,7 @@ async function handler(
organizerOrFirstDynamicGroupMemberDefaultLocationUrl = organizerOrFirstDynamicGroupMemberDefaultLocationUrl =
organizerMetadata?.defaultConferencingApp?.appLink; organizerMetadata?.defaultConferencingApp?.appLink;
} else { } else {
locationBodyString = ""; locationBodyString = "integrations:daily";
} }
} }
@@ -1463,61 +1482,16 @@ async function handler(
iCalSequence, iCalSequence,
}; };
if (req.body.thirdPartyRecurringEventId) {
evt.existingRecurringEvent = {
recurringEventId: req.body.thirdPartyRecurringEventId,
};
}
if (isTeamEventType && eventType.schedulingType === "COLLECTIVE") { if (isTeamEventType && eventType.schedulingType === "COLLECTIVE") {
evt.destinationCalendar?.push(...teamDestinationCalendars); evt.destinationCalendar?.push(...teamDestinationCalendars);
} }
/* Check if the original booking has no more attendees, if so delete the booking
and any calendar or video integrations */
const lastAttendeeDeleteBooking = async (
originalRescheduledBooking: Awaited<ReturnType<typeof getOriginalRescheduledBooking>>,
filteredAttendees: Partial<Attendee>[],
originalBookingEvt?: CalendarEvent
) => {
let deletedReferences = false;
if (filteredAttendees && filteredAttendees.length === 0 && originalRescheduledBooking) {
const integrationsToDelete = [];
for (const reference of originalRescheduledBooking.references) {
if (reference.credentialId) {
const credential = await prisma.credential.findUnique({
where: {
id: reference.credentialId,
},
select: credentialForCalendarServiceSelect,
});
if (credential) {
if (reference.type.includes("_video")) {
integrationsToDelete.push(deleteMeeting(credential, reference.uid));
}
if (reference.type.includes("_calendar") && originalBookingEvt) {
const calendar = await getCalendar(credential);
if (calendar) {
integrationsToDelete.push(
calendar?.deleteEvent(reference.uid, originalBookingEvt, reference.externalCalendarId)
);
}
}
}
}
}
await Promise.all(integrationsToDelete).then(async () => {
await prisma.booking.update({
where: {
id: originalRescheduledBooking.id,
},
data: {
status: BookingStatus.CANCELLED,
},
});
});
deletedReferences = true;
}
return deletedReferences;
};
// data needed for triggering webhooks // data needed for triggering webhooks
const eventTypeInfo: EventTypeInfo = { const eventTypeInfo: EventTypeInfo = {
eventTitle: eventType.title, eventTitle: eventType.title,
@@ -1557,640 +1531,39 @@ async function handler(
triggerEvent: WebhookTriggerEvents.MEETING_STARTED, triggerEvent: WebhookTriggerEvents.MEETING_STARTED,
teamId, teamId,
}; };
const handleSeats = async () => {
let resultBooking:
| (Partial<Booking> & {
appsStatus?: AppsStatus[];
seatReferenceUid?: string;
paymentUid?: string;
message?: string;
paymentId?: number;
})
| null = null;
const booking = await prisma.booking.findFirst({
where: {
OR: [
{
uid: rescheduleUid || reqBody.bookingUid,
},
{
eventTypeId: eventType.id,
startTime: evt.startTime,
},
],
status: BookingStatus.ACCEPTED,
},
select: {
uid: true,
id: true,
attendees: { include: { bookingSeat: true } },
userId: true,
references: true,
startTime: true,
user: true,
status: true,
smsReminderNumber: true,
endTime: true,
scheduledJobs: true,
},
});
if (!booking) {
throw new HttpError({ statusCode: 404, message: "Could not find booking" });
}
// See if attendee is already signed up for timeslot
if (
booking.attendees.find((attendee) => attendee.email === invitee[0].email) &&
dayjs.utc(booking.startTime).format() === evt.startTime
) {
throw new HttpError({ statusCode: 409, message: ErrorCode.AlreadySignedUpForBooking });
}
// There are two paths here, reschedule a booking with seats and booking seats without reschedule
if (rescheduleUid) {
// See if the new date has a booking already
const newTimeSlotBooking = await prisma.booking.findFirst({
where: {
startTime: evt.startTime,
eventTypeId: eventType.id,
status: BookingStatus.ACCEPTED,
},
select: {
id: true,
uid: true,
attendees: {
include: {
bookingSeat: true,
},
},
},
});
const credentials = await refreshCredentials(allCredentials);
const eventManager = new EventManager({ ...organizerUser, credentials });
if (!originalRescheduledBooking) {
// typescript isn't smart enough;
throw new Error("Internal Error.");
}
const updatedBookingAttendees = originalRescheduledBooking.attendees.reduce(
(filteredAttendees, attendee) => {
if (attendee.email === bookerEmail) {
return filteredAttendees; // skip current booker, as we know the language already.
}
filteredAttendees.push({
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: { translate: tAttendees, locale: attendee.locale ?? "en" },
});
return filteredAttendees;
},
[] as Person[]
);
// If original booking has video reference we need to add the videoCallData to the new evt
const videoReference = originalRescheduledBooking.references.find((reference) =>
reference.type.includes("_video")
);
const originalBookingEvt = {
...evt,
title: originalRescheduledBooking.title,
startTime: dayjs(originalRescheduledBooking.startTime).utc().format(),
endTime: dayjs(originalRescheduledBooking.endTime).utc().format(),
attendees: updatedBookingAttendees,
// If the location is a video integration then include the videoCallData
...(videoReference && {
videoCallData: {
type: videoReference.type,
id: videoReference.meetingId,
password: videoReference.meetingPassword,
url: videoReference.meetingUrl,
},
}),
};
if (!bookingSeat) {
// if no bookingSeat is given and the userId != owner, 401.
// TODO: Next step; Evaluate ownership, what about teams?
if (booking.user?.id !== req.userId) {
throw new HttpError({ statusCode: 401 });
}
// Moving forward in this block is the owner making changes to the booking. All attendees should be affected
evt.attendees = originalRescheduledBooking.attendees.map((attendee) => {
return {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: { translate: tAttendees, locale: attendee.locale ?? "en" },
};
});
// If owner reschedules the event we want to update the entire booking
// Also if owner is rescheduling there should be no bookingSeat
// If there is no booking during the new time slot then update the current booking to the new date
if (!newTimeSlotBooking) {
const newBooking: (Booking & { appsStatus?: AppsStatus[] }) | null = await prisma.booking.update({
where: {
id: booking.id,
},
data: {
startTime: evt.startTime,
endTime: evt.endTime,
cancellationReason: rescheduleReason,
},
include: {
user: true,
references: true,
payment: true,
attendees: true,
},
});
evt = addVideoCallDataToEvt(newBooking.references, evt);
const copyEvent = cloneDeep(evt);
const updateManager = await eventManager.reschedule(copyEvent, rescheduleUid, newBooking.id);
// @NOTE: This code is duplicated and should be moved to a function
// This gets overridden when updating the event - to check if notes have been hidden or not. We just reset this back
// to the default description when we are sending the emails.
evt.description = eventType.description;
const results = updateManager.results;
const calendarResult = results.find((result) => result.type.includes("_calendar"));
evt.iCalUID = calendarResult?.updatedEvent.iCalUID || undefined;
if (results.length > 0 && results.some((res) => !res.success)) {
const error = {
errorCode: "BookingReschedulingMeetingFailed",
message: "Booking Rescheduling failed",
};
loggerWithEventDetails.error(
`Booking ${organizerUser.name} failed`,
JSON.stringify({ error, results })
);
} else {
const metadata: AdditionalInformation = {};
if (results.length) {
// TODO: Handle created event metadata more elegantly
const [updatedEvent] = Array.isArray(results[0].updatedEvent)
? results[0].updatedEvent
: [results[0].updatedEvent];
if (updatedEvent) {
metadata.hangoutLink = updatedEvent.hangoutLink;
metadata.conferenceData = updatedEvent.conferenceData;
metadata.entryPoints = updatedEvent.entryPoints;
evt.appsStatus = handleAppsStatus(results, newBooking, reqAppsStatus);
}
}
}
if (noEmail !== true && isConfirmedByDefault) {
const copyEvent = cloneDeep(evt);
loggerWithEventDetails.debug("Emails: Sending reschedule emails - handleSeats");
await sendRescheduledEmails({
...copyEvent,
additionalNotes, // Resets back to the additionalNote input and not the override value
cancellationReason: `$RCH$${rescheduleReason ? rescheduleReason : ""}`, // Removable code prefix to differentiate cancellation from rescheduling for email
});
}
const foundBooking = await findBookingQuery(newBooking.id);
resultBooking = { ...foundBooking, appsStatus: newBooking.appsStatus };
} else {
// Merge two bookings together
const attendeesToMove = [],
attendeesToDelete = [];
for (const attendee of booking.attendees) {
// If the attendee already exists on the new booking then delete the attendee record of the old booking
if (
newTimeSlotBooking.attendees.some(
(newBookingAttendee) => newBookingAttendee.email === attendee.email
)
) {
attendeesToDelete.push(attendee.id);
// If the attendee does not exist on the new booking then move that attendee record to the new booking
} else {
attendeesToMove.push({ id: attendee.id, seatReferenceId: attendee.bookingSeat?.id });
}
}
// Confirm that the new event will have enough available seats
if (
!eventType.seatsPerTimeSlot ||
attendeesToMove.length +
newTimeSlotBooking.attendees.filter((attendee) => attendee.bookingSeat).length >
eventType.seatsPerTimeSlot
) {
throw new HttpError({ statusCode: 409, message: "Booking does not have enough available seats" });
}
const moveAttendeeCalls = [];
for (const attendeeToMove of attendeesToMove) {
moveAttendeeCalls.push(
prisma.attendee.update({
where: {
id: attendeeToMove.id,
},
data: {
bookingId: newTimeSlotBooking.id,
bookingSeat: {
upsert: {
create: {
referenceUid: uuid(),
bookingId: newTimeSlotBooking.id,
},
update: {
bookingId: newTimeSlotBooking.id,
},
},
},
},
})
);
}
await Promise.all([
...moveAttendeeCalls,
// Delete any attendees that are already a part of that new time slot booking
prisma.attendee.deleteMany({
where: {
id: {
in: attendeesToDelete,
},
},
}),
]);
const updatedNewBooking = await prisma.booking.findUnique({
where: {
id: newTimeSlotBooking.id,
},
include: {
attendees: true,
references: true,
},
});
if (!updatedNewBooking) {
throw new HttpError({ statusCode: 404, message: "Updated booking not found" });
}
// Update the evt object with the new attendees
const updatedBookingAttendees = updatedNewBooking.attendees.map((attendee) => {
const evtAttendee = {
...attendee,
language: { translate: tAttendees, locale: attendeeLanguage ?? "en" },
};
return evtAttendee;
});
evt.attendees = updatedBookingAttendees;
evt = addVideoCallDataToEvt(updatedNewBooking.references, evt);
const copyEvent = cloneDeep(evt);
const updateManager = await eventManager.reschedule(
copyEvent,
rescheduleUid,
newTimeSlotBooking.id
);
const results = updateManager.results;
const calendarResult = results.find((result) => result.type.includes("_calendar"));
evt.iCalUID = Array.isArray(calendarResult?.updatedEvent)
? calendarResult?.updatedEvent[0]?.iCalUID
: calendarResult?.updatedEvent?.iCalUID || undefined;
if (noEmail !== true && isConfirmedByDefault) {
// TODO send reschedule emails to attendees of the old booking
loggerWithEventDetails.debug("Emails: Sending reschedule emails - handleSeats");
await sendRescheduledEmails({
...copyEvent,
additionalNotes, // Resets back to the additionalNote input and not the override value
cancellationReason: `$RCH$${rescheduleReason ? rescheduleReason : ""}`, // Removable code prefix to differentiate cancellation from rescheduling for email
});
}
// Update the old booking with the cancelled status
await prisma.booking.update({
where: {
id: booking.id,
},
data: {
status: BookingStatus.CANCELLED,
},
});
const foundBooking = await findBookingQuery(newTimeSlotBooking.id);
resultBooking = { ...foundBooking };
}
}
// seatAttendee is null when the organizer is rescheduling.
const seatAttendee: Partial<Person> | null = bookingSeat?.attendee || null;
if (seatAttendee) {
seatAttendee["language"] = { translate: tAttendees, locale: bookingSeat?.attendee.locale ?? "en" };
// If there is no booking then remove the attendee from the old booking and create a new one
if (!newTimeSlotBooking) {
await prisma.attendee.delete({
where: {
id: seatAttendee?.id,
},
});
// Update the original calendar event by removing the attendee that is rescheduling
if (originalBookingEvt && originalRescheduledBooking) {
// Event would probably be deleted so we first check than instead of updating references
const filteredAttendees = originalRescheduledBooking?.attendees.filter((attendee) => {
return attendee.email !== bookerEmail;
});
const deletedReference = await lastAttendeeDeleteBooking(
originalRescheduledBooking,
filteredAttendees,
originalBookingEvt
);
if (!deletedReference) {
await eventManager.updateCalendarAttendees(originalBookingEvt, originalRescheduledBooking);
}
}
// We don't want to trigger rescheduling logic of the original booking
originalRescheduledBooking = null;
return null;
}
// Need to change the new seat reference and attendee record to remove it from the old booking and add it to the new booking
// https://stackoverflow.com/questions/4980963/database-insert-new-rows-or-update-existing-ones
if (seatAttendee?.id && bookingSeat?.id) {
await Promise.all([
await prisma.attendee.update({
where: {
id: seatAttendee.id,
},
data: {
bookingId: newTimeSlotBooking.id,
},
}),
await prisma.bookingSeat.update({
where: {
id: bookingSeat.id,
},
data: {
bookingId: newTimeSlotBooking.id,
},
}),
]);
}
const copyEvent = cloneDeep(evt);
const updateManager = await eventManager.reschedule(copyEvent, rescheduleUid, newTimeSlotBooking.id);
const results = updateManager.results;
const calendarResult = results.find((result) => result.type.includes("_calendar"));
evt.iCalUID = Array.isArray(calendarResult?.updatedEvent)
? calendarResult?.updatedEvent[0]?.iCalUID
: calendarResult?.updatedEvent?.iCalUID || undefined;
await sendRescheduledSeatEmail(copyEvent, seatAttendee as Person);
const filteredAttendees = originalRescheduledBooking?.attendees.filter((attendee) => {
return attendee.email !== bookerEmail;
});
await lastAttendeeDeleteBooking(originalRescheduledBooking, filteredAttendees, originalBookingEvt);
const foundBooking = await findBookingQuery(newTimeSlotBooking.id);
resultBooking = { ...foundBooking, seatReferenceUid: bookingSeat?.referenceUid };
}
} else {
// Need to add translation for attendees to pass type checks. Since these values are never written to the db we can just use the new attendee language
const bookingAttendees = booking.attendees.map((attendee) => {
return { ...attendee, language: { translate: tAttendees, locale: attendeeLanguage ?? "en" } };
});
evt = { ...evt, attendees: [...bookingAttendees, invitee[0]] };
if (eventType.seatsPerTimeSlot && eventType.seatsPerTimeSlot <= booking.attendees.length) {
throw new HttpError({ statusCode: 409, message: "Booking seats are full" });
}
const videoCallReference = booking.references.find((reference) => reference.type.includes("_video"));
if (videoCallReference) {
evt.videoCallData = {
type: videoCallReference.type,
id: videoCallReference.meetingId,
password: videoCallReference?.meetingPassword,
url: videoCallReference.meetingUrl,
};
}
const attendeeUniqueId = uuid();
await prisma.booking.update({
where: {
uid: reqBody.bookingUid,
},
include: {
attendees: true,
},
data: {
attendees: {
create: {
email: invitee[0].email,
name: invitee[0].name,
timeZone: invitee[0].timeZone,
locale: invitee[0].language.locale,
bookingSeat: {
create: {
referenceUid: attendeeUniqueId,
data: {
description: additionalNotes,
},
booking: {
connect: {
id: booking.id,
},
},
},
},
},
},
...(booking.status === BookingStatus.CANCELLED && { status: BookingStatus.ACCEPTED }),
},
});
evt.attendeeSeatId = attendeeUniqueId;
const newSeat = booking.attendees.length !== 0;
/**
* Remember objects are passed into functions as references
* so if you modify it in a inner function it will be modified in the outer function
* deep cloning evt to avoid this
*/
if (!evt?.uid) {
evt.uid = booking?.uid ?? null;
}
const copyEvent = cloneDeep(evt);
copyEvent.uid = booking.uid;
if (noEmail !== true) {
let isHostConfirmationEmailsDisabled = false;
let isAttendeeConfirmationEmailDisabled = false;
const workflows = eventType.workflows.map((workflow) => workflow.workflow);
if (eventType.workflows) {
isHostConfirmationEmailsDisabled =
eventType.metadata?.disableStandardEmails?.confirmation?.host || false;
isAttendeeConfirmationEmailDisabled =
eventType.metadata?.disableStandardEmails?.confirmation?.attendee || false;
if (isHostConfirmationEmailsDisabled) {
isHostConfirmationEmailsDisabled = allowDisablingHostConfirmationEmails(workflows);
}
if (isAttendeeConfirmationEmailDisabled) {
isAttendeeConfirmationEmailDisabled = allowDisablingAttendeeConfirmationEmails(workflows);
}
}
await sendScheduledSeatsEmails(
copyEvent,
invitee[0],
newSeat,
!!eventType.seatsShowAttendees,
isHostConfirmationEmailsDisabled,
isAttendeeConfirmationEmailDisabled
);
}
const credentials = await refreshCredentials(allCredentials);
const eventManager = new EventManager({ ...organizerUser, credentials });
await eventManager.updateCalendarAttendees(evt, booking);
const foundBooking = await findBookingQuery(booking.id);
if (!Number.isNaN(paymentAppData.price) && paymentAppData.price > 0 && !!booking) {
const credentialPaymentAppCategories = await prisma.credential.findMany({
where: {
...(paymentAppData.credentialId
? { id: paymentAppData.credentialId }
: { userId: organizerUser.id }),
app: {
categories: {
hasSome: ["payment"],
},
},
},
select: {
key: true,
appId: true,
app: {
select: {
categories: true,
dirName: true,
},
},
},
});
const eventTypePaymentAppCredential = credentialPaymentAppCategories.find((credential) => {
return credential.appId === paymentAppData.appId;
});
if (!eventTypePaymentAppCredential) {
throw new HttpError({ statusCode: 400, message: "Missing payment credentials" });
}
if (!eventTypePaymentAppCredential?.appId) {
throw new HttpError({ statusCode: 400, message: "Missing payment app id" });
}
const payment = await handlePayment(
evt,
eventType,
eventTypePaymentAppCredential as IEventTypePaymentCredentialType,
booking,
fullName,
bookerEmail
);
resultBooking = { ...foundBooking };
resultBooking["message"] = "Payment required";
resultBooking["paymentUid"] = payment?.uid;
resultBooking["id"] = payment?.id;
} else {
resultBooking = { ...foundBooking };
}
resultBooking["seatReferenceUid"] = evt.attendeeSeatId;
}
// Here we should handle every after action that needs to be done after booking creation
// Obtain event metadata that includes videoCallUrl
const metadata = evt.videoCallData?.url ? { videoCallUrl: evt.videoCallData.url } : undefined;
try {
await scheduleWorkflowReminders({
workflows: eventType.workflows,
smsReminderNumber: smsReminderNumber || null,
calendarEvent: { ...evt, ...{ metadata, eventType: { slug: eventType.slug } } },
isNotConfirmed: rescheduleUid ? false : evt.requiresConfirmation || false,
isRescheduleEvent: !!rescheduleUid,
isFirstRecurringEvent: true,
emailAttendeeSendToOverride: bookerEmail,
seatReferenceUid: evt.attendeeSeatId,
eventTypeRequiresConfirmation: eventType.requiresConfirmation,
});
} catch (error) {
loggerWithEventDetails.error("Error while scheduling workflow reminders", JSON.stringify({ error }));
}
const webhookData = {
...evt,
...eventTypeInfo,
uid: resultBooking?.uid || uid,
bookingId: booking?.id,
rescheduleId: originalRescheduledBooking?.id || undefined,
rescheduleUid,
rescheduleStartTime: originalRescheduledBooking?.startTime
? dayjs(originalRescheduledBooking?.startTime).utc().format()
: undefined,
rescheduleEndTime: originalRescheduledBooking?.endTime
? dayjs(originalRescheduledBooking?.endTime).utc().format()
: undefined,
metadata: { ...metadata, ...reqBody.metadata },
eventTypeId,
status: "ACCEPTED",
smsReminderNumber: booking?.smsReminderNumber || undefined,
};
await handleWebhookTrigger({ subscriberOptions, eventTrigger, webhookData });
return resultBooking;
};
// For seats, if the booking already exists then we want to add the new attendee to the existing booking // For seats, if the booking already exists then we want to add the new attendee to the existing booking
if (eventType.seatsPerTimeSlot && (reqBody.bookingUid || rescheduleUid)) { if (eventType.seatsPerTimeSlot && (reqBody.bookingUid || rescheduleUid)) {
const newBooking = await handleSeats(); const newBooking = await handleSeats({
rescheduleUid,
reqBookingUid: reqBody.bookingUid,
eventType,
evt,
invitee,
allCredentials,
organizerUser,
originalRescheduledBooking,
bookerEmail,
tAttendees,
bookingSeat,
reqUserId: req.userId,
rescheduleReason,
reqBodyUser: reqBody.user,
noEmail,
isConfirmedByDefault,
additionalNotes,
reqAppsStatus,
attendeeLanguage,
paymentAppData,
fullName,
smsReminderNumber,
eventTypeInfo,
uid,
eventTypeId,
reqBodyMetadata: reqBody.metadata,
subscriberOptions,
eventTrigger,
});
if (newBooking) { if (newBooking) {
req.statusCode = 201; req.statusCode = 201;
return newBooking; return newBooking;
@@ -2219,7 +1592,9 @@ async function handler(
let results: EventResult<AdditionalInformation & { url?: string; iCalUID?: string }>[] = []; let results: EventResult<AdditionalInformation & { url?: string; iCalUID?: string }>[] = [];
let referencesToCreate: PartialReference[] = []; let referencesToCreate: PartialReference[] = [];
let booking: (Booking & { appsStatus?: AppsStatus[] }) | null = null; let booking: (Booking & { appsStatus?: AppsStatus[]; paymentUid?: string; paymentId?: number }) | null =
null;
loggerWithEventDetails.debug( loggerWithEventDetails.debug(
"Going to create booking in DB now", "Going to create booking in DB now",
safeStringify({ safeStringify({
@@ -2321,7 +1696,7 @@ async function handler(
); );
} }
addVideoCallDataToEvt(originalRescheduledBooking.references, evt); evt = addVideoCallDataToEvent(originalRescheduledBooking.references, evt);
//update original rescheduled booking (no seats event) //update original rescheduled booking (no seats event)
if (!eventType.seatsPerTimeSlot) { if (!eventType.seatsPerTimeSlot) {
@@ -2909,6 +2284,7 @@ async function handler(
req.statusCode = 201; req.statusCode = 201;
return { return {
...booking, ...booking,
references: referencesToCreate,
seatReferenceUid: evt.attendeeSeatId, seatReferenceUid: evt.attendeeSeatId,
}; };
} }
@@ -3,6 +3,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
import { describe, expect } from "vitest"; import { describe, expect } from "vitest";
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData"; import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
import { OrganizerDefaultConferencingAppType } from "@calcom/app-store/locations";
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants"; import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { ErrorCode } from "@calcom/lib/errorCodes"; import { ErrorCode } from "@calcom/lib/errorCodes";
import { SchedulingType } from "@calcom/prisma/enums"; import { SchedulingType } from "@calcom/prisma/enums";
@@ -1087,6 +1088,212 @@ describe("handleNewBooking", () => {
timeout timeout
); );
test(
`When event type location is Organizer Default App and user metadata is empty, default to Cal Video`,
async ({ emails }) => {
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
const booker = getBooker({
email: "booker@example.com",
name: "Booker",
});
const otherTeamMembers = [
{
name: "Other Team Member 1",
username: "other-team-member-1",
timeZone: Timezones["+5:30"],
defaultScheduleId: 1001,
email: "other-team-member-1@example.com",
id: 102,
schedules: [
{
...TestData.schedules.IstWorkHours,
// Specify an ID directly here because we want to be able to use that ID in defaultScheduleId above.
id: 1001,
},
],
credentials: [getGoogleCalendarCredential()],
selectedCalendars: [TestData.selectedCalendars.google],
destinationCalendar: {
integration: TestData.apps["google-calendar"].type,
externalId: "other-team-member-1@google-calendar.com",
},
},
];
const organizer = getOrganizer({
name: "Organizer",
email: "organizer@example.com",
id: 101,
schedules: [TestData.schedules.IstWorkHours],
credentials: [
{
id: 2,
...getGoogleCalendarCredential(),
},
{
id: 1,
...getZoomAppCredential(),
},
],
metadata: {},
selectedCalendars: [TestData.selectedCalendars.google],
destinationCalendar: {
integration: TestData.apps["google-calendar"].type,
externalId: "organizer@google-calendar.com",
},
});
const { eventTypes } = await createBookingScenario(
getScenarioData({
webhooks: [
{
userId: organizer.id,
eventTriggers: ["BOOKING_CREATED"],
subscriberUrl: "http://my-webhook.example.com",
active: true,
eventTypeId: 1,
appId: null,
},
],
eventTypes: [
{
id: 1,
slotInterval: 45,
schedulingType: SchedulingType.COLLECTIVE,
length: 45,
users: [
{
id: 101,
},
{
id: 102,
},
],
locations: [
{
type: "conferencing",
},
],
destinationCalendar: {
integration: TestData.apps["google-calendar"].type,
externalId: "event-type-1@google-calendar.com",
},
},
],
organizer,
usersApartFromOrganizer: otherTeamMembers,
apps: [
TestData.apps["google-calendar"],
TestData.apps["daily-video"],
TestData.apps["zoomvideo"],
],
})
);
mockSuccessfulVideoMeetingCreation({
metadataLookupKey: appStoreMetadata.dailyvideo.dirName,
videoMeetingData: {
id: "MOCK_ID",
password: "MOCK_PASS",
url: `http://mock-dailyvideo.example.com/meeting-1`,
},
});
const calendarMock = mockCalendarToHaveNoBusySlots("googlecalendar", {
create: {
id: "MOCKED_GOOGLE_CALENDAR_EVENT_ID",
iCalUID: "MOCKED_GOOGLE_CALENDAR_ICS_ID",
},
});
const mockBookingData = getMockRequestDataForBooking({
data: {
start: `${getDate({ dateIncrement: 1 }).dateString}T05:00:00.000Z`,
end: `${getDate({ dateIncrement: 1 }).dateString}T05:30:00.000Z`,
eventTypeId: 1,
responses: {
email: booker.email,
name: booker.name,
location: { optionValue: "", value: OrganizerDefaultConferencingAppType },
},
},
});
const { req } = createMockNextJsRequest({
method: "POST",
body: mockBookingData,
});
const createdBooking = await handleNewBooking(req);
await expectBookingToBeInDatabase({
description: "",
location: BookingLocations.CalVideo,
responses: expect.objectContaining({
email: booker.email,
name: booker.name,
}),
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
uid: createdBooking.uid!,
eventTypeId: mockBookingData.eventTypeId,
status: BookingStatus.ACCEPTED,
references: [
{
type: appStoreMetadata.dailyvideo.type,
uid: "MOCK_ID",
meetingId: "MOCK_ID",
meetingPassword: "MOCK_PASS",
meetingUrl: "http://mock-dailyvideo.example.com/meeting-1",
},
{
type: TestData.apps["google-calendar"].type,
uid: "MOCKED_GOOGLE_CALENDAR_EVENT_ID",
meetingId: "MOCKED_GOOGLE_CALENDAR_EVENT_ID",
meetingPassword: "MOCK_PASSWORD",
meetingUrl: "https://UNUSED_URL",
},
],
});
// expectWorkflowToBeTriggered();
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
destinationCalendars: [
{
integration: TestData.apps["google-calendar"].type,
externalId: "event-type-1@google-calendar.com",
},
{
integration: TestData.apps["google-calendar"].type,
externalId: "other-team-member-1@google-calendar.com",
},
],
videoCallUrl: "http://mock-dailyvideo.example.com/meeting-1",
});
expectSuccessfulBookingCreationEmails({
booking: {
uid: createdBooking.uid!,
},
booker,
organizer,
otherTeamMembers,
emails,
iCalUID: "MOCKED_GOOGLE_CALENDAR_ICS_ID",
});
expectBookingCreatedWebhookToHaveBeenFired({
booker,
organizer,
location: OrganizerDefaultConferencingAppType,
subscriberUrl: "http://my-webhook.example.com",
videoCallUrl: `${WEBAPP_URL}/video/${createdBooking.uid}`,
});
},
timeout
);
describe("Team(T1) not part of any org but the organizer is part of an organization(O1)", () => { describe("Team(T1) not part of any org but the organizer is part of an organization(O1)", () => {
test( test(
`succesfully creates a booking when all the hosts are free as per their schedules `succesfully creates a booking when all the hosts are free as per their schedules
@@ -1,796 +0,0 @@
import type { Prisma, Attendee } from "@prisma/client";
// eslint-disable-next-line no-restricted-imports
import { cloneDeep } from "lodash";
import type { TFunction } from "next-i18next";
import type short from "short-uuid";
import { uuid } from "short-uuid";
import { getCalendar } from "@calcom/app-store/_utils/getCalendar";
import EventManager from "@calcom/core/EventManager";
import { deleteMeeting } from "@calcom/core/videoClient";
import dayjs from "@calcom/dayjs";
import { sendRescheduledEmails, sendRescheduledSeatEmail, sendScheduledSeatsEmails } from "@calcom/emails";
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
import {
allowDisablingAttendeeConfirmationEmails,
allowDisablingHostConfirmationEmails,
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
import type { getFullName } from "@calcom/features/form-builder/utils";
import type { GetSubscriberOptions } from "@calcom/features/webhooks/lib/getWebhooks";
import { HttpError } from "@calcom/lib/http-error";
import { handlePayment } from "@calcom/lib/payment/handlePayment";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import type { WebhookTriggerEvents } from "@calcom/prisma/enums";
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
import type { AdditionalInformation, AppsStatus, CalendarEvent, Person } from "@calcom/types/Calendar";
import type { EventTypeInfo } from "../../webhooks/lib/sendPayload";
import {
refreshCredentials,
addVideoCallDataToEvt,
createLoggerWithEventDetails,
handleAppsStatus,
findBookingQuery,
} from "./handleNewBooking";
import type {
Booking,
Invitee,
NewBookingEventType,
getAllCredentials,
OrganizerUser,
OriginalRescheduledBooking,
RescheduleReason,
NoEmail,
IsConfirmedByDefault,
AdditionalNotes,
ReqAppsStatus,
PaymentAppData,
IEventTypePaymentCredentialType,
SmsReminderNumber,
EventTypeId,
ReqBodyMetadata,
} from "./handleNewBooking";
export type BookingSeat = Prisma.BookingSeatGetPayload<{ include: { booking: true; attendee: true } }> | null;
/* Check if the original booking has no more attendees, if so delete the booking
and any calendar or video integrations */
const lastAttendeeDeleteBooking = async (
originalRescheduledBooking: OriginalRescheduledBooking,
filteredAttendees: Partial<Attendee>[],
originalBookingEvt?: CalendarEvent
) => {
let deletedReferences = false;
if (filteredAttendees && filteredAttendees.length === 0 && originalRescheduledBooking) {
const integrationsToDelete = [];
for (const reference of originalRescheduledBooking.references) {
if (reference.credentialId) {
const credential = await prisma.credential.findUnique({
where: {
id: reference.credentialId,
},
select: credentialForCalendarServiceSelect,
});
if (credential) {
if (reference.type.includes("_video")) {
integrationsToDelete.push(deleteMeeting(credential, reference.uid));
}
if (reference.type.includes("_calendar") && originalBookingEvt) {
const calendar = await getCalendar(credential);
if (calendar) {
integrationsToDelete.push(
calendar?.deleteEvent(reference.uid, originalBookingEvt, reference.externalCalendarId)
);
}
}
}
}
}
await Promise.all(integrationsToDelete).then(async () => {
await prisma.booking.update({
where: {
id: originalRescheduledBooking.id,
},
data: {
status: BookingStatus.CANCELLED,
},
});
});
deletedReferences = true;
}
return deletedReferences;
};
const handleSeats = async ({
rescheduleUid,
reqBookingUid,
eventType,
evt,
invitee,
allCredentials,
organizerUser,
originalRescheduledBooking,
bookerEmail,
tAttendees,
bookingSeat,
reqUserId,
rescheduleReason,
reqBodyUser,
noEmail,
isConfirmedByDefault,
additionalNotes,
reqAppsStatus,
attendeeLanguage,
paymentAppData,
fullName,
smsReminderNumber,
eventTypeInfo,
uid,
eventTypeId,
reqBodyMetadata,
subscriberOptions,
eventTrigger,
}: {
rescheduleUid: string;
reqBookingUid: string;
eventType: NewBookingEventType;
evt: CalendarEvent;
invitee: Invitee;
allCredentials: Awaited<ReturnType<typeof getAllCredentials>>;
organizerUser: OrganizerUser;
originalRescheduledBooking: OriginalRescheduledBooking;
bookerEmail: string;
tAttendees: TFunction;
bookingSeat: BookingSeat;
reqUserId: number | undefined;
rescheduleReason: RescheduleReason;
reqBodyUser: string | string[] | undefined;
noEmail: NoEmail;
isConfirmedByDefault: IsConfirmedByDefault;
additionalNotes: AdditionalNotes;
reqAppsStatus: ReqAppsStatus;
attendeeLanguage: string | null;
paymentAppData: PaymentAppData;
fullName: ReturnType<typeof getFullName>;
smsReminderNumber: SmsReminderNumber;
eventTypeInfo: EventTypeInfo;
uid: short.SUUID;
eventTypeId: EventTypeId;
reqBodyMetadata: ReqBodyMetadata;
subscriberOptions: GetSubscriberOptions;
eventTrigger: WebhookTriggerEvents;
}) => {
const loggerWithEventDetails = createLoggerWithEventDetails(eventType.id, reqBodyUser, eventType.slug);
let resultBooking:
| (Partial<Booking> & {
appsStatus?: AppsStatus[];
seatReferenceUid?: string;
paymentUid?: string;
message?: string;
paymentId?: number;
})
| null = null;
const booking = await prisma.booking.findFirst({
where: {
OR: [
{
uid: rescheduleUid || reqBookingUid,
},
{
eventTypeId: eventType.id,
startTime: evt.startTime,
},
],
status: BookingStatus.ACCEPTED,
},
select: {
uid: true,
id: true,
attendees: { include: { bookingSeat: true } },
userId: true,
references: true,
startTime: true,
user: true,
status: true,
smsReminderNumber: true,
endTime: true,
scheduledJobs: true,
},
});
if (!booking) {
throw new HttpError({ statusCode: 404, message: "Could not find booking" });
}
// See if attendee is already signed up for timeslot
if (
booking.attendees.find((attendee) => attendee.email === invitee[0].email) &&
dayjs.utc(booking.startTime).format() === evt.startTime
) {
throw new HttpError({ statusCode: 409, message: "Already signed up for this booking." });
}
// There are two paths here, reschedule a booking with seats and booking seats without reschedule
if (rescheduleUid) {
// See if the new date has a booking already
const newTimeSlotBooking = await prisma.booking.findFirst({
where: {
startTime: evt.startTime,
eventTypeId: eventType.id,
status: BookingStatus.ACCEPTED,
},
select: {
id: true,
uid: true,
attendees: {
include: {
bookingSeat: true,
},
},
},
});
const credentials = await refreshCredentials(allCredentials);
const eventManager = new EventManager({ ...organizerUser, credentials });
if (!originalRescheduledBooking) {
// typescript isn't smart enough;
throw new Error("Internal Error.");
}
const updatedBookingAttendees = originalRescheduledBooking.attendees.reduce(
(filteredAttendees, attendee) => {
if (attendee.email === bookerEmail) {
return filteredAttendees; // skip current booker, as we know the language already.
}
filteredAttendees.push({
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: { translate: tAttendees, locale: attendee.locale ?? "en" },
});
return filteredAttendees;
},
[] as Person[]
);
// If original booking has video reference we need to add the videoCallData to the new evt
const videoReference = originalRescheduledBooking.references.find((reference) =>
reference.type.includes("_video")
);
const originalBookingEvt = {
...evt,
title: originalRescheduledBooking.title,
startTime: dayjs(originalRescheduledBooking.startTime).utc().format(),
endTime: dayjs(originalRescheduledBooking.endTime).utc().format(),
attendees: updatedBookingAttendees,
// If the location is a video integration then include the videoCallData
...(videoReference && {
videoCallData: {
type: videoReference.type,
id: videoReference.meetingId,
password: videoReference.meetingPassword,
url: videoReference.meetingUrl,
},
}),
};
if (!bookingSeat) {
// if no bookingSeat is given and the userId != owner, 401.
// TODO: Next step; Evaluate ownership, what about teams?
if (booking.user?.id !== reqUserId) {
throw new HttpError({ statusCode: 401 });
}
// Moving forward in this block is the owner making changes to the booking. All attendees should be affected
evt.attendees = originalRescheduledBooking.attendees.map((attendee) => {
return {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: { translate: tAttendees, locale: attendee.locale ?? "en" },
};
});
// If owner reschedules the event we want to update the entire booking
// Also if owner is rescheduling there should be no bookingSeat
// If there is no booking during the new time slot then update the current booking to the new date
if (!newTimeSlotBooking) {
const newBooking: (Booking & { appsStatus?: AppsStatus[] }) | null = await prisma.booking.update({
where: {
id: booking.id,
},
data: {
startTime: evt.startTime,
endTime: evt.endTime,
cancellationReason: rescheduleReason,
},
include: {
user: true,
references: true,
payment: true,
attendees: true,
},
});
evt = addVideoCallDataToEvt(newBooking.references, evt);
const copyEvent = cloneDeep(evt);
const updateManager = await eventManager.reschedule(copyEvent, rescheduleUid, newBooking.id);
// @NOTE: This code is duplicated and should be moved to a function
// This gets overridden when updating the event - to check if notes have been hidden or not. We just reset this back
// to the default description when we are sending the emails.
evt.description = eventType.description;
const results = updateManager.results;
const calendarResult = results.find((result) => result.type.includes("_calendar"));
evt.iCalUID = calendarResult?.updatedEvent.iCalUID || undefined;
if (results.length > 0 && results.some((res) => !res.success)) {
const error = {
errorCode: "BookingReschedulingMeetingFailed",
message: "Booking Rescheduling failed",
};
loggerWithEventDetails.error(
`Booking ${organizerUser.name} failed`,
JSON.stringify({ error, results })
);
} else {
const metadata: AdditionalInformation = {};
if (results.length) {
// TODO: Handle created event metadata more elegantly
const [updatedEvent] = Array.isArray(results[0].updatedEvent)
? results[0].updatedEvent
: [results[0].updatedEvent];
if (updatedEvent) {
metadata.hangoutLink = updatedEvent.hangoutLink;
metadata.conferenceData = updatedEvent.conferenceData;
metadata.entryPoints = updatedEvent.entryPoints;
evt.appsStatus = handleAppsStatus(results, newBooking, reqAppsStatus);
}
}
}
if (noEmail !== true && isConfirmedByDefault) {
const copyEvent = cloneDeep(evt);
loggerWithEventDetails.debug("Emails: Sending reschedule emails - handleSeats");
await sendRescheduledEmails({
...copyEvent,
additionalNotes, // Resets back to the additionalNote input and not the override value
cancellationReason: `$RCH$${rescheduleReason ? rescheduleReason : ""}`, // Removable code prefix to differentiate cancellation from rescheduling for email
});
}
const foundBooking = await findBookingQuery(newBooking.id);
resultBooking = { ...foundBooking, appsStatus: newBooking.appsStatus };
} else {
// Merge two bookings together
const attendeesToMove = [],
attendeesToDelete = [];
for (const attendee of booking.attendees) {
// If the attendee already exists on the new booking then delete the attendee record of the old booking
if (
newTimeSlotBooking.attendees.some(
(newBookingAttendee) => newBookingAttendee.email === attendee.email
)
) {
attendeesToDelete.push(attendee.id);
// If the attendee does not exist on the new booking then move that attendee record to the new booking
} else {
attendeesToMove.push({ id: attendee.id, seatReferenceId: attendee.bookingSeat?.id });
}
}
// Confirm that the new event will have enough available seats
if (
!eventType.seatsPerTimeSlot ||
attendeesToMove.length +
newTimeSlotBooking.attendees.filter((attendee) => attendee.bookingSeat).length >
eventType.seatsPerTimeSlot
) {
throw new HttpError({ statusCode: 409, message: "Booking does not have enough available seats" });
}
const moveAttendeeCalls = [];
for (const attendeeToMove of attendeesToMove) {
moveAttendeeCalls.push(
prisma.attendee.update({
where: {
id: attendeeToMove.id,
},
data: {
bookingId: newTimeSlotBooking.id,
bookingSeat: {
upsert: {
create: {
referenceUid: uuid(),
bookingId: newTimeSlotBooking.id,
},
update: {
bookingId: newTimeSlotBooking.id,
},
},
},
},
})
);
}
await Promise.all([
...moveAttendeeCalls,
// Delete any attendees that are already a part of that new time slot booking
prisma.attendee.deleteMany({
where: {
id: {
in: attendeesToDelete,
},
},
}),
]);
const updatedNewBooking = await prisma.booking.findUnique({
where: {
id: newTimeSlotBooking.id,
},
include: {
attendees: true,
references: true,
},
});
if (!updatedNewBooking) {
throw new HttpError({ statusCode: 404, message: "Updated booking not found" });
}
// Update the evt object with the new attendees
const updatedBookingAttendees = updatedNewBooking.attendees.map((attendee) => {
const evtAttendee = {
...attendee,
language: { translate: tAttendees, locale: attendeeLanguage ?? "en" },
};
return evtAttendee;
});
evt.attendees = updatedBookingAttendees;
evt = addVideoCallDataToEvt(updatedNewBooking.references, evt);
const copyEvent = cloneDeep(evt);
const updateManager = await eventManager.reschedule(copyEvent, rescheduleUid, newTimeSlotBooking.id);
const results = updateManager.results;
const calendarResult = results.find((result) => result.type.includes("_calendar"));
evt.iCalUID = Array.isArray(calendarResult?.updatedEvent)
? calendarResult?.updatedEvent[0]?.iCalUID
: calendarResult?.updatedEvent?.iCalUID || undefined;
if (noEmail !== true && isConfirmedByDefault) {
// TODO send reschedule emails to attendees of the old booking
loggerWithEventDetails.debug("Emails: Sending reschedule emails - handleSeats");
await sendRescheduledEmails({
...copyEvent,
additionalNotes, // Resets back to the additionalNote input and not the override value
cancellationReason: `$RCH$${rescheduleReason ? rescheduleReason : ""}`, // Removable code prefix to differentiate cancellation from rescheduling for email
});
}
// Update the old booking with the cancelled status
await prisma.booking.update({
where: {
id: booking.id,
},
data: {
status: BookingStatus.CANCELLED,
},
});
const foundBooking = await findBookingQuery(newTimeSlotBooking.id);
resultBooking = { ...foundBooking };
}
}
// seatAttendee is null when the organizer is rescheduling.
const seatAttendee: Partial<Person> | null = bookingSeat?.attendee || null;
if (seatAttendee) {
seatAttendee["language"] = { translate: tAttendees, locale: bookingSeat?.attendee.locale ?? "en" };
// If there is no booking then remove the attendee from the old booking and create a new one
if (!newTimeSlotBooking) {
await prisma.attendee.delete({
where: {
id: seatAttendee?.id,
},
});
// Update the original calendar event by removing the attendee that is rescheduling
if (originalBookingEvt && originalRescheduledBooking) {
// Event would probably be deleted so we first check than instead of updating references
const filteredAttendees = originalRescheduledBooking?.attendees.filter((attendee) => {
return attendee.email !== bookerEmail;
});
const deletedReference = await lastAttendeeDeleteBooking(
originalRescheduledBooking,
filteredAttendees,
originalBookingEvt
);
if (!deletedReference) {
await eventManager.updateCalendarAttendees(originalBookingEvt, originalRescheduledBooking);
}
}
// We don't want to trigger rescheduling logic of the original booking
originalRescheduledBooking = null;
return null;
}
// Need to change the new seat reference and attendee record to remove it from the old booking and add it to the new booking
// https://stackoverflow.com/questions/4980963/database-insert-new-rows-or-update-existing-ones
if (seatAttendee?.id && bookingSeat?.id) {
await Promise.all([
await prisma.attendee.update({
where: {
id: seatAttendee.id,
},
data: {
bookingId: newTimeSlotBooking.id,
},
}),
await prisma.bookingSeat.update({
where: {
id: bookingSeat.id,
},
data: {
bookingId: newTimeSlotBooking.id,
},
}),
]);
}
const copyEvent = cloneDeep(evt);
const updateManager = await eventManager.reschedule(copyEvent, rescheduleUid, newTimeSlotBooking.id);
const results = updateManager.results;
const calendarResult = results.find((result) => result.type.includes("_calendar"));
evt.iCalUID = Array.isArray(calendarResult?.updatedEvent)
? calendarResult?.updatedEvent[0]?.iCalUID
: calendarResult?.updatedEvent?.iCalUID || undefined;
await sendRescheduledSeatEmail(copyEvent, seatAttendee as Person);
const filteredAttendees = originalRescheduledBooking?.attendees.filter((attendee) => {
return attendee.email !== bookerEmail;
});
await lastAttendeeDeleteBooking(originalRescheduledBooking, filteredAttendees, originalBookingEvt);
const foundBooking = await findBookingQuery(newTimeSlotBooking.id);
resultBooking = { ...foundBooking, seatReferenceUid: bookingSeat?.referenceUid };
}
} else {
// Need to add translation for attendees to pass type checks. Since these values are never written to the db we can just use the new attendee language
const bookingAttendees = booking.attendees.map((attendee) => {
return { ...attendee, language: { translate: tAttendees, locale: attendeeLanguage ?? "en" } };
});
evt = { ...evt, attendees: [...bookingAttendees, invitee[0]] };
if (eventType.seatsPerTimeSlot && eventType.seatsPerTimeSlot <= booking.attendees.length) {
throw new HttpError({ statusCode: 409, message: "Booking seats are full" });
}
const videoCallReference = booking.references.find((reference) => reference.type.includes("_video"));
if (videoCallReference) {
evt.videoCallData = {
type: videoCallReference.type,
id: videoCallReference.meetingId,
password: videoCallReference?.meetingPassword,
url: videoCallReference.meetingUrl,
};
}
const attendeeUniqueId = uuid();
await prisma.booking.update({
where: {
uid: reqBookingUid,
},
include: {
attendees: true,
},
data: {
attendees: {
create: {
email: invitee[0].email,
name: invitee[0].name,
timeZone: invitee[0].timeZone,
locale: invitee[0].language.locale,
bookingSeat: {
create: {
referenceUid: attendeeUniqueId,
data: {
description: additionalNotes,
},
booking: {
connect: {
id: booking.id,
},
},
},
},
},
},
...(booking.status === BookingStatus.CANCELLED && { status: BookingStatus.ACCEPTED }),
},
});
evt.attendeeSeatId = attendeeUniqueId;
const newSeat = booking.attendees.length !== 0;
/**
* Remember objects are passed into functions as references
* so if you modify it in a inner function it will be modified in the outer function
* deep cloning evt to avoid this
*/
if (!evt?.uid) {
evt.uid = booking?.uid ?? null;
}
const copyEvent = cloneDeep(evt);
copyEvent.uid = booking.uid;
if (noEmail !== true) {
let isHostConfirmationEmailsDisabled = false;
let isAttendeeConfirmationEmailDisabled = false;
const workflows = eventType.workflows.map((workflow) => workflow.workflow);
if (eventType.workflows) {
isHostConfirmationEmailsDisabled =
eventType.metadata?.disableStandardEmails?.confirmation?.host || false;
isAttendeeConfirmationEmailDisabled =
eventType.metadata?.disableStandardEmails?.confirmation?.attendee || false;
if (isHostConfirmationEmailsDisabled) {
isHostConfirmationEmailsDisabled = allowDisablingHostConfirmationEmails(workflows);
}
if (isAttendeeConfirmationEmailDisabled) {
isAttendeeConfirmationEmailDisabled = allowDisablingAttendeeConfirmationEmails(workflows);
}
}
await sendScheduledSeatsEmails(
copyEvent,
invitee[0],
newSeat,
!!eventType.seatsShowAttendees,
isHostConfirmationEmailsDisabled,
isAttendeeConfirmationEmailDisabled
);
}
const credentials = await refreshCredentials(allCredentials);
const eventManager = new EventManager({ ...organizerUser, credentials });
await eventManager.updateCalendarAttendees(evt, booking);
const foundBooking = await findBookingQuery(booking.id);
if (!Number.isNaN(paymentAppData.price) && paymentAppData.price > 0 && !!booking) {
const credentialPaymentAppCategories = await prisma.credential.findMany({
where: {
...(paymentAppData.credentialId
? { id: paymentAppData.credentialId }
: { userId: organizerUser.id }),
app: {
categories: {
hasSome: ["payment"],
},
},
},
select: {
key: true,
appId: true,
app: {
select: {
categories: true,
dirName: true,
},
},
},
});
const eventTypePaymentAppCredential = credentialPaymentAppCategories.find((credential) => {
return credential.appId === paymentAppData.appId;
});
if (!eventTypePaymentAppCredential) {
throw new HttpError({ statusCode: 400, message: "Missing payment credentials" });
}
if (!eventTypePaymentAppCredential?.appId) {
throw new HttpError({ statusCode: 400, message: "Missing payment app id" });
}
const payment = await handlePayment(
evt,
eventType,
eventTypePaymentAppCredential as IEventTypePaymentCredentialType,
booking,
fullName,
bookerEmail
);
resultBooking = { ...foundBooking };
resultBooking["message"] = "Payment required";
resultBooking["paymentUid"] = payment?.uid;
resultBooking["id"] = payment?.id;
} else {
resultBooking = { ...foundBooking };
}
resultBooking["seatReferenceUid"] = evt.attendeeSeatId;
}
// Here we should handle every after action that needs to be done after booking creation
// Obtain event metadata that includes videoCallUrl
const metadata = evt.videoCallData?.url ? { videoCallUrl: evt.videoCallData.url } : undefined;
try {
await scheduleWorkflowReminders({
workflows: eventType.workflows,
smsReminderNumber: smsReminderNumber || null,
calendarEvent: { ...evt, ...{ metadata, eventType: { slug: eventType.slug } } },
isNotConfirmed: evt.requiresConfirmation || false,
isRescheduleEvent: !!rescheduleUid,
isFirstRecurringEvent: true,
emailAttendeeSendToOverride: bookerEmail,
seatReferenceUid: evt.attendeeSeatId,
eventTypeRequiresConfirmation: eventType.requiresConfirmation,
});
} catch (error) {
loggerWithEventDetails.error("Error while scheduling workflow reminders", JSON.stringify({ error }));
}
const webhookData = {
...evt,
...eventTypeInfo,
uid: resultBooking?.uid || uid,
bookingId: booking?.id,
rescheduleUid,
rescheduleStartTime: originalRescheduledBooking?.startTime
? dayjs(originalRescheduledBooking?.startTime).utc().format()
: undefined,
rescheduleEndTime: originalRescheduledBooking?.endTime
? dayjs(originalRescheduledBooking?.endTime).utc().format()
: undefined,
metadata: { ...metadata, ...reqBodyMetadata },
eventTypeId,
status: "ACCEPTED",
smsReminderNumber: booking?.smsReminderNumber || undefined,
};
await handleWebhookTrigger({ subscriberOptions, eventTrigger, webhookData });
return resultBooking;
};
export default handleSeats;
@@ -0,0 +1,204 @@
// eslint-disable-next-line no-restricted-imports
import { cloneDeep } from "lodash";
import { uuid } from "short-uuid";
import EventManager from "@calcom/core/EventManager";
import { sendScheduledSeatsEmails } from "@calcom/emails";
import {
allowDisablingAttendeeConfirmationEmails,
allowDisablingHostConfirmationEmails,
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { HttpError } from "@calcom/lib/http-error";
import { handlePayment } from "@calcom/lib/payment/handlePayment";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import type { IEventTypePaymentCredentialType } from "../../handleNewBooking";
import { refreshCredentials, findBookingQuery } from "../../handleNewBooking";
import type { SeatedBooking, NewSeatedBookingObject, HandleSeatsResultBooking } from "../types";
const createNewSeat = async (
rescheduleSeatedBookingObject: NewSeatedBookingObject,
seatedBooking: SeatedBooking
) => {
const {
tAttendees,
attendeeLanguage,
invitee,
eventType,
reqBookingUid,
additionalNotes,
noEmail,
paymentAppData,
allCredentials,
organizerUser,
fullName,
bookerEmail,
} = rescheduleSeatedBookingObject;
let { evt } = rescheduleSeatedBookingObject;
let resultBooking: HandleSeatsResultBooking;
// Need to add translation for attendees to pass type checks. Since these values are never written to the db we can just use the new attendee language
const bookingAttendees = seatedBooking.attendees.map((attendee) => {
return { ...attendee, language: { translate: tAttendees, locale: attendeeLanguage ?? "en" } };
});
evt = { ...evt, attendees: [...bookingAttendees, invitee[0]] };
if (eventType.seatsPerTimeSlot && eventType.seatsPerTimeSlot <= seatedBooking.attendees.length) {
throw new HttpError({ statusCode: 409, message: ErrorCode.BookingSeatsFull });
}
const videoCallReference = seatedBooking.references.find((reference) => reference.type.includes("_video"));
if (videoCallReference) {
evt.videoCallData = {
type: videoCallReference.type,
id: videoCallReference.meetingId,
password: videoCallReference?.meetingPassword,
url: videoCallReference.meetingUrl,
};
}
const attendeeUniqueId = uuid();
const inviteeToAdd = invitee[0];
await prisma.booking.update({
where: {
uid: reqBookingUid,
},
include: {
attendees: true,
},
data: {
attendees: {
create: {
email: inviteeToAdd.email,
name: inviteeToAdd.name,
timeZone: inviteeToAdd.timeZone,
locale: inviteeToAdd.language.locale,
bookingSeat: {
create: {
referenceUid: attendeeUniqueId,
data: {
description: additionalNotes,
},
booking: {
connect: {
id: seatedBooking.id,
},
},
},
},
},
},
...(seatedBooking.status === BookingStatus.CANCELLED && { status: BookingStatus.ACCEPTED }),
},
});
evt.attendeeSeatId = attendeeUniqueId;
const newSeat = seatedBooking.attendees.length !== 0;
/**
* Remember objects are passed into functions as references
* so if you modify it in a inner function it will be modified in the outer function
* deep cloning evt to avoid this
*/
if (!evt?.uid) {
evt.uid = seatedBooking?.uid ?? null;
}
const copyEvent = cloneDeep(evt);
copyEvent.uid = seatedBooking.uid;
if (noEmail !== true) {
let isHostConfirmationEmailsDisabled = false;
let isAttendeeConfirmationEmailDisabled = false;
const workflows = eventType.workflows.map((workflow) => workflow.workflow);
if (eventType.workflows) {
isHostConfirmationEmailsDisabled =
eventType.metadata?.disableStandardEmails?.confirmation?.host || false;
isAttendeeConfirmationEmailDisabled =
eventType.metadata?.disableStandardEmails?.confirmation?.attendee || false;
if (isHostConfirmationEmailsDisabled) {
isHostConfirmationEmailsDisabled = allowDisablingHostConfirmationEmails(workflows);
}
if (isAttendeeConfirmationEmailDisabled) {
isAttendeeConfirmationEmailDisabled = allowDisablingAttendeeConfirmationEmails(workflows);
}
}
await sendScheduledSeatsEmails(
copyEvent,
inviteeToAdd,
newSeat,
!!eventType.seatsShowAttendees,
isHostConfirmationEmailsDisabled,
isAttendeeConfirmationEmailDisabled
);
}
const credentials = await refreshCredentials(allCredentials);
const eventManager = new EventManager({ ...organizerUser, credentials });
await eventManager.updateCalendarAttendees(evt, seatedBooking);
const foundBooking = await findBookingQuery(seatedBooking.id);
if (!Number.isNaN(paymentAppData.price) && paymentAppData.price > 0 && !!seatedBooking) {
const credentialPaymentAppCategories = await prisma.credential.findMany({
where: {
...(paymentAppData.credentialId ? { id: paymentAppData.credentialId } : { userId: organizerUser.id }),
app: {
categories: {
hasSome: ["payment"],
},
},
},
select: {
key: true,
appId: true,
app: {
select: {
categories: true,
dirName: true,
},
},
},
});
const eventTypePaymentAppCredential = credentialPaymentAppCategories.find((credential) => {
return credential.appId === paymentAppData.appId;
});
if (!eventTypePaymentAppCredential) {
throw new HttpError({ statusCode: 400, message: ErrorCode.MissingPaymentCredential });
}
if (!eventTypePaymentAppCredential?.appId) {
throw new HttpError({ statusCode: 400, message: ErrorCode.MissingPaymentAppId });
}
const payment = await handlePayment(
evt,
eventType,
eventTypePaymentAppCredential as IEventTypePaymentCredentialType,
seatedBooking,
fullName,
bookerEmail
);
resultBooking = { ...foundBooking };
resultBooking["message"] = "Payment required";
resultBooking["paymentUid"] = payment?.uid;
resultBooking["id"] = payment?.id;
} else {
resultBooking = { ...foundBooking };
}
resultBooking["seatReferenceUid"] = evt.attendeeSeatId;
return resultBooking;
};
export default createNewSeat;
@@ -0,0 +1,134 @@
// eslint-disable-next-line no-restricted-imports
import dayjs from "@calcom/dayjs";
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { HttpError } from "@calcom/lib/http-error";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import { createLoggerWithEventDetails } from "../handleNewBooking";
import createNewSeat from "./create/createNewSeat";
import rescheduleSeatedBooking from "./reschedule/rescheduleSeatedBooking";
import type { NewSeatedBookingObject, SeatedBooking, HandleSeatsResultBooking } from "./types";
const handleSeats = async (newSeatedBookingObject: NewSeatedBookingObject) => {
const {
eventType,
reqBodyUser,
rescheduleUid,
reqBookingUid,
invitee,
bookerEmail,
smsReminderNumber,
eventTypeInfo,
uid,
originalRescheduledBooking,
reqBodyMetadata,
eventTypeId,
subscriberOptions,
eventTrigger,
} = newSeatedBookingObject;
const { evt } = newSeatedBookingObject;
const loggerWithEventDetails = createLoggerWithEventDetails(eventType.id, reqBodyUser, eventType.slug);
let resultBooking: HandleSeatsResultBooking = null;
const seatedBooking: SeatedBooking | null = await prisma.booking.findFirst({
where: {
OR: [
{
uid: rescheduleUid || reqBookingUid,
},
{
eventTypeId: eventType.id,
startTime: evt.startTime,
},
],
status: BookingStatus.ACCEPTED,
},
select: {
uid: true,
id: true,
attendees: { include: { bookingSeat: true } },
userId: true,
references: true,
startTime: true,
user: true,
status: true,
smsReminderNumber: true,
endTime: true,
scheduledJobs: true,
},
});
if (!seatedBooking) {
throw new HttpError({ statusCode: 404, message: ErrorCode.BookingNotFound });
}
// See if attendee is already signed up for timeslot
if (
seatedBooking.attendees.find((attendee) => attendee.email === invitee[0].email) &&
dayjs.utc(seatedBooking.startTime).format() === evt.startTime
) {
throw new HttpError({ statusCode: 409, message: ErrorCode.AlreadySignedUpForBooking });
}
// There are two paths here, reschedule a booking with seats and booking seats without reschedule
if (rescheduleUid) {
resultBooking = await rescheduleSeatedBooking(
// Assert that the rescheduleUid is defined
{ ...newSeatedBookingObject, rescheduleUid },
seatedBooking,
resultBooking,
loggerWithEventDetails
);
} else {
resultBooking = await createNewSeat(newSeatedBookingObject, seatedBooking);
}
// If the resultBooking is defined we should trigger workflows else, trigger in handleNewBooking
if (resultBooking) {
// Obtain event metadata that includes videoCallUrl
const metadata = evt.videoCallData?.url ? { videoCallUrl: evt.videoCallData.url } : undefined;
try {
await scheduleWorkflowReminders({
workflows: eventType.workflows,
smsReminderNumber: smsReminderNumber || null,
calendarEvent: { ...evt, ...{ metadata, eventType: { slug: eventType.slug } } },
isNotConfirmed: evt.requiresConfirmation || false,
isRescheduleEvent: !!rescheduleUid,
isFirstRecurringEvent: true,
emailAttendeeSendToOverride: bookerEmail,
seatReferenceUid: evt.attendeeSeatId,
eventTypeRequiresConfirmation: eventType.requiresConfirmation,
});
} catch (error) {
loggerWithEventDetails.error("Error while scheduling workflow reminders", JSON.stringify({ error }));
}
const webhookData = {
...evt,
...eventTypeInfo,
uid: resultBooking?.uid || uid,
bookingId: seatedBooking?.id,
rescheduleUid,
rescheduleStartTime: originalRescheduledBooking?.startTime
? dayjs(originalRescheduledBooking?.startTime).utc().format()
: undefined,
rescheduleEndTime: originalRescheduledBooking?.endTime
? dayjs(originalRescheduledBooking?.endTime).utc().format()
: undefined,
metadata: { ...metadata, ...reqBodyMetadata },
eventTypeId,
status: "ACCEPTED",
smsReminderNumber: seatedBooking?.smsReminderNumber || undefined,
};
await handleWebhookTrigger({ subscriberOptions, eventTrigger, webhookData });
}
return resultBooking;
};
export default handleSeats;
@@ -0,0 +1,64 @@
import type { Attendee } from "@prisma/client";
// eslint-disable-next-line no-restricted-imports
import { getCalendar } from "@calcom/app-store/_utils/getCalendar";
import { deleteMeeting } from "@calcom/core/videoClient";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
import type { CalendarEvent } from "@calcom/types/Calendar";
import type { OriginalRescheduledBooking } from "../../handleNewBooking";
/* Check if the original booking has no more attendees, if so delete the booking
and any calendar or video integrations */
const lastAttendeeDeleteBooking = async (
originalRescheduledBooking: OriginalRescheduledBooking,
filteredAttendees: Partial<Attendee>[] | undefined,
originalBookingEvt?: CalendarEvent
) => {
let deletedReferences = false;
if ((!filteredAttendees || filteredAttendees.length === 0) && originalRescheduledBooking) {
const integrationsToDelete = [];
for (const reference of originalRescheduledBooking.references) {
if (reference.credentialId) {
const credential = await prisma.credential.findUnique({
where: {
id: reference.credentialId,
},
select: credentialForCalendarServiceSelect,
});
if (credential) {
if (reference.type.includes("_video")) {
integrationsToDelete.push(deleteMeeting(credential, reference.uid));
}
if (reference.type.includes("_calendar") && originalBookingEvt) {
const calendar = await getCalendar(credential);
if (calendar) {
integrationsToDelete.push(
calendar?.deleteEvent(reference.uid, originalBookingEvt, reference.externalCalendarId)
);
}
}
}
}
}
await Promise.all(integrationsToDelete).then(async () => {
await prisma.booking.update({
where: {
id: originalRescheduledBooking.id,
},
data: {
status: BookingStatus.CANCELLED,
},
});
});
deletedReferences = true;
}
return deletedReferences;
};
export default lastAttendeeDeleteBooking;
@@ -0,0 +1,102 @@
// eslint-disable-next-line no-restricted-imports
import { cloneDeep } from "lodash";
import type EventManager from "@calcom/core/EventManager";
import { sendRescheduledSeatEmail } from "@calcom/emails";
import prisma from "@calcom/prisma";
import type { Person, CalendarEvent } from "@calcom/types/Calendar";
import { findBookingQuery } from "../../../handleNewBooking";
import lastAttendeeDeleteBooking from "../../lib/lastAttendeeDeleteBooking";
import type { RescheduleSeatedBookingObject, SeatAttendee, NewTimeSlotBooking } from "../../types";
const attendeeRescheduleSeatedBooking = async (
rescheduleSeatedBookingObject: RescheduleSeatedBookingObject,
seatAttendee: SeatAttendee,
newTimeSlotBooking: NewTimeSlotBooking | null,
originalBookingEvt: CalendarEvent,
eventManager: EventManager
) => {
const { tAttendees, bookingSeat, bookerEmail, rescheduleUid, evt } = rescheduleSeatedBookingObject;
let { originalRescheduledBooking } = rescheduleSeatedBookingObject;
seatAttendee["language"] = { translate: tAttendees, locale: bookingSeat?.attendee.locale ?? "en" };
// If there is no booking then remove the attendee from the old booking and create a new one
if (!newTimeSlotBooking) {
await prisma.attendee.delete({
where: {
id: seatAttendee?.id,
},
});
// Update the original calendar event by removing the attendee that is rescheduling
if (originalBookingEvt && originalRescheduledBooking) {
// Event would probably be deleted so we first check than instead of updating references
const filteredAttendees = originalRescheduledBooking?.attendees.filter((attendee) => {
return attendee.email !== bookerEmail;
});
const deletedReference = await lastAttendeeDeleteBooking(
originalRescheduledBooking,
filteredAttendees,
originalBookingEvt
);
if (!deletedReference) {
await eventManager.updateCalendarAttendees(originalBookingEvt, originalRescheduledBooking);
}
}
// We don't want to trigger rescheduling logic of the original booking
originalRescheduledBooking = null;
return null;
}
// Need to change the new seat reference and attendee record to remove it from the old booking and add it to the new booking
// https://stackoverflow.com/questions/4980963/database-insert-new-rows-or-update-existing-ones
if (seatAttendee?.id && bookingSeat?.id) {
await prisma.$transaction([
prisma.attendee.update({
where: {
id: seatAttendee.id,
},
data: {
bookingId: newTimeSlotBooking.id,
},
}),
prisma.bookingSeat.update({
where: {
id: bookingSeat.id,
},
data: {
bookingId: newTimeSlotBooking.id,
},
}),
]);
}
const copyEvent = cloneDeep(evt);
const updateManager = await eventManager.reschedule(copyEvent, rescheduleUid, newTimeSlotBooking.id);
const results = updateManager.results;
const calendarResult = results.find((result) => result.type.includes("_calendar"));
evt.iCalUID = Array.isArray(calendarResult?.updatedEvent)
? calendarResult?.updatedEvent[0]?.iCalUID
: calendarResult?.updatedEvent?.iCalUID || undefined;
await sendRescheduledSeatEmail(copyEvent, seatAttendee as Person);
const filteredAttendees = originalRescheduledBooking?.attendees.filter((attendee) => {
return attendee.email !== bookerEmail;
});
await lastAttendeeDeleteBooking(originalRescheduledBooking, filteredAttendees, originalBookingEvt);
const foundBooking = await findBookingQuery(newTimeSlotBooking.id);
return { ...foundBooking, seatReferenceUid: bookingSeat?.referenceUid };
};
export default attendeeRescheduleSeatedBooking;
@@ -0,0 +1,160 @@
// eslint-disable-next-line no-restricted-imports
import { cloneDeep } from "lodash";
import { uuid } from "short-uuid";
import type EventManager from "@calcom/core/EventManager";
import { sendRescheduledEmails } from "@calcom/emails";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { HttpError } from "@calcom/lib/http-error";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import type { createLoggerWithEventDetails } from "../../../handleNewBooking";
import { addVideoCallDataToEvent, findBookingQuery } from "../../../handleNewBooking";
import type { SeatedBooking, RescheduleSeatedBookingObject, NewTimeSlotBooking } from "../../types";
const combineTwoSeatedBookings = async (
rescheduleSeatedBookingObject: RescheduleSeatedBookingObject,
seatedBooking: SeatedBooking,
newTimeSlotBooking: NewTimeSlotBooking,
eventManager: EventManager,
loggerWithEventDetails: ReturnType<typeof createLoggerWithEventDetails>
) => {
const {
eventType,
tAttendees,
attendeeLanguage,
rescheduleUid,
noEmail,
isConfirmedByDefault,
additionalNotes,
rescheduleReason,
} = rescheduleSeatedBookingObject;
let { evt } = rescheduleSeatedBookingObject;
// Merge two bookings together
const attendeesToMove = [],
attendeesToDelete = [];
for (const attendee of seatedBooking.attendees) {
// If the attendee already exists on the new booking then delete the attendee record of the old booking
if (
newTimeSlotBooking.attendees.some((newBookingAttendee) => newBookingAttendee.email === attendee.email)
) {
attendeesToDelete.push(attendee.id);
// If the attendee does not exist on the new booking then move that attendee record to the new booking
} else {
attendeesToMove.push({ id: attendee.id, seatReferenceId: attendee.bookingSeat?.id });
}
}
// Confirm that the new event will have enough available seats
if (
!eventType.seatsPerTimeSlot ||
attendeesToMove.length + newTimeSlotBooking.attendees.filter((attendee) => attendee.bookingSeat).length >
eventType.seatsPerTimeSlot
) {
throw new HttpError({ statusCode: 409, message: ErrorCode.NotEnoughAvailableSeats });
}
const moveAttendeeCalls = [];
for (const attendeeToMove of attendeesToMove) {
moveAttendeeCalls.push(
prisma.attendee.update({
where: {
id: attendeeToMove.id,
},
data: {
bookingId: newTimeSlotBooking.id,
bookingSeat: {
upsert: {
create: {
referenceUid: uuid(),
bookingId: newTimeSlotBooking.id,
},
update: {
bookingId: newTimeSlotBooking.id,
},
},
},
},
})
);
}
await prisma.$transaction([
...moveAttendeeCalls,
// Delete any attendees that are already a part of that new time slot booking
prisma.attendee.deleteMany({
where: {
id: {
in: attendeesToDelete,
},
},
}),
]);
const updatedNewBooking = await prisma.booking.findUnique({
where: {
id: newTimeSlotBooking.id,
},
include: {
attendees: true,
references: true,
},
});
if (!updatedNewBooking) {
throw new HttpError({ statusCode: 404, message: "Updated booking not found" });
}
// Update the evt object with the new attendees
const updatedBookingAttendees = updatedNewBooking.attendees.map((attendee) => {
const evtAttendee = {
...attendee,
language: { translate: tAttendees, locale: attendeeLanguage ?? "en" },
};
return evtAttendee;
});
evt.attendees = updatedBookingAttendees;
evt = addVideoCallDataToEvent(updatedNewBooking.references, evt);
const copyEvent = cloneDeep(evt);
const updateManager = await eventManager.reschedule(copyEvent, rescheduleUid, newTimeSlotBooking.id);
const results = updateManager.results;
const calendarResult = results.find((result) => result.type.includes("_calendar"));
evt.iCalUID = Array.isArray(calendarResult?.updatedEvent)
? calendarResult?.updatedEvent[0]?.iCalUID
: calendarResult?.updatedEvent?.iCalUID || undefined;
if (noEmail !== true && isConfirmedByDefault) {
// TODO send reschedule emails to attendees of the old booking
loggerWithEventDetails.debug("Emails: Sending reschedule emails - handleSeats");
await sendRescheduledEmails({
...copyEvent,
additionalNotes, // Resets back to the additionalNote input and not the override value
cancellationReason: `$RCH$${rescheduleReason ? rescheduleReason : ""}`, // Removable code prefix to differentiate cancellation from rescheduling for email
});
}
// Update the old booking with the cancelled status
await prisma.booking.update({
where: {
id: seatedBooking.id,
},
data: {
status: BookingStatus.CANCELLED,
},
});
const foundBooking = await findBookingQuery(newTimeSlotBooking.id);
return { ...foundBooking };
};
export default combineTwoSeatedBookings;
@@ -0,0 +1,101 @@
// eslint-disable-next-line no-restricted-imports
import { cloneDeep } from "lodash";
import type EventManager from "@calcom/core/EventManager";
import { sendRescheduledEmails } from "@calcom/emails";
import prisma from "@calcom/prisma";
import type { AdditionalInformation, AppsStatus } from "@calcom/types/Calendar";
import { addVideoCallDataToEvent, handleAppsStatus, findBookingQuery } from "../../../handleNewBooking";
import type { Booking, createLoggerWithEventDetails } from "../../../handleNewBooking";
import type { SeatedBooking, RescheduleSeatedBookingObject } from "../../types";
const moveSeatedBookingToNewTimeSlot = async (
rescheduleSeatedBookingObject: RescheduleSeatedBookingObject,
seatedBooking: SeatedBooking,
eventManager: EventManager,
loggerWithEventDetails: ReturnType<typeof createLoggerWithEventDetails>
) => {
const {
rescheduleReason,
rescheduleUid,
eventType,
organizerUser,
reqAppsStatus,
noEmail,
isConfirmedByDefault,
additionalNotes,
} = rescheduleSeatedBookingObject;
let { evt } = rescheduleSeatedBookingObject;
const newBooking: (Booking & { appsStatus?: AppsStatus[] }) | null = await prisma.booking.update({
where: {
id: seatedBooking.id,
},
data: {
startTime: evt.startTime,
endTime: evt.endTime,
cancellationReason: rescheduleReason,
},
include: {
user: true,
references: true,
payment: true,
attendees: true,
},
});
evt = addVideoCallDataToEvent(newBooking.references, evt);
const copyEvent = cloneDeep(evt);
const updateManager = await eventManager.reschedule(copyEvent, rescheduleUid, newBooking.id);
// @NOTE: This code is duplicated and should be moved to a function
// This gets overridden when updating the event - to check if notes have been hidden or not. We just reset this back
// to the default description when we are sending the emails.
evt.description = eventType.description;
const results = updateManager.results;
const calendarResult = results.find((result) => result.type.includes("_calendar"));
evt.iCalUID = calendarResult?.updatedEvent.iCalUID || undefined;
if (results.length > 0 && results.some((res) => !res.success)) {
const error = {
errorCode: "BookingReschedulingMeetingFailed",
message: "Booking Rescheduling failed",
};
loggerWithEventDetails.error(`Booking ${organizerUser.name} failed`, JSON.stringify({ error, results }));
} else {
const metadata: AdditionalInformation = {};
if (results.length) {
// TODO: Handle created event metadata more elegantly
const [updatedEvent] = Array.isArray(results[0].updatedEvent)
? results[0].updatedEvent
: [results[0].updatedEvent];
if (updatedEvent) {
metadata.hangoutLink = updatedEvent.hangoutLink;
metadata.conferenceData = updatedEvent.conferenceData;
metadata.entryPoints = updatedEvent.entryPoints;
evt.appsStatus = handleAppsStatus(results, newBooking, reqAppsStatus);
}
}
}
if (noEmail !== true && isConfirmedByDefault) {
const copyEvent = cloneDeep(evt);
loggerWithEventDetails.debug("Emails: Sending reschedule emails - handleSeats");
await sendRescheduledEmails({
...copyEvent,
additionalNotes, // Resets back to the additionalNote input and not the override value
cancellationReason: `$RCH$${rescheduleReason ? rescheduleReason : ""}`, // Removable code prefix to differentiate cancellation from rescheduling for email
});
}
const foundBooking = await findBookingQuery(newBooking.id);
return { ...foundBooking, appsStatus: newBooking.appsStatus };
};
export default moveSeatedBookingToNewTimeSlot;
@@ -0,0 +1,54 @@
// eslint-disable-next-line no-restricted-imports
import type EventManager from "@calcom/core/EventManager";
import type { createLoggerWithEventDetails } from "../../../handleNewBooking";
import type {
NewTimeSlotBooking,
SeatedBooking,
RescheduleSeatedBookingObject,
HandleSeatsResultBooking,
} from "../../types";
import combineTwoSeatedBookings from "./combineTwoSeatedBookings";
import moveSeatedBookingToNewTimeSlot from "./moveSeatedBookingToNewTimeSlot";
const ownerRescheduleSeatedBooking = async (
rescheduleSeatedBookingObject: RescheduleSeatedBookingObject,
newTimeSlotBooking: NewTimeSlotBooking | null,
seatedBooking: SeatedBooking,
resultBooking: HandleSeatsResultBooking | null,
eventManager: EventManager,
loggerWithEventDetails: ReturnType<typeof createLoggerWithEventDetails>
) => {
const { originalRescheduledBooking, tAttendees } = rescheduleSeatedBookingObject;
const { evt } = rescheduleSeatedBookingObject;
evt.attendees = originalRescheduledBooking?.attendees.map((attendee) => {
return {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: { translate: tAttendees, locale: attendee.locale ?? "en" },
};
});
// If there is no booking during the new time slot then update the current booking to the new date
if (!newTimeSlotBooking) {
resultBooking = await moveSeatedBookingToNewTimeSlot(
rescheduleSeatedBookingObject,
seatedBooking,
eventManager,
loggerWithEventDetails
);
} else {
// If a booking already exists during the new time slot then merge the two bookings together
resultBooking = await combineTwoSeatedBookings(
rescheduleSeatedBookingObject,
seatedBooking,
newTimeSlotBooking,
eventManager,
loggerWithEventDetails
);
}
return resultBooking;
};
export default ownerRescheduleSeatedBooking;
@@ -0,0 +1,140 @@
// eslint-disable-next-line no-restricted-imports
import EventManager from "@calcom/core/EventManager";
import dayjs from "@calcom/dayjs";
import { HttpError } from "@calcom/lib/http-error";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import type { Person } from "@calcom/types/Calendar";
import { refreshCredentials } from "../../handleNewBooking";
import type { createLoggerWithEventDetails } from "../../handleNewBooking";
import type {
HandleSeatsResultBooking,
SeatedBooking,
RescheduleSeatedBookingObject,
SeatAttendee,
} from "../types";
import attendeeRescheduleSeatedBooking from "./attendee/attendeeRescheduleSeatedBooking";
import ownerRescheduleSeatedBooking from "./owner/ownerRescheduleSeatedBooking";
const rescheduleSeatedBooking = async (
// If this function is being called then rescheduleUid is defined
rescheduleSeatedBookingObject: RescheduleSeatedBookingObject,
seatedBooking: SeatedBooking,
resultBooking: HandleSeatsResultBooking | null,
loggerWithEventDetails: ReturnType<typeof createLoggerWithEventDetails>
) => {
const { evt, eventType, allCredentials, organizerUser, bookerEmail, tAttendees, bookingSeat, reqUserId } =
rescheduleSeatedBookingObject;
const { originalRescheduledBooking } = rescheduleSeatedBookingObject;
// See if the new date has a booking already
const newTimeSlotBooking = await prisma.booking.findFirst({
where: {
startTime: dayjs(evt.startTime).toDate(),
eventTypeId: eventType.id,
status: BookingStatus.ACCEPTED,
},
select: {
id: true,
uid: true,
attendees: {
include: {
bookingSeat: true,
},
},
},
});
const credentials = await refreshCredentials(allCredentials);
const eventManager = new EventManager({ ...organizerUser, credentials });
if (!originalRescheduledBooking) {
// typescript isn't smart enough;
throw new Error("Internal Error.");
}
const updatedBookingAttendees = originalRescheduledBooking.attendees.reduce(
(filteredAttendees, attendee) => {
if (attendee.email === bookerEmail) {
return filteredAttendees; // skip current booker, as we know the language already.
}
filteredAttendees.push({
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: { translate: tAttendees, locale: attendee.locale ?? "en" },
});
return filteredAttendees;
},
[] as Person[]
);
// If original booking has video reference we need to add the videoCallData to the new evt
const videoReference = originalRescheduledBooking.references.find((reference) =>
reference.type.includes("_video")
);
const originalBookingEvt = {
...evt,
title: originalRescheduledBooking.title,
startTime: dayjs(originalRescheduledBooking.startTime).utc().format(),
endTime: dayjs(originalRescheduledBooking.endTime).utc().format(),
attendees: updatedBookingAttendees,
// If the location is a video integration then include the videoCallData
...(videoReference && {
videoCallData: {
type: videoReference.type,
id: videoReference.meetingId,
password: videoReference.meetingPassword,
url: videoReference.meetingUrl,
},
}),
};
if (!bookingSeat) {
// if no bookingSeat is given and the userId != owner, 401.
// TODO: Next step; Evaluate ownership, what about teams?
if (seatedBooking.user?.id !== reqUserId) {
throw new HttpError({ statusCode: 401 });
}
// Moving forward in this block is the owner making changes to the booking. All attendees should be affected
evt.attendees = originalRescheduledBooking.attendees.map((attendee) => {
return {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: { translate: tAttendees, locale: attendee.locale ?? "en" },
};
});
// If owner reschedules the event we want to update the entire booking
// Also if owner is rescheduling there should be no bookingSeat
resultBooking = await ownerRescheduleSeatedBooking(
rescheduleSeatedBookingObject,
newTimeSlotBooking,
seatedBooking,
resultBooking,
eventManager,
loggerWithEventDetails
);
}
// seatAttendee is null when the organizer is rescheduling.
const seatAttendee: SeatAttendee | null = bookingSeat?.attendee || null;
if (seatAttendee) {
resultBooking = await attendeeRescheduleSeatedBooking(
rescheduleSeatedBookingObject,
seatAttendee,
newTimeSlotBooking,
originalBookingEvt,
eventManager
);
}
return resultBooking;
};
export default rescheduleSeatedBooking;
File diff suppressed because it is too large. Load diff
+80
View File
@@ -0,0 +1,80 @@
import type { Prisma } from "@prisma/client";
import type { AppsStatus } from "@calcom/types/Calendar";
import type { Booking, NewBookingEventType, OriginalRescheduledBooking } from "../handleNewBooking";
export type BookingSeat = Prisma.BookingSeatGetPayload<{ include: { booking: true; attendee: true } }> | null;
export type NewSeatedBookingObject = {
rescheduleUid: string | undefined;
reqBookingUid: string | undefined;
eventType: NewBookingEventType;
evt: CalendarEvent;
invitee: Invitee;
allCredentials: Awaited<ReturnType<typeof getAllCredentials>>;
organizerUser: OrganizerUser;
originalRescheduledBooking: OriginalRescheduledBooking;
bookerEmail: string;
tAttendees: TFunction;
bookingSeat: BookingSeat;
reqUserId: number | undefined;
rescheduleReason: RescheduleReason;
reqBodyUser: string | string[] | undefined;
noEmail: NoEmail;
isConfirmedByDefault: IsConfirmedByDefault;
additionalNotes: AdditionalNotes;
reqAppsStatus: ReqAppsStatus;
attendeeLanguage: string | null;
paymentAppData: PaymentAppData;
fullName: ReturnType<typeof getFullName>;
smsReminderNumber: SmsReminderNumber;
eventTypeInfo: EventTypeInfo;
uid: short.SUUID;
eventTypeId: EventTypeId;
reqBodyMetadata: ReqBodyMetadata;
subscriberOptions: GetSubscriberOptions;
eventTrigger: WebhookTriggerEvents;
};
export type RescheduleSeatedBookingObject = NewSeatedBookingObject & { rescheduleUid: string };
export type SeatedBooking = Prisma.BookingGetPayload<{
select: {
uid: true;
id: true;
attendees: { include: { bookingSeat: true } };
userId: true;
references: true;
startTime: true;
user: true;
status: true;
smsReminderNumber: true;
endTime: true;
scheduledJobs: true;
};
}>;
export type HandleSeatsResultBooking =
| (Partial<Booking> & {
appsStatus?: AppsStatus[];
seatReferenceUid?: string;
paymentUid?: string;
message?: string;
paymentId?: number;
})
| null;
export type NewTimeSlotBooking = Prisma.BookingGetPayload<{
select: {
id: true;
uid: true;
attendees: {
include: {
bookingSeat: true;
};
};
};
}>;
export type SeatAttendee = Partial<Person>;
+1 -1
View File
@@ -25,7 +25,7 @@ export type RecurringBookingCreateBody = BookingCreateBody & {
noEmail?: boolean; noEmail?: boolean;
recurringCount?: number; recurringCount?: number;
appsStatus?: AppsStatus[] | undefined; appsStatus?: AppsStatus[] | undefined;
allRecurringDates?: string[]; allRecurringDates?: Record<string, string>[];
currentRecurringIndex?: number; currentRecurringIndex?: number;
}; };
@@ -1,4 +1,5 @@
import type { SessionContextValue } from "next-auth/react"; import type { SessionContextValue } from "next-auth/react";
import { signIn } from "next-auth/react";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import { TopBanner } from "@calcom/ui"; import { TopBanner } from "@calcom/ui";
@@ -8,7 +9,10 @@ export type ImpersonatingBannerProps = { data: SessionContextValue["data"] };
function ImpersonatingBanner({ data }: ImpersonatingBannerProps) { function ImpersonatingBanner({ data }: ImpersonatingBannerProps) {
const { t } = useLocale(); const { t } = useLocale();
if (!data?.user.impersonatedByUID) return null; if (!data?.user.impersonatedBy) return null;
const returnToId = data.user.impersonatedBy.id;
const canReturnToSelf = data.user.impersonatedBy.role == "ADMIN" || data.user?.org?.id;
return ( return (
<> <>
@@ -16,9 +20,21 @@ function ImpersonatingBanner({ data }: ImpersonatingBannerProps) {
text={t("impersonating_user_warning", { user: data.user.username })} text={t("impersonating_user_warning", { user: data.user.username })}
variant="warning" variant="warning"
actions={ actions={
<a className="border-b border-b-black" href="/auth/logout"> canReturnToSelf ? (
{t("impersonating_stop_instructions")} <form
</a> onSubmit={(e) => {
e.preventDefault();
signIn("impersonation-auth", { returnToId });
}}>
<button className="text-emphasis hover:underline" data-testid="stop-impersonating-button">
{t("impersonating_stop_instructions")}
</button>
</form>
) : (
<a className="border-b border-b-black" href="/auth/logout">
{t("impersonating_stop_instructions")}
</a>
)
} }
/> />
</> </>
@@ -7,7 +7,7 @@ import {
parseTeamId, parseTeamId,
checkSelfImpersonation, checkSelfImpersonation,
checkUserIdentifier, checkUserIdentifier,
checkPermission, checkGlobalPermission,
} from "./ImpersonationProvider"; } from "./ImpersonationProvider";
const session: Session = { const session: Session = {
@@ -65,17 +65,17 @@ describe("checkUserIdentifier", () => {
describe("checkPermission", () => { describe("checkPermission", () => {
it("should throw an error if the user is not an admin and team impersonation is disabled", () => { it("should throw an error if the user is not an admin and team impersonation is disabled", () => {
process.env.NEXT_PUBLIC_TEAM_IMPERSONATION = "false"; process.env.NEXT_PUBLIC_TEAM_IMPERSONATION = "false";
expect(() => checkPermission(session)).toThrow(); expect(() => checkGlobalPermission(session)).toThrow();
}); });
it("should not throw an error if the user is an admin and team impersonation is disabled", () => { it("should not throw an error if the user is an admin and team impersonation is disabled", () => {
const modifiedSession = { ...session, user: { ...session.user, role: UserPermissionRole.ADMIN } }; const modifiedSession = { ...session, user: { ...session.user, role: UserPermissionRole.ADMIN } };
process.env.NEXT_PUBLIC_TEAM_IMPERSONATION = "false"; process.env.NEXT_PUBLIC_TEAM_IMPERSONATION = "false";
expect(() => checkPermission(modifiedSession)).not.toThrow(); expect(() => checkGlobalPermission(modifiedSession)).not.toThrow();
}); });
it("should not throw an error if the user is not an admin but team impersonation is enabled", () => { it("should not throw an error if the user is not an admin but team impersonation is enabled", () => {
process.env.NEXT_PUBLIC_TEAM_IMPERSONATION = "true"; process.env.NEXT_PUBLIC_TEAM_IMPERSONATION = "true";
expect(() => checkPermission(session)).not.toThrow(); expect(() => checkGlobalPermission(session)).not.toThrow();
}); });
}); });
@@ -14,7 +14,8 @@ const teamIdschema = z.object({
const auditAndReturnNextUser = async ( const auditAndReturnNextUser = async (
impersonatedUser: Pick<User, "id" | "username" | "email" | "name" | "role" | "organizationId" | "locale">, impersonatedUser: Pick<User, "id" | "username" | "email" | "name" | "role" | "organizationId" | "locale">,
impersonatedByUID: number, impersonatedByUID: number,
hasTeam?: boolean hasTeam?: boolean,
isReturningToSelf?: boolean
) => { ) => {
// Log impersonations for audit purposes // Log impersonations for audit purposes
await prisma.impersonations.create({ await prisma.impersonations.create({
@@ -38,16 +39,36 @@ const auditAndReturnNextUser = async (
email: impersonatedUser.email, email: impersonatedUser.email,
name: impersonatedUser.name, name: impersonatedUser.name,
role: impersonatedUser.role, role: impersonatedUser.role,
impersonatedByUID,
belongsToActiveTeam: hasTeam, belongsToActiveTeam: hasTeam,
organizationId: impersonatedUser.organizationId, organizationId: impersonatedUser.organizationId,
locale: impersonatedUser.locale, locale: impersonatedUser.locale,
}; };
if (!isReturningToSelf) {
const impersonatedByUser = await prisma.user.findUnique({
where: {
id: impersonatedByUID,
},
select: {
id: true,
role: true,
},
});
if (!impersonatedByUser) throw new Error("This user does not exist.");
return {
...obj,
impersonatedBy: {
id: impersonatedByUser?.id,
role: impersonatedByUser?.role,
},
};
}
return obj; return obj;
}; };
type Credentials = Record<"username" | "teamId", string> | undefined; type Credentials = Record<"username" | "teamId" | "returnToId", string> | undefined;
export function parseTeamId(creds: Partial<Credentials>) { export function parseTeamId(creds: Partial<Credentials>) {
return creds?.teamId ? teamIdschema.parse({ teamId: creds.teamId }).teamId : undefined; return creds?.teamId ? teamIdschema.parse({ teamId: creds.teamId }).teamId : undefined;
@@ -60,10 +81,13 @@ export function checkSelfImpersonation(session: Session | null, creds: Partial<C
} }
export function checkUserIdentifier(creds: Partial<Credentials>) { export function checkUserIdentifier(creds: Partial<Credentials>) {
if (!creds?.username) throw new Error("User identifier must be present"); if (!creds?.username) {
if (creds?.returnToId) return;
throw new Error("User identifier must be present");
}
} }
export function checkPermission(session: Session | null) { export function checkGlobalPermission(session: Session | null) {
if ( if (
(session?.user.role !== "ADMIN" && process.env.NEXT_PUBLIC_TEAM_IMPERSONATION === "false") || (session?.user.role !== "ADMIN" && process.env.NEXT_PUBLIC_TEAM_IMPERSONATION === "false") ||
!session?.user !session?.user
@@ -133,6 +157,60 @@ async function getImpersonatedUser({
return impersonatedUser; return impersonatedUser;
} }
async function isReturningToSelf({ session, creds }: { session: Session | null; creds: Credentials | null }) {
const impersonatedByUID = session?.user.impersonatedBy?.id;
if (!impersonatedByUID || !creds?.returnToId) return;
const returnToId = parseInt(creds?.returnToId, 10);
// Ensure session impersonatedUID + the returnToId is the same so we cant take over a random account
if (impersonatedByUID !== returnToId) return;
const returningUser = await prisma.user.findUnique({
where: {
id: returnToId,
},
select: {
id: true,
username: true,
email: true,
name: true,
role: true,
organizationId: true,
locale: true,
teams: {
where: {
accepted: true, // Ensure they are apart of the team and not just invited.
},
select: {
teamId: true,
disableImpersonation: true,
role: true,
},
},
},
});
if (returningUser) {
// Skip for none org users
if (returningUser.role !== "ADMIN" && !returningUser.organizationId) return;
const hasTeams = returningUser.teams.length >= 1;
return {
user: {
id: returningUser.id,
email: returningUser.email,
locale: returningUser.locale,
name: returningUser.name,
organizationId: returningUser.organizationId,
role: returningUser.role,
username: returningUser.username,
},
impersonatedByUID,
hasTeams,
};
}
}
const ImpersonationProvider = CredentialsProvider({ const ImpersonationProvider = CredentialsProvider({
id: "impersonation-auth", id: "impersonation-auth",
name: "Impersonation", name: "Impersonation",
@@ -140,6 +218,7 @@ const ImpersonationProvider = CredentialsProvider({
credentials: { credentials: {
username: { type: "text" }, username: { type: "text" },
teamId: { type: "text" }, teamId: { type: "text" },
returnToId: { type: "text" },
}, },
async authorize(creds, req) { async authorize(creds, req) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -148,7 +227,19 @@ const ImpersonationProvider = CredentialsProvider({
const teamId = parseTeamId(creds); const teamId = parseTeamId(creds);
checkSelfImpersonation(session, creds); checkSelfImpersonation(session, creds);
checkUserIdentifier(creds); checkUserIdentifier(creds);
checkPermission(session);
// Returning to target and UID is self without having to do perm checks.
const returnToUser = await isReturningToSelf({ session, creds });
if (returnToUser) {
return auditAndReturnNextUser(
returnToUser.user,
returnToUser.impersonatedByUID,
returnToUser.hasTeams,
true
);
}
checkGlobalPermission(session);
const impersonatedUser = await getImpersonatedUser({ session, teamId, creds }); const impersonatedUser = await getImpersonatedUser({ session, teamId, creds });
@@ -102,9 +102,11 @@ export default function WorkflowListPage({ workflows }: Props) {
{workflows.map((workflow, index) => { {workflows.map((workflow, index) => {
const firstItem = workflows[0]; const firstItem = workflows[0];
const lastItem = workflows[workflows.length - 1]; const lastItem = workflows[workflows.length - 1];
const dataTestId = `workflow-${workflow.name.toLowerCase().replaceAll(" ", "-")}`;
return ( return (
<li <li
key={workflow.id} key={workflow.id}
data-testid={dataTestId}
className="group flex w-full max-w-full items-center justify-between overflow-hidden"> className="group flex w-full max-w-full items-center justify-between overflow-hidden">
{!(firstItem && firstItem.id === workflow.id) && ( {!(firstItem && firstItem.id === workflow.id) && (
<ArrowButton onClick={() => moveWorkflow(index, -1)} arrowDirection="up" /> <ArrowButton onClick={() => moveWorkflow(index, -1)} arrowDirection="up" />
@@ -234,6 +236,7 @@ export default function WorkflowListPage({ workflows }: Props) {
StartIcon={Edit2} StartIcon={Edit2}
disabled={workflow.readOnly} disabled={workflow.readOnly}
onClick={async () => await router.replace(`/workflows/${workflow.id}`)} onClick={async () => await router.replace(`/workflows/${workflow.id}`)}
data-testid="edit-button"
/> />
</Tooltip> </Tooltip>
<Tooltip content={t("delete") as string}> <Tooltip content={t("delete") as string}>
@@ -246,6 +249,7 @@ export default function WorkflowListPage({ workflows }: Props) {
variant="icon" variant="icon"
disabled={workflow.readOnly} disabled={workflow.readOnly}
StartIcon={Trash2} StartIcon={Trash2}
data-testid="delete-button"
/> />
</Tooltip> </Tooltip>
</ButtonGroup> </ButtonGroup>
@@ -0,0 +1,29 @@
import { describe, expect, it } from "vitest";
import { getCalVideoReference } from "./get-cal-video-reference";
describe("Cal Video", () => {
it("should load latest cal video reference", () => {
expect(
getCalVideoReference([
{
uid: "UID1",
type: "daily_video",
meetingUrl: "ID1",
meetingPassword: "P1",
},
{
uid: "UID2",
type: "daily_video",
meetingUrl: "ID2",
meetingPassword: "P2",
},
])
).toEqual({
uid: "UID2",
type: "daily_video",
meetingUrl: "ID2",
meetingPassword: "P2",
});
});
});
@@ -0,0 +1,12 @@
export type Reference = {
uid: string;
type: string;
meetingUrl: string | null;
meetingPassword: string | null;
};
export const getCalVideoReference = (references: Reference[]) => {
const videoReferences = references.filter((reference) => reference.type.includes("_video"));
const latestVideoReference = videoReferences[videoReferences.length - 1];
return latestVideoReference;
};
@@ -56,7 +56,7 @@ export default function TimezoneChangeDialog() {
const { data } = useSession(); const { data } = useSession();
if (data?.user.impersonatedByUID) return null; if (data?.user.impersonatedBy) return null;
const ONE_DAY = 60 * 60 * 24; // 1 day in seconds (60 seconds * 60 minutes * 24 hours) const ONE_DAY = 60 * 60 * 24; // 1 day in seconds (60 seconds * 60 minutes * 24 hours)
const THREE_MONTHS = ONE_DAY * 90; // 90 days in seconds (90 days * 1 day in seconds) const THREE_MONTHS = ONE_DAY * 90; // 90 days in seconds (90 days * 1 day in seconds)
+2 -2
View File
@@ -200,7 +200,7 @@ const useBanners = () => {
if (isLoading || !userSession) return null; if (isLoading || !userSession) return null;
const isUserInactiveAdmin = userSession?.user.role === "INACTIVE_ADMIN"; const isUserInactiveAdmin = userSession?.user.role === "INACTIVE_ADMIN";
const userImpersonatedByUID = userSession?.user.impersonatedByUID; const userImpersonatedByUID = userSession?.user.impersonatedBy?.id;
const userSessionBanners = { const userSessionBanners = {
adminPasswordBanner: isUserInactiveAdmin ? userSession : null, adminPasswordBanner: isUserInactiveAdmin ? userSession : null,
@@ -892,7 +892,7 @@ function SideBar({ bannersHeight, user }: SideBarProps) {
<div className="flex h-full flex-col justify-between py-3 lg:pt-4"> <div className="flex h-full flex-col justify-between py-3 lg:pt-4">
<header className="todesktop:-mt-3 todesktop:flex-col-reverse todesktop:[-webkit-app-region:drag] items-center justify-between md:hidden lg:flex"> <header className="todesktop:-mt-3 todesktop:flex-col-reverse todesktop:[-webkit-app-region:drag] items-center justify-between md:hidden lg:flex">
{orgBranding ? ( {orgBranding ? (
<Link href="/settings/organizations/profile" className="mt-3 w-full px-1.5"> <Link href="/settings/organizations/profile" className="w-full px-1.5">
<div className="flex items-center gap-2 font-medium"> <div className="flex items-center gap-2 font-medium">
<Avatar <Avatar
alt={`${orgBranding.name} logo`} alt={`${orgBranding.name} logo`}
+6
View File
@@ -6,5 +6,11 @@ export enum ErrorCode {
AlreadySignedUpForBooking = "already_signed_up_for_this_booking_error", AlreadySignedUpForBooking = "already_signed_up_for_this_booking_error",
HostsUnavailableForBooking = "hosts_unavailable_for_booking", HostsUnavailableForBooking = "hosts_unavailable_for_booking",
EventTypeNotFound = "event_type_not_found_error", EventTypeNotFound = "event_type_not_found_error",
BookingNotFound = "booking_not_found_error",
BookingSeatsFull = "booking_seats_full_error",
MissingPaymentCredential = "missing_payment_credential_error",
MissingPaymentAppId = "missing_payment_app_id_error",
NotEnoughAvailableSeats = "not_enough_available_seats_error",
AvailabilityNotFoundInSchedule = "availability_not_found_in_schedule_error",
CancelledBookingsCannotBeRescheduled = "cancelled_bookings_cannot_be_rescheduled", CancelledBookingsCannotBeRescheduled = "cancelled_bookings_cannot_be_rescheduled",
} }
@@ -0,0 +1,42 @@
import { Prisma } from "@prisma/client";
import type { DefaultArgs, InternalArgs } from "@prisma/client/runtime/library";
export function excludePendingPaymentsExtension() {
return Prisma.defineExtension({
query: {
team: {
async findUnique({ args, query }) {
return excludePendingPayments(args, query);
},
async findFirst({ args, query }) {
return excludePendingPayments(args, query);
},
async findMany({ args, query }) {
return excludePendingPayments(args, query);
},
async findUniqueOrThrow({ args, query }) {
return excludePendingPayments(args, query);
},
async findFirstOrThrow({ args, query }) {
return excludePendingPayments(args, query);
},
},
},
});
}
async function excludePendingPayments(
args:
| Prisma.TeamFindUniqueArgs<InternalArgs & DefaultArgs>
| Prisma.TeamFindFirstArgs<InternalArgs & DefaultArgs>
| Prisma.TeamFindManyArgs<InternalArgs & DefaultArgs>
| Prisma.TeamFindUniqueOrThrowArgs<InternalArgs & DefaultArgs>
| Prisma.TeamFindFirstOrThrowArgs<InternalArgs & DefaultArgs>,
query: <T>(args: T) => Promise<unknown>
) {
args.where = args.where || {};
if (args.where.pendingPayment === undefined) {
args.where.pendingPayment = false;
}
return query(args);
}
+5 -29
View File
@@ -2,6 +2,7 @@ import type { Prisma } from "@prisma/client";
import { PrismaClient as PrismaClientWithoutExtension } from "@prisma/client"; import { PrismaClient as PrismaClientWithoutExtension } from "@prisma/client";
import { withAccelerate } from "@prisma/extension-accelerate"; import { withAccelerate } from "@prisma/extension-accelerate";
import { excludePendingPaymentsExtension } from "./extensions/exclude-pending-payment-teams";
import { bookingReferenceMiddleware } from "./middleware"; import { bookingReferenceMiddleware } from "./middleware";
const prismaOptions: Prisma.PrismaClientOptions = {}; const prismaOptions: Prisma.PrismaClientOptions = {};
@@ -18,7 +19,9 @@ const prismaWithoutClientExtensions =
globalForPrisma.prismaWithoutClientExtensions || new PrismaClientWithoutExtension(prismaOptions); globalForPrisma.prismaWithoutClientExtensions || new PrismaClientWithoutExtension(prismaOptions);
export const customPrisma = (options?: Prisma.PrismaClientOptions) => export const customPrisma = (options?: Prisma.PrismaClientOptions) =>
new PrismaClientWithoutExtension({ ...prismaOptions, ...options }).$extends(withAccelerate()); new PrismaClientWithoutExtension({ ...prismaOptions, ...options })
.$extends(excludePendingPaymentsExtension())
.$extends(withAccelerate());
// If any changed on middleware server restart is required // If any changed on middleware server restart is required
// TODO: Migrate it to $extends // TODO: Migrate it to $extends
@@ -27,35 +30,8 @@ bookingReferenceMiddleware(prismaWithoutClientExtensions);
// FIXME: Due to some reason, there are types failing in certain places due to the $extends. Fix it and then enable it // FIXME: Due to some reason, there are types failing in certain places due to the $extends. Fix it and then enable it
// Specifically we get errors like `Type 'string | Date | null | undefined' is not assignable to type 'Exact<string | Date | null | undefined, string | Date | null | undefined>'` // Specifically we get errors like `Type 'string | Date | null | undefined' is not assignable to type 'Exact<string | Date | null | undefined, string | Date | null | undefined>'`
const prismaWithClientExtensions = prismaWithoutClientExtensions const prismaWithClientExtensions = prismaWithoutClientExtensions
// .$extends(excludePendingPaymentsExtension())
.$extends(withAccelerate()); .$extends(withAccelerate());
// .$extends({
// query: {
// $allModels: {
// async $allOperations({ model, operation, args, query }) {
// const start = performance.now();
// /* your custom logic here */
// const res = await query(args);
// const end = performance.now();
// logger.debug("Query Perf: ", `${model}.${operation} took ${(end - start).toFixed(2)}ms\n`);
// return res;
// },
// },
// },
// });
// .$extends({
// name: "teamUpdateWithMetadata",
// query: {
// team: {
// async update({ model, operation, args, query }) {
// if (args.data.metadata) {
// // Prepare args.data with merged metadata
// }
// return query(args);
// },
// },
// },
// })
export const prisma = globalForPrisma.prismaWithClientExtensions || prismaWithClientExtensions; export const prisma = globalForPrisma.prismaWithClientExtensions || prismaWithClientExtensions;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "BookingReference" ADD COLUMN "thirdPartyRecurringEventId" TEXT;
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Team" ADD COLUMN "pendingPayment" BOOLEAN NOT NULL DEFAULT false;
+13 -11
View File
@@ -316,6 +316,7 @@ model Team {
credentials Credential[] credentials Credential[]
accessCodes AccessCode[] accessCodes AccessCode[]
instantMeetingTokens InstantMeetingToken[] instantMeetingTokens InstantMeetingToken[]
pendingPayment Boolean @default(false)
@@unique([slug, parentId]) @@unique([slug, parentId])
} }
@@ -373,19 +374,20 @@ model InstantMeetingToken {
} }
model BookingReference { model BookingReference {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
/// @zod.min(1) /// @zod.min(1)
type String type String
/// @zod.min(1) /// @zod.min(1)
uid String uid String
meetingId String? meetingId String?
meetingPassword String? thirdPartyRecurringEventId String?
meetingUrl String? meetingPassword String?
booking Booking? @relation(fields: [bookingId], references: [id], onDelete: Cascade) meetingUrl String?
bookingId Int? booking Booking? @relation(fields: [bookingId], references: [id], onDelete: Cascade)
externalCalendarId String? bookingId Int?
deleted Boolean? externalCalendarId String?
credentialId Int? deleted Boolean?
credentialId Int?
@@index([bookingId]) @@index([bookingId])
@@index([credentialId]) @@index([credentialId])
+11 -1
View File
@@ -250,7 +250,14 @@ export const extendedBookingCreateBody = bookingCreateBodySchema.merge(
z.object({ z.object({
noEmail: z.boolean().optional(), noEmail: z.boolean().optional(),
recurringCount: z.number().optional(), recurringCount: z.number().optional(),
allRecurringDates: z.string().array().optional(), allRecurringDates: z
.array(
z.object({
start: z.string(),
end: z.string(),
})
)
.optional(),
currentRecurringIndex: z.number().optional(), currentRecurringIndex: z.number().optional(),
appsStatus: z appsStatus: z
.array( .array(
@@ -301,6 +308,7 @@ export const vitalSettingsUpdateSchema = z.object({
export const createdEventSchema = z export const createdEventSchema = z
.object({ .object({
id: z.string(), id: z.string(),
thirdPartyRecurringEventId: z.string(),
password: z.union([z.string(), z.undefined()]), password: z.union([z.string(), z.undefined()]),
onlineMeetingUrl: z.string().nullable(), onlineMeetingUrl: z.string().nullable(),
iCalUID: z.string().optional(), iCalUID: z.string().optional(),
@@ -324,6 +332,8 @@ export const userMetadata = z
}) })
.nullable(); .nullable();
export type userMetadataType = z.infer<typeof userMetadata>;
export const teamMetadataSchema = z export const teamMetadataSchema = z
.object({ .object({
requestedSlug: z.string(), requestedSlug: z.string(),
@@ -26,6 +26,12 @@ export const verifyCodeHandler = async ({ ctx, input }: VerifyCodeOptions) => {
logger.warn(`Skipping code verification in dev/E2E environment`); logger.warn(`Skipping code verification in dev/E2E environment`);
return true; return true;
} }
if (user.role === "ADMIN") {
logger.warn(`Skipping code verification for instance admin`);
return true;
}
await checkRateLimitAndThrowError({ await checkRateLimitAndThrowError({
rateLimitingType: "core", rateLimitingType: "core",
identifier: email, identifier: email,
+6
View File
@@ -63,6 +63,7 @@ export type AdditionalInfo = Record<string, unknown> & { calWarnings?: string[]
export type NewCalendarEventType = { export type NewCalendarEventType = {
uid: string; uid: string;
id: string; id: string;
thirdPartyRecurringEventId?: string | null;
type: string; type: string;
password: string; password: string;
url: string; url: string;
@@ -143,6 +144,10 @@ export type CalEventResponses = Record<
} }
>; >;
export interface ExistingRecurringEvent {
recurringEventId: string;
}
// If modifying this interface, probably should update builders/calendarEvent files // If modifying this interface, probably should update builders/calendarEvent files
export interface CalendarEvent { export interface CalendarEvent {
// Instead of sending this per event. // Instead of sending this per event.
@@ -167,6 +172,7 @@ export interface CalendarEvent {
conferenceData?: ConferenceData; conferenceData?: ConferenceData;
additionalInformation?: AdditionalInformation; additionalInformation?: AdditionalInformation;
uid?: string | null; uid?: string | null;
existingRecurringEvent?: ExistingRecurringEvent | null;
bookingId?: number; bookingId?: number;
videoCallData?: VideoCallData; videoCallData?: VideoCallData;
paymentInfo?: PaymentInfo | null; paymentInfo?: PaymentInfo | null;
+1
View File
@@ -6,6 +6,7 @@ export interface PartialReference {
uid: string; uid: string;
meetingId?: string | null; meetingId?: string | null;
meetingPassword?: string | null; meetingPassword?: string | null;
thirdPartyRecurringEventId?: string | null;
meetingUrl?: string | null; meetingUrl?: string | null;
externalCalendarId?: string | null; externalCalendarId?: string | null;
credentialId?: number | null; credentialId?: number | null;
+8 -2
View File
@@ -14,7 +14,10 @@ declare module "next-auth" {
id: PrismaUser["id"]; id: PrismaUser["id"];
emailVerified?: PrismaUser["emailVerified"]; emailVerified?: PrismaUser["emailVerified"];
email_verified?: boolean; email_verified?: boolean;
impersonatedByUID?: number; impersonatedBy?: {
id: number;
role: PrismaUser["role"];
};
belongsToActiveTeam?: boolean; belongsToActiveTeam?: boolean;
org?: { org?: {
id: number; id: number;
@@ -36,7 +39,10 @@ declare module "next-auth/jwt" {
username?: string | null; username?: string | null;
email?: string | null; email?: string | null;
role?: UserPermissionRole | "INACTIVE_ADMIN" | null; role?: UserPermissionRole | "INACTIVE_ADMIN" | null;
impersonatedByUID?: number | null; impersonatedBy?: {
id: number;
role: PrismaUser["role"];
};
belongsToActiveTeam?: boolean; belongsToActiveTeam?: boolean;
org?: { org?: {
id: number; id: number;
@@ -108,7 +108,7 @@
font-size: 0.875rem; font-size: 0.875rem;
line-height: 1.25rem; line-height: 1.25rem;
border-radius: 0.375rem; border-radius: 0.375rem;
border-color: #e5e7eb; border-color: var(--cal-border);
width: 360px; width: 360px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@@ -152,7 +152,7 @@
padding: 0.5rem; padding: 0.5rem;
border-radius: 0.375rem; border-radius: 0.375rem;
border-width: 1px; border-width: 1px;
border-color: #e5e7eb; border-color: var(--cal-border);
width: 360px; width: 360px;
margin-top: 4px; margin-top: 4px;
} }
@@ -248,7 +248,7 @@ button.react-calendar__tile.react-calendar__month-view__days__day:hover,
} }
.react-daterange-picker > .react-daterange-picker__wrapper:hover { .react-daterange-picker > .react-daterange-picker__wrapper:hover {
border-color: var(--cal-bg-subtle); border-color: var(--cal-bg-inverted);
} }
.react-daterange-picker.react-daterange-picker--disabled > .react-daterange-picker__wrapper:hover { .react-daterange-picker.react-daterange-picker--disabled > .react-daterange-picker__wrapper:hover {
@@ -21,6 +21,7 @@ export type ActionType = {
label: string; label: string;
disabled?: boolean; disabled?: boolean;
color?: ButtonBaseProps["color"]; color?: ButtonBaseProps["color"];
bookingId?: number;
} & ( } & (
| { href: string; onClick?: never; actions?: never } | { href: string; onClick?: never; actions?: never }
| { href?: never; onClick: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void; actions?: never } | { href?: never; onClick: (e: React.MouseEvent<HTMLElement, MouseEvent>) => void; actions?: never }
@@ -61,6 +62,7 @@ export const DropdownActions = ({
data-testid={action.id} data-testid={action.id}
StartIcon={action.icon} StartIcon={action.icon}
href={action.href} href={action.href}
data-bookingid={action.bookingId}
onClick={action.onClick || defaultAction}> onClick={action.onClick || defaultAction}>
{action.label} {action.label}
</DropdownItem> </DropdownItem>
@@ -87,6 +89,7 @@ export const TableActions: FC<Props> = ({ actions }) => {
StartIcon={action.icon} StartIcon={action.icon}
{...(action?.actions ? { EndIcon: ChevronDown } : null)} {...(action?.actions ? { EndIcon: ChevronDown } : null)}
disabled={action.disabled} disabled={action.disabled}
data-bookingid={action.bookingId}
color={action.color || "secondary"}> color={action.color || "secondary"}>
{action.label} {action.label}
</Button> </Button>
+15 -6
View File
@@ -4233,6 +4233,15 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@calcom/mock-payment-app@workspace:packages/app-store/mock-payment-app":
version: 0.0.0-use.local
resolution: "@calcom/mock-payment-app@workspace:packages/app-store/mock-payment-app"
dependencies:
"@calcom/lib": "*"
"@calcom/types": "*"
languageName: unknown
linkType: soft
"@calcom/n8n@workspace:packages/app-store/n8n": "@calcom/n8n@workspace:packages/app-store/n8n":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@calcom/n8n@workspace:packages/app-store/n8n" resolution: "@calcom/n8n@workspace:packages/app-store/n8n"
@@ -4592,7 +4601,7 @@ __metadata:
"@calcom/lib": "*" "@calcom/lib": "*"
"@calcom/trpc": "*" "@calcom/trpc": "*"
"@calcom/tsconfig": "*" "@calcom/tsconfig": "*"
"@formkit/auto-animate": ^0.8.1 "@formkit/auto-animate": 1.0.0-beta.5
"@radix-ui/react-checkbox": ^1.0.4 "@radix-ui/react-checkbox": ^1.0.4
"@radix-ui/react-dialog": ^1.0.4 "@radix-ui/react-dialog": ^1.0.4
"@radix-ui/react-popover": ^1.0.2 "@radix-ui/react-popover": ^1.0.2
@@ -4675,7 +4684,7 @@ __metadata:
"@calcom/types": "*" "@calcom/types": "*"
"@calcom/ui": "*" "@calcom/ui": "*"
"@daily-co/daily-js": ^0.37.0 "@daily-co/daily-js": ^0.37.0
"@formkit/auto-animate": ^0.8.1 "@formkit/auto-animate": 1.0.0-beta.5
"@glidejs/glide": ^3.5.2 "@glidejs/glide": ^3.5.2
"@hookform/error-message": ^2.0.0 "@hookform/error-message": ^2.0.0
"@hookform/resolvers": ^2.9.7 "@hookform/resolvers": ^2.9.7
@@ -6237,10 +6246,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@formkit/auto-animate@npm:^0.8.1": "@formkit/auto-animate@npm:1.0.0-beta.5":
version: 0.8.1 version: 1.0.0-beta.5
resolution: "@formkit/auto-animate@npm:0.8.1" resolution: "@formkit/auto-animate@npm:1.0.0-beta.5"
checksum: ffc1a3432ce81bcf7abd4360fabf05b7aee01005e64ce519f57d134e68a5e15a81f295a7b2979678896bfd7d2048202a9fa2dbd09781267899e9056e918120c1 checksum: 479a4f694d91e7272b2d8bdf2051fd1ae2dd5fbb522612406eaef55d88b7131402a7d5f683df8eb4e7c00810ebec8d3048483a774b0864c299d2d7af81262eb2
languageName: node languageName: node
linkType: hard linkType: hard