Both searches done

This commit is contained in:
Daniel J. Summers 2021-08-08 21:25:00 -04:00
parent 33c0e5ff5b
commit 4a10f5413d
5 changed files with 52 additions and 41 deletions

View File

@ -188,6 +188,11 @@ let withReconn (conn : IConnection) =
(conn :?> Connection).Reconnect() (conn :?> Connection).Reconnect()
| false -> ())) | false -> ()))
/// Sanitize user input, and create a "contains" pattern for use with RethinkDB queries
let regexContains (it : string) =
System.Text.RegularExpressions.Regex.Escape it
|> sprintf "(?i).*%s.*"
open JobsJobsJobs.Domain.SharedTypes open JobsJobsJobs.Domain.SharedTypes
open RethinkDb.Driver.Ast open RethinkDb.Driver.Ast
@ -251,14 +256,14 @@ module Profile =
match srch.skill with match srch.skill with
| Some skl -> | Some skl ->
yield (fun q -> q.Filter(ReqlFunction1(fun it -> yield (fun q -> q.Filter(ReqlFunction1(fun it ->
upcast it.G("skills.description").Downcase().Match(skl.ToLowerInvariant ()))) :> ReqlExpr) upcast it.G("skills").Contains(ReqlFunction1(fun s ->
upcast s.G("description").Match(regexContains skl))))) :> ReqlExpr)
| None -> () | None -> ()
match srch.bioExperience with match srch.bioExperience with
| Some text -> | Some text ->
let txt = text.ToLowerInvariant () let txt = regexContains text
yield (fun q -> q.Filter(ReqlFunction1(fun it -> yield (fun q -> q.Filter(ReqlFunction1(fun it ->
upcast it.G("biography" ).Downcase().Match(txt) upcast it.G("biography").Match(txt).Or(it.G("experience").Match(txt)))) :> ReqlExpr)
.Or(it.G("experience").Downcase().Match(txt)))) :> ReqlExpr)
| None -> () | None -> ()
} }
|> Seq.toList |> Seq.toList
@ -290,8 +295,7 @@ module Profile =
match srch.region with match srch.region with
| Some reg -> | Some reg ->
yield (fun q -> yield (fun q ->
q.Filter(ReqlFunction1(fun it -> q.Filter(ReqlFunction1(fun it -> upcast it.G("region").Match(regexContains reg))) :> ReqlExpr)
upcast it.G("region").Downcase().Match(reg.ToLowerInvariant ()))) :> ReqlExpr)
| None -> () | None -> ()
match srch.remoteWork with match srch.remoteWork with
| "" -> () | "" -> ()
@ -299,7 +303,8 @@ module Profile =
match srch.skill with match srch.skill with
| Some skl -> | Some skl ->
yield (fun q -> q.Filter(ReqlFunction1(fun it -> yield (fun q -> q.Filter(ReqlFunction1(fun it ->
upcast it.G("skills.description").Downcase().Match(skl.ToLowerInvariant ()))) :> ReqlExpr) upcast it.G("skills").Contains(ReqlFunction1(fun s ->
upcast s.G("description").Match(regexContains skl))))) :> ReqlExpr)
| None -> () | None -> ()
} }
|> Seq.toList |> Seq.toList
@ -315,7 +320,7 @@ module Profile =
.HashMap("skills", .HashMap("skills",
it.G("skills").Map(ReqlFunction1(fun skill -> it.G("skills").Map(ReqlFunction1(fun skill ->
upcast r.Branch(skill.G("notes").Default_("").Eq(""), skill.G("description"), upcast r.Branch(skill.G("notes").Default_("").Eq(""), skill.G("description"),
sprintf "%O (%O)" (skill.G("description")) (skill.G("notes")))))) skill.G("description").Add(" (").Add(skill.G("notes")).Add(")")))))
.With("continent", it.G("name")))) .With("continent", it.G("name"))))
.Pluck("continent", "region", "skills", "remoteWork") .Pluck("continent", "region", "skills", "remoteWork")
.RunResultAsync<PublicSearchResult list> conn) .RunResultAsync<PublicSearchResult list> conn)

View File

@ -146,7 +146,7 @@ export default {
publicSearch: async (query : PublicSearch) : Promise<PublicSearchResult[] | string | undefined> => { publicSearch: async (query : PublicSearch) : Promise<PublicSearchResult[] | string | undefined> => {
const params = new URLSearchParams() const params = new URLSearchParams()
if (query.continentId) params.append('continentId', query.continentId) if (query.continentId) params.append('continentId', query.continentId)
if (query.region) params.append('bioExperience', query.region) if (query.region) params.append('region', query.region)
if (query.skill) params.append('skill', query.skill) if (query.skill) params.append('skill', query.skill)
params.append('remoteWork', query.remoteWork) params.append('remoteWork', query.remoteWork)
return apiResult<PublicSearchResult[]>( return apiResult<PublicSearchResult[]>(

View File

@ -2,17 +2,19 @@
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h6 class="card-title"> <h6 class="card-title">
<a href="#" :class="{ 'cp-c': isCollapsed, 'cp-o': !isCollapsed }" @click.prevent="toggle">{{headerText}}</a> <a href="#" :class="{ 'cp-c': collapsed, 'cp-o': !collapsed }" @click.prevent="toggle">{{headerText}}</a>
</h6> </h6>
<slot v-if="!isCollapsed"></slot> <slot v-if="!collapsed"></slot>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref } from 'vue' import { defineComponent } from 'vue'
export default defineComponent({ export default defineComponent({
name: 'CollapsePanel', name: 'CollapsePanel',
emits: ['toggle'],
props: { props: {
headerText: { headerText: {
type: String, type: String,
@ -23,13 +25,9 @@ export default defineComponent({
default: false default: false
} }
}, },
setup (props) { setup (props, { emit }) {
/** Whether the panel is collapsed or not */
const isCollapsed = ref(props.collapsed)
return { return {
isCollapsed, toggle: () => emit('toggle', !props.collapsed)
toggle: () => { isCollapsed.value = !isCollapsed.value }
} }
} }
}) })

View File

@ -1,19 +1,18 @@
<template> <template>
<article> <article>
<page-title title="Search Profiles" /> <page-title title="Search Profiles" />
<h3>Search Profiles</h3> <h3 class="pb-3">Search Profiles</h3>
<error-list :errors="errors">
<p v-if="searching">Searching profiles...</p>
<template v-else>
<p v-if="!searched"> <p v-if="!searched">
Enter one or more criteria to filter results, or just click &ldquo;Search&rdquo; to list all profiles. Enter one or more criteria to filter results, or just click &ldquo;Search&rdquo; to list all profiles.
</p> </p>
<collapse-panel headerText="Search Criteria" :collapsed="searched && results.length > 0"> <collapse-panel headerText="Search Criteria" :collapsed="isCollapsed" @toggle="toggleCollapse">
<profile-search-form v-model="criteria" @search="doSearch" /> <profile-search-form v-model="criteria" @search="doSearch" />
</collapse-panel> </collapse-panel>
<br> <error-list :errors="errors">
<table v-if="results.length > 0" class="table table-sm table-hover"> <p v-if="searching" class="pt-3">Searching profiles...</p>
<template v-else>
<table v-if="results.length > 0" class="table table-sm table-hover pt-3">
<thead> <thead>
<tr> <tr>
<th scope="col">Profile</th> <th scope="col">Profile</th>
@ -35,7 +34,7 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<p v-else-if="searched">No results found for the specified criteria</p> <p v-else-if="searched" class="pt-3">No results found for the specified criteria</p>
</template> </template>
</error-list> </error-list>
</article> </article>
@ -90,6 +89,9 @@ export default defineComponent({
/** The current search results */ /** The current search results */
const results : Ref<ProfileSearchResult[]> = ref([]) const results : Ref<ProfileSearchResult[]> = ref([])
/** Whether the search criteria should be collapsed */
const isCollapsed = ref(searched.value && results.value.length > 0)
/** Set up the page to match its requested state */ /** Set up the page to match its requested state */
const setUpPage = async () => { const setUpPage = async () => {
if (queryValue(route, 'searched') === 'true') { if (queryValue(route, 'searched') === 'true') {
@ -114,6 +116,7 @@ export default defineComponent({
} finally { } finally {
searching.value = false searching.value = false
} }
isCollapsed.value = searched.value && results.value.length > 0
} else { } else {
searched.value = false searched.value = false
criteria.value = emptyCriteria criteria.value = emptyCriteria
@ -127,6 +130,8 @@ export default defineComponent({
return { return {
errors, errors,
criteria, criteria,
isCollapsed,
toggleCollapse: (it : boolean) => { isCollapsed.value = it },
doSearch: () => router.push({ query: { searched: 'true', ...criteria.value } }), doSearch: () => router.push({ query: { searched: 'true', ...criteria.value } }),
searching, searching,
searched, searched,

View File

@ -1,20 +1,19 @@
<template> <template>
<article> <article>
<page-title title="People Seeking Work" /> <page-title title="People Seeking Work" />
<h3>People Seeking Work</h3> <h3 class="pb-3">People Seeking Work</h3>
<error-list :errors="errors">
<p v-if="searching">Searching profiles...</p>
<template v-else>
<p v-if="!searched"> <p v-if="!searched">
Enter one or more criteria to filter results, or just click &ldquo;Search&rdquo; to list all profiles. Enter one or more criteria to filter results, or just click &ldquo;Search&rdquo; to list all profiles.
</p> </p>
<collapse-panel headerText="Search Criteria" :collapsed="searched && results.length > 0"> <collapse-panel headerText="Search Criteria" :collapsed="isCollapsed" @toggle="toggleCollapse">
<profile-public-search-form v-model="criteria" @search="doSearch" /> <profile-public-search-form v-model="criteria" @search="doSearch" />
</collapse-panel> </collapse-panel>
<br> <error-list :errors="errors">
<p v-if="searching">Searching profiles...</p>
<template v-else>
<template v-if="results.length > 0"> <template v-if="results.length > 0">
<p> <p class="pb-3 pt-3">
These profiles match your search criteria. To learn more about these people, join the merry band of human 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! resources in the <a href="https://noagendashow.net" target="_blank">No Agenda</a> tribe!
</p> </p>
@ -39,9 +38,7 @@
</tbody> </tbody>
</table> </table>
</template> </template>
<template v-else> <p v-else-if="searched" class="pt-3">No results found for the specified criteria</p>
<p v-if="searched">No results found for the specified criteria</p>
</template>
</template> </template>
</error-list> </error-list>
</article> </article>
@ -91,6 +88,9 @@ export default defineComponent({
/** The search results */ /** The search results */
const results : Ref<PublicSearchResult[]> = ref([]) const results : Ref<PublicSearchResult[]> = ref([])
/** Whether the search results are collapsed */
const isCollapsed = ref(searched.value && results.value.length > 0)
/** Set up the page to match its requested state */ /** Set up the page to match its requested state */
const setUpPage = async () => { const setUpPage = async () => {
if (queryValue(route, 'searched') === 'true') { if (queryValue(route, 'searched') === 'true') {
@ -115,6 +115,7 @@ export default defineComponent({
} finally { } finally {
searching.value = false searching.value = false
} }
isCollapsed.value = searched.value && results.value.length > 0
} else { } else {
searched.value = false searched.value = false
criteria.value = emptyCriteria criteria.value = emptyCriteria
@ -128,6 +129,8 @@ export default defineComponent({
return { return {
errors, errors,
criteria, criteria,
isCollapsed,
toggleCollapse: (it : boolean) => { isCollapsed.value = it },
doSearch: () => router.push({ query: { searched: 'true', ...criteria.value } }), doSearch: () => router.push({ query: { searched: 'true', ...criteria.value } }),
searching, searching,
searched, searched,