Skip to content

Commit 6200e65

Browse files
committed
complete the project.
1 parent 52bdc64 commit 6200e65

Some content is hidden

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

106 files changed

+4206
-1373
lines changed

.eslintrc.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

components.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "https://ui.shadcn.com/schema.json",
33
"style": "new-york",
44
"rsc": true,
5-
"tsx": true,
5+
"tsx": false,
66
"tailwind": {
77
"config": "tailwind.config.ts",
88
"css": "src/app/globals.css",

jsconfig.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"compilerOptions": {
3+
"paths": {
4+
"@/*": [
5+
"./src/*"
6+
]
7+
}
8+
}
9+
}

next.config.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
import type { NextConfig } from "next";
22

33
const nextConfig: NextConfig = {
4+
images:{
5+
remotePatterns: [
6+
{
7+
protocol: "https",
8+
hostname: "randomuser.me"
9+
},
10+
{
11+
protocol: "https",
12+
hostname: "images.unsplash.com"
13+
},
14+
{
15+
protocol: "https",
16+
hostname: "assets.aceternity.com"
17+
}
18+
]
19+
}
420
/* config options here */
521
};
622

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@
2626
"@radix-ui/react-tabs": "^1.1.3",
2727
"@radix-ui/react-tooltip": "^1.1.8",
2828
"@tabler/icons-react": "^3.30.0",
29+
"@uiw/react-md-editor": "^4.0.5",
2930
"class-variance-authority": "^0.7.1",
3031
"clsx": "^2.1.1",
32+
"cobe": "^0.6.3",
3133
"date-fns": "^4.1.0",
3234
"framer-motion": "^12.4.7",
35+
"html2pdf.js": "0.9.0",
3336
"inngest": "^3.31.11",
3437
"lucide-react": "^0.476.0",
38+
"motion": "^12.4.7",
3539
"next": "15.1.7",
3640
"next-themes": "^0.4.4",
3741
"react": "^19.0.0",

pnpm-lock.yaml

Lines changed: 1909 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/banner.jpeg

211 KB
Loading

public/banner2.jpeg

180 KB
Loading

public/banner3.jpeg

185 KB
Loading

public/file.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

public/globe.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

public/logo.png

134 KB
Loading

public/next.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

public/vercel.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

public/window.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/actions/cover-letter.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"use server";
2+
3+
import { db } from "@/lib/prisma";
4+
import { auth } from "@clerk/nextjs/server";
5+
import { GoogleGenerativeAI } from "@google/generative-ai";
6+
7+
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
8+
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
9+
10+
export async function generateCoverLetter(data) {
11+
const { userId } = await auth();
12+
if (!userId) throw new Error("Unauthorized");
13+
14+
const user = await db.user.findUnique({
15+
where: { clerkUserId: userId },
16+
});
17+
18+
if (!user) throw new Error("User not found");
19+
20+
const prompt = `
21+
Write a professional cover letter for a ${data.jobTitle} position at ${
22+
data.companyName
23+
}.
24+
25+
About the candidate:
26+
- Industry: ${user.industry}
27+
- Years of Experience: ${user.experience}
28+
- Skills: ${user.skills?.join(", ")}
29+
- Professional Background: ${user.bio}
30+
31+
Job Description:
32+
${data.jobDescription}
33+
34+
Requirements:
35+
1. Use a professional, enthusiastic tone
36+
2. Highlight relevant skills and experience
37+
3. Show understanding of the company's needs
38+
4. Keep it concise (max 400 words)
39+
5. Use proper business letter formatting in markdown
40+
6. Include specific examples of achievements
41+
7. Relate candidate's background to job requirements
42+
43+
Format the letter in markdown.
44+
`;
45+
46+
try {
47+
const result = await model.generateContent(prompt);
48+
const content = result.response.text().trim();
49+
50+
const coverLetter = await db.coverLetter.create({
51+
data: {
52+
content,
53+
jobDescription: data.jobDescription,
54+
companyName: data.companyName,
55+
jobTitle: data.jobTitle,
56+
status: "completed",
57+
userId: user.id,
58+
},
59+
});
60+
61+
return coverLetter;
62+
} catch (error) {
63+
console.error("Error generating cover letter:", error.message);
64+
throw new Error("Failed to generate cover letter");
65+
}
66+
}
67+
68+
export async function getCoverLetters() {
69+
const { userId } = await auth();
70+
if (!userId) throw new Error("Unauthorized");
71+
72+
const user = await db.user.findUnique({
73+
where: { clerkUserId: userId },
74+
});
75+
76+
if (!user) throw new Error("User not found");
77+
78+
return await db.coverLetter.findMany({
79+
where: {
80+
userId: user.id,
81+
},
82+
orderBy: {
83+
createdAt: "desc",
84+
},
85+
});
86+
}
87+
88+
export async function getCoverLetter(id) {
89+
const { userId } = await auth();
90+
if (!userId) throw new Error("Unauthorized");
91+
92+
const user = await db.user.findUnique({
93+
where: { clerkUserId: userId },
94+
});
95+
96+
if (!user) throw new Error("User not found");
97+
98+
return await db.coverLetter.findUnique({
99+
where: {
100+
id,
101+
userId: user.id,
102+
},
103+
});
104+
}
105+
106+
export async function deleteCoverLetter(id) {
107+
const { userId } = await auth();
108+
if (!userId) throw new Error("Unauthorized");
109+
110+
const user = await db.user.findUnique({
111+
where: { clerkUserId: userId },
112+
});
113+
114+
if (!user) throw new Error("User not found");
115+
116+
return await db.coverLetter.delete({
117+
where: {
118+
id,
119+
userId: user.id,
120+
},
121+
});
122+
}

src/actions/dashboard.action.ts

Lines changed: 0 additions & 71 deletions
This file was deleted.

src/actions/dashboard.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"use server";
2+
3+
import { db } from "@/lib/prisma";
4+
import { auth } from "@clerk/nextjs/server";
5+
import { GoogleGenerativeAI } from "@google/generative-ai";
6+
7+
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
8+
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
9+
10+
export const generateAIInsights = async (industry) => {
11+
const prompt = `
12+
Analyze the current state of the ${industry} industry and provide insights in ONLY the following JSON format without any additional notes or explanations:
13+
{
14+
"salaryRanges": [
15+
{ "role": "string", "min": number, "max": number, "median": number, "location": "string" }
16+
],
17+
"growthRate": number,
18+
"demandLevel": "High" | "Medium" | "Low",
19+
"topSkills": ["skill1", "skill2"],
20+
"marketOutlook": "Positive" | "Neutral" | "Negative",
21+
"keyTrends": ["trend1", "trend2"],
22+
"recommendedSkills": ["skill1", "skill2"]
23+
}
24+
25+
IMPORTANT: Return ONLY the JSON. No additional text, notes, or markdown formatting.
26+
Include at least 5 common roles for salary ranges.
27+
Growth rate should be a percentage.
28+
Include at least 5 skills and trends.
29+
`;
30+
31+
const result = await model.generateContent(prompt);
32+
const response = result.response;
33+
const text = response.text();
34+
const cleanedText = text.replace(/```(?:json)?\n?/g, "").trim();
35+
36+
return JSON.parse(cleanedText);
37+
};
38+
39+
export async function getIndustryInsights() {
40+
const { userId } = await auth();
41+
if (!userId) throw new Error("Unauthorized");
42+
43+
const user = await db.user.findUnique({
44+
where: { clerkUserId: userId },
45+
include: {
46+
industryInsight: true,
47+
},
48+
});
49+
50+
if (!user) throw new Error("User not found");
51+
52+
// If no insights exist, generate them
53+
if (!user.industryInsight) {
54+
const insights = await generateAIInsights(user.industry);
55+
56+
const industryInsight = await db.industryInsight.create({
57+
data: {
58+
industry: user.industry,
59+
...insights,
60+
nextUpdate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
61+
},
62+
});
63+
64+
return industryInsight;
65+
}
66+
67+
return user.industryInsight;
68+
}

0 commit comments

Comments
 (0)