@phatvu/web-component-poc 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/fast-button.cjs.entry.js +46 -0
- package/dist/cjs/{fast-button_4.cjs.entry.js → fast-carousel.cjs.entry.js} +1 -231
- package/dist/cjs/fast-input_4.cjs.entry.js +499 -0
- package/dist/cjs/{index-B2BTpdbN.js → index-BEvZs91D.js} +2 -2
- package/dist/cjs/job-card.cjs.entry.js +138 -0
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/web-component-poc.cjs.js +2 -2
- package/dist/collection/collection-manifest.json +4 -1
- package/dist/collection/components/button/button.js +2 -2
- package/dist/collection/components/fast-input/fast-input.css +55 -0
- package/dist/collection/components/fast-input/fast-input.js +335 -0
- package/dist/collection/components/job-card/job-card.css +247 -0
- package/dist/collection/components/job-card/job-card.js +435 -0
- package/dist/collection/components/jobs-item/jobs-item.js +5 -5
- package/dist/collection/components/jobs-list-only/jobs-list-only.js +185 -8
- package/dist/collection/components/jobs-list-reactive/jobs-list-reactive.css +8 -0
- package/dist/collection/components/jobs-list-reactive/jobs-list-reactive.js +203 -0
- package/dist/components/fast-button.js +1 -1
- package/dist/components/fast-carousel.js +1 -1
- package/dist/components/fast-input.d.ts +11 -0
- package/dist/components/fast-input.js +1 -0
- package/dist/components/index.js +1 -1
- package/dist/components/job-card.d.ts +11 -0
- package/dist/components/job-card.js +1 -0
- package/dist/components/jobs-item.js +1 -1
- package/dist/components/jobs-list-only.js +1 -1
- package/dist/components/jobs-list-reactive.d.ts +11 -0
- package/dist/components/jobs-list-reactive.js +1 -0
- package/dist/components/{p-ClQDwJJB.js → p-DQiaLjLf.js} +1 -1
- package/dist/esm/fast-button.entry.js +44 -0
- package/dist/esm/{fast-button_4.entry.js → fast-carousel.entry.js} +2 -229
- package/dist/esm/fast-input_4.entry.js +494 -0
- package/dist/esm/{index-Dk5CvWmb.js → index-C_ZLQIpp.js} +2 -2
- package/dist/esm/job-card.entry.js +136 -0
- package/dist/esm/loader.js +3 -3
- package/dist/esm/web-component-poc.js +3 -3
- package/dist/types/components/fast-input/fast-input.d.ts +37 -0
- package/dist/types/components/job-card/job-card.d.ts +93 -0
- package/dist/types/components/jobs-item/jobs-item.d.ts +2 -2
- package/dist/types/components/jobs-list-only/jobs-list-only.d.ts +24 -2
- package/dist/types/components/jobs-list-reactive/jobs-list-reactive.d.ts +26 -0
- package/dist/types/components.d.ts +469 -7
- package/dist/types/mock/jobs-list-only.mock.d.ts +2 -2
- package/dist/types/types/jobs-list.d.ts +6 -2
- package/dist/web-component-poc/p-618fba28.entry.js +1 -0
- package/dist/web-component-poc/p-7d45772f.entry.js +1 -0
- package/dist/web-component-poc/p-bef7c8e2.entry.js +1 -0
- package/dist/web-component-poc/p-cfb9aed9.entry.js +1 -0
- package/dist/web-component-poc/web-component-poc.esm.js +1 -1
- package/hydrate/index.js +534 -6
- package/hydrate/index.mjs +534 -6
- package/package.json +9 -1
- package/dist/web-component-poc/p-df843533.entry.js +0 -1
- /package/dist/components/{p-UM9TUfe3.js → p-BiaJAQXY.js} +0 -0
- /package/dist/web-component-poc/{p-Dk5CvWmb.js → p-C_ZLQIpp.js} +0 -0
package/hydrate/index.js
CHANGED
|
@@ -132,7 +132,7 @@ function hydrateFactory($stencilWindow, $stencilHydrateOpts, $stencilHydrateResu
|
|
|
132
132
|
|
|
133
133
|
|
|
134
134
|
const NAMESPACE = 'web-component-poc';
|
|
135
|
-
const BUILD = /* web-component-poc */ { hotModuleReplacement: false, hydratedSelectorName: "hydrated",
|
|
135
|
+
const BUILD = /* web-component-poc */ { hotModuleReplacement: false, hydratedSelectorName: "hydrated", propChangeCallback: false, shadowDom: false, slotRelocation: true, state: true, updatable: true};
|
|
136
136
|
|
|
137
137
|
/*
|
|
138
138
|
Stencil Hydrate Platform v4.43.2 | MIT Licensed | https://stenciljs.com
|
|
@@ -4507,7 +4507,7 @@ var registerInstance = (lazyInstance, hostRef) => {
|
|
|
4507
4507
|
if (!hostRef) return void 0;
|
|
4508
4508
|
lazyInstance.__stencil__getHostRef = () => hostRef;
|
|
4509
4509
|
hostRef.$lazyInstance$ = lazyInstance;
|
|
4510
|
-
if (hostRef.$cmpMeta$.$flags$ & 512 /* hasModernPropertyDecls */ && (BUILD.
|
|
4510
|
+
if (hostRef.$cmpMeta$.$flags$ & 512 /* hasModernPropertyDecls */ && (BUILD.state)) {
|
|
4511
4511
|
reWireGetterSetter(lazyInstance, hostRef);
|
|
4512
4512
|
}
|
|
4513
4513
|
return hostRef;
|
|
@@ -6507,6 +6507,334 @@ class CustomButton {
|
|
|
6507
6507
|
}; }
|
|
6508
6508
|
}
|
|
6509
6509
|
|
|
6510
|
+
const fastInputCss = () => `.fast-input{position:relative;display:inline-flex;gap:0.5rem}.fast-input__field{flex:1;padding:0.5rem 0.75rem;font-size:1rem;border:1px solid #ccc;border-radius:4px}.fast-input__button{padding:0.5rem 1rem;font-size:1rem;cursor:pointer;background:#0070f3;color:#fff;border:none;border-radius:4px}.fast-input__dropdown{position:absolute;top:100%;left:0;right:0;margin:0;padding:0;list-style:none;background:#fff;border:1px solid #ccc;border-top:none;border-radius:0 0 4px 4px;z-index:100;max-height:200px;overflow-y:auto}.fast-input__dropdown-item{padding:0.5rem 0.75rem;cursor:pointer}.fast-input__dropdown-item:hover{background:#f0f0f0}.fast-input__dropdown-loading{padding:0.5rem 0.75rem;color:#999;font-style:italic}`;
|
|
6511
|
+
|
|
6512
|
+
class FastInput {
|
|
6513
|
+
constructor(hostRef) {
|
|
6514
|
+
registerInstance(this, hostRef);
|
|
6515
|
+
this.searchExecuted = createEvent(this, "searchExecuted");
|
|
6516
|
+
this.inputChanged = createEvent(this, "inputChanged");
|
|
6517
|
+
}
|
|
6518
|
+
placeholder = 'Search...';
|
|
6519
|
+
value = '';
|
|
6520
|
+
paramName = 'keyword';
|
|
6521
|
+
enableAutocomplete = false;
|
|
6522
|
+
autocompleteUrl = '/api/jobs/autocomplete';
|
|
6523
|
+
targetPath;
|
|
6524
|
+
debounceMs = 300;
|
|
6525
|
+
minChars = 3;
|
|
6526
|
+
searchExecuted;
|
|
6527
|
+
inputChanged;
|
|
6528
|
+
inputValue = '';
|
|
6529
|
+
suggestions = [];
|
|
6530
|
+
showDropdown = false;
|
|
6531
|
+
autocompleteLoading = false;
|
|
6532
|
+
debounceTimer;
|
|
6533
|
+
popstateHandler;
|
|
6534
|
+
connectedCallback() {
|
|
6535
|
+
const urlValue = this.getUrlParam();
|
|
6536
|
+
this.inputValue = urlValue !== null ? urlValue : this.value;
|
|
6537
|
+
this.popstateHandler = () => {
|
|
6538
|
+
this.inputValue = this.getUrlParam() ?? '';
|
|
6539
|
+
};
|
|
6540
|
+
window.addEventListener('popstate', this.popstateHandler);
|
|
6541
|
+
}
|
|
6542
|
+
disconnectedCallback() {
|
|
6543
|
+
window.removeEventListener('popstate', this.popstateHandler);
|
|
6544
|
+
clearTimeout(this.debounceTimer);
|
|
6545
|
+
}
|
|
6546
|
+
getUrlParam() {
|
|
6547
|
+
const params = new URLSearchParams(window.location.search);
|
|
6548
|
+
return params.get(this.paramName);
|
|
6549
|
+
}
|
|
6550
|
+
updateUrlParam(value) {
|
|
6551
|
+
const params = new URLSearchParams(window.location.search);
|
|
6552
|
+
if (value) {
|
|
6553
|
+
params.set(this.paramName, value);
|
|
6554
|
+
}
|
|
6555
|
+
else {
|
|
6556
|
+
params.delete(this.paramName);
|
|
6557
|
+
}
|
|
6558
|
+
const qs = params.toString();
|
|
6559
|
+
const newUrl = qs
|
|
6560
|
+
? `${window.location.pathname}?${qs}`
|
|
6561
|
+
: window.location.pathname;
|
|
6562
|
+
history.pushState({}, '', newUrl);
|
|
6563
|
+
}
|
|
6564
|
+
submit() {
|
|
6565
|
+
this.updateUrlParam(this.inputValue);
|
|
6566
|
+
document.dispatchEvent(new CustomEvent('search-executed', {
|
|
6567
|
+
detail: { keyword: this.inputValue },
|
|
6568
|
+
bubbles: true,
|
|
6569
|
+
composed: true,
|
|
6570
|
+
}));
|
|
6571
|
+
this.searchExecuted.emit({ keyword: this.inputValue });
|
|
6572
|
+
this.showDropdown = false;
|
|
6573
|
+
}
|
|
6574
|
+
handleInput = (e) => {
|
|
6575
|
+
const value = e.target.value;
|
|
6576
|
+
this.inputValue = value;
|
|
6577
|
+
this.inputChanged.emit({ value });
|
|
6578
|
+
if (this.enableAutocomplete) {
|
|
6579
|
+
this.scheduleAutocomplete(value);
|
|
6580
|
+
}
|
|
6581
|
+
};
|
|
6582
|
+
handleKeydown = (e) => {
|
|
6583
|
+
if (e.key === 'Enter') {
|
|
6584
|
+
this.submit();
|
|
6585
|
+
}
|
|
6586
|
+
else if (e.key === 'Escape') {
|
|
6587
|
+
this.showDropdown = false;
|
|
6588
|
+
}
|
|
6589
|
+
};
|
|
6590
|
+
handleBlur = () => {
|
|
6591
|
+
this.showDropdown = false;
|
|
6592
|
+
};
|
|
6593
|
+
scheduleAutocomplete(value) {
|
|
6594
|
+
clearTimeout(this.debounceTimer);
|
|
6595
|
+
if (value.length < this.minChars) {
|
|
6596
|
+
this.showDropdown = false;
|
|
6597
|
+
return;
|
|
6598
|
+
}
|
|
6599
|
+
this.debounceTimer = setTimeout(() => this.fetchSuggestions(value), this.debounceMs);
|
|
6600
|
+
}
|
|
6601
|
+
async fetchSuggestions(keyword) {
|
|
6602
|
+
if (!this.targetPath) {
|
|
6603
|
+
console.warn('[fast-input] target-path is required for autocomplete');
|
|
6604
|
+
return;
|
|
6605
|
+
}
|
|
6606
|
+
this.autocompleteLoading = true;
|
|
6607
|
+
this.showDropdown = true;
|
|
6608
|
+
try {
|
|
6609
|
+
const res = await fetch(this.autocompleteUrl, {
|
|
6610
|
+
method: 'POST',
|
|
6611
|
+
headers: { 'Content-Type': 'application/json' },
|
|
6612
|
+
body: JSON.stringify({ keyword, target_path: this.targetPath }),
|
|
6613
|
+
});
|
|
6614
|
+
if (!res.ok)
|
|
6615
|
+
throw new Error('autocomplete request failed');
|
|
6616
|
+
const data = await res.json();
|
|
6617
|
+
this.suggestions = data;
|
|
6618
|
+
}
|
|
6619
|
+
catch {
|
|
6620
|
+
this.showDropdown = false;
|
|
6621
|
+
this.suggestions = [];
|
|
6622
|
+
}
|
|
6623
|
+
finally {
|
|
6624
|
+
this.autocompleteLoading = false;
|
|
6625
|
+
}
|
|
6626
|
+
}
|
|
6627
|
+
selectSuggestion(title) {
|
|
6628
|
+
this.inputValue = title;
|
|
6629
|
+
this.showDropdown = false;
|
|
6630
|
+
this.submit();
|
|
6631
|
+
}
|
|
6632
|
+
render() {
|
|
6633
|
+
return (hAsync("div", { key: '3a9d31c7b109205600addc326d63979585f10bcd', class: "fast-input" }, hAsync("input", { key: '8f238fe9e002f367d4939616be8c06d938d76045', type: "text", class: "fast-input__field", placeholder: this.placeholder, value: this.inputValue, onInput: this.handleInput, onKeyDown: this.handleKeydown, onBlur: this.handleBlur }), hAsync("button", { key: '7b7404f13432750ece669da8ce68be15179921de', class: "fast-input__button", type: "button", onClick: () => this.submit() }, "Search"), this.enableAutocomplete && this.showDropdown && (hAsync("ul", { key: '1438bacadc21c183842a8bdaa3f336bffb152e14', class: "fast-input__dropdown" }, this.autocompleteLoading ? (hAsync("li", { class: "fast-input__dropdown-loading" }, "Loading...")) : (this.suggestions.map(s => (hAsync("li", { class: "fast-input__dropdown-item", onMouseDown: e => { e.preventDefault(); }, onClick: () => this.selectSuggestion(s.title) }, s.title))))))));
|
|
6634
|
+
}
|
|
6635
|
+
static get style() { return fastInputCss(); }
|
|
6636
|
+
static get cmpMeta() { return {
|
|
6637
|
+
"$flags$": 512,
|
|
6638
|
+
"$tagName$": "fast-input",
|
|
6639
|
+
"$members$": {
|
|
6640
|
+
"placeholder": [1],
|
|
6641
|
+
"value": [1],
|
|
6642
|
+
"paramName": [1, "param-name"],
|
|
6643
|
+
"enableAutocomplete": [4, "enable-autocomplete"],
|
|
6644
|
+
"autocompleteUrl": [1, "autocomplete-url"],
|
|
6645
|
+
"targetPath": [1, "target-path"],
|
|
6646
|
+
"debounceMs": [2, "debounce-ms"],
|
|
6647
|
+
"minChars": [2, "min-chars"],
|
|
6648
|
+
"inputValue": [32],
|
|
6649
|
+
"suggestions": [32],
|
|
6650
|
+
"showDropdown": [32],
|
|
6651
|
+
"autocompleteLoading": [32]
|
|
6652
|
+
},
|
|
6653
|
+
"$listeners$": undefined,
|
|
6654
|
+
"$lazyBundleId$": "-",
|
|
6655
|
+
"$attrsToReflect$": []
|
|
6656
|
+
}; }
|
|
6657
|
+
}
|
|
6658
|
+
|
|
6659
|
+
const jobCardCss = () => `.job-card{display:block;padding:16px;border:1px solid #e0e0e0;border-radius:8px;background-color:#fff;box-shadow:0 2px 4px rgba(0, 0, 0, 0.08);transition:box-shadow 0.2s ease, border-color 0.2s ease}.job-card:hover{box-shadow:0 4px 8px rgba(0, 0, 0, 0.12);border-color:#d0d0d0}.job-card__header{margin-bottom:12px}.job-card__title{margin:0;font-size:18px;font-weight:700;display:flex;align-items:center;flex-wrap:wrap;gap:8px}.job-card__title--link{text-decoration:none;color:#1f9755;transition:color 0.2s ease}.job-card__title--link:hover{text-decoration:underline;color:#1a7a43}.job-card__reference{font-size:0.875em;color:#666;background-color:#f5f5f5;padding:2px 6px;border-radius:3px}.job-card__reference--empty{display:none}.job-card__remote{background:#e8f5e9;color:#2e7d32;border-radius:100px;padding:4px 12px;text-transform:uppercase;font-size:11px;font-weight:700;line-height:1.5;white-space:nowrap}.job-card__remote--empty{display:none}.job-card__distance{display:inline-flex;align-items:center;gap:4px;margin-top:6px;font-size:13px;font-weight:500;color:#555}.job-card__distance--icon{display:inline-flex;align-items:center}.job-card__distance--icon svg{width:16px;height:16px;color:#1f9755}.job-card__content{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.job-card__info{flex:1;display:flex;flex-direction:column;gap:8px}.job-card__street,.job-card__brand,.job-card__employment-type{display:flex;align-items:center;flex-wrap:wrap;gap:4px 6px;font-size:14px}.job-card__street--empty,.job-card__brand--empty,.job-card__employment-type--empty{color:#999}.job-card__street--icon,.job-card__brand--icon,.job-card__employment-type--icon{display:inline-flex;align-items:center;flex-shrink:0}.job-card__street--icon svg,.job-card__brand--icon svg,.job-card__employment-type--icon svg{width:16px;height:16px;color:#666}.job-card__street--label,.job-card__brand--label,.job-card__employment-type--label{color:#333}.job-card__street--label__wrapper{display:flex;align-items:center;gap:6px}.job-card__street--more-locations__wrapper{display:flex;align-items:center;gap:2px;font-size:12px;margin-left:2px}.job-card__street--amount{font-weight:600;color:#1f9755}.job-card__street--more-locations{color:#999}.job-card__apply{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:10px 16px;background-color:#198754;color:#fff;border-radius:4px;text-decoration:none;font-weight:600;font-size:14px;transition:background-color 0.2s ease, transform 0.1s ease;white-space:nowrap;flex-shrink:0}.job-card__apply:hover{background-color:#1a6f47;transform:translateY(-1px)}.job-card__apply:active{transform:translateY(0)}.job-card__apply--icon{display:inline-flex;align-items:center}.job-card__apply--icon svg{width:14px;height:14px}@media (max-width: 768px){.job-card{padding:12px}.job-card__content{flex-direction:column;gap:10px}.job-card__apply{width:100%;justify-content:center}.job-card__title{font-size:16px}}@media (max-width: 480px){.job-card{padding:10px}.job-card__title{font-size:15px}.job-card__distance{font-size:12px}.job-card__street,.job-card__brand,.job-card__employment-type{font-size:13px}}`;
|
|
6660
|
+
|
|
6661
|
+
/**
|
|
6662
|
+
* Helper function to get formatted location label from location object
|
|
6663
|
+
*/
|
|
6664
|
+
function getLocationLabel$1(loc) {
|
|
6665
|
+
if (loc.cityStateAbbr)
|
|
6666
|
+
return loc.cityStateAbbr;
|
|
6667
|
+
const parts = [loc.streetAddress, loc.city, loc.stateAbbr || loc.state, loc.countryAbbr || loc.country].filter(Boolean);
|
|
6668
|
+
return parts.join(', ') || loc.locationText || '';
|
|
6669
|
+
}
|
|
6670
|
+
/**
|
|
6671
|
+
* Helper function to get the first location from job locations
|
|
6672
|
+
*/
|
|
6673
|
+
function getFirstLocation$1(job) {
|
|
6674
|
+
const locs = job.locations;
|
|
6675
|
+
if (!locs?.length)
|
|
6676
|
+
return undefined;
|
|
6677
|
+
return locs[0];
|
|
6678
|
+
}
|
|
6679
|
+
/**
|
|
6680
|
+
* JobCard Component
|
|
6681
|
+
*
|
|
6682
|
+
* A reusable card component for displaying job information based on the React JobItem component.
|
|
6683
|
+
* Features include:
|
|
6684
|
+
* - Job title with apply link
|
|
6685
|
+
* - Location with distance
|
|
6686
|
+
* - Brand/company name
|
|
6687
|
+
* - Employment type
|
|
6688
|
+
* - Reference number (optional)
|
|
6689
|
+
* - Remote indicator (optional)
|
|
6690
|
+
* - Multi-location support
|
|
6691
|
+
* - Custom extra fields
|
|
6692
|
+
*
|
|
6693
|
+
* @example
|
|
6694
|
+
* ```html
|
|
6695
|
+
* <job-card
|
|
6696
|
+
* .job={jobData}
|
|
6697
|
+
* applyButtonText="Apply"
|
|
6698
|
+
* showBrand="true"
|
|
6699
|
+
* showEmploymentType="true"
|
|
6700
|
+
* ></job-card>
|
|
6701
|
+
* ```
|
|
6702
|
+
*/
|
|
6703
|
+
class JobCard {
|
|
6704
|
+
constructor(hostRef) {
|
|
6705
|
+
registerInstance(this, hostRef);
|
|
6706
|
+
}
|
|
6707
|
+
/**
|
|
6708
|
+
* The job data object to display. Accepts either a Job object or a JSON string.
|
|
6709
|
+
*/
|
|
6710
|
+
job;
|
|
6711
|
+
/**
|
|
6712
|
+
* Index of the job in a list (used for accessibility)
|
|
6713
|
+
*/
|
|
6714
|
+
index = 0;
|
|
6715
|
+
/**
|
|
6716
|
+
* Text for the apply button
|
|
6717
|
+
*/
|
|
6718
|
+
applyButtonText = 'Apply Now';
|
|
6719
|
+
/**
|
|
6720
|
+
* Whether to show the brand/company name
|
|
6721
|
+
*/
|
|
6722
|
+
showBrand = true;
|
|
6723
|
+
/**
|
|
6724
|
+
* Whether to show the reference number
|
|
6725
|
+
*/
|
|
6726
|
+
showReference = false;
|
|
6727
|
+
/**
|
|
6728
|
+
* Whether to show employment type
|
|
6729
|
+
*/
|
|
6730
|
+
showEmploymentType = true;
|
|
6731
|
+
/**
|
|
6732
|
+
* Text shown for multiple locations
|
|
6733
|
+
*/
|
|
6734
|
+
multiLocationText = 'More locations';
|
|
6735
|
+
/**
|
|
6736
|
+
* Text shown for remote jobs
|
|
6737
|
+
*/
|
|
6738
|
+
remoteLocationText = 'Remote';
|
|
6739
|
+
/**
|
|
6740
|
+
* Whether to show distances in kilometers instead of miles
|
|
6741
|
+
*/
|
|
6742
|
+
enableKilometers = false;
|
|
6743
|
+
/**
|
|
6744
|
+
* Whether to show commute time
|
|
6745
|
+
*/
|
|
6746
|
+
showCommuteTime = false;
|
|
6747
|
+
/**
|
|
6748
|
+
* Format string for street address (not used in base web component)
|
|
6749
|
+
*/
|
|
6750
|
+
streetFormat = '{street}, {city_state_abbr}';
|
|
6751
|
+
/** Extra CSS class on the root inner element (avoid prop name "className", reserved on HTMLElement). */
|
|
6752
|
+
rootClass = '';
|
|
6753
|
+
/**
|
|
6754
|
+
* Custom extra fields configuration
|
|
6755
|
+
*/
|
|
6756
|
+
extraFieldsConfig = [];
|
|
6757
|
+
/**
|
|
6758
|
+
* Format distance with unit
|
|
6759
|
+
*/
|
|
6760
|
+
formatDistance(distance) {
|
|
6761
|
+
const value = this.enableKilometers ? distance * 1.60934 : distance;
|
|
6762
|
+
const unit = this.enableKilometers ? 'Km' : 'Miles';
|
|
6763
|
+
const str = `${value.toFixed(1)} ${unit}`.replace('.0', '');
|
|
6764
|
+
return str;
|
|
6765
|
+
}
|
|
6766
|
+
/**
|
|
6767
|
+
* Is Empty utility
|
|
6768
|
+
*/
|
|
6769
|
+
isEmpty(value) {
|
|
6770
|
+
if (value === null || value === undefined || value === '') {
|
|
6771
|
+
return true;
|
|
6772
|
+
}
|
|
6773
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
6774
|
+
return true;
|
|
6775
|
+
}
|
|
6776
|
+
return false;
|
|
6777
|
+
}
|
|
6778
|
+
/**
|
|
6779
|
+
* Parse job data from prop - handles both object and JSON string
|
|
6780
|
+
*/
|
|
6781
|
+
getJobData() {
|
|
6782
|
+
if (!this.job)
|
|
6783
|
+
return null;
|
|
6784
|
+
if (typeof this.job === 'string') {
|
|
6785
|
+
try {
|
|
6786
|
+
return JSON.parse(this.job);
|
|
6787
|
+
}
|
|
6788
|
+
catch {
|
|
6789
|
+
console.warn('job-card: Failed to parse job JSON string');
|
|
6790
|
+
return null;
|
|
6791
|
+
}
|
|
6792
|
+
}
|
|
6793
|
+
return this.job;
|
|
6794
|
+
}
|
|
6795
|
+
render() {
|
|
6796
|
+
const job = this.getJobData();
|
|
6797
|
+
if (!job)
|
|
6798
|
+
return null;
|
|
6799
|
+
const firstLoc = getFirstLocation$1(job);
|
|
6800
|
+
const locationLabel = firstLoc ? getLocationLabel$1(firstLoc) : '';
|
|
6801
|
+
const distance = firstLoc?.distance ?? 0;
|
|
6802
|
+
const distanceLabel = distance > 0 ? this.formatDistance(distance) : '';
|
|
6803
|
+
const applyHref = job.applyURL ||
|
|
6804
|
+
(job.originalURL ? `${typeof window !== 'undefined' ? window.location.origin : ''}${job.originalURL}` : '#');
|
|
6805
|
+
const applyLabel = `${this.applyButtonText}, ${job.title || ''}`;
|
|
6806
|
+
const locs = job.locations ?? [];
|
|
6807
|
+
const hasMultipleLocations = locs.length > 1;
|
|
6808
|
+
return (hAsync("div", { class: `job-card ${this.rootClass}`.trim() }, hAsync("div", { class: "job-card__header" }, hAsync("h3", { class: "job-card__title" }, hAsync("a", { class: "job-card__title--link", href: applyHref, target: "_blank", rel: "noopener noreferrer" }, job.title || ''), this.showReference && (hAsync("span", { class: `job-card__reference ${job.reference ? '' : 'job-card__reference--empty'}` }, job.reference || '')), job.isRemote && (hAsync("span", { class: this.remoteLocationText ? 'job-card__remote' : 'job-card__remote job-card__remote--empty' }, this.remoteLocationText))), distanceLabel && (hAsync("div", { class: "job-card__distance" }, hAsync("span", { class: "job-card__distance--icon" }, hAsync("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18z" }), hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M12 8v4l2 2" }))), hAsync("span", { class: "job-card__distance--label" }, distanceLabel)))), hAsync("div", { class: "job-card__content" }, hAsync("div", { class: "job-card__info" }, hAsync("div", { class: locs.length ? 'job-card__street' : 'job-card__street job-card__street--empty' }, hAsync("div", { class: "job-card__street--label__wrapper" }, hAsync("span", { class: "job-card__street--icon" }, hAsync("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0z" }), hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0z" }))), hAsync("span", { class: "job-card__street--label" }, locationLabel || '—')), hasMultipleLocations && (hAsync("div", { class: "job-card__street--more-locations__wrapper" }, hAsync("span", { class: "job-card__street--amount" }, "+", locs.length - 1), hAsync("span", { class: "job-card__street--more-locations" }, this.multiLocationText)))), this.showBrand && (hAsync("div", { class: job.brandName ? 'job-card__brand' : 'job-card__brand job-card__brand--empty' }, hAsync("span", { class: "job-card__brand--icon" }, hAsync("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M2.25 21h19.5m-18-18v18m10.5-18v18m6-13.5V21M6.75 6.75h.75m-.75 3h.75m-.75 3h.75m3-6h.75m-.75 3h.75m-.75 3h.75M6.75 21v-3.375c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21M3 3h12m-.75 4.5H21m-3.75 3.75h.008v.008h-.008v-.008zm0 3h.008v.008h-.008v-.008zm0 3h.008v.008h-.008v-.008z" }))), hAsync("span", { class: "job-card__brand--label" }, job.brandName || '—'))), this.showEmploymentType && (hAsync("div", { class: job.employmentType?.length
|
|
6809
|
+
? 'job-card__employment-type'
|
|
6810
|
+
: 'job-card__employment-type job-card__employment-type--empty' }, hAsync("span", { class: "job-card__employment-type--icon" }, hAsync("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }, hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0z" }))), job.employmentType?.length ? (job.employmentType?.map((type) => (hAsync("span", { key: type, class: "job-card__employment-type--label" }, type)))) : (hAsync("span", { class: "job-card__employment-type--label" }, "\u2014")))), (job.jobCardExtraFields ?? []).length > 0 &&
|
|
6811
|
+
job.jobCardExtraFields?.map((field, idx) => (hAsync("div", { key: idx, class: !this.isEmpty(field.value) ? `${field.classname}` : `${field.classname}--empty` }, Array.isArray(field.value) ? (field.value.map((item, itemIdx) => (hAsync("span", { key: itemIdx, class: `${field.classname}--label` }, item)))) : (hAsync("span", { class: `${field.classname}--label` }, field.value)))))), hAsync("a", { class: "job-card__apply", href: applyHref, target: "_blank", rel: "noopener noreferrer", "aria-label": applyLabel }, hAsync("span", { class: "job-card__apply--label" }, this.applyButtonText), hAsync("span", { class: "job-card__apply--icon" }, hAsync("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, hAsync("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" })))))));
|
|
6812
|
+
}
|
|
6813
|
+
static get style() { return jobCardCss(); }
|
|
6814
|
+
static get cmpMeta() { return {
|
|
6815
|
+
"$flags$": 512,
|
|
6816
|
+
"$tagName$": "job-card",
|
|
6817
|
+
"$members$": {
|
|
6818
|
+
"job": [1],
|
|
6819
|
+
"index": [2],
|
|
6820
|
+
"applyButtonText": [1, "apply-button-text"],
|
|
6821
|
+
"showBrand": [4, "show-brand"],
|
|
6822
|
+
"showReference": [4, "show-reference"],
|
|
6823
|
+
"showEmploymentType": [4, "show-employment-type"],
|
|
6824
|
+
"multiLocationText": [1, "multi-location-text"],
|
|
6825
|
+
"remoteLocationText": [1, "remote-location-text"],
|
|
6826
|
+
"enableKilometers": [4, "enable-kilometers"],
|
|
6827
|
+
"showCommuteTime": [4, "show-commute-time"],
|
|
6828
|
+
"streetFormat": [1, "street-format"],
|
|
6829
|
+
"rootClass": [1, "root-class"],
|
|
6830
|
+
"extraFieldsConfig": [16]
|
|
6831
|
+
},
|
|
6832
|
+
"$listeners$": undefined,
|
|
6833
|
+
"$lazyBundleId$": "-",
|
|
6834
|
+
"$attrsToReflect$": []
|
|
6835
|
+
}; }
|
|
6836
|
+
}
|
|
6837
|
+
|
|
6510
6838
|
const jobsItemCss = () => `.results-list__item{list-style:none;padding:10px 0;border-bottom:1px solid #ddd;margin:15px 0;display:inline-block;width:100%;position:relative}.results-list__item:last-child{border-bottom:none}.results-list__item-header{margin:10px 0;font-size:18px;font-weight:700;display:flex;flex-direction:column}.results-list__item-title{margin:0}.results-list__item-title--link{text-decoration:none;color:#1f9755}.results-list__item-title--link:hover{text-decoration:underline}.reference{margin-left:8px;font-size:0.9em;color:#666}.reference.empty{display:none}.remote{background:#f3f3f3;color:#808285;border-radius:100px;padding:6px 16px;text-transform:uppercase;font-size:12px;font-weight:700;line-height:24px;margin-left:8px}.remote--empty{display:none}.results-list__item-distance{display:inline-flex;align-items:center;gap:4px;margin-top:4px;font-size:14px;font-weight:400}.results-list__item-distance--icon{display:inline-flex}.results-list__item-distance--icon svg{width:16px;height:16px}.results-list__item-content{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-top:8px}.results-list__item-info{flex:1}.results-list__item-street,.results-list__item-brand,.results-list__item-employment-type{margin:10px 0;display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px}.results-list__item-street--empty,.results-list__item-brand--empty,.results-list__item-employment-type--empty{color:#999}.results-list__item-street--icon,.results-list__item-brand--icon,.results-list__item-employment-type--icon{margin-right:6px;display:inline-flex}.results-list__item-street--icon svg,.results-list__item-brand--icon svg,.results-list__item-employment-type--icon svg{width:16px;height:16px}.results-list__item-street--more-locations__wrapper{margin-left:8px}.results-list__item-street--amount{font-weight:600}.results-list__item-apply{margin:10px 0;padding:10px 20px;display:inline-flex;align-items:center;gap:8px;background-color:#198754;color:#fff;border-radius:3px;text-decoration:none;font-weight:600;flex-shrink:0}.results-list__item-apply:hover{background-color:#1f9755;color:#fff}.results-list__item-apply--icon svg{width:14px;height:14px}`;
|
|
6511
6839
|
|
|
6512
6840
|
function getLocationLabel(loc) {
|
|
@@ -6650,6 +6978,7 @@ const defaultNoResultsLine2 = 'Please refine your keywords in the search bar abo
|
|
|
6650
6978
|
class JobsListOnly {
|
|
6651
6979
|
constructor(hostRef) {
|
|
6652
6980
|
registerInstance(this, hostRef);
|
|
6981
|
+
this.fetchComplete = createEvent(this, "fetchComplete");
|
|
6653
6982
|
}
|
|
6654
6983
|
/**
|
|
6655
6984
|
* When "true", use built-in mock data (for local/dev/docs). Otherwise use `jobs` from API/parent.
|
|
@@ -6673,16 +7002,84 @@ class JobsListOnly {
|
|
|
6673
7002
|
enableKilometers = false;
|
|
6674
7003
|
/** Extra CSS class on the root element (avoid prop name "class" / "classname" reserved). */
|
|
6675
7004
|
rootClass = '';
|
|
7005
|
+
/** Template string for count display. Tokens: {count} = jobs on page, {total} = total from API. */
|
|
7006
|
+
showCountText = '';
|
|
6676
7007
|
showSuggestions = false;
|
|
6677
7008
|
clearResultSuggestionsTitleText = 'Suggestions';
|
|
6678
7009
|
clearResultSuggestionsLine1 = 'Try different keywords';
|
|
6679
7010
|
clearResultSuggestionsLine2 = 'Make sure everything is spelled correctly';
|
|
6680
7011
|
clearResultSuggestionsLine3 = 'Try other locations';
|
|
6681
7012
|
clearResultSuggestionsLine4 = '';
|
|
7013
|
+
/** When true, component manages its own data fetching */
|
|
7014
|
+
autoFetch = false;
|
|
7015
|
+
/** Jobs search endpoint */
|
|
7016
|
+
apiUrl = '/api/get-jobs';
|
|
7017
|
+
/** Comma-separated URL param names to watch and forward to the API */
|
|
7018
|
+
watchParams = 'keyword';
|
|
7019
|
+
fetchedJobs = [];
|
|
7020
|
+
fetchedTotal = 0;
|
|
7021
|
+
fetchLoading = false;
|
|
7022
|
+
fetchComplete;
|
|
7023
|
+
searchExecutedHandler;
|
|
7024
|
+
popstateHandler;
|
|
7025
|
+
connectedCallback() {
|
|
7026
|
+
if (this.autoFetch) {
|
|
7027
|
+
this.fetchJobs();
|
|
7028
|
+
this.searchExecutedHandler = () => this.fetchJobs();
|
|
7029
|
+
this.popstateHandler = () => this.fetchJobs();
|
|
7030
|
+
document.addEventListener('search-executed', this.searchExecutedHandler);
|
|
7031
|
+
window.addEventListener('popstate', this.popstateHandler);
|
|
7032
|
+
}
|
|
7033
|
+
}
|
|
7034
|
+
disconnectedCallback() {
|
|
7035
|
+
if (this.autoFetch) {
|
|
7036
|
+
document.removeEventListener('search-executed', this.searchExecutedHandler);
|
|
7037
|
+
window.removeEventListener('popstate', this.popstateHandler);
|
|
7038
|
+
}
|
|
7039
|
+
}
|
|
7040
|
+
async fetchJobs() {
|
|
7041
|
+
this.fetchLoading = true;
|
|
7042
|
+
const params = new URLSearchParams(window.location.search);
|
|
7043
|
+
const watchList = this.watchParams.split(',').map(p => p.trim()).filter(Boolean);
|
|
7044
|
+
const query = new URLSearchParams();
|
|
7045
|
+
for (const key of watchList) {
|
|
7046
|
+
const val = params.get(key);
|
|
7047
|
+
if (val !== null)
|
|
7048
|
+
query.set(key, val);
|
|
7049
|
+
}
|
|
7050
|
+
const url = `${this.apiUrl}?${query.toString()}`;
|
|
7051
|
+
try {
|
|
7052
|
+
const res = await fetch(url, {
|
|
7053
|
+
method: 'POST',
|
|
7054
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7055
|
+
body: JSON.stringify({ disable_switch_search_mode: false }),
|
|
7056
|
+
});
|
|
7057
|
+
if (!res.ok)
|
|
7058
|
+
throw new Error('fetch failed');
|
|
7059
|
+
const data = await res.json();
|
|
7060
|
+
this.fetchedJobs = data.jobs;
|
|
7061
|
+
this.fetchedTotal = data.totalJob;
|
|
7062
|
+
this.fetchComplete.emit({ jobs: data.jobs, totalJob: data.totalJob });
|
|
7063
|
+
}
|
|
7064
|
+
catch {
|
|
7065
|
+
// preserve stale data, just stop loading
|
|
7066
|
+
}
|
|
7067
|
+
finally {
|
|
7068
|
+
this.fetchLoading = false;
|
|
7069
|
+
}
|
|
7070
|
+
}
|
|
7071
|
+
renderCountText(count, total) {
|
|
7072
|
+
return this.showCountText
|
|
7073
|
+
.replace('{count}', String(count))
|
|
7074
|
+
.replace('{total}', String(total));
|
|
7075
|
+
}
|
|
6682
7076
|
getJobsArray() {
|
|
6683
7077
|
if (this.mockData) {
|
|
6684
7078
|
return mockJobsListOnly;
|
|
6685
7079
|
}
|
|
7080
|
+
if (this.autoFetch) {
|
|
7081
|
+
return this.fetchedJobs;
|
|
7082
|
+
}
|
|
6686
7083
|
const j = this.jobs;
|
|
6687
7084
|
if (Array.isArray(j))
|
|
6688
7085
|
return j;
|
|
@@ -6702,11 +7099,15 @@ class JobsListOnly {
|
|
|
6702
7099
|
}
|
|
6703
7100
|
render() {
|
|
6704
7101
|
const jobsArray = this.getJobsArray();
|
|
6705
|
-
const loading = this.mockData ? false : this.loading;
|
|
6706
|
-
const totalJob = this.mockData
|
|
7102
|
+
const loading = this.mockData ? false : (this.autoFetch ? this.fetchLoading : this.loading);
|
|
7103
|
+
const totalJob = this.mockData
|
|
7104
|
+
? jobsArray.length
|
|
7105
|
+
: this.autoFetch
|
|
7106
|
+
? this.fetchedTotal
|
|
7107
|
+
: (this.totalJob || jobsArray.length);
|
|
6707
7108
|
const showNoResults = !loading && totalJob === 0 && !this.showSuggestions;
|
|
6708
7109
|
const showSuggestionsBlock = !loading && totalJob === 0 && this.showSuggestions;
|
|
6709
|
-
return (hAsync("div", { key: '
|
|
7110
|
+
return (hAsync("div", { key: '1116855473d28d650641b9d962243bfcdcb434ec', class: `jobs-list-root ${this.rootClass}`.trim() }, hAsync("div", { key: 'fcef04f1da9ad4e150af9f59921688f5781d9d43', class: "results-container" }, this.autoFetch && this.fetchLoading && (hAsync("div", { key: '75b157c82c89691c7ce73d12ea0144b3b45485c3', class: "jobs-list-only__loading" }, "Loading...")), hAsync("div", { key: 'b09f9879e10ee4a93e32177611912da5f19f3526', class: loading ? 'loader' : 'loader hide', "aria-hidden": !loading }), totalJob > 0 && this.showCountText && (hAsync("p", { key: 'd10f800fb0a33d82531d5f1728bac4ceba2ed577', class: "jobs-list-only__count" }, this.renderCountText(jobsArray.length, totalJob))), totalJob > 0 && (hAsync("div", { key: '18153ed1338bd48f7be4f043b11ce15e3271f27b', class: "card" }, hAsync("ul", { key: '766e128b1fd5adb456530ae39e92ba8eb0b5d6cf', class: "results-list front" }, jobsArray.map((job, index) => this.renderJobItem(job, index))))), showNoResults && (hAsync("div", { key: 'ed6f3d2bd2bbedabd6e69d508ea1425580e6941f', class: "share-jobs__no-results" }, hAsync("h2", { key: '2302656e33340c69e84cb949afb7256b8f35f440' }, this.noResultsLine1), hAsync("h3", { key: '1c7e6642441a96c04ee26883fdec4f81b0fe6cec' }, this.noResultsLine2))), showSuggestionsBlock && (hAsync("div", { key: 'be7af85f64455918545e88952ca6ff00f0a970c5', class: "card primary-color" }, hAsync("h4", { key: '2f63deb8131190eff882308544b15f767b6f3edc', class: "result-suggestions-title" }, this.clearResultSuggestionsTitleText, ":"), hAsync("ul", { key: 'cb18daaa9e2c2c442c5b906ed370dcd653b5262d', class: "results-list front" }, hAsync("li", { key: 'ff1d5c6518b75c0daa35b43df7162b0dfecde25e', class: "result-suggestions-line" }, this.clearResultSuggestionsLine1), hAsync("li", { key: '4099fd7bf8dcf114eca28702a498ab0938f5de46', class: "result-suggestions-line" }, this.clearResultSuggestionsLine2), hAsync("li", { key: 'fb65b54c3c0b14bc58112977eb4c7c56c1246a45', class: "result-suggestions-line" }, this.clearResultSuggestionsLine3), this.clearResultSuggestionsLine4 && (hAsync("li", { key: '10f745e74cf68a2b1c42e6f49f810a8b59eb27b6', class: "result-suggestions-line" }, this.clearResultSuggestionsLine4))))))));
|
|
6710
7111
|
}
|
|
6711
7112
|
static get style() { return jobsListOnlyCss(); }
|
|
6712
7113
|
static get cmpMeta() { return {
|
|
@@ -6728,12 +7129,136 @@ class JobsListOnly {
|
|
|
6728
7129
|
"remoteLocationText": [1, "remote-location-text"],
|
|
6729
7130
|
"enableKilometers": [4, "enable-kilometers"],
|
|
6730
7131
|
"rootClass": [1, "root-class"],
|
|
7132
|
+
"showCountText": [1, "show-count-text"],
|
|
6731
7133
|
"showSuggestions": [4, "show-suggestions"],
|
|
6732
7134
|
"clearResultSuggestionsTitleText": [1, "clear-result-suggestions-title-text"],
|
|
6733
7135
|
"clearResultSuggestionsLine1": [1, "clear-result-suggestions-line-1"],
|
|
6734
7136
|
"clearResultSuggestionsLine2": [1, "clear-result-suggestions-line-2"],
|
|
6735
7137
|
"clearResultSuggestionsLine3": [1, "clear-result-suggestions-line-3"],
|
|
6736
|
-
"clearResultSuggestionsLine4": [1, "clear-result-suggestions-line-4"]
|
|
7138
|
+
"clearResultSuggestionsLine4": [1, "clear-result-suggestions-line-4"],
|
|
7139
|
+
"autoFetch": [4, "auto-fetch"],
|
|
7140
|
+
"apiUrl": [1, "api-url"],
|
|
7141
|
+
"watchParams": [1, "watch-params"],
|
|
7142
|
+
"fetchedJobs": [32],
|
|
7143
|
+
"fetchedTotal": [32],
|
|
7144
|
+
"fetchLoading": [32]
|
|
7145
|
+
},
|
|
7146
|
+
"$listeners$": undefined,
|
|
7147
|
+
"$lazyBundleId$": "-",
|
|
7148
|
+
"$attrsToReflect$": []
|
|
7149
|
+
}; }
|
|
7150
|
+
}
|
|
7151
|
+
|
|
7152
|
+
const jobsListReactiveCss = () => `jobs-list-reactive{display:block}jobs-list-reactive.loading{opacity:0.6;pointer-events:none}`;
|
|
7153
|
+
|
|
7154
|
+
class JobsListReactive {
|
|
7155
|
+
constructor(hostRef) {
|
|
7156
|
+
registerInstance(this, hostRef);
|
|
7157
|
+
this.fetchComplete = createEvent(this, "fetchComplete");
|
|
7158
|
+
}
|
|
7159
|
+
get el() { return getElement(this); }
|
|
7160
|
+
/** Jobs search endpoint */
|
|
7161
|
+
apiUrl = '/api/get-jobs';
|
|
7162
|
+
/** Comma-separated URL param names to watch and forward to the API */
|
|
7163
|
+
watchParams = 'keyword,location_name';
|
|
7164
|
+
/** CSS class added to container while fetching */
|
|
7165
|
+
loadingClass = 'loading';
|
|
7166
|
+
isLoading = false;
|
|
7167
|
+
fetchComplete;
|
|
7168
|
+
templateEl = null;
|
|
7169
|
+
searchExecutedHandler;
|
|
7170
|
+
popstateHandler;
|
|
7171
|
+
connectedCallback() {
|
|
7172
|
+
this.templateEl = this.el.querySelector('template');
|
|
7173
|
+
this.searchExecutedHandler = () => this.fetchJobs();
|
|
7174
|
+
this.popstateHandler = () => this.fetchJobs();
|
|
7175
|
+
document.addEventListener('search-executed', this.searchExecutedHandler);
|
|
7176
|
+
window.addEventListener('popstate', this.popstateHandler);
|
|
7177
|
+
}
|
|
7178
|
+
disconnectedCallback() {
|
|
7179
|
+
document.removeEventListener('search-executed', this.searchExecutedHandler);
|
|
7180
|
+
window.removeEventListener('popstate', this.popstateHandler);
|
|
7181
|
+
}
|
|
7182
|
+
buildQueryString() {
|
|
7183
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
7184
|
+
const watchList = this.watchParams.split(',').map(p => p.trim()).filter(Boolean);
|
|
7185
|
+
const query = new URLSearchParams();
|
|
7186
|
+
for (const key of watchList) {
|
|
7187
|
+
const val = urlParams.get(key);
|
|
7188
|
+
if (val !== null && val !== '') {
|
|
7189
|
+
query.set(key, val);
|
|
7190
|
+
}
|
|
7191
|
+
}
|
|
7192
|
+
return query.toString();
|
|
7193
|
+
}
|
|
7194
|
+
async fetchJobs() {
|
|
7195
|
+
this.isLoading = true;
|
|
7196
|
+
this.el.classList.add(this.loadingClass);
|
|
7197
|
+
const queryString = this.buildQueryString();
|
|
7198
|
+
const url = queryString ? `${this.apiUrl}?${queryString}` : this.apiUrl;
|
|
7199
|
+
try {
|
|
7200
|
+
const res = await fetch(url, {
|
|
7201
|
+
method: 'POST',
|
|
7202
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7203
|
+
body: JSON.stringify({ disable_switch_search_mode: false }),
|
|
7204
|
+
});
|
|
7205
|
+
if (!res.ok)
|
|
7206
|
+
throw new Error('fetch failed');
|
|
7207
|
+
const data = await res.json();
|
|
7208
|
+
this.renderJobs(data.jobs);
|
|
7209
|
+
this.updateCountElements(data.jobs.length, data.totalJob);
|
|
7210
|
+
this.fetchComplete.emit({ jobs: data.jobs, totalJob: data.totalJob });
|
|
7211
|
+
}
|
|
7212
|
+
catch {
|
|
7213
|
+
// Preserve stale data on error
|
|
7214
|
+
}
|
|
7215
|
+
finally {
|
|
7216
|
+
this.isLoading = false;
|
|
7217
|
+
this.el.classList.remove(this.loadingClass);
|
|
7218
|
+
}
|
|
7219
|
+
}
|
|
7220
|
+
renderJobs(jobs) {
|
|
7221
|
+
if (!this.templateEl)
|
|
7222
|
+
return;
|
|
7223
|
+
// Remove all children except the template
|
|
7224
|
+
const children = Array.from(this.el.children);
|
|
7225
|
+
for (const child of children) {
|
|
7226
|
+
if (child !== this.templateEl) {
|
|
7227
|
+
child.remove();
|
|
7228
|
+
}
|
|
7229
|
+
}
|
|
7230
|
+
// Clone template and render each job
|
|
7231
|
+
for (const job of jobs) {
|
|
7232
|
+
const clone = this.templateEl.content.cloneNode(true);
|
|
7233
|
+
const jobCard = clone.querySelector('job-card');
|
|
7234
|
+
if (jobCard) {
|
|
7235
|
+
jobCard.setAttribute('job', JSON.stringify(job));
|
|
7236
|
+
}
|
|
7237
|
+
this.el.appendChild(clone);
|
|
7238
|
+
}
|
|
7239
|
+
}
|
|
7240
|
+
updateCountElements(count, total) {
|
|
7241
|
+
const countEls = document.querySelectorAll('[data-job-count]');
|
|
7242
|
+
const totalEls = document.querySelectorAll('[data-job-total]');
|
|
7243
|
+
countEls.forEach(el => {
|
|
7244
|
+
el.textContent = String(count);
|
|
7245
|
+
});
|
|
7246
|
+
totalEls.forEach(el => {
|
|
7247
|
+
el.textContent = String(total);
|
|
7248
|
+
});
|
|
7249
|
+
}
|
|
7250
|
+
render() {
|
|
7251
|
+
return hAsync("slot", { key: '30a6fe9727eb877b6aafb99072c40811df121ba6' });
|
|
7252
|
+
}
|
|
7253
|
+
static get style() { return jobsListReactiveCss(); }
|
|
7254
|
+
static get cmpMeta() { return {
|
|
7255
|
+
"$flags$": 772,
|
|
7256
|
+
"$tagName$": "jobs-list-reactive",
|
|
7257
|
+
"$members$": {
|
|
7258
|
+
"apiUrl": [1, "api-url"],
|
|
7259
|
+
"watchParams": [1, "watch-params"],
|
|
7260
|
+
"loadingClass": [1, "loading-class"],
|
|
7261
|
+
"isLoading": [32]
|
|
6737
7262
|
},
|
|
6738
7263
|
"$listeners$": undefined,
|
|
6739
7264
|
"$lazyBundleId$": "-",
|
|
@@ -6744,8 +7269,11 @@ class JobsListOnly {
|
|
|
6744
7269
|
registerComponents([
|
|
6745
7270
|
AppCarousel,
|
|
6746
7271
|
CustomButton,
|
|
7272
|
+
FastInput,
|
|
7273
|
+
JobCard,
|
|
6747
7274
|
JobsItem,
|
|
6748
7275
|
JobsListOnly,
|
|
7276
|
+
JobsListReactive,
|
|
6749
7277
|
]);
|
|
6750
7278
|
|
|
6751
7279
|
exports.hydrateApp = hydrateApp;
|