First cut of public profile search
Also tweak logged-on profile search
This commit is contained in:
parent
8f5ccd7338
commit
db753699b8
|
@ -279,9 +279,9 @@ module Profile =
|
||||||
.RunResultAsync<ProfileSearchResult list> conn)
|
.RunResultAsync<ProfileSearchResult list> conn)
|
||||||
|
|
||||||
// Search profiles (public)
|
// Search profiles (public)
|
||||||
let publicSearch (srch : PublicSearch) conn = task {
|
let publicSearch (srch : PublicSearch) conn =
|
||||||
let results =
|
withReconn(conn).ExecuteAsync(fun () ->
|
||||||
seq {
|
(seq {
|
||||||
match srch.continentId with
|
match srch.continentId with
|
||||||
| Some conId ->
|
| Some conId ->
|
||||||
yield (fun (q : ReqlExpr) ->
|
yield (fun (q : ReqlExpr) ->
|
||||||
|
@ -303,10 +303,22 @@ module Profile =
|
||||||
| None -> ()
|
| None -> ()
|
||||||
}
|
}
|
||||||
|> Seq.toList
|
|> Seq.toList
|
||||||
|> List.fold (fun q f -> f q) (r.Table(Table.Profile) :> ReqlExpr)
|
|> List.fold
|
||||||
// TODO: pluck fields, compile skills
|
(fun q f -> f q)
|
||||||
return! results.RunResultAsync<PublicSearchResult list> conn
|
(r.Table(Table.Profile)
|
||||||
}
|
.EqJoin("continentId", r.Table(Table.Continent))
|
||||||
|
.Without(r.HashMap("right", "id"))
|
||||||
|
.Zip()
|
||||||
|
.Filter(r.HashMap("isPublic", true)) :> ReqlExpr))
|
||||||
|
.Merge(ReqlFunction1(fun it ->
|
||||||
|
upcast r
|
||||||
|
.HashMap("skills",
|
||||||
|
it.G("skills").Map(ReqlFunction1(fun skill ->
|
||||||
|
upcast r.Branch(skill.G("notes").Default_("").Eq(""), skill.G("description"),
|
||||||
|
sprintf "%O (%O)" (skill.G("description")) (skill.G("notes"))))))
|
||||||
|
.With("continent", it.G("name"))))
|
||||||
|
.Pluck("continent", "region", "skills", "remoteWork")
|
||||||
|
.RunResultAsync<PublicSearchResult list> conn)
|
||||||
|
|
||||||
|
|
||||||
/// Citizen data access functions
|
/// Citizen data access functions
|
||||||
|
|
|
@ -20,7 +20,6 @@ module Vue =
|
||||||
module Error =
|
module Error =
|
||||||
|
|
||||||
open System.Threading.Tasks
|
open System.Threading.Tasks
|
||||||
open Microsoft.Extensions.Logging
|
|
||||||
|
|
||||||
/// Handler that will return a status code 404 and the text "Not Found"
|
/// Handler that will return a status code 404 and the text "Not Found"
|
||||||
let notFound : HttpHandler =
|
let notFound : HttpHandler =
|
||||||
|
@ -49,7 +48,6 @@ module Helpers =
|
||||||
|
|
||||||
open NodaTime
|
open NodaTime
|
||||||
open Microsoft.Extensions.Configuration
|
open Microsoft.Extensions.Configuration
|
||||||
open Microsoft.Extensions.Logging
|
|
||||||
open RethinkDb.Driver.Net
|
open RethinkDb.Driver.Net
|
||||||
open System.Security.Claims
|
open System.Security.Claims
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,16 @@ export default defineComponent({
|
||||||
TitleBar
|
TitleBar
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return "Yes" for true and "No" for false
|
||||||
|
*
|
||||||
|
* @param cond The condition to be checked
|
||||||
|
* @returns "Yes" for true, "No" for false
|
||||||
|
*/
|
||||||
|
export function yesOrNo (cond : boolean) : string {
|
||||||
|
return cond ? 'Yes' : 'No'
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="sass">
|
<style lang="sass">
|
||||||
|
|
|
@ -8,6 +8,8 @@ import {
|
||||||
ProfileForView,
|
ProfileForView,
|
||||||
ProfileSearch,
|
ProfileSearch,
|
||||||
ProfileSearchResult,
|
ProfileSearchResult,
|
||||||
|
PublicSearch,
|
||||||
|
PublicSearchResult,
|
||||||
StoryEntry,
|
StoryEntry,
|
||||||
Success
|
Success
|
||||||
} from './types'
|
} from './types'
|
||||||
|
@ -113,6 +115,23 @@ export default {
|
||||||
/** API functions for profiles */
|
/** API functions for profiles */
|
||||||
profile: {
|
profile: {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for public profile data using the given parameters
|
||||||
|
*
|
||||||
|
* @param query The public profile search parameters
|
||||||
|
* @returns The matching public profiles (if found), undefined (if API returns 404), or an error string
|
||||||
|
*/
|
||||||
|
publicSearch: async (query : PublicSearch) : Promise<PublicSearchResult[] | string | undefined> => {
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
if (query.continentId) params.append('continentId', query.continentId)
|
||||||
|
if (query.region) params.append('bioExperience', query.region)
|
||||||
|
if (query.skill) params.append('skill', query.skill)
|
||||||
|
params.append('remoteWork', query.remoteWork)
|
||||||
|
return apiResult<PublicSearchResult[]>(
|
||||||
|
await fetch(apiUrl(`profile/public-search?${params.toString()}`), { method: 'GET' }),
|
||||||
|
'searching public profile data')
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a profile
|
* Retrieve a profile
|
||||||
*
|
*
|
||||||
|
|
|
@ -25,6 +25,12 @@ export interface Continent {
|
||||||
name : string
|
name : string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A count */
|
||||||
|
export interface Count {
|
||||||
|
/** The count being returned */
|
||||||
|
count : number
|
||||||
|
}
|
||||||
|
|
||||||
/** A successful logon */
|
/** A successful logon */
|
||||||
export interface LogOnSuccess {
|
export interface LogOnSuccess {
|
||||||
/** The JSON Web Token (JWT) to use for API access */
|
/** The JSON Web Token (JWT) to use for API access */
|
||||||
|
@ -109,10 +115,28 @@ export interface ProfileSearchResult {
|
||||||
lastUpdatedOn : string
|
lastUpdatedOn : string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A count */
|
/** The parameters for a public job search */
|
||||||
export interface Count {
|
export interface PublicSearch {
|
||||||
/** The count being returned */
|
/** Retrieve citizens from this continent */
|
||||||
count : number
|
continentId : string | undefined
|
||||||
|
/** Retrieve citizens from this region */
|
||||||
|
region : string | undefined
|
||||||
|
/** Text for a search within a citizen's skills */
|
||||||
|
skill : string | undefined
|
||||||
|
/** Whether to retrieve citizens who do or do not want remote work */
|
||||||
|
remoteWork : string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A public profile search result */
|
||||||
|
export interface PublicSearchResult {
|
||||||
|
/** The name of the continent on which the citizen resides */
|
||||||
|
continent : string
|
||||||
|
/** The region in which the citizen resides */
|
||||||
|
region : string
|
||||||
|
/** Whether this citizen is seeking remote work */
|
||||||
|
remoteWork : boolean
|
||||||
|
/** The skills this citizen has identified */
|
||||||
|
skills : string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An entry in the list of success stories */
|
/** An entry in the list of success stories */
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<form>
|
||||||
|
<v-container>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" sm="6" md="4" lg="3">
|
||||||
|
<label for="continentId" class="jjj-label">Continent</label>
|
||||||
|
<select id="continentId" class="form-control form-control-sm"
|
||||||
|
:value="criteria.continentId" @change="updateValue('continentId', $event.target.value)">
|
||||||
|
<option value="">– Any –</option>
|
||||||
|
<option v-for="c in continents" :key="c.id" :value="c.id">{{c.name}}</option>
|
||||||
|
</select>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" md="4" lg="3">
|
||||||
|
<label for="region" class="jjj-label">Region</label>
|
||||||
|
<input type="text" id="region" class="form-control form-control-sm" placeholder="(free-form text)"
|
||||||
|
:value="criteria.region" @input="updateValue('region', $event.target.value)">
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" offset-md="2" lg="3" offset-lg="0">
|
||||||
|
<label class="jjj-label">Seeking Remote Work?</label><br>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input type="radio" id="remoteNull" name="remoteWork" class="form-check-input"
|
||||||
|
:checked="criteria.remoteWork === ''" @click="updateValue('remoteWork', '')">
|
||||||
|
<label for="remoteNull" class="form-check-label">No Selection</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input type="radio" id="remoteYes" name="remoteWork" class="form-check-input"
|
||||||
|
:checked="criteria.remoteWork === 'yes'" @click="updateValue('remoteWork', 'yes')">
|
||||||
|
<label for="remoteYes" class="form-check-label">Yes</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input type="radio" id="remoteNo" name="remoteWork" class="form-check-input"
|
||||||
|
:checked="criteria.remoteWork === 'no'" @click="updateValue('remoteWork', 'no')">
|
||||||
|
<label for="remoteNo" class="form-check-label">No</label>
|
||||||
|
</div>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="6" lg="3">
|
||||||
|
<label for="skillSearch" class="jjj-label">Skill</label>
|
||||||
|
<input type="text" id="skillSearch" class="form-control form-control-sm" placeholder="(free-form text)"
|
||||||
|
:value="criteria.skill" @input="updateValue('skill', $event.target.value)">
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12">
|
||||||
|
<br>
|
||||||
|
<v-btn type="submit" color="primary" variant="outlined" @click.prevent="$emit('search')">Search</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, onMounted } from 'vue'
|
||||||
|
import { PublicSearch } from '@/api'
|
||||||
|
import { useStore } from '@/store'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ProfilePublicSearchForm',
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['search', 'update:modelValue'],
|
||||||
|
setup (props, { emit }) {
|
||||||
|
const store = useStore()
|
||||||
|
|
||||||
|
/** The initial search criteria passed; this is what we'll update and emit when data changes */
|
||||||
|
const criteria : PublicSearch = { ...props.modelValue as PublicSearch }
|
||||||
|
|
||||||
|
/** Make sure we have continents */
|
||||||
|
onMounted(async () => await store.dispatch('ensureContinents'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
criteria,
|
||||||
|
continents: computed(() => store.state.continents),
|
||||||
|
updateValue: (key : string, value : string) => emit('update:modelValue', { ...criteria, [key]: value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<form Model=@Criteria OnValidSubmit=@OnSearch>
|
<form>
|
||||||
<v-container>
|
<v-container>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" sm="6" md="4" lg="3">
|
<v-col cols="12" sm="6" md="4" lg="3">
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
:value="criteria.bioExperience" @input="updateValue('bioExperience', $event.target.value)">
|
:value="criteria.bioExperience" @input="updateValue('bioExperience', $event.target.value)">
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row class="form-row">
|
<v-row>
|
||||||
<v-col cols="12">
|
<v-col cols="12">
|
||||||
<br>
|
<br>
|
||||||
<v-btn type="submit" color="primary" variant="outlined" @click.prevent="$emit('search')">Search</v-btn>
|
<v-btn type="submit" color="primary" variant="outlined" @click.prevent="$emit('search')">Search</v-btn>
|
||||||
|
|
|
@ -13,6 +13,18 @@ import LogOn from '@/views/citizen/LogOn.vue'
|
||||||
/** The URL to which the user should be pointed once they have authorized with NAS */
|
/** The URL to which the user should be pointed once they have authorized with NAS */
|
||||||
export const AFTER_LOG_ON_URL = 'jjj-after-log-on-url'
|
export const AFTER_LOG_ON_URL = 'jjj-after-log-on-url'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a value from the query string
|
||||||
|
*
|
||||||
|
* @param route The current route
|
||||||
|
* @param key The key of the query string value to obtain
|
||||||
|
* @returns The string value, the first of many (if included multiple times), or `undefined` if not present
|
||||||
|
*/
|
||||||
|
export function queryValue (route: RouteLocationNormalizedLoaded, key : string) : string | undefined {
|
||||||
|
const value = route.query[key]
|
||||||
|
if (value) return Array.isArray(value) && value.length > 0 ? value[0]?.toString() : value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
const routes: Array<RouteRecordRaw> = [
|
const routes: Array<RouteRecordRaw> = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
|
|
|
@ -42,9 +42,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, Ref, ref, watch } from 'vue'
|
import { defineComponent, Ref, ref, watch } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { yesOrNo } from '@/App.vue'
|
||||||
import api, { LogOnSuccess, ProfileSearch, ProfileSearchResult } from '@/api'
|
import api, { LogOnSuccess, ProfileSearch, ProfileSearchResult } from '@/api'
|
||||||
|
import { queryValue } from '@/router'
|
||||||
import { useStore } from '@/store'
|
import { useStore } from '@/store'
|
||||||
|
|
||||||
import CollapsePanel from '@/components/CollapsePanel.vue'
|
import CollapsePanel from '@/components/CollapsePanel.vue'
|
||||||
|
@ -88,25 +90,17 @@ export default defineComponent({
|
||||||
/** The current search results */
|
/** The current search results */
|
||||||
const results : Ref<ProfileSearchResult[]> = ref([])
|
const results : Ref<ProfileSearchResult[]> = ref([])
|
||||||
|
|
||||||
/** Return "Yes" for true and "No" for false */
|
/** Set up the page to match its requested state */
|
||||||
const yesOrNo = (cond : boolean) => cond ? 'Yes' : 'No'
|
|
||||||
|
|
||||||
/** Get a value from the query string */
|
|
||||||
const queryValue = (key : string) : string | undefined => {
|
|
||||||
const value = route.query[key]
|
|
||||||
if (value) return Array.isArray(value) && value.length > 0 ? value[0]?.toString() : value.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
const setUpPage = async () => {
|
const setUpPage = async () => {
|
||||||
if (queryValue('searched') === 'true') {
|
if (queryValue(route, 'searched') === 'true') {
|
||||||
searched.value = true
|
searched.value = true
|
||||||
try {
|
try {
|
||||||
searching.value = true
|
searching.value = true
|
||||||
const searchParams : ProfileSearch = { // eslint-disable-line
|
const searchParams : ProfileSearch = {
|
||||||
continentId: queryValue('continentId'),
|
continentId: queryValue(route, 'continentId'),
|
||||||
skill: queryValue('skill'),
|
skill: queryValue(route, 'skill'),
|
||||||
bioExperience: queryValue('bioExperience'),
|
bioExperience: queryValue(route, 'bioExperience'),
|
||||||
remoteWork: queryValue('remoteWork') || ''
|
remoteWork: queryValue(route, 'remoteWork') || ''
|
||||||
}
|
}
|
||||||
const searchResult = await api.profile.search(searchParams, store.state.user as LogOnSuccess)
|
const searchResult = await api.profile.search(searchParams, store.state.user as LogOnSuccess)
|
||||||
if (typeof searchResult === 'string') {
|
if (typeof searchResult === 'string') {
|
||||||
|
@ -124,6 +118,7 @@ export default defineComponent({
|
||||||
searched.value = false
|
searched.value = false
|
||||||
criteria.value = emptyCriteria
|
criteria.value = emptyCriteria
|
||||||
errors.value = []
|
errors.value = []
|
||||||
|
results.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,9 +130,8 @@ export default defineComponent({
|
||||||
doSearch: () => router.push({ query: { searched: 'true', ...criteria.value } }),
|
doSearch: () => router.push({ query: { searched: 'true', ...criteria.value } }),
|
||||||
searching,
|
searching,
|
||||||
searched,
|
searched,
|
||||||
yesOrNo,
|
|
||||||
results,
|
results,
|
||||||
continents: computed(() => store.state.continents)
|
yesOrNo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,3 +1,139 @@
|
||||||
<template>
|
<template>
|
||||||
<p>TODO: convert this view</p>
|
<article>
|
||||||
|
<page-title title="People Seeking Work" />
|
||||||
|
<h3>People Seeking Work</h3>
|
||||||
|
|
||||||
|
<error-list :errors="errors">
|
||||||
|
<p v-if="searching">Searching profiles...</p>
|
||||||
|
<template v-else>
|
||||||
|
<p v-if="!searched">
|
||||||
|
Enter one or more criteria to filter results, or just click “Search” to list all profiles.
|
||||||
|
</p>
|
||||||
|
<collapse-panel headerText="Search Criteria" :collapsed="searched && results.length > 0">
|
||||||
|
<profile-public-search-form v-model="criteria" @search="doSearch" />
|
||||||
|
</collapse-panel>
|
||||||
|
<br>
|
||||||
|
<template v-if="results.length > 0">
|
||||||
|
<p>
|
||||||
|
These profiles match your search criteria. To learn more about these people, join the merry band of human
|
||||||
|
resources in the <a href="https://noagendashow.net" target="_blank">No Agenda</a> tribe!
|
||||||
|
</p>
|
||||||
|
<table class="table table-sm table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Continent</th>
|
||||||
|
<th scope="col" class="text-center">Region</th>
|
||||||
|
<th scope="col" class="text-center">Remote?</th>
|
||||||
|
<th scope="col" class="text-center">Skills</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(profile, idx) in results" :key="idx">
|
||||||
|
<td>{{profile.continent}}</td>
|
||||||
|
<td>{{profile.region}}</td>
|
||||||
|
<td class="text-center">{{yesOrNo(profile.remoteWork)}}</td>
|
||||||
|
<td>
|
||||||
|
<template v-for="(skill, idx) in profile.skills" :key="idx">{{skill}}<br></template>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<p v-if="searched">No results found for the specified criteria</p>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</error-list>
|
||||||
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, Ref, ref, watch } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { yesOrNo } from '@/App.vue'
|
||||||
|
import api, { PublicSearch, PublicSearchResult } from '@/api'
|
||||||
|
import { queryValue } from '@/router'
|
||||||
|
|
||||||
|
import CollapsePanel from '@/components/CollapsePanel.vue'
|
||||||
|
import ErrorList from '@/components/ErrorList.vue'
|
||||||
|
import ProfilePublicSearchForm from '@/components/profile/PublicSearchForm.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
CollapsePanel,
|
||||||
|
ErrorList,
|
||||||
|
ProfilePublicSearchForm
|
||||||
|
},
|
||||||
|
setup () {
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
/** Whether a search has been performed */
|
||||||
|
const searched = ref(false)
|
||||||
|
|
||||||
|
/** Indicates whether a request for matching profiles is in progress */
|
||||||
|
const searching = ref(false)
|
||||||
|
|
||||||
|
/** Error messages encountered while searching for profiles */
|
||||||
|
const errors : Ref<string[]> = ref([])
|
||||||
|
|
||||||
|
/** An empty set of search criteria */
|
||||||
|
const emptyCriteria = {
|
||||||
|
continentId: undefined,
|
||||||
|
region: undefined,
|
||||||
|
skill: undefined,
|
||||||
|
remoteWork: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The search criteria being built from the page */
|
||||||
|
const criteria : Ref<PublicSearch> = ref(emptyCriteria)
|
||||||
|
|
||||||
|
/** The search results */
|
||||||
|
const results : Ref<PublicSearchResult[]> = ref([])
|
||||||
|
|
||||||
|
/** Set up the page to match its requested state */
|
||||||
|
const setUpPage = async () => {
|
||||||
|
if (queryValue(route, 'searched') === 'true') {
|
||||||
|
searched.value = true
|
||||||
|
try {
|
||||||
|
searching.value = true
|
||||||
|
const searchParams : PublicSearch = {
|
||||||
|
continentId: queryValue(route, 'continentId'),
|
||||||
|
region: queryValue(route, 'region'),
|
||||||
|
skill: queryValue(route, 'skill'),
|
||||||
|
remoteWork: queryValue(route, 'remoteWork') || ''
|
||||||
|
}
|
||||||
|
const searchResult = await api.profile.publicSearch(searchParams)
|
||||||
|
if (typeof searchResult === 'string') {
|
||||||
|
errors.value.push(searchResult)
|
||||||
|
} else if (searchResult === undefined) {
|
||||||
|
errors.value.push('The server returned a "Not Found" response (this should not happen)')
|
||||||
|
} else {
|
||||||
|
results.value = searchResult
|
||||||
|
criteria.value = searchParams
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
searching.value = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
searched.value = false
|
||||||
|
criteria.value = emptyCriteria
|
||||||
|
errors.value = []
|
||||||
|
results.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => route.query, setUpPage, { immediate: true })
|
||||||
|
|
||||||
|
return {
|
||||||
|
errors,
|
||||||
|
criteria,
|
||||||
|
doSearch: () => router.push({ query: { searched: 'true', ...criteria.value } }),
|
||||||
|
searching,
|
||||||
|
searched,
|
||||||
|
results,
|
||||||
|
yesOrNo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
|
@ -135,6 +135,7 @@ type ProfileForView = {
|
||||||
|
|
||||||
|
|
||||||
/// The parameters for a public job search
|
/// The parameters for a public job search
|
||||||
|
[<CLIMutable>]
|
||||||
type PublicSearch = {
|
type PublicSearch = {
|
||||||
/// Retrieve citizens from this continent
|
/// Retrieve citizens from this continent
|
||||||
continentId : string option
|
continentId : string option
|
||||||
|
|
Loading…
Reference in New Issue
Block a user