Merge pull request 'working' (#40) from working into main
Docker Build and Release Upload / build (push) Successful in 1m30s
Docker Build and Release Upload / build (push) Successful in 1m30s
Reviewed-on: #40
This commit was merged in pull request #40.
This commit is contained in:
@@ -15,6 +15,19 @@ Server:
|
|||||||
Emails:
|
Emails:
|
||||||
Make emails follow theme of website better
|
Make emails follow theme of website better
|
||||||
|
|
||||||
|
JobListingController:
|
||||||
|
Dont refresh on every filter edit
|
||||||
|
Line 63 is terrible and need to be fixed in the JobListing DB JobListingController
|
||||||
|
bools and numbers are getting strigified which breaks the mysql parameters
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
Alot of the validation is only taking place client side.
|
||||||
|
Need to validate all inputs before processing
|
||||||
|
Phone number
|
||||||
|
Email Address
|
||||||
|
City, CountryCode, PostalCode
|
||||||
|
When applying to a job, The server doesnt make sure that the company only fields are not modified
|
||||||
|
|
||||||
Client:
|
Client:
|
||||||
jobs/editor:
|
jobs/editor:
|
||||||
Want to add completed job listing preview at end of carosel
|
Want to add completed job listing preview at end of carosel
|
||||||
@@ -29,6 +42,8 @@ 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
|
||||||
Dont allow users to apply to the same job more than once
|
Dont allow users to apply to the same job more than once
|
||||||
|
Need to add 'job field' to job for better filtering
|
||||||
|
Need to add 'clear filter button'
|
||||||
|
|
||||||
resume/viewer:
|
resume/viewer:
|
||||||
CSS is broken
|
CSS is broken
|
||||||
|
|||||||
+3
-6
@@ -193,8 +193,8 @@ CREATE TABLE IF NOT EXISTS `PostalCodes` (
|
|||||||
`StateCode` varchar(20),
|
`StateCode` varchar(20),
|
||||||
`County` varchar(100),
|
`County` varchar(100),
|
||||||
`CountyCode` varchar(20),
|
`CountyCode` varchar(20),
|
||||||
`Admin` varchar(100),
|
`Community` varchar(100),
|
||||||
`AdminCode` varchar(20),
|
`CommunityCode` varchar(20),
|
||||||
`Latitude` float,
|
`Latitude` float,
|
||||||
`Longitude` float,
|
`Longitude` float,
|
||||||
`Accuracy` varchar(2)
|
`Accuracy` varchar(2)
|
||||||
@@ -206,10 +206,7 @@ FIELDS TERMINATED BY '\t'
|
|||||||
ENCLOSED BY '"'
|
ENCLOSED BY '"'
|
||||||
LINES TERMINATED BY '\n';
|
LINES TERMINATED BY '\n';
|
||||||
|
|
||||||
CREATE INDEX idx_country_code ON PostalCodes(CountryCode);
|
CREATE INDEX idx_postal_country ON PostalCodes (City, PostalCode, CountryCode, Latitude, Longitude);
|
||||||
CREATE INDEX idx_postal_code ON PostalCodes(PostalCode);
|
|
||||||
CREATE INDEX idx_latitude ON PostalCodes(Latitude);
|
|
||||||
CREATE INDEX idx_longitude ON PostalCodes(Longitude);
|
|
||||||
|
|
||||||
-- Application Section
|
-- Application Section
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export class JobFilter {
|
||||||
|
public JobsPerPage: number = 20;
|
||||||
|
public CurrentPage: number = 1;
|
||||||
|
public CountryCode: string | null = null;
|
||||||
|
public PostalCode: string | null = null;
|
||||||
|
public Distance: number | null = null;
|
||||||
|
public JobType: string | null = null;
|
||||||
|
public Remote: boolean | null = null;
|
||||||
|
public SalaryMin: number | null = null;
|
||||||
|
public SalaryMax: number | null = null;
|
||||||
|
}
|
||||||
@@ -1,36 +1,56 @@
|
|||||||
button {
|
.primary-button {
|
||||||
height: 45px;
|
height: 45px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 15px 20px;
|
padding: 15px 20px;
|
||||||
transition: 0.5s;
|
transition: 0.5s;
|
||||||
background-color: #00000000;
|
background-color: var(--mistox-button-primary);
|
||||||
border: 1px solid var(--Mistox-Black);
|
border: 1px solid var(--mistox-button-primary);
|
||||||
color: var(--Mistox-Black);
|
color: var(--mistox-button-text);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
.primary-button:hover {
|
||||||
background-color: #00000044;
|
background-color: var(--mistox-button-primary-click);
|
||||||
color: var(--Mistox-Light);
|
}
|
||||||
|
|
||||||
|
.secondary-button {
|
||||||
|
height: 45px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 10px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 15px 20px;
|
||||||
|
transition: 0.5s;
|
||||||
|
background-color: var(--mistox-button-secondary);
|
||||||
|
border: 1px solid var(--mistox-button-secondary);
|
||||||
|
color: var(--mistox-button-text);
|
||||||
|
text-decoration: none;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-button:hover {
|
||||||
|
background-color: var(--mistox-button-secondary-click);
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-bar {
|
.top-bar {
|
||||||
width: 100%;
|
display: flex;
|
||||||
height: 60px;
|
break-inside: avoid;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 20px;
|
||||||
|
background-color: var(--mistox-bg-medium);
|
||||||
|
border: 1px solid var(--mistox-border);
|
||||||
|
box-shadow: var(--mistox-shadow);
|
||||||
|
color: var(--mistox-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-frame {
|
.content-frame {
|
||||||
background-color: #3c3c3c;
|
max-width: 1800px;
|
||||||
width: calc(100% - 40px);
|
|
||||||
height: calc(100vh - 400px);
|
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
margin: 10px;
|
|
||||||
overflow: scroll;
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
color: var(--Mistox-White);
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.center-item {
|
.center-item {
|
||||||
@@ -40,7 +60,7 @@ button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content-edit {
|
.content-edit {
|
||||||
position: absolute;
|
position: relative;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,22 +86,23 @@ button {
|
|||||||
|
|
||||||
.content-link a {
|
.content-link a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--Mistox-White);
|
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-desc {
|
.content-desc {
|
||||||
border: solid 1px red;
|
border: solid 1px var(--mistox-border);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin: 0 100px;
|
margin: 0 100px;
|
||||||
|
margin-bottom: 0px;
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
|
background-color: var(--mistox-bg-medium);
|
||||||
|
color: var(--mistox-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-desc h1 {
|
.content-desc h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: #ddd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-button {
|
.content-button {
|
||||||
@@ -100,8 +121,6 @@ button {
|
|||||||
|
|
||||||
.half-frame {
|
.half-frame {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
border-right: solid 1px var(--Mistox-Black);
|
|
||||||
border-left: solid 1px var(--Mistox-Black);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.half-frame h2 {
|
.half-frame h2 {
|
||||||
@@ -110,7 +129,8 @@ button {
|
|||||||
|
|
||||||
.job-tile {
|
.job-tile {
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: var(--Mistox-Black);
|
background-color: var(--mistox-bg-medium);
|
||||||
|
border: solid 1px var(--mistox-border-dark);
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -127,8 +147,3 @@ button {
|
|||||||
.job-tile h1 {
|
.job-tile h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-tile button {
|
|
||||||
color: white;
|
|
||||||
border-color: white;
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
<div class="top-bar">
|
<div class="top-bar">
|
||||||
@for(company of Employers; track company.accountID){
|
@for(company of Employers; track company.accountID){
|
||||||
<button (click)="changeSelectedCompany(company.company.id!)">{{ company.company.name.toUpperCase() }}</button>
|
<button class="secondary-button" (click)="changeSelectedCompany(company.company.id!)">{{ company.company.name.toUpperCase() }}</button>
|
||||||
}
|
}
|
||||||
<button routerLink="/company/editor" >CONNECT A COMPANY</button>
|
<button class="primary-button" routerLink="/company/editor" >CONNECT A COMPANY</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-frame">
|
<div class="content-frame">
|
||||||
@if(Comp != null){
|
@if(Comp != null){
|
||||||
<div>
|
<div>
|
||||||
<button class="content-edit" style="color: #fff; border-color: #fff;" routerLink="/company/editor" [queryParams]="{ CompanyID: Comp.id }" >EDIT COMPANY</button>
|
<button class="primary-button content-edit" routerLink="/company/editor" [queryParams]="{ CompanyID: Comp.id }" >EDIT COMPANY</button>
|
||||||
<div class="center-item">
|
<div class="center-item">
|
||||||
<a [href]="Comp.websiteURL">
|
<a [href]="Comp.websiteURL">
|
||||||
<img [src]="Comp.logo" />
|
<img [src]="Comp.logo" />
|
||||||
@@ -31,11 +31,11 @@
|
|||||||
<div class="half-frame">
|
<div class="half-frame">
|
||||||
@if (Comp.emailVerified){
|
@if (Comp.emailVerified){
|
||||||
<div class="content-button">
|
<div class="content-button">
|
||||||
<button style="color: #fff; border-color: #fff;" routerLink="/jobs/editor" [queryParams]="{ CompanyID: Comp.id }" >POST JOB</button>
|
<button class="primary-button" routerLink="/jobs/editor" [queryParams]="{ CompanyID: Comp.id }" >POST JOB</button>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<div class="content-button">
|
<div class="content-button">
|
||||||
<a style="color: #fff; border-color: #fff;" [href]="'/api/company/sendverifyemail?CompanyID=' + Comp.id" >VERIFY EMAIL></a>
|
<a class="primary-button" [href]="'/api/company/sendverifyemail?CompanyID=' + Comp.id" >VERIFY EMAIL></a>
|
||||||
<span>You must verify your company email before you can post job listings.</span>
|
<span>You must verify your company email before you can post job listings.</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -46,21 +46,21 @@
|
|||||||
<div class="center-text">
|
<div class="center-text">
|
||||||
<h1>{{ listing.title }}</h1>
|
<h1>{{ listing.title }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<button [routerLink]="['/application/viewer']" [queryParams]="{ JobID: listing.id }" >VIEW Applicants</button>
|
<button class="secondary-button" [routerLink]="['/application/viewer']" [queryParams]="{ JobID: listing.id }" >VIEW Applicants</button>
|
||||||
<button [routerLink]="['/jobs/viewer']" [queryParams]="{ JobID: listing.id }" >VIEW LISTING</button>
|
<button class="secondary-button" [routerLink]="['/jobs/viewer']" [queryParams]="{ JobID: listing.id }" >VIEW LISTING</button>
|
||||||
<button [routerLink]="['/jobs/editor']" [queryParams]="{ JobID: listing.id }" >EDIT LISTING</button>
|
<button class="secondary-button" [routerLink]="['/jobs/editor']" [queryParams]="{ JobID: listing.id }" >EDIT LISTING</button>
|
||||||
<button (click)="RemoveJobListing(listing.id!)">DELETE LISTING</button>
|
<button class="secondary-button" (click)="RemoveJobListing(listing.id!)">DELETE LISTING</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="half-frame">
|
<div class="half-frame">
|
||||||
@if (Comp.emailVerified){
|
@if (Comp.emailVerified){
|
||||||
<div class="content-button">
|
<div class="content-button">
|
||||||
<button style="color: #fff; border-color: #fff;" routerLink="/jobs/editor" [queryParams]="{ CompanyID: Comp.id }" >ADD EMPLOYEE</button>
|
<button class="primary-button" routerLink="/jobs/editor" [queryParams]="{ CompanyID: Comp.id }" >ADD EMPLOYEE</button>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<div class="content-button">
|
<div class="content-button">
|
||||||
<a style="color: #fff; border-color: #fff;" [href]="'/api/company/sendverifyemail?CompanyID=' + Comp.id" >VERIFY EMAIL></a>
|
<a class="primary-button" [href]="'/api/company/sendverifyemail?CompanyID=' + Comp.id" >VERIFY EMAIL></a>
|
||||||
<span>You must verify your company email before you can post job listings.</span>
|
<span>You must verify your company email before you can post job listings.</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -72,9 +72,9 @@
|
|||||||
<h1>{{ listing.accountName }}</h1>
|
<h1>{{ listing.accountName }}</h1>
|
||||||
</div>
|
</div>
|
||||||
@if (listing.accountID != auth.loggedInUser.id){
|
@if (listing.accountID != auth.loggedInUser.id){
|
||||||
<button (click)="RemoveJobListing(listing.id!)">Remove</button>
|
<button class="secondary-button" (click)="RemoveJobListing(listing.id!)">Remove</button>
|
||||||
} @else {
|
} @else {
|
||||||
<button disabled>SELF</button>
|
<button class="secondary-button" disabled>SELF</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,34 @@ button {
|
|||||||
background-color: var(--mistox-button-primary-click);
|
background-color: var(--mistox-button-primary-click);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.top-bar {
|
||||||
|
display: flex;
|
||||||
|
break-inside: avoid;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 20px;
|
||||||
|
background-color: var(--mistox-bg-medium);
|
||||||
|
border: 1px solid var(--mistox-border);
|
||||||
|
box-shadow: var(--mistox-shadow);
|
||||||
|
color: var(--mistox-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-bar-sub {
|
||||||
|
display: flex;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-bar-sub :nth-child(1) {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-bar-sub :nth-child(2) {
|
||||||
|
margin-right: 50px;
|
||||||
|
margin-left: 5px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.full-width {
|
.full-width {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -1,3 +1,37 @@
|
|||||||
|
<!-- Filter Bar -->
|
||||||
|
<div class="top-bar">
|
||||||
|
<div class="top-bar-sub">
|
||||||
|
<h1>Country</h1>
|
||||||
|
<input name="CountryCode" [(ngModel)]="currentFilter.CountryCode" (ngModelChange)="reloadFilters()" type="text" />
|
||||||
|
</div>
|
||||||
|
<div class="top-bar-sub">
|
||||||
|
<h1>Postal Code</h1>
|
||||||
|
<input name="PostalCode" [(ngModel)]="currentFilter.PostalCode" (ngModelChange)="reloadFilters()" type="text" />
|
||||||
|
</div>
|
||||||
|
@if (currentFilter.CountryCode != null && currentFilter.CountryCode !== "" && currentFilter.PostalCode != null && currentFilter.PostalCode !== ""){
|
||||||
|
<div class="top-bar-sub">
|
||||||
|
<h1>Distance</h1>
|
||||||
|
<input name="Distance" [(ngModel)]="currentFilter.Distance" (ngModelChange)="reloadFilters()" type="number" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="top-bar-sub">
|
||||||
|
<h1>Job Type</h1>
|
||||||
|
<input name="CountryCode" [(ngModel)]="currentFilter.JobType" (ngModelChange)="reloadFilters()" type="text" />
|
||||||
|
</div>
|
||||||
|
<div class="top-bar-sub">
|
||||||
|
<h1>Remote</h1>
|
||||||
|
<input name="Remote" [(ngModel)]="currentFilter.Remote" (ngModelChange)="reloadFilters()" type="checkbox" />
|
||||||
|
</div>
|
||||||
|
<div class="top-bar-sub">
|
||||||
|
<h1>Minimum Salary</h1>
|
||||||
|
<input name="SalaryMin" [(ngModel)]="currentFilter.SalaryMin" (ngModelChange)="reloadFilters()" type="number" />
|
||||||
|
</div>
|
||||||
|
<div class="top-bar-sub">
|
||||||
|
<h1>Maximum Salary</h1>
|
||||||
|
<input name="SalaryMax" [(ngModel)]="currentFilter.SalaryMax" (ngModelChange)="reloadFilters()" type="number" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Avaliable Jobs -->
|
<!-- Avaliable Jobs -->
|
||||||
<div class="tile-frame">
|
<div class="tile-frame">
|
||||||
@for (cur of JobListingPage; track cur.id){
|
@for (cur of JobListingPage; track cur.id){
|
||||||
@@ -21,4 +55,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
<div>
|
||||||
|
<h1>Jobs Per Page</h1>
|
||||||
|
<input name="JobsPerPage" [(ngModel)]="currentFilter.JobsPerPage" (ngModelChange)="reloadFilters()" type="number" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -6,6 +6,7 @@ import { Title } from '@angular/platform-browser';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { JobListing } from 'app/models/JobListing';
|
import { JobListing } from 'app/models/JobListing';
|
||||||
import { Authentication } from 'app/services/Authentication';
|
import { Authentication } from 'app/services/Authentication';
|
||||||
|
import { JobFilter } from 'app/models/JobFilter';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'main-jobs',
|
selector: 'main-jobs',
|
||||||
@@ -15,7 +16,7 @@ import { Authentication } from 'app/services/Authentication';
|
|||||||
})
|
})
|
||||||
export class JobsComponent {
|
export class JobsComponent {
|
||||||
|
|
||||||
public MyJobListings: JobListing[] = [];
|
public currentFilter: JobFilter;
|
||||||
public JobListingPage: JobListing[] = [];
|
public JobListingPage: JobListing[] = [];
|
||||||
public ErrorMsg: string = "";
|
public ErrorMsg: string = "";
|
||||||
|
|
||||||
@@ -23,10 +24,50 @@ export class JobsComponent {
|
|||||||
|
|
||||||
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 | BoredCareers");
|
this.title.setTitle("Jobs | BoredCareers");
|
||||||
|
this.currentFilter = new JobFilter();
|
||||||
};
|
};
|
||||||
|
|
||||||
ngOnInit(){
|
ngOnInit(){
|
||||||
this.http.get<JobListing[]>("api/joblisting?PageQuantity=" + 10 + "&Page=" + 1).subscribe({
|
this.http.get<JobListing[]>("api/joblisting?PageQuantity=" + this.currentFilter.JobsPerPage + "&Page=" + this.currentFilter.CurrentPage).subscribe({
|
||||||
|
next: data => {
|
||||||
|
this.JobListingPage = data;
|
||||||
|
},
|
||||||
|
error: err => {
|
||||||
|
this.ErrorMsg = err.error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadFilters(){
|
||||||
|
var queryBuilder = "api/joblisting?PageQuantity=" + this.currentFilter.JobsPerPage + "&Page=" + this.currentFilter.CurrentPage;
|
||||||
|
|
||||||
|
if ( this.currentFilter.PostalCode === "" || this.currentFilter.CountryCode === "" ){
|
||||||
|
this.currentFilter.Distance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.currentFilter.PostalCode != null){
|
||||||
|
queryBuilder += "&PC=" + this.currentFilter.PostalCode;
|
||||||
|
}
|
||||||
|
if (this.currentFilter.CountryCode != null){
|
||||||
|
queryBuilder += "&CC=" + this.currentFilter.CountryCode;
|
||||||
|
}
|
||||||
|
if (this.currentFilter.Distance != null){
|
||||||
|
queryBuilder += "&D=" + this.currentFilter.Distance;
|
||||||
|
}
|
||||||
|
if (this.currentFilter.JobType != null){
|
||||||
|
queryBuilder += "&JT=" + this.currentFilter.JobType;
|
||||||
|
}
|
||||||
|
if (this.currentFilter.Remote != null){
|
||||||
|
queryBuilder += "&R=" + this.currentFilter.Remote;
|
||||||
|
}
|
||||||
|
if (this.currentFilter.SalaryMin != null){
|
||||||
|
queryBuilder += "&SMI=" + this.currentFilter.SalaryMin;
|
||||||
|
}
|
||||||
|
if (this.currentFilter.SalaryMax != null){
|
||||||
|
queryBuilder += "&SMA=" + this.currentFilter.SalaryMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.http.get<JobListing[]>(queryBuilder).subscribe({
|
||||||
next: data => {
|
next: data => {
|
||||||
this.JobListingPage = data;
|
this.JobListingPage = data;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.company-details {
|
.company-details {
|
||||||
background-color: #5c3030;
|
background-color: var(--mistox-bg-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.company-details::after {
|
.company-details::after {
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.job-details {
|
.job-details {
|
||||||
background-color: #3c3c3c;
|
padding-bottom: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-timestamp {
|
.job-timestamp {
|
||||||
|
|||||||
@@ -1,26 +1,5 @@
|
|||||||
<div class="job-frame">
|
<div class="job-frame">
|
||||||
@if (jobsCompany != null){
|
|
||||||
<div class="company-details">
|
|
||||||
<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">
|
|
||||||
@for(line of jobsCompany.description.split('\n'); track line.length){
|
|
||||||
<h1>{{ line }}</h1>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@if (selectedJob != null) {
|
@if (selectedJob != null) {
|
||||||
<div class="job-details">
|
<div class="job-details">
|
||||||
@if (selectedJob.isDeleted){
|
@if (selectedJob.isDeleted){
|
||||||
@@ -32,6 +11,12 @@
|
|||||||
<h1>{{ selectedJob.title }}</h1>
|
<h1>{{ selectedJob.title }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="split">
|
<div class="split">
|
||||||
|
<div class="nobreak">
|
||||||
|
<h1>Job Description: </h1>
|
||||||
|
<div class="description-box">
|
||||||
|
<h1>{{ selectedJob.description }}</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="nobreak">
|
<div class="nobreak">
|
||||||
<h1>Job Details:</h1>
|
<h1>Job Details:</h1>
|
||||||
<div class="detail-block">
|
<div class="detail-block">
|
||||||
@@ -78,12 +63,6 @@
|
|||||||
<span>Opened: {{ selectedJob.createdTime }} | Last Updated: {{ selectedJob.modifiedTime }}</span>
|
<span>Opened: {{ selectedJob.createdTime }} | Last Updated: {{ selectedJob.modifiedTime }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="nobreak">
|
|
||||||
<h1>Job Description: </h1>
|
|
||||||
<div class="description-box">
|
|
||||||
<h1>{{ selectedJob.description }}</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bottom-bar">
|
<div class="bottom-bar">
|
||||||
@@ -93,4 +72,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (jobsCompany != null){
|
||||||
|
<div class="company-details">
|
||||||
|
<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">
|
||||||
|
@for(line of jobsCompany.description.split('\n'); track line.length){
|
||||||
|
<h1>{{ line }}</h1>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -74,16 +74,31 @@ export class Validation {
|
|||||||
///////// HELPER FUNCTIONS /////////
|
///////// HELPER FUNCTIONS /////////
|
||||||
|
|
||||||
isPrivateIPv6(ip: string): boolean {
|
isPrivateIPv6(ip: string): boolean {
|
||||||
try {
|
try {
|
||||||
const normalized = ip.replace(/^\[|\]$/g, '').toLowerCase();
|
const normalized = ip.replace(/^\[|\]$/g, '').toLowerCase();
|
||||||
if (normalized === '::1') return true;
|
if (normalized === '::1') return true;
|
||||||
if (normalized.startsWith('fc') || normalized.startsWith('fd')) return true;
|
if (normalized.startsWith('fc') || normalized.startsWith('fd')) return true;
|
||||||
const first4 = normalized.slice(0, 4);
|
const first4 = normalized.slice(0, 4);
|
||||||
if (first4 >= 'fe80' && first4 <= 'febf') return true;
|
if (first4 >= 'fe80' && first4 <= 'febf') return true;
|
||||||
return false;
|
return false;
|
||||||
} catch {
|
} catch {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////// GETTERS /////////
|
||||||
|
|
||||||
|
get ValidCountries(): string[] {
|
||||||
|
return ['AD', 'AE', 'AI', 'AL', 'AR', 'AS', 'AT', 'AU', 'AX', 'AZ', 'BD', 'BE', 'BG',
|
||||||
|
'BM', 'BR', 'BY', 'CA', 'CC', 'CH', 'CL', 'CN', 'CO', 'CR', 'CX', 'CY', 'CZ',
|
||||||
|
'DE', 'DK', 'DO', 'DZ', 'EC', 'EE', 'ES', 'FI', 'FK', 'FM', 'FO', 'FR', 'GB',
|
||||||
|
'GF', 'GG', 'GI', 'GL', 'GP', 'GS', 'GT', 'GU', 'HK', 'HM', 'HN', 'HR', 'HT',
|
||||||
|
'HU', 'ID', 'IE', 'IM', 'IN', 'IO', 'IS', 'IT', 'JE', 'JP', 'KE', 'KR', 'LI',
|
||||||
|
'LK', 'LT', 'LU', 'LV', 'MA', 'MC', 'MD', 'MH', 'MK', 'MO', 'MP', 'MQ', 'MT',
|
||||||
|
'MW', 'MX', 'MY', 'NC', 'NF', 'NL', 'NO', 'NR', 'NU', 'NZ', 'PA', 'PE', 'PF',
|
||||||
|
'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PT', 'PW', 'RE', 'RO', 'RS', 'RU', 'SE',
|
||||||
|
'SG', 'SI', 'SJ', 'SK', 'SM', 'TC', 'TH', 'TR', 'UA', 'US', 'UY', 'VA', 'VI',
|
||||||
|
'WF', 'WS', 'YT', 'ZA'];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+25
-18
@@ -12,6 +12,8 @@
|
|||||||
--mistox-border: oklch(0.6 0.13 264);
|
--mistox-border: oklch(0.6 0.13 264);
|
||||||
--mistox-border-dark: oklch(0.7 0.13 264);
|
--mistox-border-dark: oklch(0.7 0.13 264);
|
||||||
|
|
||||||
|
--mistox-button-text: oklch(1 0.00011 271.152);
|
||||||
|
|
||||||
--mistox-button-primary: oklch(0.4 0.13 264);
|
--mistox-button-primary: oklch(0.4 0.13 264);
|
||||||
--mistox-button-primary-click: oklch(0.3 0.13 264);
|
--mistox-button-primary-click: oklch(0.3 0.13 264);
|
||||||
|
|
||||||
@@ -27,28 +29,34 @@
|
|||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
dark-mode {
|
@media (prefers-color-scheme: dark) {
|
||||||
--mistox-bg-dark: oklch(0.1 0.065 264);
|
:root {
|
||||||
--mistox-bg-medium: oklch(0.15 0.065 264);
|
--mistox-bg-dark: oklch(0.1 0.065 264);
|
||||||
--mistox-bg-light: oklch(0.2 0.065 264);
|
--mistox-bg-medium: oklch(0.15 0.065 264);
|
||||||
|
--mistox-bg-light: oklch(0.2 0.065 264);
|
||||||
|
|
||||||
--mistox-text: oklch(0.96 0.1 264);
|
--mistox-text: oklch(0.96 0.1 264);
|
||||||
--mistox-text-sub: oklch(0.76 0.1 264);
|
--mistox-text-sub: oklch(0.76 0.1 264);
|
||||||
|
|
||||||
--mistox-border-light: oklch(0.5 0.13 264);
|
--mistox-border-light: oklch(0.5 0.13 264);
|
||||||
--mistox-border: oklch(0.4 0.13 264);
|
--mistox-border: oklch(0.4 0.13 264);
|
||||||
--mistox-border-dark: oklch(0.3 0.13 264);
|
--mistox-border-dark: oklch(0.3 0.13 264);
|
||||||
|
|
||||||
--mistox-button-primary: oklch(0.76 0.13 264);
|
--mistox-button-text: oklch(0 0.00011 271.152);
|
||||||
--mistox-button-secondary: oklch(0.76 0.13 84);
|
|
||||||
|
|
||||||
--mistox-alert-danger: oklch(0.7 0.13 30);
|
--mistox-button-primary: oklch(0.76 0.13 264);
|
||||||
--mistox-alert-warning: oklch(0.7 0.13 100);
|
--mistox-button-secondary: oklch(0.76 0.13 84);
|
||||||
--mistox-alert-success: oklch(0.7 0.13 160);
|
|
||||||
--mistox-alert-info: oklch(0.7 0.13 260);
|
|
||||||
|
|
||||||
--mistox-shadow: 0px 2px 2px oklch(0 0 0 / 0.2), 0px 4px 4px oklch(0 0 0 / 0.1);
|
--mistox-alert-danger: oklch(0.7 0.13 30);
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
--mistox-alert-warning: oklch(0.7 0.13 100);
|
||||||
|
--mistox-alert-success: oklch(0.7 0.13 160);
|
||||||
|
--mistox-alert-info: oklch(0.7 0.13 260);
|
||||||
|
|
||||||
|
--mistox-shadow: 0px 2px 2px oklch(0 0 0 / 0.2), 0px 4px 4px oklch(0 0 0 / 0.1);
|
||||||
|
|
||||||
|
color: #fff;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@@ -56,6 +64,5 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='49' viewBox='0 0 28 49'%3E%3Cg fill-rule='evenodd'%3E%3Cg id='hexagons' fill='%23999999' fill-opacity='0.2' fill-rule='nonzero'%3E%3Cpath d='M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
|
||||||
background-color: var(--mistox-bg-dark);
|
background-color: var(--mistox-bg-dark);
|
||||||
}
|
}
|
||||||
@@ -31,8 +31,25 @@ namespace BoredCareers.Controllers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> GetJobListings(int Page = 1, int PageQuantity = 25) {
|
public async Task<IActionResult> GetJobListings(string? CC, string? PC, float? D, string? JT, bool? R, int? SMI, int? SMA, int Page = 1, int PageQuantity = 25 ) {
|
||||||
JobListing[] jobListings = await _databaseService.GetJobListingPage(Page, PageQuantity);
|
|
||||||
|
// Get all relevant postal codes
|
||||||
|
List<string> PostalCodes = new List<string>();
|
||||||
|
if (PC != null && CC != null && D != null) {
|
||||||
|
Location[] nearby = await _databaseService.GetNearbyLocations(PC, CC, D.Value);
|
||||||
|
foreach (Location cur in nearby) {
|
||||||
|
PostalCodes.Add(cur.PostalCode);
|
||||||
|
}
|
||||||
|
} else if (PC != null) {
|
||||||
|
PostalCodes.Add(PC);
|
||||||
|
}
|
||||||
|
|
||||||
|
string[]? pc = null;
|
||||||
|
if (PostalCodes.Count > 0) {
|
||||||
|
pc = PostalCodes.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
JobListing[] jobListings = await _databaseService.GetJobListingPage(Page, PageQuantity, pc, CC, JT, R, SMI, SMA );
|
||||||
return Ok(jobListings);
|
return Ok(jobListings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using BoredCareers.Services.DatabaseService;
|
||||||
|
using BoredCareers.Entities;
|
||||||
|
|
||||||
|
namespace BoredCareers.Controllers {
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/location")]
|
||||||
|
public class LocationController : MistoxControllerBase {
|
||||||
|
|
||||||
|
public LocationController(DatabaseService db) : base(db) {}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetCompany(string PostalCode, string CountryCode, float MaxDistanceKm) {
|
||||||
|
if (isLoggedIn()) {
|
||||||
|
Location[] places = await _databaseService.GetNearbyLocations(PostalCode, CountryCode, MaxDistanceKm);
|
||||||
|
return Ok(places.ToArray());
|
||||||
|
}
|
||||||
|
return NotFound("Not logged in");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace BoredCareers.Entities {
|
||||||
|
public class Location {
|
||||||
|
public string City { get; set; } = "";
|
||||||
|
public string PostalCode { get; set; } = "";
|
||||||
|
public string CountryCode { get; set; } = "";
|
||||||
|
public float Latitude { get; set; }
|
||||||
|
public float Longitude { get; set; }
|
||||||
|
public float DistanceKM { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,21 +7,73 @@ using System.Data.Common;
|
|||||||
namespace BoredCareers.Services.DatabaseService {
|
namespace BoredCareers.Services.DatabaseService {
|
||||||
public partial class DatabaseService {
|
public partial class DatabaseService {
|
||||||
|
|
||||||
public async Task<JobListing[]> GetJobListingPage(int PageNumber, int CountPerPage) {
|
public async Task<JobListing[]> GetJobListingPage(int PageNumber, int CountPerPage, string[]? PostalCodes = null, string? CountryCode = null, string? JobType = null, bool? Remote = null, int? SalaryMin = null, int? SalaryMax = null ) {
|
||||||
List<JobListing> joblistings = new List<JobListing>();
|
List<JobListing> joblistings = new List<JobListing>();
|
||||||
using (MySqlConnection connection = GetConnection()) {
|
using (MySqlConnection connection = GetConnection()) {
|
||||||
await connection.OpenAsync();
|
await connection.OpenAsync();
|
||||||
string command = @"
|
|
||||||
SELECT *
|
|
||||||
FROM JobListing
|
|
||||||
WHERE IsDeleted = FALSE
|
|
||||||
ORDER BY CreatedTime DESC
|
|
||||||
LIMIT @PageSize OFFSET @PageNumber;
|
|
||||||
";
|
|
||||||
|
|
||||||
MySqlCommand cmd = new MySqlCommand(command, connection);
|
string select = "SELECT * FROM JobListing";
|
||||||
|
string order = " ORDER BY CreatedTime DESC";
|
||||||
|
string limit = " LIMIT @PageSize OFFSET @PageNumber;";
|
||||||
|
|
||||||
|
List<string> Filters = new List<string>();
|
||||||
|
List<object> Parameters = new List<object>();
|
||||||
|
List<string> ParameterName = new List<string>();
|
||||||
|
|
||||||
|
if (PostalCodes != null) {
|
||||||
|
for (int i = 0; i < PostalCodes.Length; i++) {
|
||||||
|
Filters.Add("PostalCode");
|
||||||
|
Parameters.Add(PostalCodes[i]);
|
||||||
|
ParameterName.Add(" = @PostalCode" + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CountryCode != null) {
|
||||||
|
Filters.Add("Country");
|
||||||
|
Parameters.Add(CountryCode);
|
||||||
|
ParameterName.Add(" = @CountryCode");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JobType != null) {
|
||||||
|
Filters.Add("JobType");
|
||||||
|
Parameters.Add(JobType);
|
||||||
|
ParameterName.Add(" = @JobType");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Remote != null) {
|
||||||
|
Filters.Add("Remote");
|
||||||
|
Parameters.Add(Remote.Value);
|
||||||
|
ParameterName.Add(" = @Remote");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SalaryMin != null) {
|
||||||
|
Filters.Add("SalaryMin");
|
||||||
|
Parameters.Add(SalaryMin.Value);
|
||||||
|
ParameterName.Add(" >= @SalaryMin");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SalaryMax != null) {
|
||||||
|
Filters.Add("SalaryMax");
|
||||||
|
Parameters.Add(SalaryMax.Value);
|
||||||
|
ParameterName.Add(" <= @SalaryMax");
|
||||||
|
}
|
||||||
|
|
||||||
|
string filter = " WHERE IsDeleted = False";
|
||||||
|
for (int i = 0; i < Filters.Count; i++) {
|
||||||
|
if (Filters[i] == "PostalCode" && i != 0) {
|
||||||
|
filter += " OR ";
|
||||||
|
} else {
|
||||||
|
filter += " AND ";
|
||||||
|
}
|
||||||
|
filter += Filters[i] + ParameterName[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
MySqlCommand cmd = new MySqlCommand(select + filter + order + limit, connection);
|
||||||
cmd.Parameters.AddWithValue("@PageSize", CountPerPage);
|
cmd.Parameters.AddWithValue("@PageSize", CountPerPage);
|
||||||
cmd.Parameters.AddWithValue("@PageNumber", (PageNumber - 1) * CountPerPage);
|
cmd.Parameters.AddWithValue("@PageNumber", (PageNumber - 1) * CountPerPage);
|
||||||
|
for (int i = 0; i < Filters.Count; i++) {
|
||||||
|
cmd.Parameters.AddWithValue( ParameterName[i].Split(' ').Last(), Parameters[i] );
|
||||||
|
}
|
||||||
|
|
||||||
using (DbDataReader reader = await cmd.ExecuteReaderAsync()) {
|
using (DbDataReader reader = await cmd.ExecuteReaderAsync()) {
|
||||||
while (await reader.ReadAsync()) {
|
while (await reader.ReadAsync()) {
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using BoredCareers.Entities;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace BoredCareers.Services.DatabaseService {
|
||||||
|
public partial class DatabaseService {
|
||||||
|
|
||||||
|
public async Task<Location[]> GetNearbyLocations(string PostalCode, string CountryCode, float MaxDistanceKm) {
|
||||||
|
List<Location> closePostalCodes = new List<Location>();
|
||||||
|
using (MySqlConnection connection = GetConnection()) {
|
||||||
|
await connection.OpenAsync();
|
||||||
|
string command = @"
|
||||||
|
SELECT
|
||||||
|
pc2.PostalCode,
|
||||||
|
pc2.CountryCode,
|
||||||
|
pc2.Latitude,
|
||||||
|
pc2.Longitude,
|
||||||
|
pc2.City,
|
||||||
|
(
|
||||||
|
6371 * acos(
|
||||||
|
cos(radians(pc1.Latitude)) *
|
||||||
|
cos(radians(pc2.Latitude)) *
|
||||||
|
cos(radians(pc2.Longitude) - radians(pc1.Longitude)) +
|
||||||
|
sin(radians(pc1.Latitude)) *
|
||||||
|
sin(radians(pc2.Latitude))
|
||||||
|
)
|
||||||
|
) AS distance_km
|
||||||
|
FROM PostalCodes pc1
|
||||||
|
JOIN PostalCodes pc2 ON pc2.CountryCode = 'us'
|
||||||
|
WHERE pc1.PostalCode = @PostalCode AND pc1.CountryCode = @CountryCode
|
||||||
|
HAVING distance_km <= @MaxDistanceKm
|
||||||
|
ORDER BY distance_km;
|
||||||
|
";
|
||||||
|
|
||||||
|
MySqlCommand cmd = new MySqlCommand(command, connection);
|
||||||
|
cmd.Parameters.AddWithValue("@PostalCode", PostalCode);
|
||||||
|
cmd.Parameters.AddWithValue("@CountryCode", CountryCode);
|
||||||
|
cmd.Parameters.AddWithValue("@MaxDistanceKm", MaxDistanceKm);
|
||||||
|
|
||||||
|
using (DbDataReader reader = await cmd.ExecuteReaderAsync()) {
|
||||||
|
while (await reader.ReadAsync()) {
|
||||||
|
string _city = reader.GetString("City");
|
||||||
|
string _postalCode = reader.GetString("PostalCode");
|
||||||
|
string _countryCode = reader.GetString("CountryCode");
|
||||||
|
float _latitude = reader.GetFloat("Latitude");
|
||||||
|
float _longitude = reader.GetFloat("Longitude");
|
||||||
|
float _distanceKm = reader.GetFloat("distance_km");
|
||||||
|
|
||||||
|
closePostalCodes.Add(new Location() {
|
||||||
|
City = _city,
|
||||||
|
PostalCode = _postalCode,
|
||||||
|
CountryCode = _countryCode,
|
||||||
|
Latitude = _latitude,
|
||||||
|
Longitude = _longitude,
|
||||||
|
DistanceKM = _distanceKm
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closePostalCodes.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user