Skip to content

Commit 0deb5b4

Browse files
committed
add coverletter section
1 parent 204b74f commit 0deb5b4

File tree

7 files changed

+318
-2
lines changed

7 files changed

+318
-2
lines changed

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ jobs:
3737
protocol: ftp
3838
passive: true
3939
local-dir: dist/
40-
clean: true
40+
clean: true

src/assets/svg/pen-icon.jsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const PenIcon = () => {
2+
return (
3+
<div>
4+
<svg
5+
xmlns="http://www.w3.org/2000/svg"
6+
width="20"
7+
height="20"
8+
viewBox="0 0 20 20"
9+
fill="none"
10+
>
11+
<path
12+
d="M17.6423 5.67682C18.0829 5.23634 18.3305 4.63888 18.3306 4.01587C18.3307 3.39285 18.0832 2.79533 17.6428 2.35474C17.2023 1.91415 16.6048 1.66658 15.9818 1.6665C15.3588 1.66643 14.7613 1.91384 14.3207 2.35432L3.19901 13.4785C3.00552 13.6714 2.86244 13.9089 2.78234 14.1702L1.68151 17.7968C1.65997 17.8689 1.65834 17.9454 1.6768 18.0184C1.69526 18.0913 1.73311 18.1578 1.78634 18.211C1.83957 18.2641 1.90619 18.3019 1.97914 18.3202C2.05209 18.3386 2.12864 18.3368 2.20067 18.3152L5.82817 17.2152C6.08915 17.1358 6.32665 16.9936 6.51984 16.801L17.6423 5.67682Z"
13+
stroke="#020817"
14+
stroke-width="1.66667"
15+
stroke-linecap="round"
16+
stroke-linejoin="round"
17+
/>
18+
</svg>
19+
</div>
20+
);
21+
};
22+
23+
export default PenIcon;

src/assets/svg/upload-icon.jsx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
const UploadIcon = () => {
2+
return (
3+
<>
4+
<svg
5+
xmlns="http://www.w3.org/2000/svg"
6+
width="40"
7+
height="30"
8+
viewBox="0 0 49 48"
9+
fill="none"
10+
>
11+
<path
12+
d="M42.5 30V38C42.5 39.0609 42.0786 40.0783 41.3284 40.8284C40.5783 41.5786 39.5609 42 38.5 42H10.5C9.43913 42 8.42172 41.5786 7.67157 40.8284C6.92143 40.0783 6.5 39.0609 6.5 38V30"
13+
stroke="url(#paint0_linear_8394_28982)"
14+
stroke-width="4"
15+
stroke-linecap="round"
16+
stroke-linejoin="round"
17+
/>
18+
<path
19+
d="M34.5 16L24.5 6L14.5 16"
20+
stroke="url(#paint1_linear_8394_28982)"
21+
stroke-width="4"
22+
stroke-linecap="round"
23+
stroke-linejoin="round"
24+
/>
25+
<path
26+
d="M24.5 6V30"
27+
stroke="url(#paint2_linear_8394_28982)"
28+
stroke-width="4"
29+
stroke-linecap="round"
30+
stroke-linejoin="round"
31+
/>
32+
<defs>
33+
<linearGradient
34+
id="paint0_linear_8394_28982"
35+
x1="50.8023"
36+
y1="36.0247"
37+
x2="2.58859"
38+
y2="43.1435"
39+
gradientUnits="userSpaceOnUse"
40+
>
41+
<stop stop-color="#504999" />
42+
<stop offset="1" stop-color="#44A199" />
43+
</linearGradient>
44+
<linearGradient
45+
id="paint1_linear_8394_28982"
46+
x1="39.1124"
47+
y1="11.0206"
48+
x2="12.0057"
49+
y2="13.6888"
50+
gradientUnits="userSpaceOnUse"
51+
>
52+
<stop stop-color="#504999" />
53+
<stop offset="1" stop-color="#44A199" />
54+
</linearGradient>
55+
<linearGradient
56+
id="paint2_linear_8394_28982"
57+
x1="25.7306"
58+
y1="18.0494"
59+
x2="24.3622"
60+
y2="18.0522"
61+
gradientUnits="userSpaceOnUse"
62+
>
63+
<stop stop-color="#504999" />
64+
<stop offset="1" stop-color="#44A199" />
65+
</linearGradient>
66+
</defs>
67+
</svg>
68+
</>
69+
);
70+
};
71+
72+
export default UploadIcon;

src/components/ui/textarea.jsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import * as React from "react"
2+
3+
import { cn } from "@/lib/utils"
4+
5+
function Textarea({
6+
className,
7+
...props
8+
}) {
9+
return (
10+
<textarea
11+
data-slot="textarea"
12+
className={cn(
13+
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
14+
className
15+
)}
16+
{...props} />
17+
);
18+
}
19+
20+
export { Textarea }

src/pages/dashboard/billing/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Check } from "lucide-react";
22
import { Button } from "@/components/ui/button";
33
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
4-
import images from "../../../assets/images/pricingbg.png";
4+
55
import {
66
Table,
77
TableBody,
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import { useState } from "react";
2+
import { Input } from "@/components/ui/input";
3+
import { Button } from "@/components/ui/button";
4+
import { Textarea } from "@/components/ui/textarea";
5+
import { Card, CardContent } from "@/components/ui/card";
6+
import { UploadCloud } from "lucide-react";
7+
import PenIcon from "@/assets/svg/pen-icon";
8+
import { useRef } from "react";
9+
import UploadIcon from "@/assets/svg/upload-icon";
10+
11+
const CoverLetterGenerator = () => {
12+
const [file, setFile] = useState(null);
13+
const [prompt, setPrompt] = useState("");
14+
const [showPreview, setShowPreview] = useState(false);
15+
const [loading, setLoading] = useState(false);
16+
17+
// Ref to access the hidden file input
18+
const fileInputRef = useRef(null);
19+
20+
const handleFileChange = (e) => {
21+
const selected = e.target.files?.[0];
22+
if (selected && selected.type.includes("pdf")) {
23+
setFile(selected);
24+
} else {
25+
alert("Only PDF files are supported for now.");
26+
}
27+
};
28+
29+
const handleGenerate = () => {
30+
if (file && prompt.trim() !== "") {
31+
setLoading(true);
32+
setTimeout(() => {
33+
setShowPreview(true);
34+
setLoading(false);
35+
}, 2000);
36+
}
37+
};
38+
return (
39+
<div className="min-h-screen bg-[#F9FAFB] p-4 md:p-8">
40+
{/* Header */}
41+
<div
42+
className="max-w-7xl mx-auto text-center md:text-left mb-10"
43+
data-aos="fade-up"
44+
>
45+
<h1 className="text-[#191919] font-poppins text-3xl md:text-5xl lg:text-[64px] font-semibold leading-tight max-w-5xl">
46+
Build Your Professional Cover Letter
47+
</h1>
48+
<p className="text-[#717171] font-poppins text-lg md:text-xl lg:text-[24px] mt-4">
49+
Let’s take the next step in your career today
50+
</p>
51+
</div>
52+
53+
{/* Main Grid */}
54+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 max-w-7xl mx-auto">
55+
{/* Left Side */}
56+
<Card
57+
className="p-6 flex flex-col justify-between gap-6 border-0 shadow-xl h-auto"
58+
data-aos="fade-right"
59+
>
60+
<div>
61+
<div className="border border-dashed border-gray-300 bg-[#F6F8FE] rounded-md p-6 flex flex-col items-center justify-center cursor-pointer">
62+
<UploadIcon className="w-10 h-5 text-gray-500" />
63+
<h2 className="text-[#020817] text-center font-poppins text-[16px] font-normal leading-none mb-2 mt-2">
64+
Upload your Resume / Job Description
65+
</h2>
66+
<span className="text-[#6B7280] text-center font-poppins text-[14px] font-normal leading-none mb-2">
67+
Drag and drop your resume/job description or click to browse
68+
</span>
69+
<Button
70+
type="button"
71+
onClick={() => fileInputRef.current?.click()}
72+
className="bg-gradient-to-r from-primary to-secondary rounded-full font-normal mb-1"
73+
>
74+
Select File
75+
</Button>
76+
<span className="text-[#6B7280] text-center font-poppins text-[14px] font-normal leading-none">
77+
Supported formats: PDF, DOCX, TXT
78+
</span>
79+
{/* Hidden file input */}
80+
<Input
81+
ref={fileInputRef}
82+
type="file"
83+
accept=".pdf,.docx,.txt"
84+
className="hidden"
85+
onChange={handleFileChange}
86+
/>
87+
</div>
88+
89+
{file && (
90+
<p className="text-sm text-green-600 mt-2 text-center md:text-left">
91+
{file.name} selected
92+
</p>
93+
)}
94+
</div>
95+
96+
{/* Prompt */}
97+
<div>
98+
<label
99+
htmlFor="prompt"
100+
className="flex items-center gap-2 text-sm font-bold mb-2"
101+
>
102+
<PenIcon />
103+
Prompt
104+
</label>
105+
<Textarea
106+
id="prompt"
107+
placeholder="Enter prompt here..."
108+
className="bg-[#F6F8FE]"
109+
value={prompt}
110+
onChange={(e) => setPrompt(e.target.value)}
111+
/>
112+
</div>
113+
114+
{/* Buttons */}
115+
<div className="flex flex-col md:flex-row items-center gap-4 mt-2">
116+
<Button variant="outline" className="w-full md:w-auto">
117+
Sample Prompts
118+
</Button>
119+
<Button variant="outline" className="w-full md:w-auto">
120+
Job Description
121+
</Button>
122+
<Button
123+
onClick={handleGenerate}
124+
className="w-full md:w-auto bg-gradient-to-r from-primary to-secondary"
125+
disabled={loading}
126+
>
127+
{loading ? (
128+
<span className="flex items-center gap-2">
129+
<svg
130+
className="animate-spin h-4 w-4 text-white"
131+
xmlns="http://www.w3.org/2000/svg"
132+
fill="none"
133+
viewBox="0 0 24 24"
134+
>
135+
<circle
136+
className="opacity-25"
137+
cx="12"
138+
cy="12"
139+
r="10"
140+
stroke="currentColor"
141+
strokeWidth="4"
142+
/>
143+
<path
144+
className="opacity-75"
145+
fill="currentColor"
146+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
147+
/>
148+
</svg>
149+
Generating...
150+
</span>
151+
) : (
152+
"Generate Cover"
153+
)}
154+
</Button>
155+
</div>
156+
</Card>
157+
158+
{/* Right Side */}
159+
{showPreview && (
160+
<Card className="p-6" data-aos="fade-left">
161+
<CardContent className="space-y-4 text-sm text-gray-700">
162+
<div className="text-right text-xs text-gray-400">
163+
09 February 2024
164+
</div>
165+
<h1 className="text-xl font-bold">Nazmul Hasan</h1>
166+
<p className="text-sm font-medium text-gray-600">UX Designer</p>
167+
168+
<p>Dear Hiring Manager,</p>
169+
170+
<p>
171+
I am excited to express my strong interest in the UX/UI Product
172+
Designer role at Mixamprint. With a passion for creating
173+
seamless digital experiences and a track record of delivering
174+
innovative user-centered designs, I am confident in my ability
175+
to contribute to your team’s success...
176+
</p>
177+
178+
<p>
179+
Thank you for your consideration, and I eagerly await the
180+
opportunity to discuss this exciting opportunity further.
181+
</p>
182+
183+
<p>
184+
Sincerely,
185+
<br />
186+
Nazmul Hasan
187+
</p>
188+
</CardContent>
189+
</Card>
190+
)}
191+
</div>
192+
</div>
193+
);
194+
};
195+
196+
export default CoverLetterGenerator;

src/routes/index.jsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import SignUp from "@/pages/auth/sign-up";
1818
import Home from "@/pages/common/home";
1919
import Billing from "@/pages/dashboard/billing";
2020
import CoachingCallBooking from "@/pages/dashboard/coaching-call-booking";
21+
import CoverLetterGenerator from "@/pages/dashboard/coverletter";
2122
import MyDashboard from "@/pages/dashboard/dashboard";
2223
import Dashboard from "@/pages/dashboard/dashboard";
2324
import AboutUs from "@/pages/main/aboutus";
@@ -141,6 +142,10 @@ export const router = createBrowserRouter([
141142
path: "billing",
142143
element: <Billing />,
143144
},
145+
{
146+
path: "cover-letter",
147+
element: <CoverLetterGenerator />,
148+
},
144149
],
145150
},
146151
],

0 commit comments

Comments
 (0)