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:
|
||||
Need to update notification email
|
||||
|
||||
CompanyEmailVerify:
|
||||
Need to update notification email
|
||||
|
||||
Client:
|
||||
jobs/new:
|
||||
jobs/editor:
|
||||
Job Listing Skills exists but isn't implimented in the UI
|
||||
Tab doesnt do anything
|
||||
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 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 ]
|
||||
Mark ghost listings to allow users to be informed and put companies on blast
|
||||
|
||||
CompanyConnect:
|
||||
need to lookup company before making a new one
|
||||
company/editor:
|
||||
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:
|
||||
|
||||
@@ -129,6 +129,7 @@ CREATE TABLE IF NOT EXISTS `Company` (
|
||||
`Name` varchar(100) DEFAULT NULL,
|
||||
`Email` varchar(255) DEFAULT NULL,
|
||||
`EmailVerified` boolean DEFAULT 0,
|
||||
`EmailToken` char(36) DEFAULT NULL,
|
||||
`WebsiteURL` varchar(255) DEFAULT NULL,
|
||||
`Logo` mediumblob DEFAULT NULL,
|
||||
`JobsClosedSuccessful` int DEFAULT 0,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<div class="top-bar">
|
||||
<div class="top-bar-buttons">
|
||||
<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>
|
||||
<a class="nav-button" routerLink="/jobs" routerLinkActive="active">JOB BOARD</a>
|
||||
<a class="nav-button" routerLink="/resumes" routerLinkActive="active">RESUMES</a>
|
||||
<a class="nav-button" routerLink="/company" routerLinkActive="active">COMPANIES</a>
|
||||
</div>
|
||||
<a class="top-bar-logo" routerLink="">
|
||||
<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 { ResumesComponent } from './pages/main/resumes/resumes.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 { CompanyJobsComponent } from './pages/main/company/jobs/jobs.component';
|
||||
import { CompanyComponent } from './pages/main/company/company.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
@@ -26,11 +25,11 @@ export const routes: Routes = [
|
||||
|
||||
// Company
|
||||
{ path: "company", component: CompanyComponent },
|
||||
{ path: "company/connect", component: CompanyConnectComponent },
|
||||
{ path: "company/jobs", component: CompanyJobsComponent },
|
||||
{ path: "company/editor", component: CompanyEditorComponent },
|
||||
|
||||
// Legal
|
||||
{ path: "about", component: AboutComponent },
|
||||
{ path: "contact", component: ContactComponent },
|
||||
{ path: "privacy", component: PrivacyComponent }
|
||||
|
||||
]
|
||||
@@ -13,10 +13,6 @@ import { isDevMode } from '@angular/core';
|
||||
})
|
||||
export class App {
|
||||
|
||||
@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){
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
.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;
|
||||
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">
|
||||
<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 class="content-frame">
|
||||
<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><a [href]="'mailto:' + Comp.email" >{{ Comp.email }}</a></div>
|
||||
<div><h1>{{ Comp.name }}</h1></div>
|
||||
<div><a [href]="Comp.websiteURL">{{ Comp.websiteURL }}</a></div>
|
||||
</div>
|
||||
<div class="center-item">
|
||||
<a [href]="Comp.websiteURL">
|
||||
<img [src]="Comp.logo" />
|
||||
</a>
|
||||
</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>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -6,6 +6,7 @@ import { Title } from '@angular/platform-browser';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Authentication } from 'app/services/Authentication';
|
||||
import { Company, Employee } from 'app/models/Company';
|
||||
import { JobListing } from 'app/models/JobListing';
|
||||
|
||||
@Component({
|
||||
selector: 'main-company',
|
||||
@@ -17,13 +18,22 @@ export class CompanyComponent {
|
||||
public ErrorMsg: string = "";
|
||||
|
||||
public Employers: Employee[] = [];
|
||||
|
||||
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 ) {
|
||||
this.title.setTitle("Companies | BoredCareers");
|
||||
http.get<Employee[]>("api/employee/").subscribe({
|
||||
next: data => {
|
||||
this.Employers = data;
|
||||
if (data[0] != null){
|
||||
if (data[0].company.id !== null){
|
||||
this.changeSelectedCompany(data[0].company.id);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: err => {
|
||||
this.ErrorMsg = err.error;
|
||||
@@ -36,6 +46,27 @@ export class CompanyComponent {
|
||||
this.http.get<Company>("api/company?CompanyID=" + companyID).subscribe({
|
||||
next: 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 => {
|
||||
this.ErrorMsg = err.error;
|
||||
|
||||
+9
-6
@@ -8,12 +8,12 @@ import { Authentication } from 'app/services/Authentication';
|
||||
import { Company } from 'app/models/Company';
|
||||
|
||||
@Component({
|
||||
selector: 'main-company-connect',
|
||||
templateUrl: './companyconnect.component.html',
|
||||
styleUrls: [ './companyconnect.component.css' ],
|
||||
selector: 'main-company-editor',
|
||||
templateUrl: './editor.component.html',
|
||||
styleUrls: [ './editor.component.css' ],
|
||||
imports: [ FormsModule, CommonModule, RouterModule ]
|
||||
})
|
||||
export class CompanyConnectComponent {
|
||||
export class CompanyEditorComponent {
|
||||
|
||||
@ViewChildren('step') formSteps!: QueryList<ElementRef<HTMLDivElement>>;
|
||||
currentStep: number = 0;
|
||||
@@ -23,7 +23,10 @@ export class CompanyConnectComponent {
|
||||
MaxFileMB: number = 3;
|
||||
|
||||
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(){
|
||||
@@ -35,7 +38,7 @@ export class CompanyConnectComponent {
|
||||
|
||||
@HostListener('window:keydown', ['$event'])
|
||||
handleGlobalKeyDown(event: KeyboardEvent){
|
||||
if (event.key === 'Tab'){
|
||||
if ( event.key === 'Tab' ){
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
<h1>POST A NEW JOB</h1>
|
||||
</div>
|
||||
<form (ngSubmit)="PostJobListing(newListing)">
|
||||
|
||||
<!-- 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>
|
||||
<form (ngSubmit)="SubmitForm(Listing)">
|
||||
|
||||
<!-- Title -->
|
||||
<div #step class="sub-frame">
|
||||
<div class="center">
|
||||
<div class="content-frame">
|
||||
<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)="nextStep()">Next</button>
|
||||
</div>
|
||||
@@ -40,7 +21,7 @@
|
||||
<div class="content-frame split">
|
||||
<div class="half-frame">
|
||||
<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="Part-time">Part-time</option>
|
||||
<option value="Contract">Contract</option>
|
||||
@@ -50,7 +31,7 @@
|
||||
</div>
|
||||
<div class="half-frame">
|
||||
<label>Remote Position</label>
|
||||
<input name="remote" [(ngModel)]="newListing.remote" type="checkbox" />
|
||||
<input name="remote" [(ngModel)]="Listing.remote" type="checkbox" />
|
||||
</div>
|
||||
<button type="button" (click)="prevStep()">Back</button>
|
||||
<button type="button" (click)="nextStep()">Next</button>
|
||||
@@ -59,28 +40,28 @@
|
||||
</div>
|
||||
|
||||
<!-- Location -->
|
||||
<div #step *ngIf="!newListing.remote" class="sub-frame">
|
||||
<div #step *ngIf="!Listing.remote" class="sub-frame">
|
||||
<div class="center">
|
||||
<h2>Job Location</h2>
|
||||
<div>
|
||||
<div class="content-frame split" style="border-radius: 10px 10px 0 0;">
|
||||
<div class="half-frame">
|
||||
<label>City</label>
|
||||
<input name="city" [(ngModel)]="newListing.city" type="text" />
|
||||
<input name="city" [(ngModel)]="Listing.city" type="text" />
|
||||
</div>
|
||||
<div class="half-frame">
|
||||
<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 class="content-frame split" style="border-radius: 0 0 10px 10px;">
|
||||
<div class="half-frame">
|
||||
<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 class="half-frame">
|
||||
<label>Postal Code</label>
|
||||
<input name="postalCode" [(ngModel)]="newListing.postalCode" type="text" />
|
||||
<input name="postalCode" [(ngModel)]="Listing.postalCode" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" (click)="prevStep()">Back</button>
|
||||
@@ -96,11 +77,11 @@
|
||||
<div class="content-frame split">
|
||||
<div class="half-frame">
|
||||
<label>Minimum Salary</label>
|
||||
<input name="salaryMin" [(ngModel)]="newListing.salaryMin" type="number" />
|
||||
<input name="salaryMin" [(ngModel)]="Listing.salaryMin" type="number" />
|
||||
</div>
|
||||
<div class="half-frame">
|
||||
<label>Maximum Salary</label>
|
||||
<input name="salaryMax" [(ngModel)]="newListing.salaryMax" type="number" />
|
||||
<input name="salaryMax" [(ngModel)]="Listing.salaryMax" type="number" />
|
||||
</div>
|
||||
<button type="button" (click)="prevStep()">Back</button>
|
||||
<button type="button" (click)="nextStep()">Next</button>
|
||||
@@ -113,7 +94,7 @@
|
||||
<div class="center">
|
||||
<div class="content-frame">
|
||||
<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)="nextStep()">Next</button>
|
||||
</div>
|
||||
|
||||
@@ -15,31 +15,46 @@ import { Company, Employee } from 'app/models/Company';
|
||||
imports: [ FormsModule, CommonModule, RouterModule ]
|
||||
})
|
||||
export class JobEditorComponent {
|
||||
public ErrorMsg: string = "";
|
||||
|
||||
@ViewChildren('step') formSteps!: QueryList<ElementRef<HTMLDivElement>>;
|
||||
currentStep: number = 0;
|
||||
|
||||
public employeeOfList: Employee[] = [];
|
||||
public selectedCompany: Company = new Company;
|
||||
public Listing: JobListing = new JobListing();
|
||||
|
||||
public newListing: JobListing = new JobListing();
|
||||
public ErrorMsg: string = "";
|
||||
public mode: string = "";
|
||||
public modeID: number = 0;
|
||||
|
||||
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
|
||||
this.title.setTitle("Jobs - Editor | BoredCareers");
|
||||
|
||||
this.http.get<Employee[]>("api/employee").subscribe({
|
||||
next: empOf => {
|
||||
if (empOf.length === 0){
|
||||
router.navigate(["company/connect"]);
|
||||
this.route.queryParams.subscribe(params => {
|
||||
const CompanyID = params['CompanyID'] ? +params['CompanyID'] : null;
|
||||
const JobID = params['JobID'] ? +params['JobID'] : null;
|
||||
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 => {
|
||||
this.ErrorMsg = err.error;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit(){
|
||||
this.formSteps.changes.subscribe(() => {
|
||||
@@ -70,8 +85,8 @@ export class JobEditorComponent {
|
||||
this.updateUI();
|
||||
}
|
||||
|
||||
PostJobListing(jobListing: JobListing){
|
||||
jobListing.companyID = this.selectedCompany.id!;
|
||||
PostNewJob(jobListing: JobListing){
|
||||
jobListing.companyID = this.modeID;
|
||||
this.http.post("api/joblisting", jobListing).subscribe({
|
||||
next: data => {
|
||||
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-title">
|
||||
<h1>{{ cur.title }}</h1>
|
||||
<h2>${{ cur.salaryMax }} - ${{ cur.salaryMin }}</h2>
|
||||
<h2>${{ cur.salaryMin }} - ${{ cur.salaryMax }}</h2>
|
||||
</div>
|
||||
<div class="tile-split">
|
||||
<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 {
|
||||
|
||||
background-color: #3c3c3c;
|
||||
}
|
||||
|
||||
.job-timestamp {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.job-timestamp h1 {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -1,21 +1,27 @@
|
||||
<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.logo }}</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 class="center-item">
|
||||
<a [href]="jobsCompany.websiteURL">
|
||||
<img [src]="jobsCompany.logo" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="center-item">
|
||||
<div class="content-link"><a [href]="'mailto:' + jobsCompany.email" >{{ jobsCompany.email }}</a></div>
|
||||
<div class="content-name"><h1>{{ jobsCompany.name }}</h1></div>
|
||||
<div class="content-link"><a [href]="'tel:' + jobsCompany.phone">{{ jobsCompany.phone }}</a></div>
|
||||
</div>
|
||||
<div class="center-item">
|
||||
<h1>{{ jobsCompany.city }}, {{ jobsCompany.stateOrRegion }} {{ jobsCompany.postalCode }}</h1>
|
||||
</div>
|
||||
<div class="content-desc">
|
||||
<h1 *ngFor="let line of jobsCompany.description.split('\n')">{{ line }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<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" >
|
||||
<h2>THIS JOB POSTING IS CLOSED</h2>
|
||||
@@ -35,8 +41,5 @@
|
||||
<h1>{{ selectedJob.postalCode }}</h1>
|
||||
|
||||
<h1>{{ selectedJob.description }}</h1>
|
||||
|
||||
<h1>{{ selectedJob.createdTime }}</h1>
|
||||
<h1>{{ selectedJob.modifiedTime }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2,19 +2,25 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using BoredCareers.Services.DatabaseService;
|
||||
using BoredCareers.Entities;
|
||||
using System.Web.Http;
|
||||
using BoredCareers.Services;
|
||||
|
||||
namespace BoredCareers.Controllers {
|
||||
[ApiController]
|
||||
[Route("api/company")]
|
||||
public class CompanyController : MistoxControllerBase {
|
||||
|
||||
public CompanyController(DatabaseService db) : base(db) {}
|
||||
EmailService _emailContext;
|
||||
|
||||
public CompanyController(DatabaseService db, EmailService emailContext) : base(db) {
|
||||
_emailContext = emailContext;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetCompany(int CompanyID) {
|
||||
if (isLoggedIn()) {
|
||||
Company? company = await _databaseService.GetCompany(CompanyID);
|
||||
if (company != null) {
|
||||
company.EmailToken = "";
|
||||
return Ok(company);
|
||||
}
|
||||
return NotFound("Company doesn't exist");
|
||||
@@ -59,6 +65,57 @@ namespace BoredCareers.Controllers {
|
||||
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 Email { get; set; } = "";
|
||||
public bool EmailVerified { get; set; } = false;
|
||||
public string EmailToken { get; set; } = "";
|
||||
public string WebsiteURL { get; set; } = "";
|
||||
public string Logo { get; set; } = "";
|
||||
public int JobsClosedSuccessful { get; set; }
|
||||
|
||||
@@ -11,6 +11,17 @@ namespace BoredCareers.Services.TimerService {
|
||||
_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) {
|
||||
while (!stoppingToken.IsCancellationRequested) {
|
||||
try {
|
||||
@@ -37,7 +48,10 @@ namespace BoredCareers.Services.TimerService {
|
||||
string[] emails = await _db.GetApplicationResponseEmailFromJobListing(listing.JobListingID);
|
||||
foreach (string email in emails) {
|
||||
// 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 _email = reader.GetString("Email");
|
||||
bool _emailVerified = reader.GetBoolean("EmailVerified");
|
||||
string _emailtoken = reader.GetString("EmailToken");
|
||||
string _websiteurl = reader.GetString("WebsiteURL");
|
||||
string _logo = Encoding.UTF8.GetString((byte[])reader["Logo"]);
|
||||
int _jobsclosedsuccessful = reader.GetInt32("JobsClosedSuccessful");
|
||||
@@ -42,6 +43,7 @@ namespace BoredCareers.Services.DatabaseService {
|
||||
Name = _name,
|
||||
Email = _email,
|
||||
EmailVerified = _emailVerified,
|
||||
EmailToken = _emailtoken,
|
||||
WebsiteURL = _websiteurl,
|
||||
Logo = _logo,
|
||||
JobsAutoClosed = _jobsautoclosed,
|
||||
@@ -64,13 +66,14 @@ namespace BoredCareers.Services.DatabaseService {
|
||||
await connection.OpenAsync();
|
||||
string command = @"
|
||||
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
|
||||
(@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
|
||||
Name = @Name,
|
||||
Email = @Email,
|
||||
EmailVerified = @EmailVerified,
|
||||
EmailToken = @EmailToken,
|
||||
WebsiteURL = @WebsiteURL,
|
||||
Logo = @Logo,
|
||||
JobsClosedSuccessful = @JobsClosedSuccessful,
|
||||
@@ -90,6 +93,7 @@ namespace BoredCareers.Services.DatabaseService {
|
||||
cmd.Parameters.AddWithValue("@Name", company.Name);
|
||||
cmd.Parameters.AddWithValue("@Email", company.Email);
|
||||
cmd.Parameters.AddWithValue("@EmailVerified", company.EmailVerified);
|
||||
cmd.Parameters.AddWithValue("@EmailToken", company.EmailToken);
|
||||
cmd.Parameters.AddWithValue("@WebsiteURL", company.WebsiteURL);
|
||||
cmd.Parameters.AddWithValue("@Logo", Encoding.UTF8.GetBytes(company.Logo));
|
||||
cmd.Parameters.AddWithValue("@JobsClosedSuccessful", company.JobsClosedSuccessful);
|
||||
|
||||
@@ -73,7 +73,8 @@ namespace BoredCareers.Services.DatabaseService {
|
||||
string command = @"
|
||||
SELECT *
|
||||
FROM JobListing
|
||||
WHERE CompanyID = @CompanyID;
|
||||
WHERE IsDeleted = FALSE
|
||||
AND CompanyID = @CompanyID;
|
||||
";
|
||||
|
||||
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
|
||||
|
||||
public static string JobAutoClosedSubject = "Verify Your Email Address";
|
||||
public static string JobAutoClosedEmail = @"
|
||||
public static string JobAutoClosedBody = @"
|
||||
<!DOCTYPE html>
|
||||
<html lang=""en"">
|
||||
<head>
|
||||
|
||||
Reference in New Issue
Block a user