Change to individual icons

The bundle size was huge; this also has tweaks to flow, and a revisiting of the data access code
This commit is contained in:
Daniel J. Summers 2021-08-29 22:12:41 -04:00
parent 34d5224c11
commit bda2b38eb4
13 changed files with 250 additions and 244 deletions

View File

@ -176,8 +176,13 @@ module Startup =
/// Determine if a record type (not nullable) is null /// Determine if a record type (not nullable) is null
let toOption x = match x |> box |> isNull with true -> None | false -> Some x let toOption x = match x |> box |> isNull with true -> None | false -> Some x
/// A retry policy where we will reconnect to RethinkDB if it has gone away [<AutoOpen>]
let withReconn (conn : IConnection) = module private Reconnect =
open System.Threading.Tasks
/// Execute a query with a retry policy that will reconnect to RethinkDB if it has gone away
let withReconn (conn : IConnection) (f : IConnection -> Task<'T>) =
Policy Policy
.Handle<ReqlDriverError>() .Handle<ReqlDriverError>()
.RetryAsync(System.Action<exn, int> (fun ex _ -> .RetryAsync(System.Action<exn, int> (fun ex _ ->
@ -185,13 +190,28 @@ let withReconn (conn : IConnection) =
match ex.Message.Contains "socket" with match ex.Message.Contains "socket" with
| true -> | true ->
printf "Reconnecting to RethinkDB" printf "Reconnecting to RethinkDB"
(conn :?> Connection).Reconnect() (conn :?> Connection).Reconnect false
| false -> ())) | false -> ()))
.ExecuteAsync(fun () -> f conn)
/// Execute a query that returns one or none item, using the reconnect logic
let withReconnOption (conn : IConnection) (f : IConnection -> Task<'T>) =
fun c -> task {
let! it = f c
return toOption it
}
|> withReconn conn
/// Execute a query that does not return a result, using the above reconnect logic
let withReconnIgnore (conn : IConnection) (f : IConnection -> Task<'T>) =
fun c -> task {
let! _ = f c
()
}
|> withReconn conn
/// Sanitize user input, and create a "contains" pattern for use with RethinkDB queries /// Sanitize user input, and create a "contains" pattern for use with RethinkDB queries
let regexContains (it : string) = let regexContains = System.Text.RegularExpressions.Regex.Escape >> sprintf "(?i)%s"
System.Text.RegularExpressions.Regex.Escape it
|> sprintf "(?i)%s"
open JobsJobsJobs.Domain open JobsJobsJobs.Domain
open JobsJobsJobs.Domain.SharedTypes open JobsJobsJobs.Domain.SharedTypes
@ -202,46 +222,37 @@ open RethinkDb.Driver.Ast
module Profile = module Profile =
let count conn = let count conn =
withReconn(conn).ExecuteAsync(fun () ->
r.Table(Table.Profile) r.Table(Table.Profile)
.Count() .Count()
.RunResultAsync<int64> conn) .RunResultAsync<int64>
|> withReconn conn
/// Find a profile by citizen ID /// Find a profile by citizen ID
let findById (citizenId : CitizenId) conn = let findById (citizenId : CitizenId) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! profile =
r.Table(Table.Profile) r.Table(Table.Profile)
.Get(citizenId) .Get(citizenId)
.RunResultAsync<Profile> conn .RunResultAsync<Profile>
return toOption profile |> withReconnOption conn
})
/// Insert or update a profile /// Insert or update a profile
let save (profile : Profile) conn = let save (profile : Profile) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Profile) r.Table(Table.Profile)
.Get(profile.id) .Get(profile.id)
.Replace(profile) .Replace(profile)
.RunWriteAsync conn .RunWriteAsync
() |> withReconnIgnore conn
})
/// Delete a citizen's profile /// Delete a citizen's profile
let delete (citizenId : CitizenId) conn = let delete (citizenId : CitizenId) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Profile) r.Table(Table.Profile)
.Get(citizenId) .Get(citizenId)
.Delete() .Delete()
.RunWriteAsync conn .RunWriteAsync
() |> withReconnIgnore conn
})
/// Search profiles (logged-on users) /// Search profiles (logged-on users)
let search (srch : ProfileSearch) conn = let search (srch : ProfileSearch) conn =
withReconn(conn).ExecuteAsync(fun () -> fun c ->
(seq { (seq {
match srch.continentId with match srch.continentId with
| Some conId -> | Some conId ->
@ -261,29 +272,31 @@ module Profile =
| Some text -> | Some text ->
let txt = regexContains text let txt = regexContains text
yield (fun q -> q.Filter (ReqlFunction1(fun it -> yield (fun q -> q.Filter (ReqlFunction1(fun it ->
upcast it.G("biography").Match(txt).Or(it.G("experience").Match(txt)))) :> ReqlExpr) upcast it.G("biography").Match(txt).Or (it.G("experience").Match txt))) :> ReqlExpr)
| None -> () | None -> ()
} }
|> Seq.toList |> Seq.toList
|> List.fold |> List.fold
(fun q f -> f q) (fun q f -> f q)
(r.Table(Table.Profile) (r.Table(Table.Profile)
.EqJoin("id", r.Table(Table.Citizen)) .EqJoin("id", r.Table Table.Citizen)
.Without(r.HashMap ("right", "id")) .Without(r.HashMap ("right", "id"))
.Zip () :> ReqlExpr)) .Zip () :> ReqlExpr))
.Merge(ReqlFunction1 (fun it -> .Merge(ReqlFunction1 (fun it ->
upcast r upcast r
.HashMap("displayName", .HashMap("displayName",
r.Branch(it.G("realName" ).Default_("").Ne(""), it.G("realName"), r.Branch (it.G("realName" ).Default_("").Ne "", it.G "realName",
it.G("displayName").Default_("").Ne(""), it.G("displayName"), it.G("displayName").Default_("").Ne "", it.G "displayName",
it.G("naUser"))) it.G "naUser"))
.With("citizenId", it.G("id")))) .With ("citizenId", it.G "id")))
.Pluck("citizenId", "displayName", "seekingEmployment", "remoteWork", "fullTime", "lastUpdatedOn") .Pluck("citizenId", "displayName", "seekingEmployment", "remoteWork", "fullTime", "lastUpdatedOn")
.RunResultAsync<ProfileSearchResult list> conn) .OrderBy(ReqlFunction1 (fun it -> upcast it.G("displayName").Downcase ()))
.RunResultAsync<ProfileSearchResult list> c
|> withReconn conn
// Search profiles (public) // Search profiles (public)
let publicSearch (srch : PublicSearch) conn = let publicSearch (srch : PublicSearch) conn =
withReconn(conn).ExecuteAsync(fun () -> fun c ->
(seq { (seq {
match srch.continentId with match srch.continentId with
| Some conId -> | Some conId ->
@ -309,7 +322,7 @@ module Profile =
|> List.fold |> List.fold
(fun q f -> f q) (fun q f -> f q)
(r.Table(Table.Profile) (r.Table(Table.Profile)
.EqJoin("continentId", r.Table(Table.Continent)) .EqJoin("continentId", r.Table Table.Continent)
.Without(r.HashMap ("right", "id")) .Without(r.HashMap ("right", "id"))
.Zip() .Zip()
.Filter(r.HashMap ("isPublic", true)) :> ReqlExpr)) .Filter(r.HashMap ("isPublic", true)) :> ReqlExpr))
@ -317,12 +330,12 @@ module Profile =
upcast r upcast r
.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",
skill.G("description").Add(" (").Add(skill.G("notes")).Add(")"))))) 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> c
|> withReconn conn
/// Citizen data access functions /// Citizen data access functions
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -330,78 +343,64 @@ module Citizen =
/// Find a citizen by their ID /// Find a citizen by their ID
let findById (citizenId : CitizenId) conn = let findById (citizenId : CitizenId) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! citizen =
r.Table(Table.Citizen) r.Table(Table.Citizen)
.Get(citizenId) .Get(citizenId)
.RunResultAsync<Citizen> conn .RunResultAsync<Citizen>
return toOption citizen |> withReconnOption conn
})
/// Find a citizen by their No Agenda Social username /// Find a citizen by their No Agenda Social username
let findByNaUser (naUser : string) conn = let findByNaUser (naUser : string) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! citizen =
r.Table(Table.Citizen) r.Table(Table.Citizen)
.GetAll(naUser).OptArg("index", "naUser").Nth(0) .GetAll(naUser).OptArg("index", "naUser").Nth(0)
.RunResultAsync<Citizen> conn .RunResultAsync<Citizen>
return toOption citizen |> withReconnOption conn
})
/// Add a citizen /// Add a citizen
let add (citizen : Citizen) conn = let add (citizen : Citizen) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Citizen) r.Table(Table.Citizen)
.Insert(citizen) .Insert(citizen)
.RunWriteAsync conn .RunWriteAsync
() |> withReconnIgnore conn
})
/// Update the display name and last seen on date for a citizen /// Update the display name and last seen on date for a citizen
let logOnUpdate (citizen : Citizen) conn = let logOnUpdate (citizen : Citizen) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Citizen) r.Table(Table.Citizen)
.Get(citizen.id) .Get(citizen.id)
.Update(r.HashMap( nameof citizen.displayName, citizen.displayName) .Update(r.HashMap( nameof citizen.displayName, citizen.displayName)
.With (nameof citizen.lastSeenOn, citizen.lastSeenOn)) .With (nameof citizen.lastSeenOn, citizen.lastSeenOn))
.RunWriteAsync conn .RunWriteAsync
() |> withReconnIgnore conn
})
/// Delete a citizen /// Delete a citizen
let delete citizenId conn = let delete citizenId conn =
withReconn(conn).ExecuteAsync(fun () -> task { fun c -> task {
do! Profile.delete citizenId conn do! Profile.delete citizenId c
let! _ = let! _ =
r.Table(Table.Success) r.Table(Table.Success)
.GetAll(citizenId).OptArg("index", "citizenId") .GetAll(citizenId).OptArg("index", "citizenId")
.Delete() .Delete()
.RunWriteAsync conn .RunWriteAsync c
let! _ = let! _ =
r.Table(Table.Listing) r.Table(Table.Listing)
.GetAll(citizenId).OptArg("index", "citizenId") .GetAll(citizenId).OptArg("index", "citizenId")
.Delete() .Delete()
.RunWriteAsync conn .RunWriteAsync c
let! _ = let! _ =
r.Table(Table.Citizen) r.Table(Table.Citizen)
.Get(citizenId) .Get(citizenId)
.Delete() .Delete()
.RunWriteAsync conn .RunWriteAsync c
() ()
}) }
|> withReconnIgnore conn
/// Update a citizen's real name /// Update a citizen's real name
let realNameUpdate (citizenId : CitizenId) (realName : string option) conn = let realNameUpdate (citizenId : CitizenId) (realName : string option) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Citizen) r.Table(Table.Citizen)
.Get(citizenId) .Get(citizenId)
.Update(r.HashMap (nameof realName, realName)) .Update(r.HashMap (nameof realName, realName))
.RunWriteAsync conn .RunWriteAsync
() |> withReconnIgnore conn
})
/// Continent data access functions /// Continent data access functions
@ -410,19 +409,16 @@ module Continent =
/// Get all continents /// Get all continents
let all conn = let all conn =
withReconn(conn).ExecuteAsync(fun () ->
r.Table(Table.Continent) r.Table(Table.Continent)
.RunResultAsync<Continent list> conn) .RunResultAsync<Continent list>
|> withReconn conn
/// Get a continent by its ID /// Get a continent by its ID
let findById (contId : ContinentId) conn = let findById (contId : ContinentId) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! continent =
r.Table(Table.Continent) r.Table(Table.Continent)
.Get(contId) .Get(contId)
.RunResultAsync<Continent> conn .RunResultAsync<Continent>
return toOption continent |> withReconnOption conn
})
/// Job listing data access functions /// Job listing data access functions
@ -433,70 +429,59 @@ module Listing =
/// Find all job listings posted by the given citizen /// Find all job listings posted by the given citizen
let findByCitizen (citizenId : CitizenId) conn = let findByCitizen (citizenId : CitizenId) conn =
withReconn(conn).ExecuteAsync(fun () ->
r.Table(Table.Listing) r.Table(Table.Listing)
.GetAll(citizenId).OptArg("index", nameof citizenId) .GetAll(citizenId).OptArg("index", nameof citizenId)
.EqJoin("continentId", r.Table(Table.Continent)) .EqJoin("continentId", r.Table Table.Continent)
.Map(ReqlFunction1(fun it -> upcast r.HashMap("listing", it.G("left")).With("continent", it.G("right")))) .Map(ReqlFunction1 (fun it -> upcast r.HashMap("listing", it.G "left").With ("continent", it.G "right")))
.RunResultAsync<ListingForView list> conn) .RunResultAsync<ListingForView list>
|> withReconn conn
/// Find a listing by its ID /// Find a listing by its ID
let findById (listingId : ListingId) conn = let findById (listingId : ListingId) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! listing =
r.Table(Table.Listing) r.Table(Table.Listing)
.Get(listingId) .Get(listingId)
.RunResultAsync<Listing> conn .RunResultAsync<Listing>
return toOption listing |> withReconnOption conn
})
/// Find a listing by its ID for viewing (includes continent information) /// Find a listing by its ID for viewing (includes continent information)
let findByIdForView (listingId : ListingId) conn = let findByIdForView (listingId : ListingId) conn =
withReconn(conn).ExecuteAsync(fun () -> task { fun c -> task {
let! listing = let! listing =
r.Table(Table.Listing) r.Table(Table.Listing)
.Filter(r.HashMap ("id", listingId)) .Filter(r.HashMap ("id", listingId))
.EqJoin("continentId", r.Table(Table.Continent)) .EqJoin("continentId", r.Table Table.Continent)
.Map(ReqlFunction1(fun it -> upcast r.HashMap("listing", it.G("left")).With("continent", it.G("right")))) .Map(ReqlFunction1 (fun it -> upcast r.HashMap("listing", it.G "left").With ("continent", it.G "right")))
.RunResultAsync<ListingForView list> conn .RunResultAsync<ListingForView list> c
return List.tryHead listing return List.tryHead listing
}) }
|> withReconn conn
/// Add a listing /// Add a listing
let add (listing : Listing) conn = let add (listing : Listing) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Listing) r.Table(Table.Listing)
.Insert(listing) .Insert(listing)
.RunWriteAsync conn .RunWriteAsync
() |> withReconnIgnore conn
})
/// Update a listing /// Update a listing
let update (listing : Listing) conn = let update (listing : Listing) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Listing) r.Table(Table.Listing)
.Get(listing.id) .Get(listing.id)
.Replace(listing) .Replace(listing)
.RunWriteAsync conn .RunWriteAsync
() |> withReconnIgnore conn
})
/// Expire a listing /// Expire a listing
let expire (listingId : ListingId) (fromHere : bool) (now : Instant) conn = let expire (listingId : ListingId) (fromHere : bool) (now : Instant) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Listing) r.Table(Table.Listing)
.Get(listingId) .Get(listingId)
.Update(r.HashMap("isExpired", true).With("wasFilledHere", fromHere).With ("updatedOn", now)) .Update(r.HashMap("isExpired", true).With("wasFilledHere", fromHere).With ("updatedOn", now))
.RunWriteAsync conn .RunWriteAsync
() |> withReconnIgnore conn
})
/// Search job listings /// Search job listings
let search (srch : ListingSearch) conn = let search (srch : ListingSearch) conn =
withReconn(conn).ExecuteAsync(fun () -> fun c ->
(seq { (seq {
match srch.continentId with match srch.continentId with
| Some conId -> | Some conId ->
@ -525,9 +510,10 @@ module Listing =
(fun q f -> f q) (fun q f -> f q)
(r.Table(Table.Listing) (r.Table(Table.Listing)
.GetAll(false).OptArg ("index", "isExpired") :> ReqlExpr)) .GetAll(false).OptArg ("index", "isExpired") :> ReqlExpr))
.EqJoin("continentId", r.Table(Table.Continent)) .EqJoin("continentId", r.Table Table.Continent)
.Map(ReqlFunction1(fun it -> upcast r.HashMap("listing", it.G("left")).With("continent", it.G("right")))) .Map(ReqlFunction1 (fun it -> upcast r.HashMap("listing", it.G "left").With ("continent", it.G "right")))
.RunResultAsync<ListingForView list> conn) .RunResultAsync<ListingForView list> c
|> withReconn conn
/// Success story data access functions /// Success story data access functions
@ -536,39 +522,33 @@ module Success =
/// Find a success report by its ID /// Find a success report by its ID
let findById (successId : SuccessId) conn = let findById (successId : SuccessId) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! success =
r.Table(Table.Success) r.Table(Table.Success)
.Get(successId) .Get(successId)
.RunResultAsync<Success> conn .RunResultAsync<Success>
return toOption success |> withReconnOption conn
})
/// Insert or update a success story /// Insert or update a success story
let save (success : Success) conn = let save (success : Success) conn =
withReconn(conn).ExecuteAsync(fun () -> task {
let! _ =
r.Table(Table.Success) r.Table(Table.Success)
.Get(success.id) .Get(success.id)
.Replace(success) .Replace(success)
.RunWriteAsync conn .RunWriteAsync
() |> withReconnIgnore conn
})
// Retrieve all success stories // Retrieve all success stories
let all conn = let all conn =
withReconn(conn).ExecuteAsync(fun () ->
r.Table(Table.Success) r.Table(Table.Success)
.EqJoin("citizenId", r.Table(Table.Citizen)) .EqJoin("citizenId", r.Table Table.Citizen)
.Without(r.HashMap ("right", "id")) .Without(r.HashMap ("right", "id"))
.Zip() .Zip()
.Merge(ReqlFunction1 (fun it -> .Merge(ReqlFunction1 (fun it ->
upcast r upcast r
.HashMap("citizenName", .HashMap("citizenName",
r.Branch(it.G("realName" ).Default_("").Ne(""), it.G("realName"), r.Branch(it.G("realName" ).Default_("").Ne "", it.G "realName",
it.G("displayName").Default_("").Ne(""), it.G("displayName"), it.G("displayName").Default_("").Ne "", it.G "displayName",
it.G("naUser"))) it.G "naUser"))
.With("hasStory", it.G("story").Default_("").Gt("")))) .With ("hasStory", it.G("story").Default_("").Gt "")))
.Pluck("id", "citizenId", "citizenName", "recordedOn", "fromHere", "hasStory") .Pluck("id", "citizenId", "citizenName", "recordedOn", "fromHere", "hasStory")
.OrderBy(r.Desc("recordedOn")) .OrderBy(r.Desc "recordedOn")
.RunResultAsync<StoryEntry list> conn) .RunResultAsync<StoryEntry list>
|> withReconn conn

View File

@ -4,12 +4,12 @@
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build": "vue-cli-service build --mode development", "build": "vue-cli-service build",
"lint": "vue-cli-service lint", "lint": "vue-cli-service lint",
"apiserve": "vue-cli-service build --mode development && cd ../Api && dotnet run -c Debug" "apiserve": "vue-cli-service build && cd ../Api && dotnet run -c Debug"
}, },
"dependencies": { "dependencies": {
"@mdi/font": "5.9.55", "@mdi/js": "^5.9.55",
"@vuelidate/core": "^2.0.0-alpha.24", "@vuelidate/core": "^2.0.0-alpha.24",
"@vuelidate/validators": "^2.0.0-alpha.21", "@vuelidate/validators": "^2.0.0-alpha.21",
"bootstrap": "^5.1.0", "bootstrap": "^5.1.0",

View File

@ -13,7 +13,6 @@
import { defineComponent } from "vue" import { defineComponent } from "vue"
import "bootstrap/dist/css/bootstrap.min.css" import "bootstrap/dist/css/bootstrap.min.css"
import "@mdi/font/css/materialdesignicons.css"
import { Citizen } from "./api" import { Citizen } from "./api"
import AppFooter from "./components/layout/AppFooter.vue" import AppFooter from "./components/layout/AppFooter.vue"

View File

@ -1,12 +1,16 @@
<template lang="pug"> <template lang="pug">
span(:class="iconClass") svg(viewbox="0 0 24 24"): path(:fill="color || 'white'" :d="icon")
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const props = defineProps<{ const props = defineProps<{
color?: string
icon: string icon: string
}>() }>()
/** The CSS class to display the requested icon */
const iconClass = `mdi mdi-${props.icon}`
</script> </script>
<style lang="sass" scoped>
svg
width: 24px
height: 24px
</style>

View File

@ -4,25 +4,37 @@ aside.collapse.show.p-3
p &nbsp; p &nbsp;
nav nav
template(v-if="isLoggedOn") template(v-if="isLoggedOn")
router-link(to="/citizen/dashboard") #[icon(icon="view-dashboard-variant")]&nbsp; Dashboard router-link(to="/citizen/dashboard") #[icon(:icon="mdiViewDashboardVariant")]&nbsp; Dashboard
router-link(to="/help-wanted") #[icon(icon="newspaper-variant-multiple-outline")]&nbsp; Help Wanted! router-link(to="/help-wanted") #[icon(:icon="mdiNewspaperVariantMultipleOutline")]&nbsp; Help Wanted!
router-link(to="/profile/search") #[icon(icon="view-list-outline")]&nbsp; Employment Profiles router-link(to="/profile/search") #[icon(:icon="mdiViewListOutline")]&nbsp; Employment Profiles
router-link(to="/success-story/list") #[icon(icon="thumb-up")]&nbsp; Success Stories router-link(to="/success-story/list") #[icon(:icon="mdiThumbUp")]&nbsp; Success Stories
.separator .separator
router-link(to="/listings/mine") #[icon(icon="sign-text")]&nbsp; My Job Listings router-link(to="/listings/mine") #[icon(:icon="mdiSignText")]&nbsp; My Job Listings
router-link(to="/citizen/profile") #[icon(icon="pencil")]&nbsp; My Employment Profile router-link(to="/citizen/profile") #[icon(:icon="mdiPencil")]&nbsp; My Employment Profile
.separator .separator
router-link(to="/citizen/log-off") #[icon(icon="logout-variant")]&nbsp; Log Off router-link(to="/citizen/log-off") #[icon(:icon="mdiLogoutVariant")]&nbsp; Log Off
template(v-else) template(v-else)
router-link(to="/") #[icon(icon="home")]&nbsp; Home router-link(to="/") #[icon(:icon="mdiHome")]&nbsp; Home
router-link(to="/profile/seeking") #[icon(icon="view-list-outline")]&nbsp; Job Seekers router-link(to="/profile/seeking") #[icon(:icon="mdiViewListOutline")]&nbsp; Job Seekers
router-link(to="/citizen/log-on") #[icon(icon="login-variant")]&nbsp; Log On router-link(to="/citizen/log-on") #[icon(:icon="mdiLoginVariant")]&nbsp; Log On
router-link(to="/how-it-works") #[icon(icon="help-circle-outline")]&nbsp; How It Works router-link(to="/how-it-works") #[icon(:icon="mdiHelpCircleOutline")]&nbsp; How It Works
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from "vue" import { computed } from "vue"
import { useStore } from "@/store" import { useStore } from "@/store"
import {
mdiHelpCircleOutline,
mdiHome,
mdiLoginVariant,
mdiLogoutVariant,
mdiNewspaperVariantMultipleOutline,
mdiPencil,
mdiSignText,
mdiThumbUp,
mdiViewDashboardVariant,
mdiViewListOutline
} from "@mdi/js"
const store = useStore() const store = useStore()
@ -40,6 +52,10 @@ aside
min-width: 250px min-width: 250px
position: sticky position: sticky
top: 0 top: 0
path
fill: white
path:hover
fill: black
a:link, a:visited a:link, a:visited
text-decoration: none text-decoration: none
color: white color: white

View File

@ -6,10 +6,12 @@ article
Welcome to Jobs, Jobs, Jobs (AKA No Agenda Careers), where citizens of Gitmo Nation can assist one another in Welcome to Jobs, Jobs, Jobs (AKA No Agenda Careers), where citizens of Gitmo Nation can assist one another in
finding employment. This will enable them to continue providing value-for-value to Adam and John, as they continue finding employment. This will enable them to continue providing value-for-value to Adam and John, as they continue
their work deconstructing the misinformation that passes for news on a day-to-day basis. their work deconstructing the misinformation that passes for news on a day-to-day basis.
p. p
Do you not understand the terms in the paragraph above? No worries; just head over to | Do you not understand the terms in the paragraph above? No worries; just head over to
#[a(href="https://noagendashow.net" target="_blank") The Best Podcast in the Universe] |
#[= " "]#[em #[audio-clip(clip="thats-true") (that&rsquo;s true!)]] and find out what you&rsquo;re missing. a(href="https://noagendashow.net" target="_blank") The Best Podcast in the Universe
= " "
| #[em #[audio-clip(clip="thats-true") (that&rsquo;s true!)]] and find out what you&rsquo;re missing.
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -2,7 +2,7 @@
article article
page-title(title="How It Works") page-title(title="How It Works")
h3 How It Works h3 How It Works
h5.pb-3.text-muted: em Last Updated August 29th, 2021 h5.pb-3.text-muted: em Last Updated August 29#[sup th], 2021
p: em. p: em.
Show me how to #[a(href="#listing-search") find a job] Show me how to #[a(href="#listing-search") find a job]
#[!= " &bull; "]#[a(href="#listing") list a job opportunity] #[!= " &bull; "]#[a(href="#listing") list a job opportunity]

View File

@ -37,8 +37,8 @@ article.container
.card-footer: router-link.btn.btn-outline-secondary(to="/profile/search") Search Profiles .card-footer: router-link.btn.btn-outline-secondary(to="/profile/search") Search Profiles
p &nbsp; p &nbsp;
p. p.
To see how this application works, check out &ldquo;How It Works&rdquo; in the sidebar (last updated June To see how this application works, check out &ldquo;How It Works&rdquo; in the sidebar (last updated August
14#[sup th], 2021). 29#[sup th], 2021).
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -55,11 +55,11 @@ article
label.form-check-label(for="isPublic") Allow my profile to be searched publicly (outside NA Social) label.form-check-label(for="isPublic") Allow my profile to be searched publicly (outside NA Social)
.col-12 .col-12
p.text-danger(v-if="v$.$error") Please correct the errors above p.text-danger(v-if="v$.$error") Please correct the errors above
button.btn.btn-primary(@click.prevent="saveProfile") #[icon(icon="content-save-outline")]&nbsp; Save button.btn.btn-primary(@click.prevent="saveProfile") #[icon(:icon="mdiContentSaveOutline")]&nbsp; Save
template(v-if="!isNew") template(v-if="!isNew")
| &nbsp; &nbsp; | &nbsp; &nbsp;
router-link.btn.btn-outline-secondary(:to="`/profile/${user.citizenId}/view`"). router-link.btn.btn-outline-secondary(:to="`/profile/${user.citizenId}/view`").
#[icon(icon="file-account-outline")]&nbsp; View Your User Profile #[icon(color="#6c757d" :icon="mdiFileAccountOutline")]&nbsp; View Your User Profile
hr hr
p.text-muted.fst-italic. p.text-muted.fst-italic.
(If you want to delete your profile, or your entire account, (If you want to delete your profile, or your entire account,
@ -69,6 +69,7 @@ article
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, reactive } from "vue" import { computed, ref, reactive } from "vue"
import { mdiContentSaveOutline, mdiFileAccountOutline } from "@mdi/js"
import useVuelidate from "@vuelidate/core" import useVuelidate from "@vuelidate/core"
import { required } from "@vuelidate/validators" import { required } from "@vuelidate/validators"

View File

@ -31,13 +31,14 @@ article
label(for="neededBy") Needed By label(for="neededBy") Needed By
.col-12 .col-12
p.text-danger(v-if="v$.$error") Please correct the errors above p.text-danger(v-if="v$.$error") Please correct the errors above
button.btn.btn-primary(@click.prevent="saveListing(true)") #[icon(icon="content-save-outline")]&nbsp; Save button.btn.btn-primary(@click.prevent="saveListing(true)") #[icon(:icon="mdiContentSaveOutline")]&nbsp; Save
maybe-save(:saveAction="doSave" :validator="v$") maybe-save(:saveAction="doSave" :validator="v$")
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, reactive } from "vue" import { computed, reactive } from "vue"
import { useRoute, useRouter } from "vue-router" import { useRoute, useRouter } from "vue-router"
import { mdiContentSaveOutline } from "@mdi/js"
import useVuelidate from "@vuelidate/core" import useVuelidate from "@vuelidate/core"
import { required } from "@vuelidate/validators" import { required } from "@vuelidate/validators"

View File

@ -17,13 +17,14 @@ article
markdown-editor(id="successStory" label="Your Success Story" v-model:text="v$.successStory.$model") markdown-editor(id="successStory" label="Your Success Story" v-model:text="v$.successStory.$model")
.col-12 .col-12
button.btn.btn-primary(@click.prevent="expireListing"). button.btn.btn-primary(@click.prevent="expireListing").
#[icon(icon="text-box-remove-outline")]&nbsp; Expire Listing #[icon(:icon="mdiTextBoxRemoveOutline")]&nbsp; Expire Listing
maybe-save(:saveAction="doSave" :validator="v$") maybe-save(:saveAction="doSave" :validator="v$")
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, reactive, Ref, ref } from "vue" import { computed, reactive, Ref, ref } from "vue"
import { useRoute, useRouter } from "vue-router" import { useRoute, useRouter } from "vue-router"
import { mdiTextBoxRemoveOutline } from "@mdi/js"
import useVuelidate from "@vuelidate/core" import useVuelidate from "@vuelidate/core"
import api, { Listing, ListingExpireForm, LogOnSuccess } from "@/api" import api, { Listing, ListingExpireForm, LogOnSuccess } from "@/api"

View File

@ -23,12 +23,13 @@ article
template(v-if="user.citizenId === it.citizen.id") template(v-if="user.citizenId === it.citizen.id")
br br
br br
router-link.btn.btn-primary(to="/citizen/profile") #[icon(icon="pencil")]&nbsp; Edit Your Profile router-link.btn.btn-primary(to="/citizen/profile") #[icon(:icon="mdiPencil")]&nbsp; Edit Your Profile
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, Ref } from "vue" import { computed, ref, Ref } from "vue"
import { useRoute } from "vue-router" import { useRoute } from "vue-router"
import { mdiPencil } from "@mdi/js"
import api, { LogOnSuccess, ProfileForView } from "@/api" import api, { LogOnSuccess, ProfileForView } from "@/api"
import { citizenName } from "@/App.vue" import { citizenName } from "@/App.vue"

View File

@ -13,7 +13,7 @@ article
markdown-editor(id="story" label="The Success Story" v-model:text="v$.story.$model") markdown-editor(id="story" label="The Success Story" v-model:text="v$.story.$model")
.col-12 .col-12
button.btn.btn-primary(type="submit" @click.prevent="saveStory(true)"). button.btn.btn-primary(type="submit" @click.prevent="saveStory(true)").
#[icon(icon="content-save-outline")]&nbsp; Save #[icon(:icon="mdiContentSaveOutline")]&nbsp; Save
p(v-if="isNew"): em (Saving this will set &ldquo;Seeking Employment&rdquo; to &ldquo;No&rdquo; on your profile.) p(v-if="isNew"): em (Saving this will set &ldquo;Seeking Employment&rdquo; to &ldquo;No&rdquo; on your profile.)
maybe-save(:saveAction="doSave" :validator="v$") maybe-save(:saveAction="doSave" :validator="v$")
</template> </template>
@ -21,6 +21,7 @@ article
<script setup lang="ts"> <script setup lang="ts">
import { computed, reactive } from "vue" import { computed, reactive } from "vue"
import { useRoute, useRouter } from "vue-router" import { useRoute, useRouter } from "vue-router"
import { mdiContentSaveOutline } from "@mdi/js"
import useVuelidate from "@vuelidate/core" import useVuelidate from "@vuelidate/core"
import api, { LogOnSuccess, StoryForm } from "@/api" import api, { LogOnSuccess, StoryForm } from "@/api"