Merge pull request 'working' (#8) from working into main
Docker Build and Release Upload / build (push) Successful in 1m21s

Reviewed-on: #8
This commit was merged in pull request #8.
This commit is contained in:
2025-07-31 04:04:19 +00:00
30 changed files with 473 additions and 303 deletions
+1
View File
@@ -22,6 +22,7 @@
},
"args": [
"build",
"--configuration=development",
"--base-href=http://localhost:5000"
],
"problemMatcher": "$msCompile"
+6
View File
@@ -36,3 +36,9 @@ Task:
Point all requests after auth to the correct regional server. -> Currently only Mistox-West exists
CompanyConnect | need to lookup company before making a new one
Finish Auth setup
Make sure autorenew works
Jobs/editor w/ Querystring JobID=# is not implimented yet
Company -> Edit employees not implimented yet
-19
View File
@@ -1,25 +1,6 @@
CREATE DATABASE IF NOT EXISTS `boredcareers`;
USE `boredcareers`;
-- Account Section
CREATE TABLE IF NOT EXISTS `Account` (
`ID` int NOT NULL AUTO_INCREMENT,
`UserName` varchar(60) NOT NULL,
`Email` varchar(255) NOT NULL,
`EmailVerified` boolean DEFAULT 0,
`PasswordHash` char(60) DEFAULT NULL,
`FailedPasswordLock` boolean DEFAULT 0,
`PasswordAttempts` int(11) DEFAULT NULL,
`CurrentPasswordAttempts` int(11) DEFAULT NULL,
`Role` varchar(45) DEFAULT NULL,
`EmailToken` varchar(45) DEFAULT NULL,
`DataServer` varchar(200) DEFAULT NULL,
UNIQUE(`Email`),
UNIQUE(`UserName`),
PRIMARY KEY (`ID`)
) AUTO_INCREMENT=1;
-- Resume Section
CREATE TABLE IF NOT EXISTS `Resume` (
+8 -3
View File
@@ -1,19 +1,24 @@
<div class="top-bar">
<div class="top-bar-buttons">
<a #homeLink class="nav-button" routerLink="">HOME</a>
<a #jobsLink class="nav-button" routerLink="/jobs">JOB BOARD</a>
<a #resumesLink class="nav-button" routerLink="/resumes">RESUMES</a>
<a #companiesLink class="nav-button" routerLink="/company">COMPANIES</a>
</div>
<a class="top-bar-logo" routerLink="">
<img class="top-bar-logo" style="margin: 0;" src="img/logo-full.png" />
</a>
<div *ngIf="auth.isLoggedIn" class="top-bar-buttons flex-right">
<a class="nav-button nav-button-login" routerLink="/account/settings"><span>{{ auth.loggedInUser.userName.toUpperCase() }}</span></a>
<a class="nav-button nav-button-login" href="https://auth.mistox.com/"><span>{{ auth.loggedInUser.userName.toUpperCase() }}</span></a>
<a class="nav-button nav-button-login" href="/api/account/logout"><span>LOGOUT</span></a>
</div>
<div *ngIf="!auth.isLoggedIn" class="top-bar-buttons flex-right">
<!-- Testing Login -->
<a *ngIf="devMode" class="nav-button nav-button-login" href="https://auth.mistox.com/account/login?returnURL=http://localhost:5000/"><span>Testing Login</span></a>
<!-- Testing Login -->
<a class="nav-button nav-button-login" href="https://auth.mistox.com/account/login?returnURL=https://boredcareers.com/"><span>LOGIN</span></a>
<a class="nav-button nav-button-login" routerLink="/account/register"><span>REGISTER</span></a>
<a class="nav-button nav-button-login" href="https://auth.mistox.com/account/register?returnURL=https://boredcareers.com/"><span>REGISTER</span></a>
</div>
</div>
<div class="content">
+10 -4
View File
@@ -5,23 +5,29 @@ import { ContactComponent } from './pages/legal/contact/contact.component';
import { PrivacyComponent } from './pages/legal/privacy/privacy.component';
import { JobsComponent } from './pages/main/jobs/jobs.component';
import { ResumesComponent } from './pages/main/resumes/resumes.component';
import { JobNewComponent } from './pages/main/jobs/new/jobnew.component';
import { JobEditComponent } from './pages/main/jobs/edit/jobedit.component';
import { JobEditorComponent } from './pages/main/jobs/editor/jobeditor.component';
import { CompanyConnectComponent } from './pages/main/company/connect/companyconnect.component';
import { JobViewerComponent } from './pages/main/jobs/viewer/jobviewer.component';
import { CompanyJobsComponent } from './pages/main/company/jobs/jobs.component';
import { CompanyComponent } from './pages/main/company/company.component';
export const routes: Routes = [
// Home
{ path: "", component: HomeComponent },
// Resumes
{ path: "resumes", component: ResumesComponent },
// Jobs
{ path: "jobs", component: JobsComponent },
{ path: "jobs/new", component: JobNewComponent },
{ path: "jobs/edit", component: JobEditComponent },
{ path: "jobs/editor", component: JobEditorComponent },
{ path: "jobs/viewer", component: JobViewerComponent },
// Company
{ path: "company", component: CompanyComponent },
{ path: "company/connect", component: CompanyConnectComponent },
{ path: "company/jobs", component: CompanyJobsComponent },
// Legal
{ path: "about", component: AboutComponent },
+8 -2
View File
@@ -3,6 +3,7 @@ import { Router, RouterModule, RouterOutlet, ActivatedRoute } from '@angular/rou
import { Authentication } from './services/Authentication';
import { CommonModule, Location } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { isDevMode } from '@angular/core';
@Component({
selector: 'app-root',
@@ -12,11 +13,16 @@ import { HttpClient } from '@angular/common/http';
})
export class App {
@ViewChild('homeLink') homeLink!: ElementRef<HTMLAnchorElement>;
@ViewChild('companiesLink') companiesLink!: ElementRef<HTMLAnchorElement>;
@ViewChild('jobsLink') jobLink!: ElementRef<HTMLAnchorElement>;
@ViewChild('resumesLink') resumeLink!: ElementRef<HTMLAnchorElement>;
devMode: boolean = false;
constructor( private http: HttpClient, public auth: Authentication, private router: Router, private route: ActivatedRoute, private location: Location){
this.devMode = isDevMode();
this.route.queryParams.subscribe(params => {
const loginToken = params['LoginToken'];
@@ -43,7 +49,7 @@ export class App {
}
ngAfterViewInit(){
let ViewLinks = [ this.homeLink, this.resumeLink, this.jobLink ];
let ViewLinks = [ this.companiesLink, this.resumeLink, this.jobLink ];
ViewLinks.forEach(link => {
if (new URL(link.nativeElement.href).pathname === new URL(window.location.href).pathname){
link.nativeElement.classList.add("active");
+2 -2
View File
@@ -1,5 +1,5 @@
export class Company {
public id: number = 0;
public id: number = -1;
public name: string = "";
public email: string = "";
public emailVerified: boolean = false;
@@ -14,7 +14,7 @@ export class Company {
}
export class Employee {
public id: number = 0;
public id: number = -1;
public accountID: number = 0;
public company: Company = new Company;
}
+1 -1
View File
@@ -1,5 +1,5 @@
export class JobListing {
public id: number = 0;
public id: number = -1;
public companyID: number = 0;
public title: string = "";
public postalCode: string = "";
+11 -11
View File
@@ -1,5 +1,5 @@
export class Resume {
public id: number = 0;
public id: number = -1;
public accountID: number = 0;
public name: string = "";
public field: string = "";
@@ -20,7 +20,7 @@ export class Resume {
}
export class ResumeExperience {
id: number = 0;
id: number = -1;
resumeID: number = 0;
jobTitle: string = "";
company: string = "";
@@ -35,14 +35,14 @@ export class ResumeExperience {
}
export class ResumeExperienceBullet {
id: number = 0;
id: number = -1;
resumeID: number = 0;
resumeExperienceID: number = 0;
jobFunction: string = "";
}
export class ResumeMilitary {
id: number = 0;
id: number = -1;
resumeID: number = 0;
country: string = "";
rank: string = "";
@@ -53,7 +53,7 @@ export class ResumeMilitary {
}
export class ResumeMilitaryBullet {
id: number = 0;
id: number = -1;
resumeID: number = 0;
resumeMilitaryID: number = 0;
achievement: string = "";
@@ -61,7 +61,7 @@ export class ResumeMilitaryBullet {
}
export class ResumeEducation {
id: number = 0;
id: number = -1;
resumeID: number = 0;
degreeType: string = "";
degreeField: string = "";
@@ -76,21 +76,21 @@ export class ResumeEducation {
}
export class ResumeSkill {
id: number = 0; // PK
resumeID: number = 0; // FK
id: number = -1;
resumeID: number = 0;
name: string = "";
description: string = "";
}
export class ResumeLanguage {
id: number = 0;
id: number = -1;
resumeID: number = 0;
language: string = "";
proficiency: string = "";
}
export class ResumeCertification {
id: number = 0;
id: number = -1;
resumeID: number = 0;
name: string = "";
verificationURL: string = "";
@@ -98,7 +98,7 @@ export class ResumeCertification {
}
export class ResumeProject {
id: number = 0;
id: number = -1;
resumeID: number = 0;
name: string = "";
url: string = "";
@@ -0,0 +1,33 @@
button {
height: 45px;
border-radius: 5px;
margin: 10px;
text-align: center;
padding: 15px 20px;
transition: 0.5s;
background-color: #00000000;
border: 1px solid var(--Mistox-Black);
color: var(--Mistox-Black);
text-decoration: none;
font: inherit;
}
button:hover {
background-color: #00000044;
color: var(--Mistox-Light);
}
.top-bar {
width: 100%;
height: 60px;
}
.content-frame {
background-color: #3c3c3c;
width: calc(100% - 40px);
height: calc(100vh - 400px);
border-radius: 20px;
margin: 10px;
overflow: scroll;
padding: 10px;
}
@@ -0,0 +1,20 @@
<div class="top-bar">
<button *ngFor="let company of Employers" (click)="changeSelectedCompany(company.company.id)">{{ company.company.name.toUpperCase() }}</button>
<button routerLink="/company/connect" >CONNECT A COMPANY</button>
</div>
<div class="content-frame">
<div *ngIf="Comp != null">
<h1>{{ Comp.name }}</h1>
<h1>{{ Comp.email }}</h1>
<h1>{{ Comp.emailVerified }}</h1>
<h1>{{ Comp.websiteURL }}</h1>
<h1>{{ Comp.logoURL }}</h1>
<h1>{{ Comp.phone }}</h1>
<h1>{{ Comp.postalCode }}</h1>
<h1>{{ Comp.country }}</h1>
<h1>{{ Comp.stateOrRegion }}</h1>
<h1>{{ Comp.city }}</h1>
<h1>{{ Comp.description }}</h1>
<button routerLink="/company/jobs" [queryParams]="{ CompanyID: Comp.id }" >ACTIVE JOB LISTINGS</button>
</div>
</div>
@@ -0,0 +1,46 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute, RouterModule } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { Authentication } from 'app/services/Authentication';
import { Company, Employee } from 'app/models/Company';
@Component({
selector: 'main-company',
templateUrl: './company.component.html',
styleUrls: [ './company.component.css' ],
imports: [ FormsModule, CommonModule, RouterModule ]
})
export class CompanyComponent {
public ErrorMsg: string = "";
public Employers: Employee[] = [];
public Comp: Company | null = null;
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
this.title.setTitle("Companies | BoredCareers");
http.get<Employee[]>("api/employee/").subscribe({
next: data => {
this.Employers = data;
},
error: err => {
this.ErrorMsg = err.error;
}
});
};
changeSelectedCompany(companyID: number){
this.http.get<Company>("api/company?CompanyID=" + companyID).subscribe({
next: data => {
this.Comp = data;
},
error: err => {
this.ErrorMsg = err.error;
}
});
}
}
@@ -0,0 +1,87 @@
button {
width: 150px;
border-radius: 5px;
margin: 10px;
text-align: center;
padding: 15px 0;
transition: .5s;
background-color: #0000;
border: 1px solid var(--Mistox-White);
color: var(--Mistox-White);
text-decoration: none;
}
button:hover {
background-color: #00000044;
color: var(--Mistox-Light);
}
.full-width {
display: block;
width: 100%;
column-count: 2;
}
.tile-frame {
display: grid;
grid-template-columns: repeat(4, 1fr);
column-gap: 20px;
padding: 20px;
width: calc(100% - 40px);
}
.tile{
background-color: var(--Mistox-Dark);
color: var(--Mistox-White);
break-inside: avoid;
padding: 20px;
border-radius: 20px;
margin-bottom: 20px;
}
.jobs-frame {
width: 100%;
background-color: #8888;
border-top: 2px solid black;
}
.post-job-frame {
display: flex;
justify-content: center;
padding: 10px 0;
}
.tile-title {
text-align: center;
border-bottom: 1px solid;
}
.tile-title h1 {
font-size: 40px;
margin: 5px 0;
}
.tile-title h2 {
font-size: 14px;
}
.tile-split {
columns: 2;
text-align: center;
padding: 10px 0;
}
.tile-split h1 {
margin: 0;
}
.tile-button {
display: flex;
width: 100%;
justify-content: center;
}
.post-job-frame button {
border-color: var(--Mistox-Black);
color: var(--Mistox-Black);
}
@@ -0,0 +1,23 @@
<div class="post-job-frame">
<button [routerLink]="['/jobs/editor']">POST JOB</button>
</div>
<div *ngIf="auth.isLoggedIn" class="jobs-frame">
<div class="posted-jobs-frame" *ngFor="let cur of MyJobListings">
<div class="tile">
<h1>{{ cur.title }}</h1>
<h1>{{ cur.jobType }}</h1>
<h1>Is Remote: {{ cur.remote }}</h1>
<h1>{{ cur.salaryMin }}</h1>
<h1>{{ cur.salaryMax }}</h1>
<h1>{{ cur.city }}</h1>
<h1>{{ cur.stateOrRegion }}</h1>
<h1>{{ cur.country }}</h1>
<h1>{{ cur.postalCode }}</h1>
<h1>Posted: {{ cur.createdTime }}</h1>
<h1>Modified: {{ cur.modifiedTime }}</h1>
</div>
<button [routerLink]="['/jobs/editor']" [queryParams]="{ JobID: cur.id }" >EDIT</button>
<button (click)="RemoveJobListing(cur.id)">DELETE</button>
</div>
</div>
@@ -8,35 +8,34 @@ import { JobListing } from 'app/models/JobListing';
import { Authentication } from 'app/services/Authentication';
@Component({
selector: 'main-jobs-edit',
templateUrl: './jobedit.component.html',
styleUrls: [ './jobedit.component.css' ],
selector: 'main-company-jobs',
templateUrl: './jobs.component.html',
styleUrls: [ './jobs.component.css' ],
imports: [ FormsModule, CommonModule, RouterModule ]
})
export class JobEditComponent {
export class CompanyJobsComponent {
public MyJobListings: JobListing[] = [];
public JobListingPage: JobListing[] = [];
public ErrorMsg: string = "";
public Page: number = 1;
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
this.title.setTitle("Jobs - edit | BoredCareers");
this.title.setTitle("Company - Jobs | BoredCareers");
if (this.Page == 1){
}
http.get<JobListing[]>("api/joblisting?PageQuantity=" + 10 + "&Page=" + 1).subscribe({
this.route.queryParams.subscribe(params => {
const companyID = params['CompanyID'];
if (companyID){
http.get<JobListing[]>("api/joblisting/company?CompanyID=" + companyID).subscribe({
next: data => {
this.JobListingPage = data;
this.MyJobListings = data;
},
error: err => {
this.ErrorMsg = err.error;
}
});
}else{
router.navigate(["/company"]);
}
});
};
RemoveJobListing( JobListingID: number ){
@@ -1,12 +0,0 @@
.tile-frame {
column-count: 4;
column-gap: 20px;
padding: 20px;
width: calc(100% - 40px);
}
.tile{
background-color: var(--Mistox-Dark)\);
height: 40px;
break-inside: avoid;
}
@@ -1,16 +0,0 @@
<div class="tile-frame" *ngFor="let cur of JobListingPage">
<div class="tile">
<h1>{{ cur.title }}</h1>
<h1>{{ cur.jobType }}</h1>
<h1>Is Remote: {{ cur.remote }}</h1>
<h1>{{ cur.salaryMin }}</h1>
<h1>{{ cur.salaryMax }}</h1>
<h1>{{ cur.city }}</h1>
<h1>{{ cur.stateOrRegion }}</h1>
<h1>{{ cur.country }}</h1>
<h1>{{ cur.postalCode }}</h1>
<h1>{{ cur.description }}</h1>
<h1>Posted: {{ cur.createdTime }}</h1>
<h1>Modified: {{ cur.modifiedTime }}</h1>
</div>
</div>
@@ -9,12 +9,12 @@ import { Authentication } from 'app/services/Authentication';
import { Company, Employee } from 'app/models/Company';
@Component({
selector: 'main-jobs-new',
templateUrl: './jobnew.component.html',
styleUrls: [ './jobnew.component.css' ],
selector: 'main-jobs-editor',
templateUrl: './jobeditor.component.html',
styleUrls: [ './jobeditor.component.css' ],
imports: [ FormsModule, CommonModule, RouterModule ]
})
export class JobNewComponent {
export class JobEditorComponent {
@ViewChildren('step') formSteps!: QueryList<ElementRef<HTMLDivElement>>;
currentStep: number = 0;
@@ -26,7 +26,7 @@ export class JobNewComponent {
public ErrorMsg: string = "";
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
this.title.setTitle("Jobs - new | BoredCareers");
this.title.setTitle("Jobs - Editor | BoredCareers");
this.http.get<Employee[]>("api/employee").subscribe({
next: empOf => {
@@ -1,27 +1,3 @@
<!-- My Jobs -->
<div *ngIf="auth.isLoggedIn" class="jobs-frame">
<div class="posted-jobs-frame" *ngFor="let cur of MyJobListings">
<div class="tile">
<h1>{{ cur.title }}</h1>
<h1>{{ cur.jobType }}</h1>
<h1>Is Remote: {{ cur.remote }}</h1>
<h1>{{ cur.salaryMin }}</h1>
<h1>{{ cur.salaryMax }}</h1>
<h1>{{ cur.city }}</h1>
<h1>{{ cur.stateOrRegion }}</h1>
<h1>{{ cur.country }}</h1>
<h1>{{ cur.postalCode }}</h1>
<h1>Posted: {{ cur.createdTime }}</h1>
<h1>Modified: {{ cur.modifiedTime }}</h1>
</div>
<button [routerLink]="['/jobs/edit']" [queryParams]="{ JobID: cur.id }" >EDIT</button>
<button (click)="RemoveJobListing(cur.id)">DELETE</button>
</div>
<div class="post-job-frame">
<button [routerLink]="['/jobs/new']">POST JOB</button>
</div>
</div>
<!-- Avaliable Jobs -->
<div class="tile-frame" *ngFor="let cur of JobListingPage">
<div class="tile">
@@ -38,7 +14,7 @@
<h1>{{ cur.stateOrRegion }}</h1>
</div>
<div class="tile-button">
<button [routerLink]="['/jobs/new']">VIEW LISTING</button>
<button [routerLink]="['/jobs/viewer']" [queryParams]="{ JobID: cur.id }" >VIEW LISTING</button>
</div>
</div>
</div>
@@ -0,0 +1,11 @@
.job-frame {
}
.job-warning {
}
.job-details {
}
@@ -0,0 +1,42 @@
<div class="job-frame">
<div class="company-details" *ngIf="jobsCompany != null" >
<h1>{{ jobsCompany.name }}</h1>
<h1>{{ jobsCompany.email }}</h1>
<h1>{{ jobsCompany.websiteURL }}</h1>
<h1>{{ jobsCompany.logoURL }}</h1>
<h1>{{ jobsCompany.phone }}</h1>
<h1>{{ jobsCompany.city }}</h1>
<h1>{{ jobsCompany.stateOrRegion }}</h1>
<h1>{{ jobsCompany.country }}</h1>
<h1>{{ jobsCompany.postalCode }}</h1>
<h1>{{ jobsCompany.description }}</h1>
</div>
<div class="job-details" *ngIf="selectedJob != null" >
<div class="job-warning" *ngIf="selectedJob.isDeleted" >
<h2>THIS JOB POSTING IS CLOSED</h2>
</div>
<h1>{{ selectedJob.title }}</h1>
<h1>{{ selectedJob.jobType }}</h1>
<h1>{{ selectedJob.remote }}</h1>
<h1>{{ selectedJob.salaryMin }}</h1>
<h1>{{ selectedJob.salaryMax }}</h1>
<h1>{{ selectedJob.city }}</h1>
<h1>{{ selectedJob.stateOrRegion }}</h1>
<h1>{{ selectedJob.country }}</h1>
<h1>{{ selectedJob.postalCode }}</h1>
<h1>{{ selectedJob.description }}</h1>
<h1>{{ selectedJob.createdTime }}</h1>
<h1>{{ selectedJob.modifiedTime }}</h1>
</div>
</div>
@@ -0,0 +1,55 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute, RouterModule } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { Authentication } from 'app/services/Authentication';
import { JobListing } from 'app/models/JobListing';
import { Company } from 'app/models/Company';
@Component({
selector: 'main-jobs-viewer',
templateUrl: './jobviewer.component.html',
styleUrls: [ './jobviewer.component.css' ],
imports: [ FormsModule, CommonModule, RouterModule ]
})
export class JobViewerComponent {
public selectedJob: JobListing | null = null;
public jobsCompany: Company | null = null;
public ErrorMsg: string = "";
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
this.title.setTitle("Jobs - Viewer | BoredCareers");
this.route.queryParams.subscribe(params => {
const JobID = params['JobID'];
if (JobID){
this.http.get<JobListing>( "api/joblisting/" + JobID ).subscribe({
next: data => {
this.selectedJob = data;
this.http.get<Company>("api/company?CompanyID=" + this.selectedJob.companyID).subscribe({
next: data => {
this.jobsCompany = data;
},
error: err => {
this.ErrorMsg = err.ErrorMsg;
}
})
},
error: err => {
this.ErrorMsg = err.error;
}
})
}else{
router.navigate(["/"]);
}
if (this.selectedJob != null){
}
});
};
}
@@ -1,13 +1,4 @@
<div class="tile-frame" *ngFor="let cur of ResumePage">
<div class="tile">
<h1>{{ cur.name }}</h1>
<h1>{{ cur.field }}</h1>
<h1>{{ cur.email }}</h1>
<h1>{{ cur.phoneNumber }}</h1>
<h1>{{ cur.city }}</h1>
<h1>{{ cur.stateOrRegion }}</h1>
<h1>{{ cur.country }}</h1>
<h1>{{ cur.postalCode }}</h1>
<h1>Active: {{ cur.isActive }}</h1>
</div>
<!-- My Resumes -->
<div *ngIf="auth.isLoggedIn" class="jobs-frame">
</div>
@@ -1,24 +1,33 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Router, ActivatedRoute, RouterModule } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { Resume } from 'app/models/Resume';
import { Authentication } from 'app/services/Authentication';
@Component({
selector: 'main-resumes',
templateUrl: './resumes.component.html',
styleUrls: [ './resumes.component.css' ],
imports: [ FormsModule, CommonModule ]
imports: [ FormsModule, CommonModule, RouterModule ]
})
export class ResumesComponent {
public ResumePage: Resume[] = [];
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title ) {
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
this.title.setTitle("Resumes | BoredCareers");
this.http.get<Resume[]>("api/resume").subscribe({
next: data => {
this.ResumePage = data;
},
error: err => {
console.log("Error fetching resumes: " + err.error);
}
});
};
}
@@ -19,6 +19,18 @@ namespace BoredCareers.Controllers {
return NotFound("Job listing not found");
}
[HttpGet("company")]
public async Task<IActionResult> GetCompanysJobListings([FromQuery] int CompanyID) {
if (isLoggedIn()) {
if (await isLoggedInUserEmployeeOf(CompanyID)) {
JobListing[] jobListings = await _databaseService.GetJobListingFromCompany(CompanyID);
return Ok(jobListings);
}
return NotFound("You are not an employee of company");
}
return NotFound("Not logged in");
}
[HttpGet]
public async Task<IActionResult> GetJobListings(int Page = 1, int PageQuantity = 25) {
JobListing[] jobListings = await _databaseService.GetJobListingPage(Page, PageQuantity);
-6
View File
@@ -3,13 +3,7 @@ namespace BoredCareers.Entities {
public int ID { get; set; } // PK
public string UserName { get; set; } = "";
public string Email { get; set; } = "";
public bool EmailVerified { get; set; } = false;
public string PasswordHash { get; set; } = "";
public bool FailedPasswordLock { get; set; } = false;
public int PasswordAttempts { get; set; } = 5;
public int CurrentPasswordAttempts { get; set; } = 0;
public string Role { get; set; } = "Generic";
public string EmailToken { get; set; } = "";
public string DataServer { get; set; } = "";
}
}
@@ -1,160 +0,0 @@
using BoredCareers.Entities;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.Common;
namespace BoredCareers.Services.DatabaseService {
public partial class DatabaseService {
public async Task<Account?> GetAccount( string UserNameOrEmail ) {
Account? account = null;
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT *
FROM Account
WHERE UserName = @UorE OR Email = @UorE;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@UorE", UserNameOrEmail);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) { break; }
int _id = reader.GetInt32("ID");
string _username = reader.GetString("UserName");
string _email = reader.GetString("Email");
bool _emailVerified = reader.GetBoolean("EmailVerified");
string _passwordhash = reader.GetString("PasswordHash");
bool _failedpasswordlock = reader.GetBoolean( "FailedPasswordLock" );
int _passwordattempts = reader.GetInt32( "PasswordAttempts" );
int _curpasswordattempts = reader.GetInt32( "CurrentPasswordAttempts" );
string _role = reader.GetString( "Role" );
string _emailtoken = reader.GetString( "EmailToken" );
string _dataserver = reader.GetString( "DataServer" );
account = new Account() {
ID = _id,
UserName = _username,
Email = _email,
EmailVerified = _emailVerified,
PasswordHash = _passwordhash,
CurrentPasswordAttempts = _curpasswordattempts,
PasswordAttempts = _passwordattempts,
EmailToken = _emailtoken,
FailedPasswordLock = _failedpasswordlock,
Role = _role,
DataServer = _dataserver
};
}
}
}
return account;
}
public async Task<Account?> GetAccount( int AccountID ) {
Account? account = null;
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
SELECT *
FROM Account
WHERE ID = @ID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@ID", AccountID);
using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) {
while( await reader.ReadAsync() ) {
if( reader == null ) {
break;
}
int _id = reader.GetInt32("ID");
string _username = reader.GetString("UserName");
string _email = reader.GetString("Email");
bool _emailVerified = reader.GetBoolean("EmailVerified");
string _passwordhash = reader.GetString("PasswordHash");
bool _failedpasswordlock = reader.GetBoolean( "FailedPasswordLock" );
int _passwordattempts = reader.GetInt32( "PasswordAttempts" );
int _curpasswordattempts = reader.GetInt32( "CurrentPasswordAttempts" );
string _role = reader.GetString( "Role" );
string _emailtoken = reader.GetString( "EmailToken" );
string _dataserver = reader.GetString("DataServer");
account = new Account() {
ID = _id,
UserName = _username,
Email = _email,
EmailVerified = _emailVerified,
PasswordHash = _passwordhash,
CurrentPasswordAttempts = _passwordattempts,
PasswordAttempts = _passwordattempts,
EmailToken = _emailtoken,
FailedPasswordLock = _failedpasswordlock,
Role = _role,
DataServer = _dataserver
};
}
}
}
return account;
}
public async Task SetAccount( Account Profile ) {
using( MySqlConnection connection = GetConnection() ) {
connection.Open();
string command = @"
INSERT INTO Account
(ID,UserName,Email,EmailVerified,PasswordHash,FailedPasswordLock,PasswordAttempts,CurrentPasswordAttempts,Role,EmailToken,DataServer)
VALUES
(@ID,@UserName,@Email,@EmailVerified,@PasswordHash,@FailedPasswordLock,@PasswordAttempts,@CurrentPasswordAttempts,@Role,@EmailToken,@DataServer)
ON DUPLICATE KEY UPDATE
UserName = @UserName,
Email = @Email,
EmailVerified = @EmailVerified,
PasswordHash = @PasswordHash,
FailedPasswordLock = @FailedPasswordLock,
PasswordAttempts = @PasswordAttempts,
CurrentPasswordAttempts = @CurrentPasswordAttempts,
Role = @Role,
EmailToken = @EmailToken,
DataServer = @DataServer;
";
MySqlCommand cmd = new MySqlCommand( command , connection);
cmd.Parameters.AddWithValue("@ID", Profile.ID);
cmd.Parameters.AddWithValue("@UserName", Profile.UserName);
cmd.Parameters.AddWithValue("@Email", Profile.Email);
cmd.Parameters.AddWithValue("@EmailVerified", Profile.EmailVerified);
cmd.Parameters.AddWithValue("@PasswordHash", Profile.PasswordHash);
cmd.Parameters.AddWithValue("@FailedPasswordLock", Profile.FailedPasswordLock);
cmd.Parameters.AddWithValue("@PasswordAttempts", Profile.PasswordAttempts);
cmd.Parameters.AddWithValue("@CurrentPasswordAttempts", Profile.CurrentPasswordAttempts);
cmd.Parameters.AddWithValue("@Role", Profile.Role);
cmd.Parameters.AddWithValue("@EmailToken", Profile.EmailToken);
cmd.Parameters.AddWithValue("@DataServer", Profile.DataServer);
await cmd.ExecuteNonQueryAsync();
}
}
public async Task DeleteAccount( int AccountID ) {
using( MySqlConnection connection = GetConnection() ) {
MySqlCommand cmd;
connection.Open();
string command = @"
DELETE FROM Account WHERE ID = @ID;
";
cmd = new MySqlCommand( command, connection );
cmd.Parameters.AddWithValue("@ID", AccountID);
await cmd.ExecuteNonQueryAsync();
}
}
}
}
@@ -64,6 +64,61 @@ namespace BoredCareers.Services.DatabaseService {
return joblistings.ToArray();
}
public async Task<JobListing[]> GetJobListingFromCompany(int CompanyID) {
List<JobListing> joblistings = new List<JobListing>(); ;
using (MySqlConnection connection = GetConnection()) {
connection.Open();
string command = @"
SELECT *
FROM JobListing
WHERE CompanyID = @CompanyID;
";
MySqlCommand cmd = new MySqlCommand(command, connection);
cmd.Parameters.AddWithValue("@CompanyID", CompanyID);
using (DbDataReader reader = await cmd.ExecuteReaderAsync()) {
while (await reader.ReadAsync()) {
if (reader == null) { break; }
int _id = reader.GetInt32("ID");
int _companyid = reader.GetInt32("CompanyID");
string _title = reader.GetString("Title");
string _postalcode = reader.GetString("PostalCode");
string _country = reader.GetString("Country");
string _state = reader.GetString("StateOrRegion");
string _city = reader.GetString("City");
int _salarymin = reader.GetInt32("SalaryMin");
int _salarymax = reader.GetInt32("SalaryMax");
string _jobtype = reader.GetString("JobType");
bool _remote = reader.GetBoolean("Remote");
string _description = reader.GetString("Description");
DateTime _createtime = reader.GetDateTime("CreatedTime");
DateTime _modifiedtime = reader.GetDateTime("ModifiedTime");
bool _isdeleted = reader.GetBoolean("IsDeleted");
joblistings.Add(new JobListing() {
ID = _id,
CompanyID = _companyid,
Title = _title,
PostalCode = _postalcode,
Country = _country,
StateOrRegion = _state,
City = _city,
SalaryMin = _salarymin,
SalaryMax = _salarymax,
JobType = _jobtype,
Remote = _remote,
Description = _description,
CreatedTime = _createtime,
ModifiedTime = _modifiedtime,
IsDeleted = _isdeleted
});
}
}
}
return joblistings.ToArray();
}
public async Task<JobListing?> GetJobListing(int JobListingID) {
JobListing? joblisting = null;
using (MySqlConnection connection = GetConnection()) {