@inploi/plugin-job-search 0.10.0 → 0.12.0

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.
@@ -283,6 +283,7 @@ export declare const getAvailableFilters: ({ apiClient, filters, logger, signal,
283
283
  [x: string]: {
284
284
  label: string;
285
285
  value: string;
286
+ jobs: number;
286
287
  }[];
287
288
  }>;
288
289
  export type GetAvailableFiltersResponse = Awaited<ReturnType<typeof getAvailableFilters>>;
@@ -48,6 +48,17 @@ export type FilterProperty = {
48
48
  * - "only-available" (⚠️ leads to layout shifts) will show results that are available for the current filters
49
49
  */
50
50
  results?: 'only-available' | 'all';
51
+ /** How to sort the suggestions
52
+ * @default { by: 'results-count', order: 'ascending' }
53
+ * @description
54
+ * - 'results-count' will sort the suggestions by the number of results they have
55
+ * - 'alphabetically' will sort the suggestions alphabetically
56
+ * - 'numerically' will sort the suggestions numerically, coercing any string values to numbers
57
+ */
58
+ sortSuggestions?: {
59
+ by: 'results-count' | 'alphabetically' | 'numerically';
60
+ order: 'ascending' | 'descending';
61
+ };
51
62
  };
52
63
  export type DisplayableFilterProperty = {
53
64
  key: /** Enter any key or traverse any custom_data object returned by the job with dot-separated keys.
@@ -87,7 +98,8 @@ export type JobSearchPluginParams = {
87
98
  subheading: DisplayableFilterProperty | null;
88
99
  infoTags: DisplayableFilterProperty[];
89
100
  logo?: 'hidden' | '1:1' | '2:1';
90
- /** Whether to show the posted date or not.
101
+ /**
102
+ * Whether to show the posted date or not.
91
103
  * @default 'show'
92
104
  * @description
93
105
  * - 'show' will show the posted date and the "NEW" badge if the job is new (less than 7 days old).
@@ -96,6 +108,23 @@ export type JobSearchPluginParams = {
96
108
  */
97
109
  postedDate?: 'hide' | 'show' | 'only-new';
98
110
  overrides?: JobSearchPluginCardOverrides;
111
+ /**
112
+ * Specifies how the job link should be opened.
113
+ * @default 'redirect'
114
+ * @description
115
+ * - 'redirect': Navigates to the job's URL in the current tab.
116
+ * - 'redirect-new-tab': Opens the job's URL in a new tab.
117
+ */
118
+ openJob?: 'redirect' | 'redirect-new-tab';
119
+ };
120
+ filter?: {
121
+ /**
122
+ * Defines the number of filter options to show per page in a filter section.
123
+ * @default 8
124
+ * @description
125
+ * Also specifies the number of additional options to show upon clicking the "Load more" button.
126
+ */
127
+ suggestionsPerPage?: number;
99
128
  };
100
129
  /** Enables the job alerts button */
101
130
  enableJobAlerts?: boolean;
@@ -165,10 +194,10 @@ export declare const jobSearchPlugin: () => ({ apiClient, logger, analytics }: {
165
194
  town?: string | undefined;
166
195
  city?: string | undefined;
167
196
  country?: string | undefined;
168
- };
169
- id: number; /** Whether users can select one or multiple values.
197
+ }; /** Whether users can select one or multiple values.
170
198
  * @default `'one'`
171
199
  */
200
+ id: number;
172
201
  isNew: boolean;
173
202
  company: {
174
203
  name: string;
@@ -3,16 +3,20 @@ import { Output } from 'valibot';
3
3
  export declare const AvailablePropertiesSchema: import('valibot').RecordSchema<import('valibot').StringSchema<string>, import('valibot').ArraySchema<import('valibot').ObjectSchema<{
4
4
  label: import('valibot').StringSchema<string>;
5
5
  value: import('valibot').StringSchema<string>;
6
+ jobs: import('valibot').NumberSchema<number>;
6
7
  }, undefined, {
7
8
  label: string;
8
9
  value: string;
10
+ jobs: number;
9
11
  }>, {
10
12
  label: string;
11
13
  value: string;
14
+ jobs: number;
12
15
  }[]>, {
13
16
  [x: string]: {
14
17
  label: string;
15
18
  value: string;
19
+ jobs: number;
16
20
  }[];
17
21
  }>;
18
22
  export type AvailablePropertiesSchema = Output<typeof AvailablePropertiesSchema>;
@@ -1161,7 +1161,7 @@ const getRelativeTimeDifference = ({ from = /* @__PURE__ */ new Date(), to, form
1161
1161
  };
1162
1162
  const SEVEN_DAYS_MS = 1e3 * 60 * 60 * 24 * 7;
1163
1163
  const AvailablePropertiesSchema = record(
1164
- array(object({ label: coerce(string(), String), value: coerce(string(), String) }))
1164
+ array(object({ label: coerce(string(), String), value: coerce(string(), String), jobs: number() }))
1165
1165
  );
1166
1166
  const URLSchema = string([url()]);
1167
1167
  const PaginationSchema = object({
@@ -3375,6 +3375,31 @@ const toggleInArray = (array2, value) => {
3375
3375
  return [...array2, value];
3376
3376
  return array2.filter((_2, i2) => i2 !== index);
3377
3377
  };
3378
+ const coerceNumberLoosely = (value) => {
3379
+ var _a;
3380
+ const number2 = Number((_a = value.match(/\d/g)) == null ? void 0 : _a.join(""));
3381
+ return isNaN(number2) ? 0 : number2;
3382
+ };
3383
+ const sortResults = (results, sort = { by: "results-count", order: "descending" }) => {
3384
+ switch (sort.by) {
3385
+ case "results-count":
3386
+ results.sort((a2, b2) => b2.jobs - a2.jobs);
3387
+ break;
3388
+ case "alphabetically":
3389
+ results.sort((a2, b2) => a2.label.localeCompare(b2.label));
3390
+ break;
3391
+ case "numerically":
3392
+ results.sort((a2, b2) => {
3393
+ const aValue = coerceNumberLoosely(a2.value);
3394
+ const bValue = coerceNumberLoosely(b2.value);
3395
+ return aValue - bValue;
3396
+ });
3397
+ break;
3398
+ }
3399
+ if (sort.order === "descending")
3400
+ results.reverse();
3401
+ return results;
3402
+ };
3378
3403
  const FilterContentActive = ({
3379
3404
  value,
3380
3405
  property,
@@ -3382,7 +3407,8 @@ const FilterContentActive = ({
3382
3407
  useQuery,
3383
3408
  onValueChange,
3384
3409
  unfilteredQuery,
3385
- onRender
3410
+ onRender,
3411
+ filterParams
3386
3412
  }) => {
3387
3413
  const filterQuery = getQueryState(useQuery());
3388
3414
  const availablePropertyKeys = T$1(() => {
@@ -3401,7 +3427,8 @@ const FilterContentActive = ({
3401
3427
  }, [filterQuery, property.key]);
3402
3428
  const selectedValues = getSelectedValues(value);
3403
3429
  const hasValue = selectedValues.length > 0;
3404
- const [displayCount, setDisplayCount] = h(SUGGESTION_COUNT);
3430
+ const suggestionsPerPage = (filterParams == null ? void 0 : filterParams.suggestionsPerPage) ?? SUGGESTION_COUNT;
3431
+ const [displayCount, setDisplayCount] = h(suggestionsPerPage);
3405
3432
  const [search, setSearch] = h();
3406
3433
  y(() => {
3407
3434
  onRender();
@@ -3454,7 +3481,7 @@ const FilterContentActive = ({
3454
3481
  ) });
3455
3482
  case "revalidating":
3456
3483
  case "loaded": {
3457
- const allSuggestions = unfilteredQuery.data[property.key] ?? [];
3484
+ const allSuggestions = sortResults(unfilteredQuery.data[property.key] ?? [], property.sortSuggestions);
3458
3485
  const filteredSuggestions = search && search.length > 1 ? allSuggestions.filter((s2) => s2.label.toLowerCase().includes(search.toLowerCase())) : allSuggestions;
3459
3486
  const suggestions = [];
3460
3487
  for (const suggestion of filteredSuggestions) {
@@ -3525,7 +3552,7 @@ const FilterContentActive = ({
3525
3552
  class: "border-px flex w-full max-w-full flex-none flex-nowrap justify-center truncate border-transparent"
3526
3553
  }),
3527
3554
  onClick: () => {
3528
- setDisplayCount(displayCount + SUGGESTION_COUNT);
3555
+ setDisplayCount(displayCount + suggestionsPerPage);
3529
3556
  },
3530
3557
  children: [
3531
3558
  /* @__PURE__ */ u$1("span", { class: "font-style-tag font-family-tag font-weight-tag text-style-tag", children: "Show More" }),
@@ -3636,7 +3663,8 @@ const Filter = ({
3636
3663
  onSkip,
3637
3664
  onValueChange,
3638
3665
  unfilteredQuery,
3639
- useQuery
3666
+ useQuery,
3667
+ filterParams
3640
3668
  }) => {
3641
3669
  const defaultIcon = isKeyInObj(property.key, filterIcons) ? filterIcons[property.key] : void 0;
3642
3670
  const icon = property.icon ?? defaultIcon ?? "filter";
@@ -3678,7 +3706,8 @@ const Filter = ({
3678
3706
  onValueChange,
3679
3707
  unfilteredQuery,
3680
3708
  property,
3681
- value
3709
+ value,
3710
+ filterParams
3682
3711
  }
3683
3712
  ) }) })
3684
3713
  ] }),
@@ -3758,7 +3787,8 @@ const FilterSection = ({
3758
3787
  appliedFilters,
3759
3788
  onFilterValueChange,
3760
3789
  apiClient,
3761
- logger
3790
+ logger,
3791
+ filterParams
3762
3792
  }) => {
3763
3793
  var _a;
3764
3794
  const [activeKey, setActiveKey] = h((_a = properties[0]) == null ? void 0 : _a.key);
@@ -3825,7 +3855,8 @@ const FilterSection = ({
3825
3855
  setActiveKey(nextPropertyKey);
3826
3856
  },
3827
3857
  useQuery,
3828
- onValueChange
3858
+ onValueChange,
3859
+ filterParams
3829
3860
  }
3830
3861
  ) }, property.key);
3831
3862
  }) }) });
@@ -4337,7 +4368,7 @@ const JobCard = ({ job, config }) => {
4337
4368
  ((_a = config.overrides) == null ? void 0 : _a.salary) ? getDisplayableProperty({ job, property: config.overrides.salary }) : job.pay
4338
4369
  ).filter(Boolean);
4339
4370
  const postedDateConfig = config.postedDate ?? "show";
4340
- return /* @__PURE__ */ u$1("a", { href: job.href, class: wrapperClass(), children: [
4371
+ return /* @__PURE__ */ u$1("a", { href: job.href, target: config.openJob === "redirect-new-tab" ? "_blank" : void 0, class: wrapperClass(), children: [
4341
4372
  /* @__PURE__ */ u$1("div", { class: "flex min-w-0 flex-1 flex-col gap-4", children: [
4342
4373
  /* @__PURE__ */ u$1("div", { class: "gap-logo flex items-center", children: [
4343
4374
  /* @__PURE__ */ u$1(JobCardLogo, { job, config: config.logo }),
@@ -4567,6 +4598,7 @@ const JobSearch = ({
4567
4598
  return { default: false, filters: { ...prev.filters, [key]: value } };
4568
4599
  });
4569
4600
  },
4601
+ filterParams: params.filter,
4570
4602
  properties,
4571
4603
  parentRef: appRef,
4572
4604
  logger,
@@ -1159,7 +1159,7 @@ const getRelativeTimeDifference = ({ from = /* @__PURE__ */ new Date(), to, form
1159
1159
  };
1160
1160
  const SEVEN_DAYS_MS = 1e3 * 60 * 60 * 24 * 7;
1161
1161
  const AvailablePropertiesSchema = record(
1162
- array(object({ label: coerce(string(), String), value: coerce(string(), String) }))
1162
+ array(object({ label: coerce(string(), String), value: coerce(string(), String), jobs: number() }))
1163
1163
  );
1164
1164
  const URLSchema = string([url()]);
1165
1165
  const PaginationSchema = object({
@@ -3373,6 +3373,31 @@ const toggleInArray = (array2, value) => {
3373
3373
  return [...array2, value];
3374
3374
  return array2.filter((_2, i2) => i2 !== index);
3375
3375
  };
3376
+ const coerceNumberLoosely = (value) => {
3377
+ var _a;
3378
+ const number2 = Number((_a = value.match(/\d/g)) == null ? void 0 : _a.join(""));
3379
+ return isNaN(number2) ? 0 : number2;
3380
+ };
3381
+ const sortResults = (results, sort = { by: "results-count", order: "descending" }) => {
3382
+ switch (sort.by) {
3383
+ case "results-count":
3384
+ results.sort((a2, b2) => b2.jobs - a2.jobs);
3385
+ break;
3386
+ case "alphabetically":
3387
+ results.sort((a2, b2) => a2.label.localeCompare(b2.label));
3388
+ break;
3389
+ case "numerically":
3390
+ results.sort((a2, b2) => {
3391
+ const aValue = coerceNumberLoosely(a2.value);
3392
+ const bValue = coerceNumberLoosely(b2.value);
3393
+ return aValue - bValue;
3394
+ });
3395
+ break;
3396
+ }
3397
+ if (sort.order === "descending")
3398
+ results.reverse();
3399
+ return results;
3400
+ };
3376
3401
  const FilterContentActive = ({
3377
3402
  value,
3378
3403
  property,
@@ -3380,7 +3405,8 @@ const FilterContentActive = ({
3380
3405
  useQuery,
3381
3406
  onValueChange,
3382
3407
  unfilteredQuery,
3383
- onRender
3408
+ onRender,
3409
+ filterParams
3384
3410
  }) => {
3385
3411
  const filterQuery = getQueryState(useQuery());
3386
3412
  const availablePropertyKeys = T$1(() => {
@@ -3399,7 +3425,8 @@ const FilterContentActive = ({
3399
3425
  }, [filterQuery, property.key]);
3400
3426
  const selectedValues = getSelectedValues(value);
3401
3427
  const hasValue = selectedValues.length > 0;
3402
- const [displayCount, setDisplayCount] = h(SUGGESTION_COUNT);
3428
+ const suggestionsPerPage = (filterParams == null ? void 0 : filterParams.suggestionsPerPage) ?? SUGGESTION_COUNT;
3429
+ const [displayCount, setDisplayCount] = h(suggestionsPerPage);
3403
3430
  const [search, setSearch] = h();
3404
3431
  y(() => {
3405
3432
  onRender();
@@ -3452,7 +3479,7 @@ const FilterContentActive = ({
3452
3479
  ) });
3453
3480
  case "revalidating":
3454
3481
  case "loaded": {
3455
- const allSuggestions = unfilteredQuery.data[property.key] ?? [];
3482
+ const allSuggestions = sortResults(unfilteredQuery.data[property.key] ?? [], property.sortSuggestions);
3456
3483
  const filteredSuggestions = search && search.length > 1 ? allSuggestions.filter((s2) => s2.label.toLowerCase().includes(search.toLowerCase())) : allSuggestions;
3457
3484
  const suggestions = [];
3458
3485
  for (const suggestion of filteredSuggestions) {
@@ -3523,7 +3550,7 @@ const FilterContentActive = ({
3523
3550
  class: "border-px flex w-full max-w-full flex-none flex-nowrap justify-center truncate border-transparent"
3524
3551
  }),
3525
3552
  onClick: () => {
3526
- setDisplayCount(displayCount + SUGGESTION_COUNT);
3553
+ setDisplayCount(displayCount + suggestionsPerPage);
3527
3554
  },
3528
3555
  children: [
3529
3556
  /* @__PURE__ */ u$1("span", { class: "font-style-tag font-family-tag font-weight-tag text-style-tag", children: "Show More" }),
@@ -3634,7 +3661,8 @@ const Filter = ({
3634
3661
  onSkip,
3635
3662
  onValueChange,
3636
3663
  unfilteredQuery,
3637
- useQuery
3664
+ useQuery,
3665
+ filterParams
3638
3666
  }) => {
3639
3667
  const defaultIcon = isKeyInObj(property.key, filterIcons) ? filterIcons[property.key] : void 0;
3640
3668
  const icon = property.icon ?? defaultIcon ?? "filter";
@@ -3676,7 +3704,8 @@ const Filter = ({
3676
3704
  onValueChange,
3677
3705
  unfilteredQuery,
3678
3706
  property,
3679
- value
3707
+ value,
3708
+ filterParams
3680
3709
  }
3681
3710
  ) }) })
3682
3711
  ] }),
@@ -3756,7 +3785,8 @@ const FilterSection = ({
3756
3785
  appliedFilters,
3757
3786
  onFilterValueChange,
3758
3787
  apiClient,
3759
- logger
3788
+ logger,
3789
+ filterParams
3760
3790
  }) => {
3761
3791
  var _a;
3762
3792
  const [activeKey, setActiveKey] = h((_a = properties[0]) == null ? void 0 : _a.key);
@@ -3823,7 +3853,8 @@ const FilterSection = ({
3823
3853
  setActiveKey(nextPropertyKey);
3824
3854
  },
3825
3855
  useQuery,
3826
- onValueChange
3856
+ onValueChange,
3857
+ filterParams
3827
3858
  }
3828
3859
  ) }, property.key);
3829
3860
  }) }) });
@@ -4335,7 +4366,7 @@ const JobCard = ({ job, config }) => {
4335
4366
  ((_a = config.overrides) == null ? void 0 : _a.salary) ? getDisplayableProperty({ job, property: config.overrides.salary }) : job.pay
4336
4367
  ).filter(Boolean);
4337
4368
  const postedDateConfig = config.postedDate ?? "show";
4338
- return /* @__PURE__ */ u$1("a", { href: job.href, class: wrapperClass(), children: [
4369
+ return /* @__PURE__ */ u$1("a", { href: job.href, target: config.openJob === "redirect-new-tab" ? "_blank" : void 0, class: wrapperClass(), children: [
4339
4370
  /* @__PURE__ */ u$1("div", { class: "flex min-w-0 flex-1 flex-col gap-4", children: [
4340
4371
  /* @__PURE__ */ u$1("div", { class: "gap-logo flex items-center", children: [
4341
4372
  /* @__PURE__ */ u$1(JobCardLogo, { job, config: config.logo }),
@@ -4565,6 +4596,7 @@ const JobSearch = ({
4565
4596
  return { default: false, filters: { ...prev.filters, [key]: value } };
4566
4597
  });
4567
4598
  },
4599
+ filterParams: params.filter,
4568
4600
  properties,
4569
4601
  parentRef: appRef,
4570
4602
  logger,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inploi/plugin-job-search",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "main": "dist/plugin-job-search.js",
6
6
  "types": "dist/index.d.ts",
@@ -48,7 +48,7 @@
48
48
  "@inploi/core": "1.14.0",
49
49
  "@inploi/design-tokens": "0.2.1",
50
50
  "@inploi/plugin-job-alerts": "0.2.0",
51
- "@inploi/sdk": "1.13.1",
51
+ "@inploi/sdk": "1.13.2",
52
52
  "eslint-config-custom": "0.1.0"
53
53
  },
54
54
  "msw": {