Skip to content

Commit 81ec3a9

Browse files
stefanskoricdevivke995vele616
authored
FEAT: Add consult us form and update page hero (#577)
* feat: Add consult us form * fix: Image size * fix: Section widths * feat: Update hero section on staff augmentation page * refactor: Split Staff augmentation into components. Add content to md collections * fix: Smaller styling issues * refactor: Move components to staff augmentation dir * feat: Add Book a call button * fix: improve color contrast on form submit button * fix: Remove button opacity when disabled * fix: Heading elements in sequentially-descending order * fix: Heading elements in sequentially-descending order * feat: create thank you card, edit existing form to fit design * fix: Staff augmentation background pattern issue on wide screens * feat: Make main content fact check section dynamic based on URL query param * feat: Add content for different hero and ideal for section variants * chore: copy updates * feat: Make page content dynamic and update existing content (#605) * feat: 2 separated requests for modal and primary form, created form for modal * feat: add source into getPlan endpoint * feat: add notification error message and source in api response message * feat: show 2 quarters ahead on the dropdown * fix: Svg background gets blurry on bigger screens * fix: Smaller styling issues * fix: edit variant id's, pill width on small screens * fix: add opacity for disabled button * chore: copy updates * chore: minor copy correction * fix: edit line heights and text sizes for headers and description paragraphs * fix: change button background * fix: add hover bg and cursor pointer on thank you card button * fix: enable all button click but show error message if something wrong * fix: path to notion * feat: frontend contact us validation on get your plan form * feat: add frontend validation for base contact form * fix: import collections with astro:content * fix: astro:content available only on server-side * fix: allow 1 char for name in contact form API * fix: revert direct .md file import for single files * Feat ideal for section redesign (#619) * feat: Redesign of 'Ideal for teams who' section * fix: Smaller styling issues * fix: add listener on whole form and check input clicked * FEAT: Update specialization page (#622) * FEAT: Add Trusted by and Testimonial section (#626) * feat: Add Trusted by and Testimonial section * feat: Optimize image for smaller screens --------- Co-authored-by: ivke995 <ivanivic842@gmail.com> Co-authored-by: vele616 <vele@crocoder.dev>
1 parent 4662e26 commit 81ec3a9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2020
-716
lines changed

apps/contact/app/api/contact/route.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ import { NextRequest } from "next/server";
66
import z from "zod";
77

88
const bodyValidationSchema = z.object({
9-
name: z
10-
.string()
11-
.min(2, { message: "Name must be at least 2 characters long" }),
9+
name: z.string(),
1210
email: z
1311
.email({ message: "Invalid email adress" })
1412
.min(1, { message: "Required field" }),
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { processContact } from "@/helpers/notion";
2+
import { nanoid } from "nanoid";
3+
import { NextRequest } from "next/server";
4+
import z from "zod";
5+
6+
const { NOTION_GET_PLAN_DATABASE_ID } = process.env;
7+
8+
const bodyValidationSchema = z.object({
9+
name: z.string(),
10+
email: z
11+
.email({ message: "Invalid email adress" })
12+
.min(1, { message: "Required field" }),
13+
companyName: z.string().optional(),
14+
mainChallenge: z.string().optional(),
15+
expectedStartDate: z.string().optional(),
16+
hasConsent: z.boolean().optional(),
17+
});
18+
19+
type RequestBody = z.infer<typeof bodyValidationSchema>;
20+
21+
const allowRequest = async (request: Request & { ip?: string }) => {
22+
return { success: true, limit: 1, reset: Date.now() + 30000, remaining: 1 };
23+
};
24+
25+
export async function OPTIONS() {
26+
return new Response(null, {
27+
status: 200,
28+
headers: {
29+
"Access-Control-Allow-Origin": "*",
30+
"Access-Control-Allow-Methods": "OPTIONS, POST",
31+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
32+
},
33+
});
34+
}
35+
36+
export async function POST(request: NextRequest) {
37+
const corsHeaders = {
38+
"Access-Control-Allow-Origin": "*",
39+
"Access-Control-Allow-Credentials": "true",
40+
"Access-Control-Allow-Methods": "POST, OPTIONS",
41+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
42+
};
43+
44+
if (request.headers.get("Content-Type") === "application/json") {
45+
try {
46+
const body = (await request.json()) as RequestBody;
47+
const bodyValidationResult = bodyValidationSchema.safeParse(body);
48+
49+
if (!body || bodyValidationResult.error) {
50+
return new Response(
51+
JSON.stringify({
52+
message: bodyValidationResult.error?.message || "No body was found",
53+
}),
54+
{
55+
status: 400,
56+
headers: {
57+
...corsHeaders,
58+
},
59+
},
60+
);
61+
}
62+
63+
const {
64+
name,
65+
email,
66+
companyName,
67+
mainChallenge,
68+
expectedStartDate,
69+
hasConsent,
70+
} = body;
71+
72+
const message = `
73+
Company name: ${companyName || ""} \n
74+
Main challenge: ${mainChallenge || ""} \n
75+
Expected start date: ${expectedStartDate || ""} \n`;
76+
77+
if (!hasConsent) {
78+
return new Response(JSON.stringify({ message: "No consent by user" }), {
79+
status: 403,
80+
headers: {
81+
...corsHeaders,
82+
},
83+
});
84+
}
85+
86+
const { success, limit, reset, remaining } = await allowRequest(request);
87+
88+
if (!success) {
89+
return new Response(
90+
JSON.stringify({
91+
message: "Too many requests. Please try again in a minute",
92+
}),
93+
{
94+
status: 429,
95+
headers: {
96+
...corsHeaders,
97+
},
98+
},
99+
);
100+
}
101+
102+
await processContact({
103+
id: nanoid(),
104+
email,
105+
name,
106+
message: message || "",
107+
databaseID: NOTION_GET_PLAN_DATABASE_ID || "",
108+
source: request.nextUrl.searchParams.get("source") || "Unknown",
109+
});
110+
111+
return new Response(
112+
JSON.stringify({
113+
message: "Success",
114+
}),
115+
{
116+
status: 200,
117+
headers: {
118+
...corsHeaders,
119+
"X-RateLimit-Limit": limit.toString(),
120+
"X-RateLimit-Remaining": remaining.toString(),
121+
"X-RateLimit-Reset": reset.toString(),
122+
},
123+
},
124+
);
125+
} catch (error) {
126+
console.error("Error - api/get-plan", error);
127+
128+
const statusCode = (error as any).statusCode || 501;
129+
const message =
130+
(error as any)?.body?.message || "Issue while processing request";
131+
132+
return new Response(JSON.stringify({ message }), {
133+
status: statusCode,
134+
headers: {
135+
...corsHeaders,
136+
},
137+
});
138+
}
139+
}
140+
141+
return new Response(null, { status: 400, headers: corsHeaders });
142+
}

apps/website/astro.config.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ export default defineConfig({
1919
adapter: vercel({
2020
imageService: true,
2121
imagesConfig: {
22-
sizes: [320, 480, 578, 640, 720, 800, 940, 1200, 1412, 1536, 1800, 1920],
22+
sizes: [
23+
320, 480, 578, 640, 720, 800, 940, 960, 1200, 1280, 1412, 1440, 1536,
24+
1600, 1800, 1920,
25+
],
2326
formats: ["image/avif", "image/webp"],
2427
},
2528
}),
Lines changed: 6 additions & 0 deletions
Loading
Lines changed: 9 additions & 0 deletions
Loading
190 KB
Loading
237 KB
Loading
Lines changed: 8 additions & 0 deletions
Loading
122 KB
Loading
Lines changed: 10 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)