Skip to content

Commit fcd2f1a

Browse files
committed
landingsections
1 parent f9516da commit fcd2f1a

13 files changed

Lines changed: 1112 additions & 11 deletions

frontend/src/api/awards.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import { http } from "./config";
3+
4+
const normalizeArrayParam = (value) => {
5+
if (!value) return undefined;
6+
if (Array.isArray(value)) return value.filter(Boolean).join(",");
7+
return value;
8+
};
9+
10+
const buildParams = ({
11+
search,
12+
page,
13+
limit,
14+
start,
15+
populate,
16+
filters,
17+
sort,
18+
} = {}) => {
19+
const params = {};
20+
if (search) params.q = search;
21+
if (typeof start === "number") params._start = start;
22+
if (typeof limit === "number") params._limit = limit;
23+
if (typeof page === "number") params.page = page;
24+
if (sort) params.sort = sort;
25+
26+
const populateValue = normalizeArrayParam(populate);
27+
if (populateValue) params.populate = populateValue;
28+
29+
if (filters && typeof filters === "object") {
30+
Object.entries(filters).forEach(([key, value]) => {
31+
if (value === undefined || value === null || value === "") return;
32+
params[key] = normalizeArrayParam(value) ?? value;
33+
});
34+
}
35+
36+
return params;
37+
};
38+
39+
const fetchAwards = async ({
40+
search,
41+
page,
42+
limit,
43+
start,
44+
populate,
45+
filters,
46+
sort,
47+
signal,
48+
}) => {
49+
const { data } = await http.get("/api/awards", {
50+
params: buildParams({
51+
search,
52+
page,
53+
limit,
54+
start,
55+
populate,
56+
filters,
57+
sort,
58+
}),
59+
signal,
60+
});
61+
return data;
62+
};
63+
64+
export const useAwards = ({
65+
search = "",
66+
page,
67+
limit = 20,
68+
start,
69+
populate,
70+
filters,
71+
sort,
72+
enabled = true,
73+
} = {}) =>
74+
useQuery({
75+
queryKey: ["awards", { search, page, limit, start, populate, filters, sort }],
76+
queryFn: ({ signal }) =>
77+
fetchAwards({
78+
search,
79+
page,
80+
limit,
81+
start,
82+
populate,
83+
filters,
84+
sort,
85+
signal,
86+
}),
87+
enabled,
88+
});
89+
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import { http } from "./config";
3+
4+
const normalizeArrayParam = (value) => {
5+
if (!value) return undefined;
6+
if (Array.isArray(value)) return value.filter(Boolean).join(",");
7+
return value;
8+
};
9+
10+
const buildParams = ({
11+
search,
12+
page,
13+
limit,
14+
start,
15+
populate,
16+
filters,
17+
sort,
18+
} = {}) => {
19+
const params = {};
20+
if (search) params.q = search;
21+
if (typeof start === "number") params._start = start;
22+
if (typeof limit === "number") params._limit = limit;
23+
if (typeof page === "number") params.page = page;
24+
if (sort) params.sort = sort;
25+
26+
const populateValue = normalizeArrayParam(populate);
27+
if (populateValue) params.populate = populateValue;
28+
29+
if (filters && typeof filters === "object") {
30+
Object.entries(filters).forEach(([key, value]) => {
31+
if (value === undefined || value === null || value === "") return;
32+
params[key] = normalizeArrayParam(value) ?? value;
33+
});
34+
}
35+
36+
return params;
37+
};
38+
39+
const fetchDeveloperProfiles = async ({
40+
search,
41+
page,
42+
limit,
43+
start,
44+
populate,
45+
filters,
46+
sort,
47+
signal,
48+
}) => {
49+
const { data } = await http.get("/api/developer-profiles", {
50+
params: buildParams({
51+
search,
52+
page,
53+
limit,
54+
start,
55+
populate,
56+
filters,
57+
sort,
58+
}),
59+
signal,
60+
});
61+
return data;
62+
};
63+
64+
export const useDeveloperProfiles = ({
65+
search = "",
66+
page,
67+
limit = 20,
68+
start,
69+
populate,
70+
filters,
71+
sort,
72+
enabled = true,
73+
} = {}) =>
74+
useQuery({
75+
queryKey: [
76+
"developer-profiles",
77+
{ search, page, limit, start, populate, filters, sort },
78+
],
79+
queryFn: ({ signal }) =>
80+
fetchDeveloperProfiles({
81+
search,
82+
page,
83+
limit,
84+
start,
85+
populate,
86+
filters,
87+
sort,
88+
signal,
89+
}),
90+
enabled,
91+
});
92+

frontend/src/api/projects.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,21 @@ const normalizeArrayParam = (value) => {
1414
return value;
1515
};
1616

17-
const buildParams = ({ search, page, limit, start, populate, filters } = {}) => {
17+
const buildParams = ({
18+
search,
19+
page,
20+
limit,
21+
start,
22+
populate,
23+
filters,
24+
sort,
25+
} = {}) => {
1826
const params = {};
1927
if (search) params.q = search;
2028
if (typeof start === "number") params._start = start;
2129
if (typeof limit === "number") params._limit = limit;
2230
if (typeof page === "number") params.page = page;
31+
if (sort) params.sort = sort;
2332

2433
const populateValue = normalizeArrayParam(populate);
2534
if (populateValue) params.populate = populateValue;
@@ -41,10 +50,11 @@ const fetchProjects = async ({
4150
start,
4251
populate,
4352
filters,
53+
sort,
4454
signal,
4555
}) => {
4656
const { data } = await http.get("/api/projects", {
47-
params: buildParams({ search, page, limit, start, populate, filters }),
57+
params: buildParams({ search, page, limit, start, populate, filters, sort }),
4858
signal,
4959
});
5060
return data;
@@ -57,12 +67,25 @@ export const useProjects = ({
5767
start,
5868
populate = DEFAULT_POPULATE,
5969
filters,
70+
sort,
6071
enabled = true,
6172
} = {}) =>
6273
useQuery({
63-
queryKey: ["projects", { search, page, limit, start, populate, filters }],
74+
queryKey: [
75+
"projects",
76+
{ search, page, limit, start, populate, filters, sort },
77+
],
6478
queryFn: ({ signal }) =>
65-
fetchProjects({ search, page, limit, start, populate, filters, signal }),
79+
fetchProjects({
80+
search,
81+
page,
82+
limit,
83+
start,
84+
populate,
85+
filters,
86+
sort,
87+
signal,
88+
}),
6689
enabled,
6790
});
6891

@@ -155,4 +178,3 @@ export const useDeleteProject = () => {
155178
},
156179
});
157180
};
158-
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { motion, useReducedMotion } from "framer-motion";
2+
import { Link } from "react-router-dom";
3+
import { Sparkles } from "lucide-react";
4+
5+
const AIMatchingBanner = () => {
6+
const shouldReduceMotion = useReducedMotion();
7+
8+
const sectionMotion = shouldReduceMotion
9+
? {}
10+
: {
11+
initial: { opacity: 0, y: 24 },
12+
whileInView: { opacity: 1, y: 0 },
13+
viewport: { once: true, amount: 0.2 },
14+
transition: { duration: 0.6, ease: "easeOut" },
15+
};
16+
17+
return (
18+
<motion.section className="py-16 sm:py-20" {...sectionMotion}>
19+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
20+
<div className="relative overflow-hidden rounded-3xl bg-gradient-to-r from-stone-900 via-slate-900 to-zinc-900 border border-white/10 px-8 py-12 sm:px-12 sm:py-16">
21+
<div className="absolute inset-0 opacity-40 bg-[radial-gradient(circle_at_top_left,_rgba(255,255,255,0.35),_transparent_55%)]" />
22+
<div className="absolute -bottom-24 right-10 h-56 w-56 rounded-full bg-white/10 blur-3xl" />
23+
24+
<div className="relative z-10 grid lg:grid-cols-[1.1fr_0.9fr] gap-10 items-center">
25+
<div className="space-y-5">
26+
<div className="inline-flex items-center gap-2 rounded-full border border-white/15 px-4 py-1 text-xs uppercase tracking-[0.3em] text-white/70">
27+
<Sparkles className="h-4 w-4 text-white/70" />
28+
AI Matching
29+
</div>
30+
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-semibold text-white font-['Playfair_Display']">
31+
Find the Perfect Developer Instantly
32+
</h2>
33+
<p className="text-sm sm:text-base text-white/70 max-w-xl font-['Plus_Jakarta_Sans']">
34+
Submit your project requirements and our AI matching engine will
35+
recommend the most compatible developers based on skills,
36+
experience, and portfolio similarity.
37+
</p>
38+
</div>
39+
<div className="flex flex-wrap gap-4 lg:justify-end">
40+
<Link
41+
to="/projects"
42+
className="rounded-full bg-white text-stone-900 px-6 py-3 text-xs font-semibold uppercase tracking-[0.25em] hover:bg-stone-100 transition"
43+
>
44+
Find Developers
45+
</Link>
46+
<Link
47+
to="/submit"
48+
className="rounded-full border border-white/30 text-white px-6 py-3 text-xs font-semibold uppercase tracking-[0.25em] hover:bg-white hover:text-stone-900 transition"
49+
>
50+
Submit Project
51+
</Link>
52+
</div>
53+
</div>
54+
</div>
55+
</div>
56+
</motion.section>
57+
);
58+
};
59+
60+
export default AIMatchingBanner;

0 commit comments

Comments
 (0)