Repository: rrs301/AI-Resume-Builder
Branch: main
Commit: 4d8d1129190f
Files: 49
Total size: 97.8 KB
Directory structure:
gitextract_vnaeaso2/
├── .eslintrc.cjs
├── .gitignore
├── README.md
├── components.json
├── index.html
├── jsconfig.json
├── package.json
├── postcss.config.js
├── service/
│ ├── AIModal.js
│ └── GlobalApi.js
├── src/
│ ├── App.css
│ ├── App.jsx
│ ├── auth/
│ │ └── sign-in/
│ │ └── index.jsx
│ ├── components/
│ │ ├── custom/
│ │ │ └── Header.jsx
│ │ └── ui/
│ │ ├── alert-dialog.jsx
│ │ ├── button.jsx
│ │ ├── dialog.jsx
│ │ ├── dropdown-menu.jsx
│ │ ├── input.jsx
│ │ ├── popover.jsx
│ │ ├── sonner.jsx
│ │ └── textarea.jsx
│ ├── context/
│ │ └── ResumeInfoContext.jsx
│ ├── dashboard/
│ │ ├── components/
│ │ │ ├── AddResume.jsx
│ │ │ └── ResumeCardItem.jsx
│ │ ├── index.jsx
│ │ └── resume/
│ │ ├── [resumeId]/
│ │ │ └── edit/
│ │ │ └── index.jsx
│ │ └── components/
│ │ ├── FormSection.jsx
│ │ ├── ResumePreview.jsx
│ │ ├── RichTextEditor.jsx
│ │ ├── ThemeColor.jsx
│ │ ├── forms/
│ │ │ ├── Education.jsx
│ │ │ ├── Experience.jsx
│ │ │ ├── PersonalDetail.jsx
│ │ │ ├── Skills.jsx
│ │ │ └── Summery.jsx
│ │ └── preview/
│ │ ├── EducationalPreview.jsx
│ │ ├── ExperiencePreview.jsx
│ │ ├── PersonalDetailPreview.jsx
│ │ ├── SkillsPreview.jsx
│ │ └── SummeryPreview.jsx
│ ├── data/
│ │ └── dummy.jsx
│ ├── home/
│ │ └── index.jsx
│ ├── index.css
│ ├── lib/
│ │ └── utils.js
│ ├── main.jsx
│ └── my-resume/
│ └── [resumeId]/
│ └── view/
│ └── index.jsx
├── tailwind.config.js
└── vite.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc.cjs
================================================
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
settings: { react: { version: '18.2' } },
plugins: ['react-refresh'],
rules: {
'react/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
.env*.local
.env
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
================================================
FILE: README.md
================================================

# React + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
================================================
FILE: components.json
================================================
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": false,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
================================================
FILE: index.html
================================================
AI Resume Builder
================================================
FILE: jsconfig.json
================================================
{
"compilerOptions": {
// ...
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
// ...
}
}
================================================
FILE: package.json
================================================
{
"name": "ai-resume-builder",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@clerk/clerk-react": "^5.2.4",
"@google/generative-ai": "^0.12.0",
"@radix-ui/react-alert-dialog": "^1.0.5",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-slot": "^1.0.2",
"@smastrom/react-rating": "^1.5.0",
"axios": "^1.7.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"lucide-react": "^0.394.0",
"next-themes": "^0.3.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.1",
"react-simple-wysiwyg": "^3.0.2",
"react-to-pdf": "^1.0.1",
"react-web-share": "^2.0.2",
"sonner": "^1.5.0",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"uuid": "^10.0.0"
},
"devDependencies": {
"@types/node": "^20.14.2",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.4",
"vite": "^5.2.0"
}
}
================================================
FILE: postcss.config.js
================================================
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
================================================
FILE: service/AIModal.js
================================================
// const {
// GoogleGenerativeAI,
// HarmCategory,
// HarmBlockThreshold,
// } = require("@google/generative-ai");
import { GoogleGenerativeAI } from "@google/generative-ai";
const apiKey =import.meta.env.VITE_GOOGLE_AI_API_KEY;
const genAI = new GoogleGenerativeAI(apiKey);
const model = genAI.getGenerativeModel({
model: "gemini-1.5-flash",
});
const generationConfig = {
temperature: 1,
topP: 0.95,
topK: 64,
maxOutputTokens: 8192,
responseMimeType: "application/json",
};
export const AIChatSession = model.startChat({
generationConfig,
// safetySettings: Adjust safety settings
// See https://ai.google.dev/gemini-api/docs/safety-settings
history: [
],
});
================================================
FILE: service/GlobalApi.js
================================================
import axios from "axios";
const API_KEY=import.meta.env.VITE_STRAPI_API_KEY;
const axiosClient=axios.create({
baseURL:import.meta.env.VITE_API_BASE_URL+"/api/",
headers:{
'Content-Type':'application/json',
'Authorization':`Bearer ${API_KEY}`
}
})
const CreateNewResume=(data)=>axiosClient.post('/user-resumes',data);
const GetUserResumes=(userEmail)=>axiosClient.get('/user-resumes?filters[userEmail][$eq]='+userEmail);
const UpdateResumeDetail=(id,data)=>axiosClient.put('/user-resumes/'+id,data)
const GetResumeById=(id)=>axiosClient.get('/user-resumes/'+id+"?populate=*")
const DeleteResumeById=(id)=>axiosClient.delete('/user-resumes/'+id)
export default{
CreateNewResume,
GetUserResumes,
UpdateResumeDetail,
GetResumeById,
DeleteResumeById
}
================================================
FILE: src/App.css
================================================
#root {
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
================================================
FILE: src/App.jsx
================================================
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import { Button } from './components/ui/button'
import { Navigate, Outlet } from 'react-router-dom'
import { useUser } from '@clerk/clerk-react'
import Header from './components/custom/Header'
import { Toaster } from './components/ui/sonner'
function App() {
const [count, setCount] = useState(0)
const {user,isLoaded,isSignedIn}=useUser();
if(!isSignedIn&&isLoaded)
{
return
}
return (
<>
>
)
}
export default App
================================================
FILE: src/auth/sign-in/index.jsx
================================================
import { SignIn } from '@clerk/clerk-react'
import React from 'react'
function SignInPage() {
return (
)
}
export default SignInPage
================================================
FILE: src/components/custom/Header.jsx
================================================
import React from 'react'
import { Button } from '../ui/button'
import { Link } from 'react-router-dom'
import { UserButton, useUser } from '@clerk/clerk-react'
function Header() {
const { user, isSignedIn } = useUser();
return (
{isSignedIn ?
Dashboard
:
Get Started
}
)
}
export default Header
================================================
FILE: src/components/ui/alert-dialog.jsx
================================================
import * as React from "react"
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
const AlertDialog = AlertDialogPrimitive.Root
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
const AlertDialogPortal = AlertDialogPrimitive.Portal
const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => (
))
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => (
))
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
const AlertDialogHeader = ({
className,
...props
}) => (
)
AlertDialogHeader.displayName = "AlertDialogHeader"
const AlertDialogFooter = ({
className,
...props
}) => (
)
AlertDialogFooter.displayName = "AlertDialogFooter"
const AlertDialogTitle = React.forwardRef(({ className, ...props }, ref) => (
))
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
const AlertDialogDescription = React.forwardRef(({ className, ...props }, ref) => (
))
AlertDialogDescription.displayName =
AlertDialogPrimitive.Description.displayName
const AlertDialogAction = React.forwardRef(({ className, ...props }, ref) => (
))
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
const AlertDialogCancel = React.forwardRef(({ className, ...props }, ref) => (
))
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogHeader,
AlertDialogFooter,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogAction,
AlertDialogCancel,
}
================================================
FILE: src/components/ui/button.jsx
================================================
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva } from "class-variance-authority";
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
( )
);
})
Button.displayName = "Button"
export { Button, buttonVariants }
================================================
FILE: src/components/ui/dialog.jsx
================================================
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
const DialogPortal = DialogPrimitive.Portal
const DialogClose = DialogPrimitive.Close
const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
const DialogContent = React.forwardRef(({ className, children, ...props }, ref) => (
{children}
Close
))
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({
className,
...props
}) => (
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({
className,
...props
}) => (
)
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef(({ className, ...props }, ref) => (
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
const DialogDescription = React.forwardRef(({ className, ...props }, ref) => (
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogClose,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
}
================================================
FILE: src/components/ui/dropdown-menu.jsx
================================================
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { Check, ChevronRight, Circle } from "lucide-react"
import { cn } from "@/lib/utils"
const DropdownMenu = DropdownMenuPrimitive.Root
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
const DropdownMenuGroup = DropdownMenuPrimitive.Group
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
const DropdownMenuSub = DropdownMenuPrimitive.Sub
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
const DropdownMenuSubTrigger = React.forwardRef(({ className, inset, children, ...props }, ref) => (
{children}
))
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName
const DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) => (
))
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName
const DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => (
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
const DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) => (
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
const DropdownMenuCheckboxItem = React.forwardRef(({ className, children, checked, ...props }, ref) => (
{children}
))
DropdownMenuCheckboxItem.displayName =
DropdownMenuPrimitive.CheckboxItem.displayName
const DropdownMenuRadioItem = React.forwardRef(({ className, children, ...props }, ref) => (
{children}
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
const DropdownMenuLabel = React.forwardRef(({ className, inset, ...props }, ref) => (
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
const DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) => (
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
const DropdownMenuShortcut = ({
className,
...props
}) => {
return (
( )
);
}
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
}
================================================
FILE: src/components/ui/input.jsx
================================================
import * as React from "react"
import { cn } from "@/lib/utils"
const Input = React.forwardRef(({ className, type, ...props }, ref) => {
return (
( )
);
})
Input.displayName = "Input"
export { Input }
================================================
FILE: src/components/ui/popover.jsx
================================================
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
const Popover = PopoverPrimitive.Root
const PopoverTrigger = PopoverPrimitive.Trigger
const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
export { Popover, PopoverTrigger, PopoverContent }
================================================
FILE: src/components/ui/sonner.jsx
================================================
import { useTheme } from "next-themes"
import { Toaster as Sonner } from "sonner"
const Toaster = ({
...props
}) => {
const { theme = "system" } = useTheme()
return (
( )
);
}
export { Toaster }
================================================
FILE: src/components/ui/textarea.jsx
================================================
import * as React from "react"
import { cn } from "@/lib/utils"
const Textarea = React.forwardRef(({ className, ...props }, ref) => {
return (
()
);
})
Textarea.displayName = "Textarea"
export { Textarea }
================================================
FILE: src/context/ResumeInfoContext.jsx
================================================
import { createContext } from "react";
export const ResumeInfoContext=createContext(null);
================================================
FILE: src/dashboard/components/AddResume.jsx
================================================
import { Loader2, PlusSquare } from 'lucide-react'
import React, { useState } from 'react'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { v4 as uuidv4 } from 'uuid';
import GlobalApi from './../../../service/GlobalApi'
import { useUser } from '@clerk/clerk-react'
import { Navigate, useNavigate } from 'react-router-dom'
function AddResume() {
const [openDialog,setOpenDialog]=useState(false)
const [resumeTitle,setResumeTitle]=useState();
const {user}=useUser();
const [loading,setLoading]=useState(false);
const navigation=useNavigate();
const onCreate=async()=>{
setLoading(true)
const uuid=uuidv4();
const data={
data:{
title:resumeTitle,
resumeId:uuid,
userEmail:user?.primaryEmailAddress?.emailAddress,
userName:user?.fullName
}
}
GlobalApi.CreateNewResume(data).then(resp=>{
console.log(resp.data.data.documentId);
if(resp)
{
setLoading(false);
navigation('/dashboard/resume/'+resp.data.data.documentId+"/edit");
}
},(error)=>{
setLoading(false);
})
}
return (
)
}
export default AddResume
================================================
FILE: src/dashboard/components/ResumeCardItem.jsx
================================================
import { Loader2Icon, MoreVertical, Notebook } from 'lucide-react'
import React, { useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import GlobalApi from './../../../service/GlobalApi'
import { toast } from 'sonner'
function ResumeCardItem({resume,refreshData}) {
const navigation=useNavigate();
const [openAlert,setOpenAlert]=useState(false);
const [loading,setLoading]=useState(false);
// const onMenuClick=(url)=>{
// navigation(url)
// }
const onDelete=()=>{
setLoading(true);
GlobalApi.DeleteResumeById(resume.documentId).then(resp=>{
console.log(resp);
toast('Resume Deleted!');
refreshData()
setLoading(false);
setOpenAlert(false);
},(error)=>{
setLoading(false);
})
}
return (
{/*
*/}
{resume.title}
navigation('/dashboard/resume/'+resume.documentId+"/edit")}>Edit
navigation('/my-resume/'+resume.documentId+"/view")}>View
navigation('/my-resume/'+resume.documentId+"/view")}>Download
setOpenAlert(true)}>Delete
Are you absolutely sure?
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.
setOpenAlert(false)}>Cancel
{loading? :'Delete'}
)
}
export default ResumeCardItem
================================================
FILE: src/dashboard/index.jsx
================================================
import React, { useEffect, useState } from 'react'
import AddResume from './components/AddResume'
import { useUser } from '@clerk/clerk-react'
import GlobalApi from './../../service/GlobalApi';
import ResumeCardItem from './components/ResumeCardItem';
function Dashboard() {
const {user}=useUser();
const [resumeList,setResumeList]=useState([]);
useEffect(()=>{
user&&GetResumesList()
},[user])
/**
* Used to Get Users Resume List
*/
const GetResumesList=()=>{
GlobalApi.GetUserResumes(user?.primaryEmailAddress?.emailAddress)
.then(resp=>{
console.log(resp.data.data)
setResumeList(resp.data.data);
})
}
return (
My Resume
Start Creating AI resume to your next Job role
{resumeList.length>0?resumeList.map((resume,index)=>(
)):
[1,2,3,4].map((item,index)=>(
))
}
)
}
export default Dashboard
================================================
FILE: src/dashboard/resume/[resumeId]/edit/index.jsx
================================================
import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import FormSection from '../../components/FormSection';
import ResumePreview from '../../components/ResumePreview';
import { ResumeInfoContext } from '@/context/ResumeInfoContext';
import dummy from '@/data/dummy';
import GlobalApi from './../../../../../service/GlobalApi';
function EditResume() {
const {resumeId}=useParams();
const [resumeInfo,setResumeInfo]=useState();
useEffect(()=>{
GetResumeInfo();
},[])
const GetResumeInfo=()=>{
GlobalApi.GetResumeById(resumeId).then(resp=>{
console.log(resp.data.data);
setResumeInfo(resp.data.data);
})
}
return (
{/* Form Section */}
{/* Preview Section */}
)
}
export default EditResume
================================================
FILE: src/dashboard/resume/components/FormSection.jsx
================================================
import React, { useState } from 'react'
import PersonalDetail from './forms/PersonalDetail'
import { Button } from '@/components/ui/button'
import { ArrowLeft, ArrowRight, Home, LayoutGrid } from 'lucide-react'
import Summery from './forms/Summery';
import Experience from './forms/Experience';
import Education from './forms/Education';
import Skills from './forms/Skills';
import { Link, Navigate, useParams } from 'react-router-dom';
import ThemeColor from './ThemeColor';
function FormSection() {
const [activeFormIndex,setActiveFormIndex]=useState(1);
const [enableNext,setEnableNext]=useState(true);
const {resumeId}=useParams();
return (
{activeFormIndex>1
&&
setActiveFormIndex(activeFormIndex-1)}> }
setActiveFormIndex(activeFormIndex+1)}
> Next
{/* Personal Detail */}
{activeFormIndex==1?
setEnableNext(v)} />
:activeFormIndex==2?
setEnableNext(v)} />
:activeFormIndex==3?
:activeFormIndex==4?
:activeFormIndex==5?
:activeFormIndex==6?
:null
}
{/* Experience */}
{/* Educational Detail */}
{/* Skills */}
)
}
export default FormSection
================================================
FILE: src/dashboard/resume/components/ResumePreview.jsx
================================================
import { ResumeInfoContext } from '@/context/ResumeInfoContext'
import React, { useContext } from 'react'
import PersonalDetailPreview from './preview/PersonalDetailPreview'
import SummeryPreview from './preview/SummeryPreview'
import ExperiencePreview from './preview/ExperiencePreview'
import EducationalPreview from './preview/EducationalPreview'
import SkillsPreview from './preview/SkillsPreview'
function ResumePreview() {
const {resumeInfo,setResumeInfo}=useContext(ResumeInfoContext)
return (
{/* Personal Detail */}
{/* Summery */}
{/* Professional Experience */}
{resumeInfo?.Experience?.length>0&&
}
{/* Educational */}
{resumeInfo?.education?.length>0&&
}
{/* Skilss */}
{resumeInfo?.skills?.length>0&&
}
)
}
export default ResumePreview
================================================
FILE: src/dashboard/resume/components/RichTextEditor.jsx
================================================
import { Button } from '@/components/ui/button';
import { ResumeInfoContext } from '@/context/ResumeInfoContext';
import { Brain, LoaderCircle } from 'lucide-react';
import React, { useContext, useState } from 'react'
import { BtnBold, BtnBulletList, BtnClearFormatting, BtnItalic, BtnLink, BtnNumberedList, BtnStrikeThrough, BtnStyles, BtnUnderline, Editor, EditorProvider, HtmlButton, Separator, Toolbar } from 'react-simple-wysiwyg'
import { AIChatSession } from './../../../../service/AIModal';
import { toast } from 'sonner';
const PROMPT='position titile: {positionTitle} , Depends on position title give me 5-7 bullet points for my experience in resume (Please do not add experince level and No JSON array) , give me result in HTML tags'
function RichTextEditor({onRichTextEditorChange,index,defaultValue}) {
const [value,setValue]=useState(defaultValue);
const {resumeInfo,setResumeInfo}=useContext(ResumeInfoContext)
const [loading,setLoading]=useState(false);
const GenerateSummeryFromAI=async()=>{
if(!resumeInfo?.Experience[index]?.title)
{
toast('Please Add Position Title');
return ;
}
setLoading(true)
const prompt=PROMPT.replace('{positionTitle}',resumeInfo.Experience[index].title);
const result=await AIChatSession.sendMessage(prompt);
console.log(result.response.text());
const resp=result.response.text()
setValue(resp.replace('[','').replace(']',''));
setLoading(false);
}
return (
Summery
{loading?
:
<>
Generate from AI
>
}
{
setValue(e.target.value);
onRichTextEditorChange(e)
}}>
)
}
export default RichTextEditor
================================================
FILE: src/dashboard/resume/components/ThemeColor.jsx
================================================
import React, { useContext, useState } from 'react'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import { Button } from '@/components/ui/button'
import { LayoutGrid } from 'lucide-react'
import { ResumeInfoContext } from '@/context/ResumeInfoContext'
import GlobalApi from './../../../../service/GlobalApi'
import { useParams } from 'react-router-dom'
import { toast } from 'sonner'
function ThemeColor() {
const colors=[
"#FF5733", "#33FF57", "#3357FF", "#FF33A1", "#A133FF",
"#33FFA1", "#FF7133", "#71FF33", "#7133FF", "#FF3371",
"#33FF71", "#3371FF", "#A1FF33", "#33A1FF", "#FF5733",
"#5733FF", "#33FF5A", "#5A33FF", "#FF335A", "#335AFF"
]
const {resumeInfo,setResumeInfo}=useContext(ResumeInfoContext);
const [selectedColor,setSelectedColor]=useState();
const {resumeId}=useParams();
const onColorSelect=(color)=>{
setSelectedColor(color)
setResumeInfo({
...resumeInfo,
themeColor:color
});
const data={
data:{
themeColor:color
}
}
GlobalApi.UpdateResumeDetail(resumeId,data).then(resp=>{
console.log(resp);
toast('Theme Color Updated')
})
}
return (
Theme
Select Theme Color
{colors.map((item,index)=>(
onColorSelect(item)}
className={`h-5 w-5 rounded-full cursor-pointer
hover:border-black border
${selectedColor==item&&'border border-black'}
`}
style={{
background:item
}}>
))}
)
}
export default ThemeColor
================================================
FILE: src/dashboard/resume/components/forms/Education.jsx
================================================
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { ResumeInfoContext } from '@/context/ResumeInfoContext'
import { LoaderCircle } from 'lucide-react'
import React, { useContext, useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import GlobalApi from './../../../../../service/GlobalApi'
import { toast } from 'sonner'
function Education() {
const [loading,setLoading]=useState(false);
const {resumeInfo,setResumeInfo}=useContext(ResumeInfoContext);
const params=useParams();
const [educationalList,setEducationalList]=useState([
{
universityName:'',
degree:'',
major:'',
startDate:'',
endDate:'',
description:''
}
])
useEffect(()=>{
resumeInfo&&setEducationalList(resumeInfo?.education)
},[])
const handleChange=(event,index)=>{
const newEntries=educationalList.slice();
const {name,value}=event.target;
newEntries[index][name]=value;
setEducationalList(newEntries);
}
const AddNewEducation=()=>{
setEducationalList([...educationalList,
{
universityName:'',
degree:'',
major:'',
startDate:'',
endDate:'',
description:''
}
])
}
const RemoveEducation=()=>{
setEducationalList(educationalList=>educationalList.slice(0,-1))
}
const onSave=()=>{
setLoading(true)
const data={
data:{
education:educationalList.map(({ id, ...rest }) => rest)
}
}
GlobalApi.UpdateResumeDetail(params.resumeId,data).then(resp=>{
console.log(resp);
setLoading(false)
toast('Details updated !')
},(error)=>{
setLoading(false);
toast('Server Error, Please try again!')
})
}
useEffect(()=>{
setResumeInfo({
...resumeInfo,
education:educationalList
})
},[educationalList])
return (
Education
Add Your educational details
{educationalList.map((item,index)=>(
))}
+ Add More Education
- Remove
onSave()}>
{loading? :'Save'}
)
}
export default Education
================================================
FILE: src/dashboard/resume/components/forms/Experience.jsx
================================================
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import React, { useContext, useEffect, useState } from 'react'
import RichTextEditor from '../RichTextEditor'
import { ResumeInfoContext } from '@/context/ResumeInfoContext'
import { useParams } from 'react-router-dom'
import GlobalApi from './../../../../../service/GlobalApi'
import { toast } from 'sonner'
import { LoaderCircle } from 'lucide-react'
const formField={
title:'',
companyName:'',
city:'',
state:'',
startDate:'',
endDate:'',
workSummery:'',
}
function Experience() {
const [experinceList,setExperinceList]=useState([]);
const {resumeInfo,setResumeInfo}=useContext(ResumeInfoContext);
const params=useParams();
const [loading,setLoading]=useState(false);
useEffect(()=>{
resumeInfo?.Experience.length>0&&setExperinceList(resumeInfo?.Experience)
},[])
const handleChange=(index,event)=>{
const newEntries=experinceList.slice();
const {name,value}=event.target;
newEntries[index][name]=value;
console.log(newEntries)
setExperinceList(newEntries);
}
const AddNewExperience=()=>{
setExperinceList([...experinceList,{
title:'',
companyName:'',
city:'',
state:'',
startDate:'',
endDate:'',
workSummery:'',
}])
}
const RemoveExperience=()=>{
setExperinceList(experinceList=>experinceList.slice(0,-1))
}
const handleRichTextEditor=(e,name,index)=>{
const newEntries=experinceList.slice();
newEntries[index][name]=e.target.value;
setExperinceList(newEntries);
}
useEffect(()=>{
setResumeInfo({
...resumeInfo,
Experience:experinceList
});
},[experinceList]);
const onSave=()=>{
setLoading(true)
const data={
data:{
Experience:experinceList.map(({ id, ...rest }) => rest)
}
}
console.log(experinceList)
GlobalApi.UpdateResumeDetail(params?.resumeId,data).then(res=>{
console.log(res);
setLoading(false);
toast('Details updated !')
},(error)=>{
setLoading(false);
})
}
return (
Professional Experience
Add Your previous Job experience
{experinceList.map((item,index)=>(
))}
+ Add More Experience
- Remove
onSave()}>
{loading? :'Save'}
)
}
export default Experience
================================================
FILE: src/dashboard/resume/components/forms/PersonalDetail.jsx
================================================
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { ResumeInfoContext } from '@/context/ResumeInfoContext'
import { LoaderCircle } from 'lucide-react';
import React, { useContext, useEffect, useState } from 'react'
import { useParams } from 'react-router-dom';
import GlobalApi from './../../../../../service/GlobalApi';
import { toast } from 'sonner';
function PersonalDetail({enabledNext}) {
const params=useParams();
const {resumeInfo,setResumeInfo}=useContext(ResumeInfoContext)
const [formData,setFormData]=useState();
const [loading,setLoading]=useState(false);
useEffect(()=>{
console.log("---",resumeInfo)
},[])
const handleInputChange=(e)=>{
enabledNext(false)
const {name,value}=e.target;
setFormData({
...formData,
[name]:value
})
setResumeInfo({
...resumeInfo,
[name]:value
})
}
const onSave=(e)=>{
e.preventDefault();
setLoading(true)
const data={
data:formData
}
GlobalApi.UpdateResumeDetail(params?.resumeId,data).then(resp=>{
console.log(resp);
enabledNext(true);
setLoading(false);
toast("Details updated")
},(error)=>{
setLoading(false);
})
}
return (
Personal Detail
Get Started with the basic information
)
}
export default PersonalDetail
================================================
FILE: src/dashboard/resume/components/forms/Skills.jsx
================================================
import { Input } from '@/components/ui/input'
import React, { useContext, useEffect, useState } from 'react'
import { Rating } from '@smastrom/react-rating'
import '@smastrom/react-rating/style.css'
import { Button } from '@/components/ui/button'
import { LoaderCircle } from 'lucide-react'
import { ResumeInfoContext } from '@/context/ResumeInfoContext'
import GlobalApi from './../../../../../service/GlobalApi'
import { useParams } from 'react-router-dom'
import { toast } from 'sonner'
function Skills() {
const [skillsList,setSkillsList]=useState([{
name:'',
rating:0
}])
const {resumeId}=useParams();
const [loading,setLoading]=useState(false);
const {resumeInfo,setResumeInfo}=useContext(ResumeInfoContext);
useEffect(()=>{
resumeInfo&&setSkillsList(resumeInfo?.skills)
},[])
const handleChange=(index,name,value)=>{
const newEntries=skillsList.slice();
newEntries[index][name]=value;
setSkillsList(newEntries);
}
const AddNewSkills=()=>{
setSkillsList([...skillsList,{
name:'',
rating:0
}])
}
const RemoveSkills=()=>{
setSkillsList(skillsList=>skillsList.slice(0,-1))
}
const onSave=()=>{
setLoading(true);
const data={
data:{
skills:skillsList.map(({ id, ...rest }) => rest)
}
}
GlobalApi.UpdateResumeDetail(resumeId,data)
.then(resp=>{
console.log(resp);
setLoading(false);
toast('Details updated !')
},(error)=>{
setLoading(false);
toast('Server Error, Try again!')
})
}
useEffect(()=>{
setResumeInfo({
...resumeInfo,
skills:skillsList
})
},[skillsList])
return (
Skills
Add Your top professional key skills
{skillsList.map((item,index)=>(
))}
+ Add More Skill
- Remove
onSave()}>
{loading? :'Save'}
)
}
export default Skills
================================================
FILE: src/dashboard/resume/components/forms/Summery.jsx
================================================
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'
import { ResumeInfoContext } from '@/context/ResumeInfoContext'
import React, { useContext, useEffect, useState } from 'react'
import { useParams } from 'react-router-dom';
import GlobalApi from './../../../../../service/GlobalApi';
import { Brain, LoaderCircle } from 'lucide-react';
import { toast } from 'sonner';
import { AIChatSession } from './../../../../../service/AIModal';
const prompt="Job Title: {jobTitle} , Depends on job title give me list of summery for 3 experience level, Mid Level and Freasher level in 3 -4 lines in array format, With summery and experience_level Field in JSON Format"
function Summery({enabledNext}) {
const {resumeInfo,setResumeInfo}=useContext(ResumeInfoContext);
const [summery,setSummery]=useState();
const [loading,setLoading]=useState(false);
const params=useParams();
const [aiGeneratedSummeryList,setAiGenerateSummeryList]=useState();
useEffect(()=>{
summery&&setResumeInfo({
...resumeInfo,
summery:summery
})
},[summery])
const GenerateSummeryFromAI=async()=>{
setLoading(true)
const PROMPT=prompt.replace('{jobTitle}',resumeInfo?.jobTitle);
console.log(PROMPT);
const result=await AIChatSession.sendMessage(PROMPT);
console.log(JSON.parse(result.response.text()))
setAiGenerateSummeryList(JSON.parse(result.response.text()))
setLoading(false);
}
const onSave=(e)=>{
e.preventDefault();
setLoading(true)
const data={
data:{
summery:summery
}
}
GlobalApi.UpdateResumeDetail(params?.resumeId,data).then(resp=>{
console.log(resp);
enabledNext(true);
setLoading(false);
toast("Details updated")
},(error)=>{
setLoading(false);
})
}
return (
Summery
Add Summery for your job title
{aiGeneratedSummeryList&&
Suggestions
{aiGeneratedSummeryList?.map((item,index)=>(
setSummery(item?.summary)}
className='p-5 shadow-lg my-4 rounded-lg cursor-pointer'>
Level: {item?.experience_level}
{item?.summary}
))}
}
)
}
export default Summery
================================================
FILE: src/dashboard/resume/components/preview/EducationalPreview.jsx
================================================
import React from 'react'
function EducationalPreview({resumeInfo}) {
return (
Education
{resumeInfo?.education.map((education,index)=>(
{education.universityName}
{education?.degree} in {education?.major}
{education?.startDate} - {education?.endDate}
{education?.description}
))}
)
}
export default EducationalPreview
================================================
FILE: src/dashboard/resume/components/preview/ExperiencePreview.jsx
================================================
import React from 'react'
function ExperiencePreview({resumeInfo}) {
return (
Professional Experience
{resumeInfo?.Experience?.map((experience,index)=>(
{experience?.title}
{experience?.companyName},
{experience?.city},
{experience?.state}
{experience?.startDate} To {experience?.currentlyWorking?'Present':experience.endDate}
{/*
{experience.workSummery}
*/}
))}
)
}
export default ExperiencePreview
================================================
FILE: src/dashboard/resume/components/preview/PersonalDetailPreview.jsx
================================================
import React from 'react'
function PersonalDetailPreview({resumeInfo}) {
return (
{resumeInfo?.firstName} {resumeInfo?.lastName}
{resumeInfo?.jobTitle}
{resumeInfo?.address}
{resumeInfo?.phone}
{resumeInfo?.email}
)
}
export default PersonalDetailPreview
================================================
FILE: src/dashboard/resume/components/preview/SkillsPreview.jsx
================================================
import React from 'react'
function SkillsPreview({resumeInfo}) {
return (
Education
{resumeInfo?.skills.map((skill,index)=>(
))}
)
}
export default SkillsPreview
================================================
FILE: src/dashboard/resume/components/preview/SummeryPreview.jsx
================================================
import React from 'react'
function SummeryPreview({resumeInfo}) {
return (
{resumeInfo?.summery}
)
}
export default SummeryPreview
================================================
FILE: src/data/dummy.jsx
================================================
export default{
firstName:'James',
lastName:'Carter',
jobTitle:'full stack developer',
address:'525 N tryon Street, NC 28117',
phone:'(123)-456-7890',
email:'exmaple@gmail.com',
themeColor:"#ff6666",
summery:'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
experience:[
{
id:1,
title:'Full Stack Developer',
companyName:'Amazon',
city:'New York',
state:'NY',
startDate:'Jan 2021',
endDate:'',
currentlyWorking:true,
workSummery:' Designed, developed, and maintained full-stack applications using React and Node.js.\n'+
'• Implemented responsive user interfaces with React, ensuring seamless user experiences across\n'+
'various devices and browsers.\n'+
'• Maintaining the React Native in-house organization application.'+
'• CreatedRESTfulAPIs withNode.js and Express,facilitating data communicationbetween the front-end'+
'and back-end systems.'
},
{
id:2,
title:'Frontend Developer',
companyName:'Google',
city:'Charlotte',
state:'NC',
startDate:'May 2019',
endDate:'Jan 2021',
currentlyWorking:false,
workSummery:' Designed, developed, and maintained full-stack applications using React and Node.js.'+
'• Implemented responsive user interfaces with React, ensuring seamless user experiences across'+
'various devices and browsers.'+
'• Maintaining the React Native in-house organization application.'+
'• CreatedRESTfulAPIs withNode.js and Express,facilitating data communicationbetween the front-end'+
'and back-end systems.'
}
],
education:[
{
id:1,
universityName:'Western Illinois University',
startDate:'Aug 2018',
endDate:'Dec:2019',
degree:'Master',
major:'Computer Science',
description:'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud'
},
{
id:2,
universityName:'Western Illinois University',
startDate:'Aug 2018',
endDate:'Dec:2019',
degree:'Master',
major:'Computer Science',
description:'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud'
}
],
skills:[
{
id:1,
name:'Angular',
rating:80,
},
{
id:1,
name:'React',
rating:100,
},
{
id:1,
name:'MySql',
rating:80,
},
{
id:1,
name:'React Native',
rating:100,
}
]
}
================================================
FILE: src/home/index.jsx
================================================
import Header from '@/components/custom/Header'
import { UserButton } from '@clerk/clerk-react'
import { AtomIcon, Edit, Share2 } from 'lucide-react'
import React from 'react'
function Home() {
return (
{/*
*/}
{/*
*/}
How it Works?
Give mock interview in just 3 simplar easy step
)
}
export default Home
================================================
FILE: src/index.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;
@media print{
#no-print{
display: none;
}
#print-area{
display: block;
}
body{
margin: 0;
padding: 0;
box-shadow: none;
}
}
@page{
size: auto;
margin: 0mm;
}
.rsw-ce ul {
list-style: disc;
padding-left: 2em;
}
.rsw-ce ol {
list-style: decimal;
padding-left: 2em;
}
ol, ul, menu {
list-style: disc;
margin: 0;
padding: 0;
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
================================================
FILE: src/lib/utils.js
================================================
import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs) {
return twMerge(clsx(inputs))
}
================================================
FILE: src/main.jsx
================================================
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import { RouterProvider, createBrowserRouter } from 'react-router-dom'
import SignInPage from './auth/sign-in/index.jsx'
import Home from './home/index.jsx'
import Dashboard from './dashboard/index.jsx'
import { ClerkProvider } from '@clerk/clerk-react'
import EditResume from './dashboard/resume/[resumeId]/edit/index.jsx'
import ViewResume from './my-resume/[resumeId]/view/index.jsx'
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
const router=createBrowserRouter([
{
path:'/',
element:
},
{
element: ,
children:[
{
path:'/dashboard',
element:
},
{
path:'/dashboard/resume/:resumeId/edit',
element:
},
]
},
,
{
path:'/auth/sign-in',
element:
},
{
path:'/my-resume/:resumeId/view',
element:
}
])
ReactDOM.createRoot(document.getElementById('root')).render(
,
)
================================================
FILE: src/my-resume/[resumeId]/view/index.jsx
================================================
import Header from '@/components/custom/Header'
import { Button } from '@/components/ui/button'
import { ResumeInfoContext } from '@/context/ResumeInfoContext'
import ResumePreview from '@/dashboard/resume/components/ResumePreview'
import React, { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import GlobalApi from './../../../../service/GlobalApi'
import { RWebShare } from 'react-web-share'
function ViewResume() {
const [resumeInfo,setResumeInfo]=useState();
const {resumeId}=useParams();
useEffect(()=>{
GetResumeInfo();
},[])
const GetResumeInfo=()=>{
GlobalApi.GetResumeById(resumeId).then(resp=>{
console.log(resp.data.data);
setResumeInfo(resp.data.data);
})
}
const HandleDownload=()=>{
window.print();
}
return (
Congrats! Your Ultimate AI generates Resume is ready !
Now you are ready to download your resume and you can share unique
resume url with your friends and family
Download
console.log("shared successfully!")}
> Share
)
}
export default ViewResume
================================================
FILE: tailwind.config.js
================================================
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: [
'./pages/**/*.{js,jsx}',
'./components/**/*.{js,jsx}',
'./app/**/*.{js,jsx}',
'./src/**/*.{js,jsx}',
],
prefix: "",
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "#9f5bff",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}
================================================
FILE: vite.config.js
================================================
import path from "path"
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
})