Merge pull request 'Merge in UI updates' (#19) from working into main
Docker Build and Release Upload / build (push) Successful in 1m18s
Docker Build and Release Upload / build (push) Successful in 1m18s
Reviewed-on: #19
This commit was merged in pull request #19.
This commit is contained in:
@@ -17,16 +17,14 @@ Server:
|
|||||||
JobCleanupService:
|
JobCleanupService:
|
||||||
Need to update notification email
|
Need to update notification email
|
||||||
|
|
||||||
|
CompanyEmailVerify:
|
||||||
|
Need to update notification email
|
||||||
|
|
||||||
Client:
|
Client:
|
||||||
jobs/new:
|
jobs/editor:
|
||||||
Job Listing Skills exists but isn't implimented in the UI
|
Job Listing Skills exists but isn't implimented in the UI
|
||||||
Tab doesnt do anything
|
Tab doesnt do anything
|
||||||
Want to add completed job listing preview at end of carosel
|
Want to add completed job listing preview at end of carosel
|
||||||
Edit employees not implimented yet
|
|
||||||
|
|
||||||
Jobs/editor:
|
|
||||||
Jobs/editor w/ Querystring JobID=# is not implimented yet
|
|
||||||
Edit employees not implimented yet
|
|
||||||
|
|
||||||
Resume:
|
Resume:
|
||||||
Resume builder minimal user input [ Dont allow AI input ]
|
Resume builder minimal user input [ Dont allow AI input ]
|
||||||
@@ -38,8 +36,16 @@ Client:
|
|||||||
Allow users to look up jobs and apply [ Boost visibility | Completely manual ]
|
Allow users to look up jobs and apply [ Boost visibility | Completely manual ]
|
||||||
Mark ghost listings to allow users to be informed and put companies on blast
|
Mark ghost listings to allow users to be informed and put companies on blast
|
||||||
|
|
||||||
CompanyConnect:
|
company/editor:
|
||||||
need to lookup company before making a new one
|
Need to lookup company before making a new one
|
||||||
|
Tab key does nothing
|
||||||
|
Format phone number for database
|
||||||
|
Check DataType's for email and phone.
|
||||||
|
Setup QueryParam's for Edit and New
|
||||||
|
Edit employees not implimented yet
|
||||||
|
|
||||||
|
Company:
|
||||||
|
No employees for table yet
|
||||||
|
|
||||||
|
|
||||||
database:
|
database:
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ CREATE TABLE IF NOT EXISTS `Company` (
|
|||||||
`Name` varchar(100) DEFAULT NULL,
|
`Name` varchar(100) DEFAULT NULL,
|
||||||
`Email` varchar(255) DEFAULT NULL,
|
`Email` varchar(255) DEFAULT NULL,
|
||||||
`EmailVerified` boolean DEFAULT 0,
|
`EmailVerified` boolean DEFAULT 0,
|
||||||
|
`EmailToken` char(36) DEFAULT NULL,
|
||||||
`WebsiteURL` varchar(255) DEFAULT NULL,
|
`WebsiteURL` varchar(255) DEFAULT NULL,
|
||||||
`Logo` mediumblob DEFAULT NULL,
|
`Logo` mediumblob DEFAULT NULL,
|
||||||
`JobsClosedSuccessful` int DEFAULT 0,
|
`JobsClosedSuccessful` int DEFAULT 0,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<div class="top-bar">
|
<div class="top-bar">
|
||||||
<div class="top-bar-buttons">
|
<div class="top-bar-buttons">
|
||||||
<a #jobsLink class="nav-button" routerLink="/jobs">JOB BOARD</a>
|
<a class="nav-button" routerLink="/jobs" routerLinkActive="active">JOB BOARD</a>
|
||||||
<a #resumesLink class="nav-button" routerLink="/resumes">RESUMES</a>
|
<a class="nav-button" routerLink="/resumes" routerLinkActive="active">RESUMES</a>
|
||||||
<a #companiesLink class="nav-button" routerLink="/company">COMPANIES</a>
|
<a class="nav-button" routerLink="/company" routerLinkActive="active">COMPANIES</a>
|
||||||
</div>
|
</div>
|
||||||
<a class="top-bar-logo" routerLink="">
|
<a class="top-bar-logo" routerLink="">
|
||||||
<img class="top-bar-logo" style="margin: 0;" src="img/logo-full.png" />
|
<img class="top-bar-logo" style="margin: 0;" src="img/logo-full.png" />
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import { PrivacyComponent } from './pages/legal/privacy/privacy.component';
|
|||||||
import { JobsComponent } from './pages/main/jobs/jobs.component';
|
import { JobsComponent } from './pages/main/jobs/jobs.component';
|
||||||
import { ResumesComponent } from './pages/main/resumes/resumes.component';
|
import { ResumesComponent } from './pages/main/resumes/resumes.component';
|
||||||
import { JobEditorComponent } from './pages/main/jobs/editor/jobeditor.component';
|
import { JobEditorComponent } from './pages/main/jobs/editor/jobeditor.component';
|
||||||
import { CompanyConnectComponent } from './pages/main/company/connect/companyconnect.component';
|
import { CompanyEditorComponent } from './pages/main/company/editor/editor.component';
|
||||||
import { JobViewerComponent } from './pages/main/jobs/viewer/jobviewer.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';
|
import { CompanyComponent } from './pages/main/company/company.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
@@ -26,11 +25,11 @@ export const routes: Routes = [
|
|||||||
|
|
||||||
// Company
|
// Company
|
||||||
{ path: "company", component: CompanyComponent },
|
{ path: "company", component: CompanyComponent },
|
||||||
{ path: "company/connect", component: CompanyConnectComponent },
|
{ path: "company/editor", component: CompanyEditorComponent },
|
||||||
{ path: "company/jobs", component: CompanyJobsComponent },
|
|
||||||
|
|
||||||
// Legal
|
// Legal
|
||||||
{ path: "about", component: AboutComponent },
|
{ path: "about", component: AboutComponent },
|
||||||
{ path: "contact", component: ContactComponent },
|
{ path: "contact", component: ContactComponent },
|
||||||
{ path: "privacy", component: PrivacyComponent }
|
{ path: "privacy", component: PrivacyComponent }
|
||||||
|
|
||||||
]
|
]
|
||||||
@@ -13,10 +13,6 @@ import { isDevMode } from '@angular/core';
|
|||||||
})
|
})
|
||||||
export class App {
|
export class App {
|
||||||
|
|
||||||
@ViewChild('companiesLink') companiesLink!: ElementRef<HTMLAnchorElement>;
|
|
||||||
@ViewChild('jobsLink') jobLink!: ElementRef<HTMLAnchorElement>;
|
|
||||||
@ViewChild('resumesLink') resumeLink!: ElementRef<HTMLAnchorElement>;
|
|
||||||
|
|
||||||
devMode: boolean = false;
|
devMode: boolean = false;
|
||||||
|
|
||||||
constructor( private http: HttpClient, public auth: Authentication, private router: Router, private route: ActivatedRoute, private location: Location){
|
constructor( private http: HttpClient, public auth: Authentication, private router: Router, private route: ActivatedRoute, private location: Location){
|
||||||
@@ -48,13 +44,4 @@ export class App {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(){
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,96 @@ button {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-edit {
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.center-item img {
|
.center-item img {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content-name {
|
||||||
|
width: 300px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-name h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-link {
|
||||||
|
display: flex;
|
||||||
|
width: 300px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-link a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--Mistox-White);
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-desc {
|
||||||
|
border: solid 1px red;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 0 100px;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-desc h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-button span {
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-frame {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.half-frame {
|
||||||
|
width: 50%;
|
||||||
|
border-right: solid 1px var(--Mistox-Black);
|
||||||
|
border-left: solid 1px var(--Mistox-Black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.half-frame h2 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-tile {
|
||||||
|
display: flex;
|
||||||
|
background-color: var(--Mistox-Black);
|
||||||
|
justify-content: end;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 0 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-text {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-tile h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-tile button {
|
||||||
|
color: white;
|
||||||
|
border-color: white;
|
||||||
|
}
|
||||||
@@ -1,25 +1,50 @@
|
|||||||
<div class="top-bar">
|
<div class="top-bar">
|
||||||
<button *ngFor="let company of Employers" (click)="changeSelectedCompany(company.company.id!)">{{ company.company.name.toUpperCase() }}</button>
|
<button *ngFor="let company of Employers" (click)="changeSelectedCompany(company.company.id!)">{{ company.company.name.toUpperCase() }}</button>
|
||||||
<button routerLink="/company/connect" >CONNECT A COMPANY</button>
|
<button routerLink="/company/editor" >CONNECT A COMPANY</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-frame">
|
<div class="content-frame">
|
||||||
<div *ngIf="Comp != null">
|
<div *ngIf="Comp != null">
|
||||||
|
<button class="content-edit" style="color: #fff; border-color: #fff;" routerLink="/company/editor" [queryParams]="{ CompanyID: Comp.id }" >EDIT COMPANY</button>
|
||||||
<div class="center-item">
|
<div class="center-item">
|
||||||
<div><a [href]="'mailto:' + Comp.email" >{{ Comp.email }}</a></div>
|
<a [href]="Comp.websiteURL">
|
||||||
<div><h1>{{ Comp.name }}</h1></div>
|
|
||||||
<div><a [href]="Comp.websiteURL">{{ Comp.websiteURL }}</a></div>
|
|
||||||
</div>
|
|
||||||
<div class="center-item">
|
|
||||||
<img [src]="Comp.logo" />
|
<img [src]="Comp.logo" />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<h1>{{ Comp.emailVerified }}</h1>
|
<div class="center-item">
|
||||||
|
<div class="content-link"><a [href]="'mailto:' + Comp.email" >{{ Comp.email }}</a></div>
|
||||||
|
<div class="content-name"><h1>{{ Comp.name }}</h1></div>
|
||||||
|
<div class="content-link"><a [href]="'tel:' + Comp.phone">{{ Comp.phone }}</a></div>
|
||||||
|
</div>
|
||||||
|
<div class="center-item">
|
||||||
|
<h1>{{ Comp.city }}, {{ Comp.stateOrRegion }} {{ Comp.postalCode }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="content-desc">
|
||||||
|
<h1 *ngFor="let line of Desc">{{ line }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="content-button" *ngIf="Comp.emailVerified">
|
||||||
|
<button style="color: #fff; border-color: #fff;" routerLink="/jobs/editor" [queryParams]="{ CompanyID: Comp.id }" >POST JOB</button>
|
||||||
|
</div>
|
||||||
|
<div class="content-button" *ngIf="!Comp.emailVerified">
|
||||||
|
<button style="color: #fff; border-color: #fff;" routerLink="/" [queryParams]="{ CompanyID: Comp.id }" >VERIFY EMAIL</button>
|
||||||
|
<span>You must verify your company email before you can post job listings.</span>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div class="split-frame">
|
||||||
|
<div class="half-frame">
|
||||||
|
<h2>Active Job Listings</h2>
|
||||||
|
<div class="job-tile" *ngFor="let listing of List">
|
||||||
|
<div class="center-text">
|
||||||
|
<h1>{{ listing.title }}</h1>
|
||||||
|
</div>
|
||||||
|
<button [routerLink]="['/jobs/viewer']" [queryParams]="{ JobID: listing.id }" >VIEW LISTING</button>
|
||||||
|
<button [routerLink]="['/jobs/editor']" [queryParams]="{ JobID: listing.id }" >EDIT LISTING</button>
|
||||||
|
<button (click)="RemoveJobListing(listing.id!)">DELETE LISTING</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="half-frame">
|
||||||
|
<h2>Employees</h2>
|
||||||
|
|
||||||
<h1>{{ Comp.phone }}</h1>
|
</div>
|
||||||
<h1>{{ Comp.postalCode }}</h1>
|
</div>
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
@@ -6,6 +6,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { Authentication } from 'app/services/Authentication';
|
import { Authentication } from 'app/services/Authentication';
|
||||||
import { Company, Employee } from 'app/models/Company';
|
import { Company, Employee } from 'app/models/Company';
|
||||||
|
import { JobListing } from 'app/models/JobListing';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'main-company',
|
selector: 'main-company',
|
||||||
@@ -17,13 +18,22 @@ export class CompanyComponent {
|
|||||||
public ErrorMsg: string = "";
|
public ErrorMsg: string = "";
|
||||||
|
|
||||||
public Employers: Employee[] = [];
|
public Employers: Employee[] = [];
|
||||||
|
|
||||||
public Comp: Company | null = null;
|
public Comp: Company | null = null;
|
||||||
|
public Desc: string[] = [];
|
||||||
|
|
||||||
|
public List: JobListing[] = [];
|
||||||
|
|
||||||
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
|
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
|
||||||
this.title.setTitle("Companies | BoredCareers");
|
this.title.setTitle("Companies | BoredCareers");
|
||||||
http.get<Employee[]>("api/employee/").subscribe({
|
http.get<Employee[]>("api/employee/").subscribe({
|
||||||
next: data => {
|
next: data => {
|
||||||
this.Employers = data;
|
this.Employers = data;
|
||||||
|
if (data[0] != null){
|
||||||
|
if (data[0].company.id !== null){
|
||||||
|
this.changeSelectedCompany(data[0].company.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
this.ErrorMsg = err.error;
|
this.ErrorMsg = err.error;
|
||||||
@@ -36,6 +46,27 @@ export class CompanyComponent {
|
|||||||
this.http.get<Company>("api/company?CompanyID=" + companyID).subscribe({
|
this.http.get<Company>("api/company?CompanyID=" + companyID).subscribe({
|
||||||
next: data => {
|
next: data => {
|
||||||
this.Comp = data;
|
this.Comp = data;
|
||||||
|
this.Desc = data.description.split("\n");
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
this.ErrorMsg = err.error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.http.get<JobListing[]>("api/joblisting/company?CompanyID=" + companyID).subscribe({
|
||||||
|
next: data => {
|
||||||
|
this.List = data;
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
this.ErrorMsg = err.error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveJobListing( JobListingID: number ){
|
||||||
|
this.http.delete("api/joblisting?JobListingID=" + JobListingID).subscribe({
|
||||||
|
next: data => {
|
||||||
|
window.location.reload();
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
this.ErrorMsg = err.error;
|
this.ErrorMsg = err.error;
|
||||||
|
|||||||
+8
-5
@@ -8,12 +8,12 @@ import { Authentication } from 'app/services/Authentication';
|
|||||||
import { Company } from 'app/models/Company';
|
import { Company } from 'app/models/Company';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'main-company-connect',
|
selector: 'main-company-editor',
|
||||||
templateUrl: './companyconnect.component.html',
|
templateUrl: './editor.component.html',
|
||||||
styleUrls: [ './companyconnect.component.css' ],
|
styleUrls: [ './editor.component.css' ],
|
||||||
imports: [ FormsModule, CommonModule, RouterModule ]
|
imports: [ FormsModule, CommonModule, RouterModule ]
|
||||||
})
|
})
|
||||||
export class CompanyConnectComponent {
|
export class CompanyEditorComponent {
|
||||||
|
|
||||||
@ViewChildren('step') formSteps!: QueryList<ElementRef<HTMLDivElement>>;
|
@ViewChildren('step') formSteps!: QueryList<ElementRef<HTMLDivElement>>;
|
||||||
currentStep: number = 0;
|
currentStep: number = 0;
|
||||||
@@ -23,7 +23,10 @@ export class CompanyConnectComponent {
|
|||||||
MaxFileMB: number = 3;
|
MaxFileMB: number = 3;
|
||||||
|
|
||||||
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
|
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
|
||||||
this.title.setTitle("Company - Connect | BoredCareers");
|
this.title.setTitle("Company - Editor | BoredCareers");
|
||||||
|
|
||||||
|
// Query param CompanyID -> Edit
|
||||||
|
// Query param null -> New
|
||||||
};
|
};
|
||||||
|
|
||||||
ngAfterViewInit(){
|
ngAfterViewInit(){
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
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 { JobListing } from 'app/models/JobListing';
|
|
||||||
import { Authentication } from 'app/services/Authentication';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'main-company-jobs',
|
|
||||||
templateUrl: './jobs.component.html',
|
|
||||||
styleUrls: [ './jobs.component.css' ],
|
|
||||||
imports: [ FormsModule, CommonModule, RouterModule ]
|
|
||||||
})
|
|
||||||
export class CompanyJobsComponent {
|
|
||||||
|
|
||||||
public MyJobListings: JobListing[] = [];
|
|
||||||
public ErrorMsg: string = "";
|
|
||||||
|
|
||||||
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
|
|
||||||
this.title.setTitle("Company - Jobs | BoredCareers");
|
|
||||||
|
|
||||||
this.route.queryParams.subscribe(params => {
|
|
||||||
const companyID = params['CompanyID'];
|
|
||||||
if (companyID){
|
|
||||||
http.get<JobListing[]>("api/joblisting/company?CompanyID=" + companyID).subscribe({
|
|
||||||
next: data => {
|
|
||||||
this.MyJobListings = data;
|
|
||||||
},
|
|
||||||
error: err => {
|
|
||||||
this.ErrorMsg = err.error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
router.navigate(["/company"]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
RemoveJobListing( JobListingID: number ){
|
|
||||||
this.http.delete("api/joblisting?JobListingID=" + JobListingID).subscribe({
|
|
||||||
next: data => {
|
|
||||||
window.location.reload();
|
|
||||||
},
|
|
||||||
error: err => {
|
|
||||||
this.ErrorMsg = err.error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +1,14 @@
|
|||||||
<div class="title-text">
|
<div class="title-text">
|
||||||
<h1>POST A NEW JOB</h1>
|
<h1>POST A NEW JOB</h1>
|
||||||
</div>
|
</div>
|
||||||
<form (ngSubmit)="PostJobListing(newListing)">
|
<form (ngSubmit)="SubmitForm(Listing)">
|
||||||
|
|
||||||
<!-- Attach To Company -->
|
|
||||||
<div #step class="sub-frame">
|
|
||||||
<div class="center">
|
|
||||||
<div class="content-frame">
|
|
||||||
<label>For What Company</label>
|
|
||||||
<select name="company" [(ngModel)]="selectedCompany">
|
|
||||||
<option *ngFor="let cur of employeeOfList" [ngValue]="cur.company">{{ cur.company.name }}</option>
|
|
||||||
</select>
|
|
||||||
<button type="button" (click)="nextStep()">Next</button>
|
|
||||||
</div>
|
|
||||||
<div class="footer-frame">
|
|
||||||
<span>
|
|
||||||
Choose the company you want the listing to be created under.
|
|
||||||
</span>
|
|
||||||
<button [routerLink]="['/company/connect']">CONNECT A NEW COMPANY</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div #step class="sub-frame">
|
<div #step class="sub-frame">
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<div class="content-frame">
|
<div class="content-frame">
|
||||||
<label>Job Title</label>
|
<label>Job Title</label>
|
||||||
<input name="title" [(ngModel)]="newListing.title" type="text" />
|
<input name="title" [(ngModel)]="Listing.title" type="text" />
|
||||||
<button type="button" (click)="prevStep()">Back</button>
|
<button type="button" (click)="prevStep()">Back</button>
|
||||||
<button type="button" (click)="nextStep()">Next</button>
|
<button type="button" (click)="nextStep()">Next</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,7 +21,7 @@
|
|||||||
<div class="content-frame split">
|
<div class="content-frame split">
|
||||||
<div class="half-frame">
|
<div class="half-frame">
|
||||||
<label>Job Type</label>
|
<label>Job Type</label>
|
||||||
<select name="jobType" [(ngModel)]="newListing.jobType">
|
<select name="jobType" [(ngModel)]="Listing.jobType">
|
||||||
<option value="Full-time">Full-time</option>
|
<option value="Full-time">Full-time</option>
|
||||||
<option value="Part-time">Part-time</option>
|
<option value="Part-time">Part-time</option>
|
||||||
<option value="Contract">Contract</option>
|
<option value="Contract">Contract</option>
|
||||||
@@ -50,7 +31,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="half-frame">
|
<div class="half-frame">
|
||||||
<label>Remote Position</label>
|
<label>Remote Position</label>
|
||||||
<input name="remote" [(ngModel)]="newListing.remote" type="checkbox" />
|
<input name="remote" [(ngModel)]="Listing.remote" type="checkbox" />
|
||||||
</div>
|
</div>
|
||||||
<button type="button" (click)="prevStep()">Back</button>
|
<button type="button" (click)="prevStep()">Back</button>
|
||||||
<button type="button" (click)="nextStep()">Next</button>
|
<button type="button" (click)="nextStep()">Next</button>
|
||||||
@@ -59,28 +40,28 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Location -->
|
<!-- Location -->
|
||||||
<div #step *ngIf="!newListing.remote" class="sub-frame">
|
<div #step *ngIf="!Listing.remote" class="sub-frame">
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<h2>Job Location</h2>
|
<h2>Job Location</h2>
|
||||||
<div>
|
<div>
|
||||||
<div class="content-frame split" style="border-radius: 10px 10px 0 0;">
|
<div class="content-frame split" style="border-radius: 10px 10px 0 0;">
|
||||||
<div class="half-frame">
|
<div class="half-frame">
|
||||||
<label>City</label>
|
<label>City</label>
|
||||||
<input name="city" [(ngModel)]="newListing.city" type="text" />
|
<input name="city" [(ngModel)]="Listing.city" type="text" />
|
||||||
</div>
|
</div>
|
||||||
<div class="half-frame">
|
<div class="half-frame">
|
||||||
<label>2 Letter State/Region</label>
|
<label>2 Letter State/Region</label>
|
||||||
<input name="stateOrRegion" maxlength="2" minlength="2" [(ngModel)]="newListing.stateOrRegion" type="text" />
|
<input name="stateOrRegion" maxlength="2" minlength="2" [(ngModel)]="Listing.stateOrRegion" type="text" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-frame split" style="border-radius: 0 0 10px 10px;">
|
<div class="content-frame split" style="border-radius: 0 0 10px 10px;">
|
||||||
<div class="half-frame">
|
<div class="half-frame">
|
||||||
<label>2 Letter Country</label>
|
<label>2 Letter Country</label>
|
||||||
<input name="country" maxlength="2" minlength="2" [(ngModel)]="newListing.country" type="text" />
|
<input name="country" maxlength="2" minlength="2" [(ngModel)]="Listing.country" type="text" />
|
||||||
</div>
|
</div>
|
||||||
<div class="half-frame">
|
<div class="half-frame">
|
||||||
<label>Postal Code</label>
|
<label>Postal Code</label>
|
||||||
<input name="postalCode" [(ngModel)]="newListing.postalCode" type="text" />
|
<input name="postalCode" [(ngModel)]="Listing.postalCode" type="text" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" (click)="prevStep()">Back</button>
|
<button type="button" (click)="prevStep()">Back</button>
|
||||||
@@ -96,11 +77,11 @@
|
|||||||
<div class="content-frame split">
|
<div class="content-frame split">
|
||||||
<div class="half-frame">
|
<div class="half-frame">
|
||||||
<label>Minimum Salary</label>
|
<label>Minimum Salary</label>
|
||||||
<input name="salaryMin" [(ngModel)]="newListing.salaryMin" type="number" />
|
<input name="salaryMin" [(ngModel)]="Listing.salaryMin" type="number" />
|
||||||
</div>
|
</div>
|
||||||
<div class="half-frame">
|
<div class="half-frame">
|
||||||
<label>Maximum Salary</label>
|
<label>Maximum Salary</label>
|
||||||
<input name="salaryMax" [(ngModel)]="newListing.salaryMax" type="number" />
|
<input name="salaryMax" [(ngModel)]="Listing.salaryMax" type="number" />
|
||||||
</div>
|
</div>
|
||||||
<button type="button" (click)="prevStep()">Back</button>
|
<button type="button" (click)="prevStep()">Back</button>
|
||||||
<button type="button" (click)="nextStep()">Next</button>
|
<button type="button" (click)="nextStep()">Next</button>
|
||||||
@@ -113,7 +94,7 @@
|
|||||||
<div class="center">
|
<div class="center">
|
||||||
<div class="content-frame">
|
<div class="content-frame">
|
||||||
<label>Description</label>
|
<label>Description</label>
|
||||||
<textarea name="description" [(ngModel)]="newListing.description" type="text"></textarea>
|
<textarea name="description" [(ngModel)]="Listing.description" type="text"></textarea>
|
||||||
<button type="button" (click)="prevStep()">Back</button>
|
<button type="button" (click)="prevStep()">Back</button>
|
||||||
<button type="button" (click)="nextStep()">Next</button>
|
<button type="button" (click)="nextStep()">Next</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,31 +15,46 @@ import { Company, Employee } from 'app/models/Company';
|
|||||||
imports: [ FormsModule, CommonModule, RouterModule ]
|
imports: [ FormsModule, CommonModule, RouterModule ]
|
||||||
})
|
})
|
||||||
export class JobEditorComponent {
|
export class JobEditorComponent {
|
||||||
|
public ErrorMsg: string = "";
|
||||||
|
|
||||||
@ViewChildren('step') formSteps!: QueryList<ElementRef<HTMLDivElement>>;
|
@ViewChildren('step') formSteps!: QueryList<ElementRef<HTMLDivElement>>;
|
||||||
currentStep: number = 0;
|
currentStep: number = 0;
|
||||||
|
|
||||||
public employeeOfList: Employee[] = [];
|
public Listing: JobListing = new JobListing();
|
||||||
public selectedCompany: Company = new Company;
|
|
||||||
|
|
||||||
public newListing: JobListing = new JobListing();
|
public mode: string = "";
|
||||||
public ErrorMsg: string = "";
|
public modeID: number = 0;
|
||||||
|
|
||||||
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
|
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
|
||||||
this.title.setTitle("Jobs - Editor | BoredCareers");
|
this.title.setTitle("Jobs - Editor | BoredCareers");
|
||||||
|
|
||||||
this.http.get<Employee[]>("api/employee").subscribe({
|
this.route.queryParams.subscribe(params => {
|
||||||
next: empOf => {
|
const CompanyID = params['CompanyID'] ? +params['CompanyID'] : null;
|
||||||
if (empOf.length === 0){
|
const JobID = params['JobID'] ? +params['JobID'] : null;
|
||||||
router.navigate(["company/connect"]);
|
if (CompanyID !== null && JobID !== null){
|
||||||
|
this.router.navigate([""]);
|
||||||
|
}else if (CompanyID !== null ){
|
||||||
|
this.mode = "new";
|
||||||
|
this.modeID = CompanyID;
|
||||||
|
}else if(JobID !== null){
|
||||||
|
this.mode = "edit";
|
||||||
|
this.modeID = JobID;
|
||||||
|
}else if (CompanyID === null && JobID === null){
|
||||||
|
this.router.navigate([""]);
|
||||||
}
|
}
|
||||||
this.employeeOfList = empOf;
|
|
||||||
|
if (this.mode === "edit") {
|
||||||
|
this.http.get<JobListing>("api/joblisting/" + JobID).subscribe({
|
||||||
|
next: data => {
|
||||||
|
this.Listing = data;
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
this.ErrorMsg = err.error;
|
this.ErrorMsg = err.error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ngAfterViewInit(){
|
ngAfterViewInit(){
|
||||||
this.formSteps.changes.subscribe(() => {
|
this.formSteps.changes.subscribe(() => {
|
||||||
@@ -70,8 +85,8 @@ export class JobEditorComponent {
|
|||||||
this.updateUI();
|
this.updateUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
PostJobListing(jobListing: JobListing){
|
PostNewJob(jobListing: JobListing){
|
||||||
jobListing.companyID = this.selectedCompany.id!;
|
jobListing.companyID = this.modeID;
|
||||||
this.http.post("api/joblisting", jobListing).subscribe({
|
this.http.post("api/joblisting", jobListing).subscribe({
|
||||||
next: data => {
|
next: data => {
|
||||||
this.router.navigate([""]);
|
this.router.navigate([""]);
|
||||||
@@ -82,4 +97,23 @@ export class JobEditorComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PostEditJob(jobListing: JobListing){
|
||||||
|
this.http.post("api/joblisting", jobListing).subscribe({
|
||||||
|
next: data => {
|
||||||
|
this.router.navigate([""]);
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
this.ErrorMsg = err.error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
SubmitForm(job: JobListing){
|
||||||
|
if (this.mode === "new"){
|
||||||
|
this.PostNewJob(job);
|
||||||
|
}else if (this.mode === "edit"){
|
||||||
|
this.PostEditJob(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="tile">
|
<div class="tile">
|
||||||
<div class="tile-title">
|
<div class="tile-title">
|
||||||
<h1>{{ cur.title }}</h1>
|
<h1>{{ cur.title }}</h1>
|
||||||
<h2>${{ cur.salaryMax }} - ${{ cur.salaryMin }}</h2>
|
<h2>${{ cur.salaryMin }} - ${{ cur.salaryMax }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile-split">
|
<div class="tile-split">
|
||||||
<h1>{{ cur.jobType }}</h1>
|
<h1>{{ cur.jobType }}</h1>
|
||||||
|
|||||||
@@ -1,11 +1,91 @@
|
|||||||
.job-frame {
|
.company-details {
|
||||||
|
background-color: #5c3030;
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-warning {
|
.company-details::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 50px;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-frame {
|
||||||
|
background-color: #3c3c3c;
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
height: calc(100vh - 400px);
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 10px;
|
||||||
|
overflow: scroll;
|
||||||
|
padding: 10px;
|
||||||
|
color: var(--Mistox-White);
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-item {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-edit {
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-item img {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-name {
|
||||||
|
width: 300px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-name h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-link {
|
||||||
|
display: flex;
|
||||||
|
width: 300px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-link a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--Mistox-White);
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-desc {
|
||||||
|
border: solid 1px red;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 0 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-desc h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-button span {
|
||||||
|
align-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-details {
|
.job-details {
|
||||||
|
background-color: #3c3c3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-timestamp {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-timestamp h1 {
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,27 @@
|
|||||||
<div class="job-frame">
|
<div class="job-frame">
|
||||||
<div class="company-details" *ngIf="jobsCompany != null" >
|
<div class="company-details" *ngIf="jobsCompany != null" >
|
||||||
<h1>{{ jobsCompany.name }}</h1>
|
<div class="center-item">
|
||||||
|
<a [href]="jobsCompany.websiteURL">
|
||||||
<h1>{{ jobsCompany.email }}</h1>
|
<img [src]="jobsCompany.logo" />
|
||||||
<h1>{{ jobsCompany.websiteURL }}</h1>
|
</a>
|
||||||
|
</div>
|
||||||
<h1>{{ jobsCompany.logo }}</h1>
|
<div class="center-item">
|
||||||
<h1>{{ jobsCompany.phone }}</h1>
|
<div class="content-link"><a [href]="'mailto:' + jobsCompany.email" >{{ jobsCompany.email }}</a></div>
|
||||||
|
<div class="content-name"><h1>{{ jobsCompany.name }}</h1></div>
|
||||||
<h1>{{ jobsCompany.city }}</h1>
|
<div class="content-link"><a [href]="'tel:' + jobsCompany.phone">{{ jobsCompany.phone }}</a></div>
|
||||||
<h1>{{ jobsCompany.stateOrRegion }}</h1>
|
</div>
|
||||||
<h1>{{ jobsCompany.country }}</h1>
|
<div class="center-item">
|
||||||
<h1>{{ jobsCompany.postalCode }}</h1>
|
<h1>{{ jobsCompany.city }}, {{ jobsCompany.stateOrRegion }} {{ jobsCompany.postalCode }}</h1>
|
||||||
|
</div>
|
||||||
<h1>{{ jobsCompany.description }}</h1>
|
<div class="content-desc">
|
||||||
|
<h1 *ngFor="let line of jobsCompany.description.split('\n')">{{ line }}</h1>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="job-details" *ngIf="selectedJob != null" >
|
<div class="job-details" *ngIf="selectedJob != null" >
|
||||||
|
<div class="job-timestamp">
|
||||||
|
<h1>Opened: {{ selectedJob.createdTime }}</h1>
|
||||||
|
<h1>Modified: {{ selectedJob.modifiedTime }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="job-warning" *ngIf="selectedJob.isDeleted" >
|
<div class="job-warning" *ngIf="selectedJob.isDeleted" >
|
||||||
<h2>THIS JOB POSTING IS CLOSED</h2>
|
<h2>THIS JOB POSTING IS CLOSED</h2>
|
||||||
@@ -35,8 +41,5 @@
|
|||||||
<h1>{{ selectedJob.postalCode }}</h1>
|
<h1>{{ selectedJob.postalCode }}</h1>
|
||||||
|
|
||||||
<h1>{{ selectedJob.description }}</h1>
|
<h1>{{ selectedJob.description }}</h1>
|
||||||
|
|
||||||
<h1>{{ selectedJob.createdTime }}</h1>
|
|
||||||
<h1>{{ selectedJob.modifiedTime }}</h1>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2,19 +2,25 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using BoredCareers.Services.DatabaseService;
|
using BoredCareers.Services.DatabaseService;
|
||||||
using BoredCareers.Entities;
|
using BoredCareers.Entities;
|
||||||
using System.Web.Http;
|
using System.Web.Http;
|
||||||
|
using BoredCareers.Services;
|
||||||
|
|
||||||
namespace BoredCareers.Controllers {
|
namespace BoredCareers.Controllers {
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/company")]
|
[Route("api/company")]
|
||||||
public class CompanyController : MistoxControllerBase {
|
public class CompanyController : MistoxControllerBase {
|
||||||
|
|
||||||
public CompanyController(DatabaseService db) : base(db) {}
|
EmailService _emailContext;
|
||||||
|
|
||||||
|
public CompanyController(DatabaseService db, EmailService emailContext) : base(db) {
|
||||||
|
_emailContext = emailContext;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetCompany(int CompanyID) {
|
public async Task<IActionResult> GetCompany(int CompanyID) {
|
||||||
if (isLoggedIn()) {
|
if (isLoggedIn()) {
|
||||||
Company? company = await _databaseService.GetCompany(CompanyID);
|
Company? company = await _databaseService.GetCompany(CompanyID);
|
||||||
if (company != null) {
|
if (company != null) {
|
||||||
|
company.EmailToken = "";
|
||||||
return Ok(company);
|
return Ok(company);
|
||||||
}
|
}
|
||||||
return NotFound("Company doesn't exist");
|
return NotFound("Company doesn't exist");
|
||||||
@@ -59,6 +65,57 @@ namespace BoredCareers.Controllers {
|
|||||||
return NotFound("Not logged in");
|
return NotFound("Not logged in");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("sendverifyemail")]
|
||||||
|
public async Task<ActionResult<string>> SendVerify([FromQuery] int CompanyID) {
|
||||||
|
try {
|
||||||
|
string key = "v" + CompanyID;
|
||||||
|
// Stop from sending multiple emails quickly
|
||||||
|
if (_emailContext._SentEmails.ContainsKey(key)) {
|
||||||
|
DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key);
|
||||||
|
if (PreviousSentTime.AddMinutes(5) > DateTime.Now) {
|
||||||
|
return NotFound("Cannot sent another verify email until 5 minutes has elapsed");
|
||||||
|
} else {
|
||||||
|
_emailContext._SentEmails.Remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Company? test = await _databaseService.GetCompany(CompanyID);
|
||||||
|
if (test != null) {
|
||||||
|
test.EmailToken = Guid.NewGuid().ToString();
|
||||||
|
await _databaseService.SetCompany(test);
|
||||||
|
|
||||||
|
string EmailContents = EmailService.CompanyVerifyEmailSubject;
|
||||||
|
EmailContents = Substitue(EmailContents, "@CompanyName", test.Name);
|
||||||
|
EmailContents = Substitue(EmailContents, "@ID", CompanyID.ToString());
|
||||||
|
EmailContents = Substitue(EmailContents, "@VerifyPassword", test.EmailToken);
|
||||||
|
|
||||||
|
string result = _emailContext.Send(test.Email, EmailService.CompanyVerifyEmailSubject, EmailContents);
|
||||||
|
_emailContext._SentEmails.Add(key, DateTime.Now);
|
||||||
|
return Redirect("/");
|
||||||
|
}
|
||||||
|
return NotFound("Account not found");
|
||||||
|
} catch (Exception) {
|
||||||
|
return NotFound("An internal server error has occured");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("verifyemail")]
|
||||||
|
public async Task<ActionResult<bool>> VerifyEmail([FromQuery] int CompanyID, [FromQuery] string EmailToken) {
|
||||||
|
try {
|
||||||
|
Company? test = await _databaseService.GetCompany(CompanyID);
|
||||||
|
if (test != null) {
|
||||||
|
if (test.EmailToken == EmailToken) {
|
||||||
|
test.EmailToken = "";
|
||||||
|
test.EmailVerified = true;
|
||||||
|
await _databaseService.SetCompany(test);
|
||||||
|
return Redirect("/");
|
||||||
|
}
|
||||||
|
return BadRequest("The token isn't valid");
|
||||||
|
}
|
||||||
|
return BadRequest("Account not found"); ;
|
||||||
|
} catch {
|
||||||
|
return BadRequest("An internal server error has occured");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace BoredCareers.Entities {
|
|||||||
public string Name { get; set; } = "";
|
public string Name { get; set; } = "";
|
||||||
public string Email { get; set; } = "";
|
public string Email { get; set; } = "";
|
||||||
public bool EmailVerified { get; set; } = false;
|
public bool EmailVerified { get; set; } = false;
|
||||||
|
public string EmailToken { get; set; } = "";
|
||||||
public string WebsiteURL { get; set; } = "";
|
public string WebsiteURL { get; set; } = "";
|
||||||
public string Logo { get; set; } = "";
|
public string Logo { get; set; } = "";
|
||||||
public int JobsClosedSuccessful { get; set; }
|
public int JobsClosedSuccessful { get; set; }
|
||||||
|
|||||||
@@ -11,6 +11,17 @@ namespace BoredCareers.Services.TimerService {
|
|||||||
_em = em;
|
_em = em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Substitue(string message, string subString, string Replacement) {
|
||||||
|
for (int i = 0; i < (message.Length - subString.Length); i++) {
|
||||||
|
if (message.Substring(i, subString.Length) == subString) {
|
||||||
|
string before = message.Substring(0, i);
|
||||||
|
string after = message.Substring(i + subString.Length);
|
||||||
|
return before + Replacement + after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
|
||||||
while (!stoppingToken.IsCancellationRequested) {
|
while (!stoppingToken.IsCancellationRequested) {
|
||||||
try {
|
try {
|
||||||
@@ -37,7 +48,10 @@ namespace BoredCareers.Services.TimerService {
|
|||||||
string[] emails = await _db.GetApplicationResponseEmailFromJobListing(listing.JobListingID);
|
string[] emails = await _db.GetApplicationResponseEmailFromJobListing(listing.JobListingID);
|
||||||
foreach (string email in emails) {
|
foreach (string email in emails) {
|
||||||
// Send Notify Email
|
// Send Notify Email
|
||||||
_em.Send(email, EmailService.JobAutoClosedSubject, EmailService.JobAutoClosedEmail);
|
string emailbody = EmailService.JobAutoClosedBody;
|
||||||
|
//Substitue(emailbody, "@job", listing.JobListingID);
|
||||||
|
|
||||||
|
_em.Send(email, EmailService.JobAutoClosedSubject, emailbody);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ namespace BoredCareers.Services.DatabaseService {
|
|||||||
string _name = reader.GetString("Name");
|
string _name = reader.GetString("Name");
|
||||||
string _email = reader.GetString("Email");
|
string _email = reader.GetString("Email");
|
||||||
bool _emailVerified = reader.GetBoolean("EmailVerified");
|
bool _emailVerified = reader.GetBoolean("EmailVerified");
|
||||||
|
string _emailtoken = reader.GetString("EmailToken");
|
||||||
string _websiteurl = reader.GetString("WebsiteURL");
|
string _websiteurl = reader.GetString("WebsiteURL");
|
||||||
string _logo = Encoding.UTF8.GetString((byte[])reader["Logo"]);
|
string _logo = Encoding.UTF8.GetString((byte[])reader["Logo"]);
|
||||||
int _jobsclosedsuccessful = reader.GetInt32("JobsClosedSuccessful");
|
int _jobsclosedsuccessful = reader.GetInt32("JobsClosedSuccessful");
|
||||||
@@ -42,6 +43,7 @@ namespace BoredCareers.Services.DatabaseService {
|
|||||||
Name = _name,
|
Name = _name,
|
||||||
Email = _email,
|
Email = _email,
|
||||||
EmailVerified = _emailVerified,
|
EmailVerified = _emailVerified,
|
||||||
|
EmailToken = _emailtoken,
|
||||||
WebsiteURL = _websiteurl,
|
WebsiteURL = _websiteurl,
|
||||||
Logo = _logo,
|
Logo = _logo,
|
||||||
JobsAutoClosed = _jobsautoclosed,
|
JobsAutoClosed = _jobsautoclosed,
|
||||||
@@ -64,13 +66,14 @@ namespace BoredCareers.Services.DatabaseService {
|
|||||||
await connection.OpenAsync();
|
await connection.OpenAsync();
|
||||||
string command = @"
|
string command = @"
|
||||||
INSERT INTO Company
|
INSERT INTO Company
|
||||||
(ID,Name,Email,EmailVerified,WebsiteURL,Logo,JobsClosedSuccessful,JobsAutoClosed,Phone,PostalCode,Country,StateOrRegion,City,Description)
|
(ID,Name,Email,EmailVerified,EmailToken,WebsiteURL,Logo,JobsClosedSuccessful,JobsAutoClosed,Phone,PostalCode,Country,StateOrRegion,City,Description)
|
||||||
VALUES
|
VALUES
|
||||||
(@ID,@Name,@Email,@EmailVerified,@WebsiteURL,@Logo,@JobsClosedSuccessful,@JobsAutoClosed,@Phone,@PostalCode,@Country,@StateOrRegion,@City,@Description)
|
(@ID,@Name,@Email,@EmailVerified,@EmailToken,@WebsiteURL,@Logo,@JobsClosedSuccessful,@JobsAutoClosed,@Phone,@PostalCode,@Country,@StateOrRegion,@City,@Description)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
Name = @Name,
|
Name = @Name,
|
||||||
Email = @Email,
|
Email = @Email,
|
||||||
EmailVerified = @EmailVerified,
|
EmailVerified = @EmailVerified,
|
||||||
|
EmailToken = @EmailToken,
|
||||||
WebsiteURL = @WebsiteURL,
|
WebsiteURL = @WebsiteURL,
|
||||||
Logo = @Logo,
|
Logo = @Logo,
|
||||||
JobsClosedSuccessful = @JobsClosedSuccessful,
|
JobsClosedSuccessful = @JobsClosedSuccessful,
|
||||||
@@ -90,6 +93,7 @@ namespace BoredCareers.Services.DatabaseService {
|
|||||||
cmd.Parameters.AddWithValue("@Name", company.Name);
|
cmd.Parameters.AddWithValue("@Name", company.Name);
|
||||||
cmd.Parameters.AddWithValue("@Email", company.Email);
|
cmd.Parameters.AddWithValue("@Email", company.Email);
|
||||||
cmd.Parameters.AddWithValue("@EmailVerified", company.EmailVerified);
|
cmd.Parameters.AddWithValue("@EmailVerified", company.EmailVerified);
|
||||||
|
cmd.Parameters.AddWithValue("@EmailToken", company.EmailToken);
|
||||||
cmd.Parameters.AddWithValue("@WebsiteURL", company.WebsiteURL);
|
cmd.Parameters.AddWithValue("@WebsiteURL", company.WebsiteURL);
|
||||||
cmd.Parameters.AddWithValue("@Logo", Encoding.UTF8.GetBytes(company.Logo));
|
cmd.Parameters.AddWithValue("@Logo", Encoding.UTF8.GetBytes(company.Logo));
|
||||||
cmd.Parameters.AddWithValue("@JobsClosedSuccessful", company.JobsClosedSuccessful);
|
cmd.Parameters.AddWithValue("@JobsClosedSuccessful", company.JobsClosedSuccessful);
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ namespace BoredCareers.Services.DatabaseService {
|
|||||||
string command = @"
|
string command = @"
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM JobListing
|
FROM JobListing
|
||||||
WHERE CompanyID = @CompanyID;
|
WHERE IsDeleted = FALSE
|
||||||
|
AND CompanyID = @CompanyID;
|
||||||
";
|
";
|
||||||
|
|
||||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||||
|
|||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
namespace BoredCareers.Services {
|
||||||
|
public partial class EmailService {
|
||||||
|
|
||||||
|
// @UserName
|
||||||
|
// @VerifyPassword
|
||||||
|
// https://mistox.com/api/account/verifyemail?UserName=@UserName&Guid=@VerifyPassword
|
||||||
|
|
||||||
|
public static string CompanyVerifyEmailSubject = "Verify Your Email Address";
|
||||||
|
public static string CompanyVerifyEmailBody = @"
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang=""en"">
|
||||||
|
<head>
|
||||||
|
<meta charset=""UTF-8"">
|
||||||
|
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"">
|
||||||
|
<title>Verify Your Email</title>
|
||||||
|
</head>
|
||||||
|
<body style=""font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 0;"">
|
||||||
|
<table role=""presentation"" style=""width: 100%; background-color: #f4f4f4; padding: 20px 0;"">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table role=""presentation"" style=""max-width: 600px; width: 100%; background-color: #ffffff; margin: 0 auto; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);"">
|
||||||
|
<tr>
|
||||||
|
<td style=""padding: 20px; text-align: center; background-color: #4CAF50; color: #ffffff; border-top-left-radius: 8px; border-top-right-radius: 8px;"">
|
||||||
|
<h2>Verify Email Request</h2>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style=""padding: 20px; text-align: left; font-size: 16px; color: #333333;"">
|
||||||
|
<p>Hi @CompanyName,</p>
|
||||||
|
<p>Thank you for making an account with us:</p>
|
||||||
|
<p>In order to start using your account we need to verify your email address by clicking the link below:</p>
|
||||||
|
<p style=""text-align: center;"">
|
||||||
|
<a href=""https://boredcareers.com/api/company/verifyemail?CompanyID=@ID&EmailToken=@VerifyPassword"" style=""background-color: #4CAF50; color: #ffffff; text-decoration: none; padding: 15px 25px; font-size: 16px; border-radius: 5px; display: inline-block;"">Verify Email</a>
|
||||||
|
</p>
|
||||||
|
<p>If you didn't create an account please ignore this email.</p>
|
||||||
|
<p>Best regards</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style=""padding: 10px; text-align: center; background-color: #f4f4f4; color: #888888; font-size: 12px; border-bottom-left-radius: 8px; border-bottom-right-radius: 8px;"">
|
||||||
|
<p>If you have any questions, feel free to <a href=""mailto:webmaster@mistox.com"" style=""color: #4CAF50; text-decoration: none;"">contact support</a>.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ namespace BoredCareers.Services {
|
|||||||
// https://mistox.com/api/account/verifyemail?UserName=@UserName&Guid=@VerifyPassword
|
// https://mistox.com/api/account/verifyemail?UserName=@UserName&Guid=@VerifyPassword
|
||||||
|
|
||||||
public static string JobAutoClosedSubject = "Verify Your Email Address";
|
public static string JobAutoClosedSubject = "Verify Your Email Address";
|
||||||
public static string JobAutoClosedEmail = @"
|
public static string JobAutoClosedBody = @"
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang=""en"">
|
<html lang=""en"">
|
||||||
<head>
|
<head>
|
||||||
|
|||||||
Reference in New Issue
Block a user