Merge pull request 'working' (#8) from working into main
Docker Build and Release Upload / build (push) Successful in 1m21s
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:
Vendored
+1
@@ -22,6 +22,7 @@
|
||||
},
|
||||
"args": [
|
||||
"build",
|
||||
"--configuration=development",
|
||||
"--base-href=http://localhost:5000"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
|
||||
@@ -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
|
||||
@@ -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` (
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
export class JobListing {
|
||||
public id: number = 0;
|
||||
public id: number = -1;
|
||||
public companyID: number = 0;
|
||||
public title: string = "";
|
||||
public postalCode: string = "";
|
||||
|
||||
@@ -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>
|
||||
+14
-15
@@ -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>
|
||||
+5
-5
@@ -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);
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
Reference in New Issue
Block a user