Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33,972 changes: 22,882 additions & 11,090 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"test:ci": "jest --ci"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.27.3",
"@heroicons/react": "^2.0.18",
"@react-pdf/renderer": "^3.1.10",
"@reduxjs/toolkit": "^1.9.5",
Expand All @@ -19,16 +20,21 @@
"@types/react-dom": "18.2.4",
"@vercel/analytics": "^1.0.1",
"autoprefixer": "10.4.14",
"cohere-ai": "^7.14.0",
"docx": "^8.5.0",
"eslint": "8.41.0",
"eslint-config-next": "13.4.4",
"lucide-react": "^0.451.0",
"next": "13.4.4",
"open-resume": "file:",
"pdfjs": "^2.5.0",
"pdfjs-dist": "^3.7.107",
"postcss": "8.4.24",
"react": "18.2.0",
"react-contenteditable": "^3.3.7",
"react-dom": "18.2.0",
"react-frame-component": "^5.2.6",
"react-icons": "^5.3.0",
"react-redux": "^8.0.7",
"tailwind-scrollbar": "^3.0.4",
"tailwindcss": "3.3.2",
Expand Down
Binary file added public/assets/aiicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/fonts/Garamond-Bold.ttf
Binary file not shown.
Binary file added public/fonts/Garamond-Regular.ttf
Binary file not shown.
Binary file added public/fonts/Helvetica-Bold.ttf
Binary file not shown.
Binary file added public/fonts/Helvetica.ttf
Binary file not shown.
6 changes: 5 additions & 1 deletion public/fonts/fonts.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
@font-face {font-family: "OpenSans"; src: url("/fonts/OpenSans-Bold.ttf"); font-weight: bold;}
@font-face {font-family: "Raleway"; src: url("/fonts/Raleway-Regular.ttf");}
@font-face {font-family: "Raleway"; src: url("/fonts/Raleway-Bold.ttf"); font-weight: bold;}

@font-face {font-family: "Helevetica"; src: url("/fonts/Helevetica.ttf");}
@font-face {font-family: "Helevetica"; src: url("/fonts/Helevetica-Bold.ttf"); font-weight: bold;}
/* Serif Fonts */
@font-face {font-family: "Caladea"; src: url("/fonts/Caladea-Regular.ttf");}
@font-face {font-family: "Caladea"; src: url("/fonts/Caladea-Bold.ttf"); font-weight: bold;}
Expand All @@ -22,3 +23,6 @@
@font-face {font-family: "PlayfairDisplay"; src: url("/fonts/PlayfairDisplay-Bold.ttf"); font-weight: bold;}
@font-face {font-family: "Merriweather"; src: url("/fonts/Merriweather-Regular.ttf");}
@font-face {font-family: "Merriweather"; src: url("/fonts/Merriweather-Bold.ttf"); font-weight: bold;}
@font-face {font-family: "Garamond"; src: url("/fonts/Garamond-Regular.ttf");}
@font-face {font-family: "Garamond"; src: url("/fonts/Garamond-Bold.ttf"); font-weight: bold;}

36 changes: 36 additions & 0 deletions src/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { NextRequest, NextResponse } from 'next/server';
import { CohereClientV2 } from 'cohere-ai';


export async function POST(request: NextRequest) {
const { prompt }: { prompt: string } = await request.json();

const cohere = new CohereClientV2({
token: '',
});

try {
const response = await cohere.chat({
model: 'command-r-plus',
messages: [
{
role: 'user',
content: prompt,
},
],
});

console.log('Cohere response:', response);

const messageContent = response?.message?.content?.[0]?.text || response?.message?.content;

if (messageContent) {
return NextResponse.json({ message: messageContent }, { status: 200 });
} else {
return NextResponse.json({ error: 'No message returned from Cohere API.' }, { status: 500 });
}
} catch (error) {
console.error('Error calling Cohere API:', error);
return NextResponse.json({ error: 'An error occurred while processing your request.' }, { status: 500 });
}
}
25 changes: 25 additions & 0 deletions src/app/components/DarkModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client";
import { useTheme } from "../contexts/ThemeContext"; // Import the useTheme hook

const DarkModeToggle = () => {
const { isDarkMode, toggleTheme } = useTheme(); // Get dark mode state and toggle function from context

return (
<button
onClick={toggleTheme}
className="flex items-center justify-center px-4 py-2 bg-gray-200 dark:bg-gray-800 text-black dark:text-white rounded-md shadow-md hover:bg-gray-300 dark:hover:bg-gray-700 transition duration-300"
>
{isDarkMode ? (
<>
<span className="mr-2">🌞</span>
</>
) : (
<>
<span className="mr-2">🌙</span>
</>
)}
</button>
);
};

export default DarkModeToggle;
13 changes: 4 additions & 9 deletions src/app/components/ExpanderWithHeightTransition.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
/**
* ExpanderWithHeightTransition is a div wrapper with built-in transition animation based on height.
* If expanded is true, it slowly expands its content and vice versa.
*
* Note: There is no easy way to animate height transition in CSS: https://github.com/w3c/csswg-drafts/issues/626.
* This is a clever solution based on css grid and is borrowed from https://css-tricks.com/css-grid-can-do-auto-height-transitions/
*
*/
export const ExpanderWithHeightTransition = ({
expanded,
children,
Expand All @@ -20,7 +12,10 @@ export const ExpanderWithHeightTransition = ({
}`}
style={{ gridTemplateRows: expanded ? "1fr" : "0fr" }}
>
<div className="min-h-0">{children}</div>
<div className="min-h-0 dark:bg-gray-800 bg-white">
{/* You can apply dark mode classes to the container */}
{children}
</div>
</div>
);
};
61 changes: 46 additions & 15 deletions src/app/components/Resume/ResumeControlBar.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"use client";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { useSetDefaultScale } from "components/Resume/hooks";
import {
MagnifyingGlassIcon,
ArrowDownTrayIcon,
} from "@heroicons/react/24/outline";
import { MagnifyingGlassIcon, ArrowDownTrayIcon } from "@heroicons/react/24/outline";
import { usePDF } from "@react-pdf/renderer";
import dynamic from "next/dynamic";
import createDocx from "./ResumeDocx"; // Import the createDocx function
import { useAppSelector } from "lib/redux/hooks"; // Import the hook to access Redux store
import { selectResume } from "lib/redux/resumeSlice"; // Import the selector to get resume data

const ResumeControlBar = ({
scale,
Expand All @@ -27,12 +27,30 @@ const ResumeControlBar = ({
});

const [instance, update] = usePDF({ document });
const [selectedFormat, setSelectedFormat] = useState<string>("pdf");

// Access the resume data from the Redux store
const resume = useAppSelector(selectResume); // Get the current resume

// Hook to update pdf when document changes
useEffect(() => {
update();
}, [update, document]);

// Function to trigger download based on selected format
const handleDownload = async () => {
if (selectedFormat === "pdf") {
// PDF Download
const link = window.document.createElement("a");
link.href = instance.url!;
link.download = fileName;
link.click();
} else if (selectedFormat === "docx") {
// DOCX Download
await createDocx(resume); // Call the createDocx function directly
}
};

return (
<div className="sticky bottom-0 left-0 right-0 flex h-[var(--resume-control-bar-height)] items-center justify-center px-[var(--resume-padding)] text-gray-600 lg:justify-between">
<div className="flex items-center gap-2">
Expand All @@ -48,8 +66,8 @@ const ResumeControlBar = ({
setScale(Number(e.target.value));
}}
/>
<div className="w-10">{`${Math.round(scale * 100)}%`}</div>
<label className="hidden items-center gap-1 lg:flex">
<div className="w-9">{`${Math.round(scale * 100)}%`}</div>
<label className="hidden items-center gap-2 lg:flex">
<input
type="checkbox"
className="mt-0.5 h-4 w-4"
Expand All @@ -59,14 +77,25 @@ const ResumeControlBar = ({
<span className="select-none">Autoscale</span>
</label>
</div>
<a
className="ml-1 flex items-center gap-1 rounded-md border border-gray-300 px-3 py-0.5 hover:bg-gray-100 lg:ml-8"
href={instance.url!}
download={fileName}
>
<ArrowDownTrayIcon className="h-4 w-4" />
<span className="whitespace-nowrap">Download Resume</span>
</a>

<div className="flex items-center gap-2">
<select
className="border rounded-md p-1 ml-2"
value={selectedFormat}
onChange={(e) => setSelectedFormat(e.target.value)}
>
<option value="pdf">Export to PDF</option>
<option value="docx">Export to DOCX</option>
</select>

<button
className="ml-1 flex items-center gap-1 rounded-md border border-gray-300 px-3 py-0.5 hover:bg-gray-100"
onClick={handleDownload}
>
<ArrowDownTrayIcon className="h-4 w-4" />
<span className="whitespace-nowrap">Download Resume</span>
</button>
</div>
</div>
);
};
Expand All @@ -84,3 +113,5 @@ export const ResumeControlBarCSR = dynamic(
export const ResumeControlBarBorder = () => (
<div className="absolute bottom-[var(--resume-control-bar-height)] w-full border-t-2 bg-gray-50" />
);

export default ResumeControlBar; // Don't forget to export the component
107 changes: 107 additions & 0 deletions src/app/components/Resume/ResumeDocx/ResumeDocxAwards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Paragraph, TextRun, AlignmentType } from "docx";

// Define the props type for the ResumeDocxProjects
export interface ResumeAward {
award: string;
date: string;
descriptions: string[];
}

export interface ResumeDocxAwardsProps {
awards: ResumeAward[];
}

const ResumeDocxAwards = ({ awards }: ResumeDocxAwardsProps) => {
const awardElements: (Paragraph | undefined)[] = [];

// Add a line break before the projects section
awardElements.push(
new Paragraph({ children: [new TextRun({ text: "" })] })
);

// Add heading for Projects
awardElements.push(
new Paragraph({
children: [
new TextRun({
text: "Awards:",
bold: true,
color: "000000",
size: 24, // Size for the heading
}),
],
alignment: AlignmentType.START, // Align heading to the start
})
);

// Add a line break after the heading
awardElements.push(
new Paragraph({ children: [new TextRun({ text: "" })] })
);

// Loop through project entries and create paragraphs for each
awards.forEach((awar) => {
// Project Title
awardElements.push(
new Paragraph({
children: [
new TextRun({
text: awar.award,
bold: true,
color: "000000",
size: 22,
italics: true,
}),
],
alignment: AlignmentType.START, // Align project title to the start
})
);

// Date on the right
awardElements.push(
new Paragraph({
children: [
new TextRun({
text: awar.date, // Date for the project
color: "000000", // Color for the date
size: 20,
italics: true,
bold: true,
}),
],
alignment: AlignmentType.START, // Align the date to the end
})
);

// Add a line break
awardElements.push(
new Paragraph({ children: [new TextRun({ text: "" })] })
);

// Descriptions (details about the project)
awar.descriptions.forEach((desc) => {
awardElements.push(
new Paragraph({
children: [
new TextRun({
text: `• ${desc}`, // Bullet point for each description
size: 20,
color: "000000",
}),
],
alignment: AlignmentType.LEFT, // Left-align the descriptions
})
);
});

// Add a line break after each project entry
awardElements.push(
new Paragraph({ children: [new TextRun({ text: "" })] })
);
});

// Return the elements as an array of Paragraphs
return awardElements.filter(Boolean) as Paragraph[];
};

export default ResumeDocxAwards;
Loading