├── frontend ├── src │ ├── pages │ │ ├── home │ │ │ ├── home.scss │ │ │ └── Home.page.tsx │ │ ├── jobs │ │ │ ├── jobs.scss │ │ │ ├── Jobs.page.tsx │ │ │ └── AddJob.page.tsx │ │ ├── companies │ │ │ ├── companies.scss │ │ │ ├── Companies.page.tsx │ │ │ └── AddCompany.page.tsx │ │ └── candidates │ │ │ ├── candidates.scss │ │ │ ├── Candidates.page.tsx │ │ │ └── AddCandidate.page.tsx │ ├── react-app-env.d.ts │ ├── constants │ │ └── url.constants.ts │ ├── components │ │ ├── jobs │ │ │ ├── jobs-grid.scss │ │ │ └── JobsGrid.component.tsx │ │ ├── companies │ │ │ ├── companies-grid.scss │ │ │ └── CompaniesGrid.component.tsx │ │ ├── candidates │ │ │ ├── candidates-grid.scss │ │ │ └── CandidatesGrid.component.tsx │ │ ├── custom-linear-progress │ │ │ └── CustomLinearProgress.component.tsx │ │ └── navbar │ │ │ ├── navbar.scss │ │ │ └── Navbar.component.tsx │ ├── _mixins.scss │ ├── helpers │ │ └── http.module.ts │ ├── index.tsx │ ├── context │ │ └── theme.context.tsx │ ├── global.scss │ ├── types │ │ └── global.typing.ts │ └── App.tsx ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── .gitignore ├── tsconfig.json ├── package.json └── README.md ├── backend ├── backend │ ├── documents │ │ └── pdfs │ │ │ └── readme.md │ ├── appsettings.Development.json │ ├── Core │ │ ├── Enums │ │ │ ├── CompanySize.cs │ │ │ └── JobLevel.cs │ │ ├── Dtos │ │ │ ├── Company │ │ │ │ ├── CompanyCreateDto.cs │ │ │ │ └── CompanyGetDto.cs │ │ │ ├── Job │ │ │ │ ├── JobCreateDto.cs │ │ │ │ └── JobGetDto.cs │ │ │ └── Candidate │ │ │ │ ├── CandidateCreateDto.cs │ │ │ │ └── CandidateGetDto.cs │ │ ├── Entities │ │ │ ├── Company.cs │ │ │ ├── BaseEntity.cs │ │ │ ├── Job.cs │ │ │ └── Candidate.cs │ │ ├── AutoMapperConfig │ │ │ └── AutoMapperConfigProfile.cs │ │ └── Context │ │ │ └── ApplicationDbContext.cs │ ├── appsettings.json │ ├── backend.csproj │ ├── Properties │ │ └── launchSettings.json │ ├── Program.cs │ ├── Controllers │ │ ├── JobController.cs │ │ ├── CompanyController.cs │ │ └── CandidateController.cs │ └── Migrations │ │ ├── 20230324222318_update-enum-to-string.cs │ │ ├── 20230324220612_initila-migration.cs │ │ ├── ApplicationDbContextModelSnapshot.cs │ │ ├── 20230324220612_initila-migration.Designer.cs │ │ └── 20230324222318_update-enum-to-string.Designer.cs └── backend.sln ├── DB.png ├── resume-management.jpg ├── .gitignore └── README.md /frontend/src/pages/home/home.scss: -------------------------------------------------------------------------------- 1 | .home { 2 | 3 | } -------------------------------------------------------------------------------- /backend/backend/documents/pdfs/readme.md: -------------------------------------------------------------------------------- 1 | This folder is used to save pdf files -------------------------------------------------------------------------------- /frontend/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /frontend/src/constants/url.constants.ts: -------------------------------------------------------------------------------- 1 | export const baseUrl = "https://localhost:7129/api"; -------------------------------------------------------------------------------- /DB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammad-taheri1/Youtube-Resume-Management-dotnet-react-ts/HEAD/DB.png -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/src/components/jobs/jobs-grid.scss: -------------------------------------------------------------------------------- 1 | .jobs-grid { 2 | * { 3 | color: var(--text-color1); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/components/companies/companies-grid.scss: -------------------------------------------------------------------------------- 1 | .companies-grid { 2 | * { 3 | color: var(--text-color1); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /resume-management.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammad-taheri1/Youtube-Resume-Management-dotnet-react-ts/HEAD/resume-management.jpg -------------------------------------------------------------------------------- /frontend/src/components/candidates/candidates-grid.scss: -------------------------------------------------------------------------------- 1 | .candidates-grid { 2 | * { 3 | color: var(--text-color1); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammad-taheri1/Youtube-Resume-Management-dotnet-react-ts/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammad-taheri1/Youtube-Resume-Management-dotnet-react-ts/HEAD/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohammad-taheri1/Youtube-Resume-Management-dotnet-react-ts/HEAD/frontend/public/logo512.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | *.dll 3 | *.pdb 4 | *.cache 5 | bin 6 | */bin 7 | **/bin 8 | **/debug 9 | **/obj 10 | */data-mssql 11 | **/data-mssql 12 | *.user -------------------------------------------------------------------------------- /backend/backend/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin d-flex($direction, $justify, $align) { 2 | display: flex; 3 | flex-direction: $direction; 4 | justify-content: $justify; 5 | align-items: $align; 6 | } 7 | -------------------------------------------------------------------------------- /backend/backend/Core/Enums/CompanySize.cs: -------------------------------------------------------------------------------- 1 | namespace backend.Core.Enums 2 | { 3 | public enum CompanySize 4 | { 5 | Small, 6 | Medium, 7 | Large 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/helpers/http.module.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { baseUrl } from "../constants/url.constants"; 3 | 4 | const httpModule = axios.create({ 5 | baseURL: baseUrl, 6 | }); 7 | 8 | export default httpModule; -------------------------------------------------------------------------------- /backend/backend/Core/Enums/JobLevel.cs: -------------------------------------------------------------------------------- 1 | namespace backend.Core.Enums 2 | { 3 | public enum JobLevel 4 | { 5 | Intern, 6 | Junior, 7 | MidLevel, 8 | Senior, 9 | TeamLead, 10 | Cto, 11 | Architect 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/backend/Core/Dtos/Company/CompanyCreateDto.cs: -------------------------------------------------------------------------------- 1 | using backend.Core.Enums; 2 | 3 | namespace backend.Core.Dtos.Company 4 | { 5 | public class CompanyCreateDto 6 | { 7 | public string Name { get; set; } 8 | public CompanySize Size { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /backend/backend/Core/Dtos/Job/JobCreateDto.cs: -------------------------------------------------------------------------------- 1 | using backend.Core.Enums; 2 | 3 | namespace backend.Core.Dtos.Job 4 | { 5 | public class JobCreateDto 6 | { 7 | public string Title { get; set; } 8 | public JobLevel Level { get; set; } 9 | public long CompanyId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/components/custom-linear-progress/CustomLinearProgress.component.tsx: -------------------------------------------------------------------------------- 1 | import { Box, LinearProgress } from "@mui/material"; 2 | 3 | const CustomLinearProgress = () => { 4 | return ( 5 | 6 | 7 | 8 | ); 9 | }; 10 | 11 | export default CustomLinearProgress; 12 | -------------------------------------------------------------------------------- /backend/backend/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "ConnectionStrings": { 10 | "local": "Server=.;Database=ResumeManagementDRTS;Trusted_Connection=true;TrustServerCertificate=true;" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /backend/backend/Core/Entities/Company.cs: -------------------------------------------------------------------------------- 1 | using backend.Core.Enums; 2 | 3 | namespace backend.Core.Entities 4 | { 5 | public class Company : BaseEntity 6 | { 7 | public string Name { get; set; } 8 | public CompanySize Size{ get; set; } 9 | 10 | // Relations 11 | public ICollection Jobs { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/pages/jobs/jobs.scss: -------------------------------------------------------------------------------- 1 | @import "../../mixins"; 2 | 3 | .jobs { 4 | .heading { 5 | @include d-flex(row, space-between, center); 6 | width: 100%; 7 | } 8 | } 9 | .add-job { 10 | width: 500px; 11 | max-width: 100%; 12 | @include d-flex(column, flex-start, center); 13 | gap: 1rem; 14 | 15 | * { 16 | color: var(--text-color1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/backend/Core/Dtos/Company/CompanyGetDto.cs: -------------------------------------------------------------------------------- 1 | using backend.Core.Enums; 2 | 3 | namespace backend.Core.Dtos.Company 4 | { 5 | public class CompanyGetDto 6 | { 7 | public long ID { get; set; } 8 | public string Name { get; set; } 9 | public CompanySize Size { get; set; } 10 | public DateTime CreatedAt { get; set; } = DateTime.Now; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/pages/companies/companies.scss: -------------------------------------------------------------------------------- 1 | @import "../../mixins"; 2 | 3 | .comapnies { 4 | .heading { 5 | @include d-flex(row, space-between, center); 6 | width: 100%; 7 | } 8 | } 9 | .add-company { 10 | width: 500px; 11 | max-width: 100%; 12 | @include d-flex(column, flex-start, center); 13 | gap: 1rem; 14 | 15 | * { 16 | color: var(--text-color1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/pages/candidates/candidates.scss: -------------------------------------------------------------------------------- 1 | @import "../../mixins"; 2 | 3 | .candidates { 4 | .heading { 5 | @include d-flex(row, space-between, center); 6 | width: 100%; 7 | } 8 | } 9 | .add-candidate { 10 | width: 500px; 11 | max-width: 100%; 12 | @include d-flex(column, flex-start, center); 13 | gap: 1rem; 14 | 15 | * { 16 | color: var(--text-color1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /backend/backend/Core/Dtos/Candidate/CandidateCreateDto.cs: -------------------------------------------------------------------------------- 1 | namespace backend.Core.Dtos.Candidate 2 | { 3 | public class CandidateCreateDto 4 | { 5 | public string FirstName { get; set; } 6 | public string LastName { get; set; } 7 | public string Email { get; set; } 8 | public string Phone { get; set; } 9 | public string CoverLetter { get; set; } 10 | public long JobId { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /backend/backend/Core/Entities/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace backend.Core.Entities 4 | { 5 | public abstract class BaseEntity 6 | { 7 | [Key] 8 | public long ID { get; set; } 9 | public DateTime CreatedAt { get; set; } = DateTime.Now; 10 | public DateTime UpdatedAt { get; set; } = DateTime.Now; 11 | public bool IsActive { get; set; } = true; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/backend/Core/Entities/Job.cs: -------------------------------------------------------------------------------- 1 | using backend.Core.Enums; 2 | 3 | namespace backend.Core.Entities 4 | { 5 | public class Job : BaseEntity 6 | { 7 | public string Title { get; set; } 8 | public JobLevel Level{ get; set; } 9 | 10 | // Relations 11 | public long CompanyId { get; set; } 12 | public Company Company { get; set; } 13 | 14 | public ICollection Candidates { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /backend/backend/Core/Dtos/Job/JobGetDto.cs: -------------------------------------------------------------------------------- 1 | using backend.Core.Entities; 2 | using backend.Core.Enums; 3 | 4 | namespace backend.Core.Dtos.Job 5 | { 6 | public class JobGetDto 7 | { 8 | public long ID { get; set; } 9 | public string Title { get; set; } 10 | public JobLevel Level { get; set; } 11 | public long CompanyId { get; set; } 12 | public string CompanyName { get; set; } 13 | public DateTime CreatedAt { get; set; } = DateTime.Now; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import ThemeContextProvider from "./context/theme.context"; 5 | import { BrowserRouter } from "react-router-dom"; 6 | import "./global.scss"; 7 | 8 | const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); 9 | root.render( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /backend/backend/Core/Entities/Candidate.cs: -------------------------------------------------------------------------------- 1 | namespace backend.Core.Entities 2 | { 3 | public class Candidate : BaseEntity 4 | { 5 | public string FirstName { get; set; } 6 | public string LastName { get; set; } 7 | public string Email { get; set; } 8 | public string Phone { get; set; } 9 | public string CoverLetter { get; set; } 10 | public string ResumeUrl { get; set; } 11 | 12 | // Relations 13 | 14 | public long JobId { get; set; } 15 | public Job Job { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/backend/Core/Dtos/Candidate/CandidateGetDto.cs: -------------------------------------------------------------------------------- 1 | namespace backend.Core.Dtos.Candidate 2 | { 3 | public class CandidateGetDto 4 | { 5 | public long ID { get; set; } 6 | public string FirstName { get; set; } 7 | public string LastName { get; set; } 8 | public string Email { get; set; } 9 | public string Phone { get; set; } 10 | public string CoverLetter { get; set; } 11 | public string ResumeUrl { get; set; } 12 | public long JobId { get; set; } 13 | public string JobTitle { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/context/theme.context.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useState } from "react"; 2 | 3 | interface IThemeContextInterface { 4 | darkMode: boolean; 5 | toggleDarkMode: () => void; 6 | } 7 | 8 | export const ThemeContext = createContext({ 9 | darkMode: false, 10 | toggleDarkMode: () => {}, 11 | }); 12 | 13 | interface IThemeContextProviderProps { 14 | children: React.ReactNode; 15 | } 16 | 17 | const ThemeContextProvider = ({ children }: IThemeContextProviderProps) => { 18 | const [darkMode, setDarkMode] = useState(false); 19 | 20 | const toggleDarkMode: () => void = () => { 21 | setDarkMode((prevState) => !prevState); 22 | }; 23 | 24 | return {children}; 25 | }; 26 | 27 | export default ThemeContextProvider; 28 | -------------------------------------------------------------------------------- /frontend/src/global.scss: -------------------------------------------------------------------------------- 1 | @import "./mixins"; 2 | 3 | *, 4 | *::before, 5 | *::after { 6 | padding: 0; 7 | margin: 0; 8 | box-sizing: border-box; 9 | font-family: Arial, Helvetica, sans-serif; 10 | } 11 | :root { 12 | --blue: #4864ff; 13 | --bg-color: #fff; 14 | --text-color1: #000; 15 | --text-color2: #555; 16 | } 17 | a { 18 | text-decoration: none; 19 | } 20 | ul { 21 | list-style: none; 22 | } 23 | .wrapper { 24 | width: 100%; 25 | padding: 2rem; 26 | min-height: calc(100vh - 60px); 27 | } 28 | .content { 29 | @include d-flex(column, flex-start, center); 30 | gap: 2rem; 31 | } 32 | .app { 33 | color: var(--text-color1); 34 | background-color: var(--bg-color); 35 | transition: all 0.3s ease-in-out; 36 | 37 | &.dark { 38 | --bg-color: #000; 39 | --text-color1: #fff; 40 | --text-color2: #999; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Resume Management 2 | 3 | ## YouTube Tutorial 4 | https://www.youtube.com/watch?v=AiwzQMupPsU 5 | 6 | ## Solution Technologies 7 | 8 | - Asp.Net Core (.NET 7.0) 9 | - React 18 10 | - TypeScript 11 | - MS SQL SERVER 12 | - Entity Framework Core 13 | 14 | ## Solution Architecture 15 | 16 | 17 | 18 | ## Database Structure 19 | 20 | 21 | 22 | ## We will learn these topics together 23 | 24 | ### On Backend project 25 | 26 | - Entities 27 | - Dtos 28 | - Context 29 | - ORM 30 | - Http Methods 31 | - Swagger 32 | - AutoMapper 33 | - Entities Relationtions 34 | - 1 to Many Relation 35 | 36 | ### On Frontend project 37 | 38 | - Nested Routing 39 | - useState 40 | - useEffect 41 | - useContext 42 | - DarkMode 43 | - Elegant, Beatifull and fully Responsive Navbar 44 | - TypeScript Interfaces 45 | - Dtos 46 | - Axios 47 | - SASS 48 | - Mixin 49 | - Moment -------------------------------------------------------------------------------- /frontend/src/types/global.typing.ts: -------------------------------------------------------------------------------- 1 | export interface ICompany { 2 | id: string; 3 | name: string; 4 | size: string; 5 | createdAt: string; 6 | } 7 | export interface ICreateCompanyDto { 8 | name: string; 9 | size: string; 10 | } 11 | export interface IJob { 12 | id: string; 13 | title: string; 14 | level: string; 15 | companyId: string; 16 | companyName: string; 17 | createdAt: string; 18 | } 19 | export interface ICreateJobDto { 20 | title: string; 21 | level: string; 22 | companyId: string; 23 | } 24 | export interface ICandidate { 25 | id: string; 26 | firstName: string; 27 | lastName: string; 28 | email: string; 29 | phone: string; 30 | coverLetter: string; 31 | resumeUrl: string; 32 | jobId: string; 33 | jobTitle: string; 34 | } 35 | export interface ICreateCandidateDto { 36 | firstName: string; 37 | lastName: string; 38 | email: string; 39 | phone: string; 40 | coverLetter: string; 41 | jobId: string; 42 | } 43 | -------------------------------------------------------------------------------- /backend/backend/Core/AutoMapperConfig/AutoMapperConfigProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using backend.Core.Dtos.Candidate; 3 | using backend.Core.Dtos.Company; 4 | using backend.Core.Dtos.Job; 5 | using backend.Core.Entities; 6 | 7 | namespace backend.Core.AutoMapperConfig 8 | { 9 | public class AutoMapperConfigProfile : Profile 10 | { 11 | public AutoMapperConfigProfile() 12 | { 13 | // Company 14 | CreateMap(); 15 | CreateMap(); 16 | 17 | // Job 18 | CreateMap(); 19 | CreateMap() 20 | .ForMember(dest => dest.CompanyName, opt => opt.MapFrom(src => src.Company.Name)); 21 | 22 | // Candidate 23 | CreateMap(); 24 | CreateMap() 25 | .ForMember(dest => dest.JobTitle, opt => opt.MapFrom(src => src.Job.Title)); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /backend/backend/backend.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | all 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /frontend/src/components/companies/CompaniesGrid.component.tsx: -------------------------------------------------------------------------------- 1 | import "./companies-grid.scss"; 2 | import { Box } from "@mui/material"; 3 | import { DataGrid } from "@mui/x-data-grid"; 4 | import { GridColDef } from "@mui/x-data-grid/models"; 5 | import moment from "moment"; 6 | import React from "react"; 7 | import { ICompany } from "../../types/global.typing"; 8 | 9 | 10 | const column: GridColDef[] = [ 11 | { field: "id", headerName: "ID", width: 100 }, 12 | { field: "name", headerName: "Name", width: 200 }, 13 | { field: "size", headerName: "Size", width: 150 }, 14 | { 15 | field: "createdAt", 16 | headerName: "Creation Time", 17 | width: 200, 18 | renderCell: (params) => moment(params.row.createdAt).format("YYYY-MM-DD"), 19 | }, 20 | ]; 21 | 22 | interface ICompaniesGridProps { 23 | data: ICompany[]; 24 | } 25 | 26 | const CompaniesGrid = ({ data }: ICompaniesGridProps) => { 27 | return ( 28 | 29 | row.id} rowHeight={50} /> 30 | 31 | ); 32 | }; 33 | 34 | export default CompaniesGrid; 35 | -------------------------------------------------------------------------------- /frontend/src/components/jobs/JobsGrid.component.tsx: -------------------------------------------------------------------------------- 1 | import "./jobs-grid.scss"; 2 | import { Box } from "@mui/material"; 3 | import { DataGrid } from "@mui/x-data-grid"; 4 | import { GridColDef } from "@mui/x-data-grid/models"; 5 | import moment from "moment"; 6 | import React from "react"; 7 | import { IJob } from "../../types/global.typing"; 8 | 9 | const column: GridColDef[] = [ 10 | { field: "id", headerName: "ID", width: 100 }, 11 | { field: "title", headerName: "Title", width: 500 }, 12 | { field: "level", headerName: "Level", width: 150 }, 13 | { field: "companyName", headerName: "Company Name", width: 150 }, 14 | { 15 | field: "createdAt", 16 | headerName: "Creation Time", 17 | width: 150, 18 | renderCell: (params) => moment(params.row.createdAt).fromNow(), 19 | }, 20 | ]; 21 | 22 | interface IJobsGridProps { 23 | data: IJob[]; 24 | } 25 | 26 | const JobsGrid = ({ data }: IJobsGridProps) => { 27 | return ( 28 | 29 | row.id} rowHeight={50} /> 30 | 31 | ); 32 | }; 33 | 34 | export default JobsGrid; 35 | -------------------------------------------------------------------------------- /frontend/src/pages/home/Home.page.tsx: -------------------------------------------------------------------------------- 1 | import "./home.scss"; 2 | 3 | const Home = () => { 4 | return ( 5 |
6 |

Welcome To Website

7 |
8 |
9 | 10 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Impedit fugiat iusto explicabo voluptatum, 11 | blanditiis libero eum tempora excepturi inventore labore nesciunt cupiditate commodi beatae. Neque, totam 12 | tempore. Esse itaque vitae, reiciendis dolores provident placeat sequi nam pariatur praesentium quaerat 13 | autem libero quasi dolorem sapiente aliquid odio dolore maxime repellendus vel incidunt minima? Dolore sequi 14 | ea laudantium fuga autem officia, cum fugit rem voluptates, vel quis aperiam consectetur dolor perspiciatis 15 | labore. Blanditiis hic eaque natus accusantium officiis quam accusamus. Aperiam sed ullam in. Nisi inventore 16 | iste est placeat corrupti? Minima, mollitia hic dolorem porro molestiae nulla itaque incidunt veritatis 17 | sunt. Soluta! 18 | 19 |
20 | ); 21 | }; 22 | 23 | export default Home; 24 | -------------------------------------------------------------------------------- /backend/backend.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.33213.308 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "backend", "backend\backend.csproj", "{64F7A085-74C7-4BA2-ACCB-55A897DBFBF1}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {64F7A085-74C7-4BA2-ACCB-55A897DBFBF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {64F7A085-74C7-4BA2-ACCB-55A897DBFBF1}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {64F7A085-74C7-4BA2-ACCB-55A897DBFBF1}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {64F7A085-74C7-4BA2-ACCB-55A897DBFBF1}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {160C4B15-69B2-4622-B35E-3AA6DBF0D77B} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /backend/backend/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:25591", 8 | "sslPort": 44374 9 | } 10 | }, 11 | "profiles": { 12 | "http": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "http://localhost:5115", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "https": { 23 | "commandName": "Project", 24 | "dotnetRunMessages": true, 25 | "launchBrowser": true, 26 | "launchUrl": "swagger", 27 | "applicationUrl": "https://localhost:7129;http://localhost:5115", 28 | "environmentVariables": { 29 | "ASPNETCORE_ENVIRONMENT": "Development" 30 | } 31 | }, 32 | "IIS Express": { 33 | "commandName": "IISExpress", 34 | "launchBrowser": true, 35 | "launchUrl": "swagger", 36 | "environmentVariables": { 37 | "ASPNETCORE_ENVIRONMENT": "Development" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /backend/backend/Program.cs: -------------------------------------------------------------------------------- 1 | using backend.Core.AutoMapperConfig; 2 | using backend.Core.Context; 3 | using Microsoft.EntityFrameworkCore; 4 | using System.Text.Json.Serialization; 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | 8 | // DB Configuration 9 | builder.Services.AddDbContext(options => 10 | { 11 | options.UseSqlServer(builder.Configuration.GetConnectionString("local")); 12 | }); 13 | 14 | // Automapper Configuration 15 | builder.Services.AddAutoMapper(typeof(AutoMapperConfigProfile)); 16 | 17 | builder.Services 18 | .AddControllers() 19 | .AddJsonOptions(options => 20 | { 21 | options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); 22 | }); 23 | 24 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 25 | builder.Services.AddEndpointsApiExplorer(); 26 | builder.Services.AddSwaggerGen(); 27 | 28 | var app = builder.Build(); 29 | 30 | // Configure the HTTP request pipeline. 31 | if (app.Environment.IsDevelopment()) 32 | { 33 | app.UseSwagger(); 34 | app.UseSwaggerUI(); 35 | } 36 | 37 | app.UseCors(options => 38 | { 39 | options 40 | .AllowAnyOrigin() 41 | .AllowAnyMethod() 42 | .AllowAnyHeader(); 43 | }); 44 | 45 | app.UseHttpsRedirection(); 46 | 47 | app.UseAuthorization(); 48 | 49 | app.MapControllers(); 50 | 51 | app.Run(); 52 | -------------------------------------------------------------------------------- /backend/backend/Core/Context/ApplicationDbContext.cs: -------------------------------------------------------------------------------- 1 | using backend.Core.Entities; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace backend.Core.Context 5 | { 6 | public class ApplicationDbContext : DbContext 7 | { 8 | public ApplicationDbContext(DbContextOptions options) : base(options) 9 | { 10 | } 11 | 12 | public DbSet Companies { get; set; } 13 | public DbSet Jobs { get; set; } 14 | public DbSet Candidates { get; set; } 15 | 16 | protected override void OnModelCreating(ModelBuilder modelBuilder) 17 | { 18 | base.OnModelCreating(modelBuilder); 19 | 20 | modelBuilder.Entity() 21 | .HasOne(job => job.Company) 22 | .WithMany(company => company.Jobs) 23 | .HasForeignKey(job => job.CompanyId); 24 | 25 | modelBuilder.Entity() 26 | .HasOne(candidate => candidate.Job) 27 | .WithMany(job => job.Candidates) 28 | .HasForeignKey(candidate => candidate.JobId); 29 | 30 | modelBuilder.Entity() 31 | .Property(company => company.Size) 32 | .HasConversion(); 33 | 34 | modelBuilder.Entity() 35 | .Property(job => job.Level) 36 | .HasConversion(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/react": "^11.10.6", 7 | "@emotion/styled": "^11.10.6", 8 | "@mui/icons-material": "^5.11.11", 9 | "@mui/material": "^5.11.14", 10 | "@mui/x-data-grid": "^6.0.3", 11 | "@testing-library/jest-dom": "^5.16.5", 12 | "@testing-library/react": "^13.4.0", 13 | "@testing-library/user-event": "^13.5.0", 14 | "@types/jest": "^27.5.2", 15 | "@types/node": "^16.18.20", 16 | "@types/react": "^18.0.29", 17 | "@types/react-dom": "^18.0.11", 18 | "axios": "^1.3.4", 19 | "moment": "^2.29.4", 20 | "react": "^18.2.0", 21 | "react-dom": "^18.2.0", 22 | "react-router-dom": "^6.9.0", 23 | "react-scripts": "5.0.1", 24 | "sass": "^1.60.0", 25 | "typescript": "^4.9.5", 26 | "web-vitals": "^2.1.4" 27 | }, 28 | "scripts": { 29 | "start": "react-scripts start", 30 | "build": "react-scripts build", 31 | "test": "react-scripts test", 32 | "eject": "react-scripts eject" 33 | }, 34 | "eslintConfig": { 35 | "extends": [ 36 | "react-app", 37 | "react-app/jest" 38 | ] 39 | }, 40 | "browserslist": { 41 | "production": [ 42 | ">0.2%", 43 | "not dead", 44 | "not op_mini all" 45 | ], 46 | "development": [ 47 | "last 1 chrome version", 48 | "last 1 firefox version", 49 | "last 1 safari version" 50 | ] 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /frontend/src/pages/jobs/Jobs.page.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import "./jobs.scss"; 3 | import httpModule from "../../helpers/http.module"; 4 | import { IJob } from "../../types/global.typing"; 5 | import { Button, CircularProgress } from "@mui/material"; 6 | import { Add } from "@mui/icons-material"; 7 | import { useNavigate } from "react-router-dom"; 8 | import JobsGrid from "../../components/jobs/JobsGrid.component"; 9 | 10 | const Jobs = () => { 11 | const [jobs, setJobs] = useState([]); 12 | const [loading, setLoading] = useState(false); 13 | const redirect = useNavigate(); 14 | 15 | useEffect(() => { 16 | setLoading(true); 17 | httpModule 18 | .get("/Job/Get") 19 | .then((response) => { 20 | setJobs(response.data); 21 | setLoading(false); 22 | }) 23 | .catch((error) => { 24 | alert("Error"); 25 | console.log(error); 26 | setLoading(false); 27 | }); 28 | }, []); 29 | 30 | return ( 31 |
32 |
33 |

Jobs

34 | 37 |
38 | {loading ? : jobs.length === 0 ?

No Job

: } 39 |
40 | ); 41 | }; 42 | 43 | export default Jobs; 44 | -------------------------------------------------------------------------------- /frontend/src/components/candidates/CandidatesGrid.component.tsx: -------------------------------------------------------------------------------- 1 | import './candidates-grid.scss'; 2 | import { Box } from "@mui/material"; 3 | import { DataGrid } from "@mui/x-data-grid"; 4 | import { GridColDef } from "@mui/x-data-grid/models"; 5 | import moment from "moment"; 6 | import React from "react"; 7 | import { baseUrl } from "../../constants/url.constants"; 8 | import { ICandidate } from "../../types/global.typing"; 9 | import { PictureAsPdf } from "@mui/icons-material"; 10 | 11 | const column: GridColDef[] = [ 12 | { field: "id", headerName: "ID", width: 100 }, 13 | { field: "firstName", headerName: "First Name", width: 120 }, 14 | { field: "lastName", headerName: "Last Name", width: 120 }, 15 | { field: "email", headerName: "Email", width: 150 }, 16 | { field: "phone", headerName: "Phone", width: 150 }, 17 | { field: "coverLetter", headerName: "C V", width: 500 }, 18 | 19 | { 20 | field: "resumeUrl", 21 | headerName: "Download", 22 | width: 150, 23 | renderCell: (params) => ( 24 | 25 | 26 | 27 | ), 28 | }, 29 | ]; 30 | 31 | interface ICandidatesGridProps { 32 | data: ICandidate[]; 33 | } 34 | 35 | const CandidatesGrid = ({ data }: ICandidatesGridProps) => { 36 | return ( 37 | 38 | row.id} rowHeight={50} /> 39 | 40 | ); 41 | }; 42 | 43 | export default CandidatesGrid; 44 | -------------------------------------------------------------------------------- /backend/backend/Controllers/JobController.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using backend.Core.Context; 3 | using backend.Core.Dtos.Job; 4 | using backend.Core.Entities; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | namespace backend.Controllers 10 | { 11 | [Route("api/[controller]")] 12 | [ApiController] 13 | public class JobController : ControllerBase 14 | { 15 | private ApplicationDbContext _context { get; } 16 | private IMapper _mapper { get; } 17 | 18 | public JobController(ApplicationDbContext context, IMapper mapper) 19 | { 20 | _context = context; 21 | _mapper = mapper; 22 | } 23 | 24 | // CRUD 25 | 26 | // Create 27 | [HttpPost] 28 | [Route("Create")] 29 | public async Task CreateJob([FromBody] JobCreateDto dto) 30 | { 31 | var newJob = _mapper.Map(dto); 32 | await _context.Jobs.AddAsync(newJob); 33 | await _context.SaveChangesAsync(); 34 | 35 | return Ok("Job Created Successfully"); 36 | } 37 | 38 | // Read 39 | [HttpGet] 40 | [Route("Get")] 41 | public async Task>> GetJobs() 42 | { 43 | var jobs = await _context.Jobs.Include(job => job.Company).OrderByDescending(q => q.CreatedAt).ToListAsync(); 44 | var convertdJobs = _mapper.Map>(jobs); 45 | 46 | return Ok(convertdJobs); 47 | } 48 | 49 | // Read (Get Job By ID) 50 | 51 | // Update 52 | 53 | // Delete 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /backend/backend/Migrations/20230324222318_update-enum-to-string.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace backend.Migrations 6 | { 7 | /// 8 | public partial class updateenumtostring : Migration 9 | { 10 | /// 11 | protected override void Up(MigrationBuilder migrationBuilder) 12 | { 13 | migrationBuilder.AlterColumn( 14 | name: "Level", 15 | table: "Jobs", 16 | type: "nvarchar(max)", 17 | nullable: false, 18 | oldClrType: typeof(int), 19 | oldType: "int"); 20 | 21 | migrationBuilder.AlterColumn( 22 | name: "Size", 23 | table: "Companies", 24 | type: "nvarchar(max)", 25 | nullable: false, 26 | oldClrType: typeof(int), 27 | oldType: "int"); 28 | } 29 | 30 | /// 31 | protected override void Down(MigrationBuilder migrationBuilder) 32 | { 33 | migrationBuilder.AlterColumn( 34 | name: "Level", 35 | table: "Jobs", 36 | type: "int", 37 | nullable: false, 38 | oldClrType: typeof(string), 39 | oldType: "nvarchar(max)"); 40 | 41 | migrationBuilder.AlterColumn( 42 | name: "Size", 43 | table: "Companies", 44 | type: "int", 45 | nullable: false, 46 | oldClrType: typeof(string), 47 | oldType: "nvarchar(max)"); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /frontend/src/pages/candidates/Candidates.page.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import "./candidates.scss"; 3 | import httpModule from "../../helpers/http.module"; 4 | import { ICandidate } from "../../types/global.typing"; 5 | import { Button, CircularProgress } from "@mui/material"; 6 | import { Add } from "@mui/icons-material"; 7 | import { useNavigate } from "react-router-dom"; 8 | import CandidatesGrid from "../../components/candidates/CandidatesGrid.component"; 9 | 10 | const Candidates = () => { 11 | const [candidates, setCandidates] = useState([]); 12 | const [loading, setLoading] = useState(false); 13 | const redirect = useNavigate(); 14 | 15 | useEffect(() => { 16 | setLoading(true); 17 | httpModule 18 | .get("/Candidate/Get") 19 | .then((response) => { 20 | setCandidates(response.data); 21 | setLoading(false); 22 | }) 23 | .catch((error) => { 24 | alert("Error"); 25 | console.log(error); 26 | setLoading(false); 27 | }); 28 | }, []); 29 | 30 | return ( 31 |
32 |
33 |

Candidates

34 | 37 |
38 | {loading ? ( 39 | 40 | ) : candidates.length === 0 ? ( 41 |

No Candidate

42 | ) : ( 43 | 44 | )} 45 |
46 | ); 47 | }; 48 | 49 | export default Candidates; 50 | -------------------------------------------------------------------------------- /frontend/src/pages/companies/Companies.page.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import "./companies.scss"; 3 | import httpModule from "../../helpers/http.module"; 4 | import { ICompany } from "../../types/global.typing"; 5 | import { Button, CircularProgress } from "@mui/material"; 6 | import { Add } from "@mui/icons-material"; 7 | import { useNavigate } from "react-router-dom"; 8 | import CompaniesGrid from "../../components/companies/CompaniesGrid.component"; 9 | 10 | const Companies = () => { 11 | const [companies, setCompanies] = useState([]); 12 | const [loading, setLoading] = useState(false); 13 | const redirect = useNavigate(); 14 | 15 | useEffect(() => { 16 | setLoading(true); 17 | httpModule 18 | .get("/Company/Get") 19 | .then((response) => { 20 | setCompanies(response.data); 21 | setLoading(false); 22 | }) 23 | .catch((error) => { 24 | alert("Error"); 25 | console.log(error); 26 | setLoading(false); 27 | }); 28 | }, []); 29 | 30 | // console.log(companies); 31 | 32 | return ( 33 |
34 |
35 |

Companies

36 | 39 |
40 | {loading ? ( 41 | 42 | ) : companies.length === 0 ? ( 43 |

No Company

44 | ) : ( 45 | 46 | )} 47 |
48 | ); 49 | }; 50 | 51 | export default Companies; 52 | -------------------------------------------------------------------------------- /frontend/src/components/navbar/navbar.scss: -------------------------------------------------------------------------------- 1 | @import "../../mixins"; 2 | 3 | .navbar { 4 | @include d-flex(row, space-between, center); 5 | color: #fff; 6 | background-color: rgb(52, 51, 51); 7 | width: 100%; 8 | height: 60px; 9 | padding: 0 2rem; 10 | 11 | .brand { 12 | flex: 1; 13 | } 14 | 15 | .menu { 16 | ul { 17 | @include d-flex(row, flex-start, center); 18 | gap: 1rem; 19 | 20 | li { 21 | a { 22 | color: #fff; 23 | cursor: pointer; 24 | } 25 | } 26 | } 27 | } 28 | 29 | .hamburger { 30 | display: none; 31 | cursor: pointer; 32 | } 33 | 34 | .toggle { 35 | margin-left: 1rem; 36 | cursor: pointer; 37 | svg { 38 | color: var(--blue); 39 | } 40 | } 41 | } 42 | 43 | @media (max-width: 600px) { 44 | .navbar { 45 | padding: 0 1rem; 46 | 47 | .menu { 48 | @include d-flex(column, flex-start, center); 49 | color: #fff; 50 | background-color: rgb(52, 51, 51); 51 | width: 240px; 52 | height: 100vh; 53 | position: fixed; 54 | top: 0; 55 | left: -240px; 56 | z-index: 100; 57 | transition: all 0.3s ease-in-out; 58 | 59 | &.open { 60 | left: 0; 61 | } 62 | ul { 63 | @include d-flex(column, center, center); 64 | gap: 2rem; 65 | height: 100%; 66 | li { 67 | a { 68 | color: #fff; 69 | cursor: pointer; 70 | } 71 | } 72 | } 73 | } 74 | 75 | .hamburger { 76 | display: block; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /backend/backend/Controllers/CompanyController.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using backend.Core.Context; 3 | using backend.Core.Dtos.Company; 4 | using backend.Core.Entities; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | namespace backend.Controllers 10 | { 11 | [Route("api/[controller]")] 12 | [ApiController] 13 | public class CompanyController : ControllerBase 14 | { 15 | private ApplicationDbContext _context { get; } 16 | private IMapper _mapper { get; } 17 | 18 | public CompanyController(ApplicationDbContext context, IMapper mapper) 19 | { 20 | _context = context; 21 | _mapper = mapper; 22 | } 23 | 24 | // CRUD 25 | 26 | // Create 27 | [HttpPost] 28 | [Route("Create")] 29 | public async Task CreateCompany([FromBody] CompanyCreateDto dto) 30 | { 31 | Company newCompany = _mapper.Map(dto); 32 | await _context.Companies.AddAsync(newCompany); 33 | await _context.SaveChangesAsync(); 34 | 35 | return Ok("Companty Created Successfully"); 36 | } 37 | 38 | // Read 39 | [HttpGet] 40 | [Route("Get")] 41 | public async Task>> GetCompanies() 42 | { 43 | var companies = await _context.Companies.OrderByDescending(q => q.CreatedAt).ToListAsync(); 44 | var convertedCompanies = _mapper.Map>(companies); 45 | 46 | return Ok(convertedCompanies); 47 | } 48 | 49 | // Read (Get Company By ID) 50 | 51 | // Update 52 | 53 | // Delete 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /frontend/src/components/navbar/Navbar.component.tsx: -------------------------------------------------------------------------------- 1 | import { useContext, useState } from "react"; 2 | import "./navbar.scss"; 3 | import { Link } from "react-router-dom"; 4 | import { Menu, LightMode, DarkMode } from "@mui/icons-material"; 5 | import { ToggleButton } from "@mui/material"; 6 | import { ThemeContext } from "../../context/theme.context"; 7 | 8 | const links = [ 9 | { href: "/", label: "Home" }, 10 | { href: "/companies", label: "Companies" }, 11 | { href: "/jobs", label: "Jobs" }, 12 | { href: "/candidates", label: "Candidates" }, 13 | ]; 14 | 15 | const Navbar = () => { 16 | const [open, setOpen] = useState(false); 17 | const { darkMode, toggleDarkMode } = useContext(ThemeContext); 18 | 19 | const ToggleOpenMenu = () => { 20 | setOpen((prevState) => !prevState); 21 | }; 22 | 23 | const menuStyles = open ? "menu open" : "menu"; 24 | 25 | return ( 26 |
27 |
28 | Resume Management 29 |
30 |
31 |
    32 | {links.map((item) => ( 33 |
  • 34 | {item.label} 35 |
  • 36 | ))} 37 |
38 |
39 |
40 | 41 |
42 |
43 | 44 | {darkMode ? : } 45 | 46 |
47 |
48 | ); 49 | }; 50 | 51 | export default Navbar; 52 | -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /frontend/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useContext, lazy, Suspense } from "react"; 2 | import Navbar from "./components/navbar/Navbar.component"; 3 | import { ThemeContext } from "./context/theme.context"; 4 | import { Routes, Route } from "react-router-dom"; 5 | import CustomLinearProgress from "./components/custom-linear-progress/CustomLinearProgress.component"; 6 | 7 | // Imports with Lazy loading 8 | const Home = lazy(() => import("./pages/home/Home.page")); 9 | const Companies = lazy(() => import("./pages/companies/Companies.page")); 10 | const AddCompany = lazy(() => import("./pages/companies/AddCompany.page")); 11 | const Jobs = lazy(() => import("./pages/jobs/Jobs.page")); 12 | const AddJob = lazy(() => import("./pages/jobs/AddJob.page")); 13 | const Candidates = lazy(() => import("./pages/candidates/Candidates.page")); 14 | const AddCandidate = lazy(() => import("./pages/candidates/AddCandidate.page")); 15 | 16 | const App = () => { 17 | const { darkMode } = useContext(ThemeContext); 18 | 19 | const appStyles = darkMode ? "app dark" : "app"; 20 | 21 | return ( 22 |
23 | 24 |
25 | }> 26 | 27 | } /> 28 | 29 | } /> 30 | } /> 31 | 32 | 33 | } /> 34 | } /> 35 | 36 | 37 | } /> 38 | } /> 39 | 40 | 41 | 42 |
43 |
44 | ); 45 | }; 46 | 47 | export default App; 48 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /frontend/src/pages/companies/AddCompany.page.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import "./companies.scss"; 3 | import { ICreateCompanyDto } from "../../types/global.typing"; 4 | import {} from "@mui/material"; 5 | import TextField from "@mui/material/TextField/TextField"; 6 | import FormControl from "@mui/material/FormControl/FormControl"; 7 | import InputLabel from "@mui/material/InputLabel/InputLabel"; 8 | import Select from "@mui/material/Select/Select"; 9 | import MenuItem from "@mui/material/MenuItem/MenuItem"; 10 | import Button from "@mui/material/Button/Button"; 11 | import { useNavigate } from "react-router-dom"; 12 | import httpModule from "../../helpers/http.module"; 13 | 14 | const AddCompany = () => { 15 | const [company, setCompany] = useState({ name: "", size: "" }); 16 | const redirect = useNavigate(); 17 | 18 | const handleClickSaveBtn = () => { 19 | if (company.name === "" || company.size === "") { 20 | alert("Fill all fields"); 21 | return; 22 | } 23 | httpModule 24 | .post("/Company/Create", company) 25 | .then((responst) => redirect("/companies")) 26 | .catch((error) => console.log(error)); 27 | }; 28 | 29 | const handleClickBackBtn = () => { 30 | redirect("/companies"); 31 | }; 32 | 33 | return ( 34 |
35 |
36 |

Add New Company

37 | setCompany({ ...company, name: e.target.value })} 43 | /> 44 | 45 | Company Size 46 | 55 | 56 |
57 | 60 | 63 |
64 |
65 |
66 | ); 67 | }; 68 | 69 | export default AddCompany; 70 | -------------------------------------------------------------------------------- /backend/backend/Controllers/CandidateController.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using backend.Core.Context; 3 | using backend.Core.Dtos.Candidate; 4 | using backend.Core.Entities; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.EntityFrameworkCore; 8 | 9 | namespace backend.Controllers 10 | { 11 | [Route("api/[controller]")] 12 | [ApiController] 13 | public class CandidateController : ControllerBase 14 | { 15 | private ApplicationDbContext _context { get; } 16 | private IMapper _mapper { get; } 17 | 18 | public CandidateController(ApplicationDbContext context, IMapper mapper) 19 | { 20 | _context = context; 21 | _mapper = mapper; 22 | } 23 | 24 | // CRUD 25 | 26 | // Create 27 | [HttpPost] 28 | [Route("Create")] 29 | public async Task CreateCandidate([FromForm] CandidateCreateDto dto, IFormFile pdfFile) 30 | { 31 | // Firt => Save pdf to Server 32 | // Then => save url into our entity 33 | var fiveMegaByte = 5 * 1024 * 1024; 34 | var pdfMimeType = "application/pdf"; 35 | 36 | if (pdfFile.Length > fiveMegaByte || pdfFile.ContentType != pdfMimeType) 37 | { 38 | return BadRequest("File is not valid"); 39 | } 40 | 41 | var resumeUrl = Guid.NewGuid().ToString() + ".pdf"; 42 | var filePath = Path.Combine(Directory.GetCurrentDirectory(), "documents", "pdfs", resumeUrl); 43 | using (var stream = new FileStream(filePath, FileMode.Create)) 44 | { 45 | await pdfFile.CopyToAsync(stream); 46 | } 47 | var newCandidate = _mapper.Map(dto); 48 | newCandidate.ResumeUrl = resumeUrl; 49 | await _context.Candidates.AddAsync(newCandidate); 50 | await _context.SaveChangesAsync(); 51 | 52 | return Ok("Candidate Saved Successfully"); 53 | } 54 | 55 | // Read 56 | [HttpGet] 57 | [Route("Get")] 58 | public async Task>> GetCandidates() 59 | { 60 | var candidates = await _context.Candidates.Include(c => c.Job).OrderByDescending(q => q.CreatedAt).ToListAsync(); 61 | var convertedCandidates = _mapper.Map>(candidates); 62 | 63 | return Ok(convertedCandidates); 64 | } 65 | 66 | // Read (Download Pdf File) 67 | [HttpGet] 68 | [Route("download/{url}")] 69 | public IActionResult DownloadPdfFile(string url) 70 | { 71 | var filePath = Path.Combine(Directory.GetCurrentDirectory(), "documents", "pdfs", url); 72 | 73 | if(!System.IO.File.Exists(filePath)) 74 | { 75 | return NotFound("File Not Found"); 76 | } 77 | 78 | var pdfBytes = System.IO.File.ReadAllBytes(filePath); 79 | var file = File(pdfBytes, "application/pdf", url); 80 | return file; 81 | } 82 | 83 | // Read (Get Candidate By ID) 84 | 85 | // Update 86 | 87 | // Delete 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /frontend/src/pages/jobs/AddJob.page.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import "./jobs.scss"; 3 | import { ICompany, ICreateCompanyDto, ICreateJobDto } from "../../types/global.typing"; 4 | 5 | import TextField from "@mui/material/TextField/TextField"; 6 | import FormControl from "@mui/material/FormControl/FormControl"; 7 | import InputLabel from "@mui/material/InputLabel/InputLabel"; 8 | import Select from "@mui/material/Select/Select"; 9 | import MenuItem from "@mui/material/MenuItem/MenuItem"; 10 | import Button from "@mui/material/Button/Button"; 11 | import { useNavigate } from "react-router-dom"; 12 | import httpModule from "../../helpers/http.module"; 13 | 14 | const levelsArray: string[] = ["Intern", "Junior", "MidLevel", "Senior", "TeamLead", "Cto", "Architect"]; 15 | 16 | const AddJob = () => { 17 | const [job, setJob] = useState({ 18 | title: "", 19 | level: "", 20 | companyId: "", 21 | }); 22 | const [companies, setCompanies] = useState([]); 23 | 24 | const redirect = useNavigate(); 25 | 26 | useEffect(() => { 27 | httpModule 28 | .get("/Company/Get") 29 | .then((response) => { 30 | setCompanies(response.data); 31 | }) 32 | .catch((error) => { 33 | alert("Error"); 34 | console.log(error); 35 | }); 36 | }, []); 37 | 38 | const handleClickSaveBtn = () => { 39 | if (job.title === "" || job.level === "" || job.companyId === "") { 40 | alert("Fill all fields"); 41 | return; 42 | } 43 | httpModule 44 | .post("/Job/Create", job) 45 | .then((responst) => redirect("/jobs")) 46 | .catch((error) => console.log(error)); 47 | }; 48 | 49 | const handleClickBackBtn = () => { 50 | redirect("/jobs"); 51 | }; 52 | 53 | return ( 54 |
55 |
56 |

Add New Job

57 | setJob({ ...job, title: e.target.value })} 63 | /> 64 | 65 | 66 | Job Level 67 | 74 | 75 | 76 | 77 | Company 78 | 89 | 90 | 91 |
92 | 95 | 98 |
99 |
100 |
101 | ); 102 | }; 103 | 104 | export default AddJob; 105 | -------------------------------------------------------------------------------- /backend/backend/Migrations/20230324220612_initila-migration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | #nullable disable 5 | 6 | namespace backend.Migrations 7 | { 8 | /// 9 | public partial class initilamigration : Migration 10 | { 11 | /// 12 | protected override void Up(MigrationBuilder migrationBuilder) 13 | { 14 | migrationBuilder.CreateTable( 15 | name: "Companies", 16 | columns: table => new 17 | { 18 | ID = table.Column(type: "bigint", nullable: false) 19 | .Annotation("SqlServer:Identity", "1, 1"), 20 | Name = table.Column(type: "nvarchar(max)", nullable: false), 21 | Size = table.Column(type: "int", nullable: false), 22 | CreatedAt = table.Column(type: "datetime2", nullable: false), 23 | UpdatedAt = table.Column(type: "datetime2", nullable: false), 24 | IsActive = table.Column(type: "bit", nullable: false) 25 | }, 26 | constraints: table => 27 | { 28 | table.PrimaryKey("PK_Companies", x => x.ID); 29 | }); 30 | 31 | migrationBuilder.CreateTable( 32 | name: "Jobs", 33 | columns: table => new 34 | { 35 | ID = table.Column(type: "bigint", nullable: false) 36 | .Annotation("SqlServer:Identity", "1, 1"), 37 | Title = table.Column(type: "nvarchar(max)", nullable: false), 38 | Level = table.Column(type: "int", nullable: false), 39 | CompanyId = table.Column(type: "bigint", nullable: false), 40 | CreatedAt = table.Column(type: "datetime2", nullable: false), 41 | UpdatedAt = table.Column(type: "datetime2", nullable: false), 42 | IsActive = table.Column(type: "bit", nullable: false) 43 | }, 44 | constraints: table => 45 | { 46 | table.PrimaryKey("PK_Jobs", x => x.ID); 47 | table.ForeignKey( 48 | name: "FK_Jobs_Companies_CompanyId", 49 | column: x => x.CompanyId, 50 | principalTable: "Companies", 51 | principalColumn: "ID", 52 | onDelete: ReferentialAction.Cascade); 53 | }); 54 | 55 | migrationBuilder.CreateTable( 56 | name: "Candidates", 57 | columns: table => new 58 | { 59 | ID = table.Column(type: "bigint", nullable: false) 60 | .Annotation("SqlServer:Identity", "1, 1"), 61 | FirstName = table.Column(type: "nvarchar(max)", nullable: false), 62 | LastName = table.Column(type: "nvarchar(max)", nullable: false), 63 | Email = table.Column(type: "nvarchar(max)", nullable: false), 64 | Phone = table.Column(type: "nvarchar(max)", nullable: false), 65 | CoverLetter = table.Column(type: "nvarchar(max)", nullable: false), 66 | ResumeUrl = table.Column(type: "nvarchar(max)", nullable: false), 67 | JobId = table.Column(type: "bigint", nullable: false), 68 | CreatedAt = table.Column(type: "datetime2", nullable: false), 69 | UpdatedAt = table.Column(type: "datetime2", nullable: false), 70 | IsActive = table.Column(type: "bit", nullable: false) 71 | }, 72 | constraints: table => 73 | { 74 | table.PrimaryKey("PK_Candidates", x => x.ID); 75 | table.ForeignKey( 76 | name: "FK_Candidates_Jobs_JobId", 77 | column: x => x.JobId, 78 | principalTable: "Jobs", 79 | principalColumn: "ID", 80 | onDelete: ReferentialAction.Cascade); 81 | }); 82 | 83 | migrationBuilder.CreateIndex( 84 | name: "IX_Candidates_JobId", 85 | table: "Candidates", 86 | column: "JobId"); 87 | 88 | migrationBuilder.CreateIndex( 89 | name: "IX_Jobs_CompanyId", 90 | table: "Jobs", 91 | column: "CompanyId"); 92 | } 93 | 94 | /// 95 | protected override void Down(MigrationBuilder migrationBuilder) 96 | { 97 | migrationBuilder.DropTable( 98 | name: "Candidates"); 99 | 100 | migrationBuilder.DropTable( 101 | name: "Jobs"); 102 | 103 | migrationBuilder.DropTable( 104 | name: "Companies"); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /frontend/src/pages/candidates/AddCandidate.page.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import "./candidates.scss"; 3 | import { ICompany, ICreateCandidateDto, ICreateCompanyDto, ICreateJobDto, IJob } from "../../types/global.typing"; 4 | 5 | import TextField from "@mui/material/TextField/TextField"; 6 | import FormControl from "@mui/material/FormControl/FormControl"; 7 | import InputLabel from "@mui/material/InputLabel/InputLabel"; 8 | import Select from "@mui/material/Select/Select"; 9 | import MenuItem from "@mui/material/MenuItem/MenuItem"; 10 | import Button from "@mui/material/Button/Button"; 11 | import { useNavigate } from "react-router-dom"; 12 | import httpModule from "../../helpers/http.module"; 13 | 14 | const AddCandidate = () => { 15 | const [candidate, setCandidate] = useState({ 16 | firstName: "", 17 | lastName: "", 18 | email: "", 19 | phone: "", 20 | coverLetter: "", 21 | jobId: "", 22 | }); 23 | const [jobs, setJobs] = useState([]); 24 | const [pdfFile, setPdfFile] = useState(); 25 | 26 | const redirect = useNavigate(); 27 | 28 | useEffect(() => { 29 | httpModule 30 | .get("/Job/Get") 31 | .then((response) => { 32 | setJobs(response.data); 33 | }) 34 | .catch((error) => { 35 | alert("Error"); 36 | console.log(error); 37 | }); 38 | }, []); 39 | 40 | const handleClickSaveBtn = () => { 41 | if ( 42 | candidate.firstName === "" || 43 | candidate.lastName === "" || 44 | candidate.email === "" || 45 | candidate.phone === "" || 46 | candidate.coverLetter === "" || 47 | candidate.jobId === "" || 48 | !pdfFile 49 | ) { 50 | alert("Fill all fields"); 51 | return; 52 | } 53 | const newCandidateFormData = new FormData(); 54 | newCandidateFormData.append("firstName", candidate.firstName); 55 | newCandidateFormData.append("lastName", candidate.lastName); 56 | newCandidateFormData.append("email", candidate.email); 57 | newCandidateFormData.append("phone", candidate.phone); 58 | newCandidateFormData.append("coverLetter", candidate.coverLetter); 59 | newCandidateFormData.append("jobId", candidate.jobId); 60 | newCandidateFormData.append("pdfFile", pdfFile); 61 | httpModule 62 | .post("/Candidate/Create", newCandidateFormData) 63 | .then((responst) => redirect("/candidates")) 64 | .catch((error) => console.log(error)); 65 | }; 66 | 67 | const handleClickBackBtn = () => { 68 | redirect("/candidates"); 69 | }; 70 | 71 | return ( 72 |
73 |
74 |

Add New Candidate

75 | 76 | Job 77 | 88 | 89 | setCandidate({ ...candidate, firstName: e.target.value })} 95 | /> 96 | setCandidate({ ...candidate, lastName: e.target.value })} 102 | /> 103 | setCandidate({ ...candidate, email: e.target.value })} 109 | /> 110 | setCandidate({ ...candidate, phone: e.target.value })} 116 | /> 117 | setCandidate({ ...candidate, coverLetter: e.target.value })} 123 | multiline 124 | /> 125 | setPdfFile(event.target.files ? event.target.files[0] : null)} /> 126 | 127 |
128 | 131 | 134 |
135 |
136 |
137 | ); 138 | }; 139 | 140 | export default AddCandidate; 141 | -------------------------------------------------------------------------------- /backend/backend/Migrations/ApplicationDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | using backend.Core.Context; 8 | 9 | #nullable disable 10 | 11 | namespace backend.Migrations 12 | { 13 | [DbContext(typeof(ApplicationDbContext))] 14 | partial class ApplicationDbContextModelSnapshot : ModelSnapshot 15 | { 16 | protected override void BuildModel(ModelBuilder modelBuilder) 17 | { 18 | #pragma warning disable 612, 618 19 | modelBuilder 20 | .HasAnnotation("ProductVersion", "7.0.4") 21 | .HasAnnotation("Relational:MaxIdentifierLength", 128); 22 | 23 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); 24 | 25 | modelBuilder.Entity("backend.Core.Entities.Candidate", b => 26 | { 27 | b.Property("ID") 28 | .ValueGeneratedOnAdd() 29 | .HasColumnType("bigint"); 30 | 31 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); 32 | 33 | b.Property("CoverLetter") 34 | .IsRequired() 35 | .HasColumnType("nvarchar(max)"); 36 | 37 | b.Property("CreatedAt") 38 | .HasColumnType("datetime2"); 39 | 40 | b.Property("Email") 41 | .IsRequired() 42 | .HasColumnType("nvarchar(max)"); 43 | 44 | b.Property("FirstName") 45 | .IsRequired() 46 | .HasColumnType("nvarchar(max)"); 47 | 48 | b.Property("IsActive") 49 | .HasColumnType("bit"); 50 | 51 | b.Property("JobId") 52 | .HasColumnType("bigint"); 53 | 54 | b.Property("LastName") 55 | .IsRequired() 56 | .HasColumnType("nvarchar(max)"); 57 | 58 | b.Property("Phone") 59 | .IsRequired() 60 | .HasColumnType("nvarchar(max)"); 61 | 62 | b.Property("ResumeUrl") 63 | .IsRequired() 64 | .HasColumnType("nvarchar(max)"); 65 | 66 | b.Property("UpdatedAt") 67 | .HasColumnType("datetime2"); 68 | 69 | b.HasKey("ID"); 70 | 71 | b.HasIndex("JobId"); 72 | 73 | b.ToTable("Candidates"); 74 | }); 75 | 76 | modelBuilder.Entity("backend.Core.Entities.Company", b => 77 | { 78 | b.Property("ID") 79 | .ValueGeneratedOnAdd() 80 | .HasColumnType("bigint"); 81 | 82 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); 83 | 84 | b.Property("CreatedAt") 85 | .HasColumnType("datetime2"); 86 | 87 | b.Property("IsActive") 88 | .HasColumnType("bit"); 89 | 90 | b.Property("Name") 91 | .IsRequired() 92 | .HasColumnType("nvarchar(max)"); 93 | 94 | b.Property("Size") 95 | .IsRequired() 96 | .HasColumnType("nvarchar(max)"); 97 | 98 | b.Property("UpdatedAt") 99 | .HasColumnType("datetime2"); 100 | 101 | b.HasKey("ID"); 102 | 103 | b.ToTable("Companies"); 104 | }); 105 | 106 | modelBuilder.Entity("backend.Core.Entities.Job", b => 107 | { 108 | b.Property("ID") 109 | .ValueGeneratedOnAdd() 110 | .HasColumnType("bigint"); 111 | 112 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); 113 | 114 | b.Property("CompanyId") 115 | .HasColumnType("bigint"); 116 | 117 | b.Property("CreatedAt") 118 | .HasColumnType("datetime2"); 119 | 120 | b.Property("IsActive") 121 | .HasColumnType("bit"); 122 | 123 | b.Property("Level") 124 | .IsRequired() 125 | .HasColumnType("nvarchar(max)"); 126 | 127 | b.Property("Title") 128 | .IsRequired() 129 | .HasColumnType("nvarchar(max)"); 130 | 131 | b.Property("UpdatedAt") 132 | .HasColumnType("datetime2"); 133 | 134 | b.HasKey("ID"); 135 | 136 | b.HasIndex("CompanyId"); 137 | 138 | b.ToTable("Jobs"); 139 | }); 140 | 141 | modelBuilder.Entity("backend.Core.Entities.Candidate", b => 142 | { 143 | b.HasOne("backend.Core.Entities.Job", "Job") 144 | .WithMany("Candidates") 145 | .HasForeignKey("JobId") 146 | .OnDelete(DeleteBehavior.Cascade) 147 | .IsRequired(); 148 | 149 | b.Navigation("Job"); 150 | }); 151 | 152 | modelBuilder.Entity("backend.Core.Entities.Job", b => 153 | { 154 | b.HasOne("backend.Core.Entities.Company", "Company") 155 | .WithMany("Jobs") 156 | .HasForeignKey("CompanyId") 157 | .OnDelete(DeleteBehavior.Cascade) 158 | .IsRequired(); 159 | 160 | b.Navigation("Company"); 161 | }); 162 | 163 | modelBuilder.Entity("backend.Core.Entities.Company", b => 164 | { 165 | b.Navigation("Jobs"); 166 | }); 167 | 168 | modelBuilder.Entity("backend.Core.Entities.Job", b => 169 | { 170 | b.Navigation("Candidates"); 171 | }); 172 | #pragma warning restore 612, 618 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /backend/backend/Migrations/20230324220612_initila-migration.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using backend.Core.Context; 9 | 10 | #nullable disable 11 | 12 | namespace backend.Migrations 13 | { 14 | [DbContext(typeof(ApplicationDbContext))] 15 | [Migration("20230324220612_initila-migration")] 16 | partial class initilamigration 17 | { 18 | /// 19 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 20 | { 21 | #pragma warning disable 612, 618 22 | modelBuilder 23 | .HasAnnotation("ProductVersion", "7.0.4") 24 | .HasAnnotation("Relational:MaxIdentifierLength", 128); 25 | 26 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); 27 | 28 | modelBuilder.Entity("backend.Core.Entities.Candidate", b => 29 | { 30 | b.Property("ID") 31 | .ValueGeneratedOnAdd() 32 | .HasColumnType("bigint"); 33 | 34 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); 35 | 36 | b.Property("CoverLetter") 37 | .IsRequired() 38 | .HasColumnType("nvarchar(max)"); 39 | 40 | b.Property("CreatedAt") 41 | .HasColumnType("datetime2"); 42 | 43 | b.Property("Email") 44 | .IsRequired() 45 | .HasColumnType("nvarchar(max)"); 46 | 47 | b.Property("FirstName") 48 | .IsRequired() 49 | .HasColumnType("nvarchar(max)"); 50 | 51 | b.Property("IsActive") 52 | .HasColumnType("bit"); 53 | 54 | b.Property("JobId") 55 | .HasColumnType("bigint"); 56 | 57 | b.Property("LastName") 58 | .IsRequired() 59 | .HasColumnType("nvarchar(max)"); 60 | 61 | b.Property("Phone") 62 | .IsRequired() 63 | .HasColumnType("nvarchar(max)"); 64 | 65 | b.Property("ResumeUrl") 66 | .IsRequired() 67 | .HasColumnType("nvarchar(max)"); 68 | 69 | b.Property("UpdatedAt") 70 | .HasColumnType("datetime2"); 71 | 72 | b.HasKey("ID"); 73 | 74 | b.HasIndex("JobId"); 75 | 76 | b.ToTable("Candidates"); 77 | }); 78 | 79 | modelBuilder.Entity("backend.Core.Entities.Company", b => 80 | { 81 | b.Property("ID") 82 | .ValueGeneratedOnAdd() 83 | .HasColumnType("bigint"); 84 | 85 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); 86 | 87 | b.Property("CreatedAt") 88 | .HasColumnType("datetime2"); 89 | 90 | b.Property("IsActive") 91 | .HasColumnType("bit"); 92 | 93 | b.Property("Name") 94 | .IsRequired() 95 | .HasColumnType("nvarchar(max)"); 96 | 97 | b.Property("Size") 98 | .HasColumnType("int"); 99 | 100 | b.Property("UpdatedAt") 101 | .HasColumnType("datetime2"); 102 | 103 | b.HasKey("ID"); 104 | 105 | b.ToTable("Companies"); 106 | }); 107 | 108 | modelBuilder.Entity("backend.Core.Entities.Job", b => 109 | { 110 | b.Property("ID") 111 | .ValueGeneratedOnAdd() 112 | .HasColumnType("bigint"); 113 | 114 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); 115 | 116 | b.Property("CompanyId") 117 | .HasColumnType("bigint"); 118 | 119 | b.Property("CreatedAt") 120 | .HasColumnType("datetime2"); 121 | 122 | b.Property("IsActive") 123 | .HasColumnType("bit"); 124 | 125 | b.Property("Level") 126 | .HasColumnType("int"); 127 | 128 | b.Property("Title") 129 | .IsRequired() 130 | .HasColumnType("nvarchar(max)"); 131 | 132 | b.Property("UpdatedAt") 133 | .HasColumnType("datetime2"); 134 | 135 | b.HasKey("ID"); 136 | 137 | b.HasIndex("CompanyId"); 138 | 139 | b.ToTable("Jobs"); 140 | }); 141 | 142 | modelBuilder.Entity("backend.Core.Entities.Candidate", b => 143 | { 144 | b.HasOne("backend.Core.Entities.Job", "Job") 145 | .WithMany("Candidates") 146 | .HasForeignKey("JobId") 147 | .OnDelete(DeleteBehavior.Cascade) 148 | .IsRequired(); 149 | 150 | b.Navigation("Job"); 151 | }); 152 | 153 | modelBuilder.Entity("backend.Core.Entities.Job", b => 154 | { 155 | b.HasOne("backend.Core.Entities.Company", "Company") 156 | .WithMany("Jobs") 157 | .HasForeignKey("CompanyId") 158 | .OnDelete(DeleteBehavior.Cascade) 159 | .IsRequired(); 160 | 161 | b.Navigation("Company"); 162 | }); 163 | 164 | modelBuilder.Entity("backend.Core.Entities.Company", b => 165 | { 166 | b.Navigation("Jobs"); 167 | }); 168 | 169 | modelBuilder.Entity("backend.Core.Entities.Job", b => 170 | { 171 | b.Navigation("Candidates"); 172 | }); 173 | #pragma warning restore 612, 618 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /backend/backend/Migrations/20230324222318_update-enum-to-string.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Metadata; 6 | using Microsoft.EntityFrameworkCore.Migrations; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | using backend.Core.Context; 9 | 10 | #nullable disable 11 | 12 | namespace backend.Migrations 13 | { 14 | [DbContext(typeof(ApplicationDbContext))] 15 | [Migration("20230324222318_update-enum-to-string")] 16 | partial class updateenumtostring 17 | { 18 | /// 19 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 20 | { 21 | #pragma warning disable 612, 618 22 | modelBuilder 23 | .HasAnnotation("ProductVersion", "7.0.4") 24 | .HasAnnotation("Relational:MaxIdentifierLength", 128); 25 | 26 | SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); 27 | 28 | modelBuilder.Entity("backend.Core.Entities.Candidate", b => 29 | { 30 | b.Property("ID") 31 | .ValueGeneratedOnAdd() 32 | .HasColumnType("bigint"); 33 | 34 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); 35 | 36 | b.Property("CoverLetter") 37 | .IsRequired() 38 | .HasColumnType("nvarchar(max)"); 39 | 40 | b.Property("CreatedAt") 41 | .HasColumnType("datetime2"); 42 | 43 | b.Property("Email") 44 | .IsRequired() 45 | .HasColumnType("nvarchar(max)"); 46 | 47 | b.Property("FirstName") 48 | .IsRequired() 49 | .HasColumnType("nvarchar(max)"); 50 | 51 | b.Property("IsActive") 52 | .HasColumnType("bit"); 53 | 54 | b.Property("JobId") 55 | .HasColumnType("bigint"); 56 | 57 | b.Property("LastName") 58 | .IsRequired() 59 | .HasColumnType("nvarchar(max)"); 60 | 61 | b.Property("Phone") 62 | .IsRequired() 63 | .HasColumnType("nvarchar(max)"); 64 | 65 | b.Property("ResumeUrl") 66 | .IsRequired() 67 | .HasColumnType("nvarchar(max)"); 68 | 69 | b.Property("UpdatedAt") 70 | .HasColumnType("datetime2"); 71 | 72 | b.HasKey("ID"); 73 | 74 | b.HasIndex("JobId"); 75 | 76 | b.ToTable("Candidates"); 77 | }); 78 | 79 | modelBuilder.Entity("backend.Core.Entities.Company", b => 80 | { 81 | b.Property("ID") 82 | .ValueGeneratedOnAdd() 83 | .HasColumnType("bigint"); 84 | 85 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); 86 | 87 | b.Property("CreatedAt") 88 | .HasColumnType("datetime2"); 89 | 90 | b.Property("IsActive") 91 | .HasColumnType("bit"); 92 | 93 | b.Property("Name") 94 | .IsRequired() 95 | .HasColumnType("nvarchar(max)"); 96 | 97 | b.Property("Size") 98 | .IsRequired() 99 | .HasColumnType("nvarchar(max)"); 100 | 101 | b.Property("UpdatedAt") 102 | .HasColumnType("datetime2"); 103 | 104 | b.HasKey("ID"); 105 | 106 | b.ToTable("Companies"); 107 | }); 108 | 109 | modelBuilder.Entity("backend.Core.Entities.Job", b => 110 | { 111 | b.Property("ID") 112 | .ValueGeneratedOnAdd() 113 | .HasColumnType("bigint"); 114 | 115 | SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); 116 | 117 | b.Property("CompanyId") 118 | .HasColumnType("bigint"); 119 | 120 | b.Property("CreatedAt") 121 | .HasColumnType("datetime2"); 122 | 123 | b.Property("IsActive") 124 | .HasColumnType("bit"); 125 | 126 | b.Property("Level") 127 | .IsRequired() 128 | .HasColumnType("nvarchar(max)"); 129 | 130 | b.Property("Title") 131 | .IsRequired() 132 | .HasColumnType("nvarchar(max)"); 133 | 134 | b.Property("UpdatedAt") 135 | .HasColumnType("datetime2"); 136 | 137 | b.HasKey("ID"); 138 | 139 | b.HasIndex("CompanyId"); 140 | 141 | b.ToTable("Jobs"); 142 | }); 143 | 144 | modelBuilder.Entity("backend.Core.Entities.Candidate", b => 145 | { 146 | b.HasOne("backend.Core.Entities.Job", "Job") 147 | .WithMany("Candidates") 148 | .HasForeignKey("JobId") 149 | .OnDelete(DeleteBehavior.Cascade) 150 | .IsRequired(); 151 | 152 | b.Navigation("Job"); 153 | }); 154 | 155 | modelBuilder.Entity("backend.Core.Entities.Job", b => 156 | { 157 | b.HasOne("backend.Core.Entities.Company", "Company") 158 | .WithMany("Jobs") 159 | .HasForeignKey("CompanyId") 160 | .OnDelete(DeleteBehavior.Cascade) 161 | .IsRequired(); 162 | 163 | b.Navigation("Company"); 164 | }); 165 | 166 | modelBuilder.Entity("backend.Core.Entities.Company", b => 167 | { 168 | b.Navigation("Jobs"); 169 | }); 170 | 171 | modelBuilder.Entity("backend.Core.Entities.Job", b => 172 | { 173 | b.Navigation("Candidates"); 174 | }); 175 | #pragma warning restore 612, 618 176 | } 177 | } 178 | } 179 | --------------------------------------------------------------------------------