Finish expire; mod Success Story

Add label display to several view pages; fix lint errors
This commit is contained in:
Daniel J. Summers 2021-08-29 15:42:18 -04:00
parent 8c2ff4c84d
commit 144c34919c
16 changed files with 102 additions and 61 deletions

View File

@ -489,7 +489,7 @@ module Listing =
let! _ =
r.Table(Table.Listing)
.Get(listingId)
.Update(r.HashMap("isExpired", true).With("fromHere", fromHere).With("updatedOn", now))
.Update(r.HashMap("isExpired", true).With("wasFilledHere", fromHere).With("updatedOn", now))
.RunWriteAsync conn
()
})
@ -569,4 +569,5 @@ module Success =
it.G("naUser")))
.With("hasStory", it.G("story").Default_("").Gt(""))))
.Pluck("id", "citizenId", "citizenName", "recordedOn", "fromHere", "hasStory")
.OrderBy(r.Desc("recordedOn"))
.RunResultAsync<StoryEntry list> conn)

View File

@ -22,7 +22,7 @@ module.exports = {
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"vue/no-multiple-template-root": "off",
"vue/script-setup-uses-vars": 1,
"quotes": ["error", "double", { avoidEscape: true }],
"quotes": ["error", "double", { avoidEscape: true, allowTemplateLiterals: true }],
"func-call-spacing": "off",
"@typescript-eslint/no-unused-vars": "off"
}

View File

@ -3,8 +3,8 @@
app-nav
.jjj-main
title-bar
main.container-fluid: router-view(v-slot="{ Component }"): transition(name='fade' mode='out-in')
component(:is='Component')
main.container-fluid: router-view(v-slot="{ Component }"): transition(name="fade" mode="out-in")
component(:is="Component")
app-footer
app-toaster
</template>
@ -63,6 +63,10 @@ a:not(.btn):hover
label.jjj-required::after
color: red
content: ' *'
.jjj-heading-label
display: inline-block
font-size: 1rem
text-transform: uppercase
// Styles for this component
.jjj-app
display: flex

View File

@ -1,5 +1,5 @@
import { parseJSON } from 'date-fns'
import { utcToZonedTime } from 'date-fns-tz'
import { parseJSON } from "date-fns"
import { utcToZonedTime } from "date-fns-tz"
/**
* Parse a date from its JSON representation to a UTC-aligned date

View File

@ -2,7 +2,7 @@
nav.navbar.navbar-light.bg-light
span &nbsp;
span.navbar-text.
(&hellip;and Jobs &ndash; #[audio-clip(clip="pelosi-jobs") Let's Vote for Jobs!])
(&hellip;and Jobs &ndash; #[audio-clip(clip="pelosi-jobs") Let&rsquo;s Vote for Jobs!])
</template>
<script setup lang="ts">

View File

@ -9,7 +9,7 @@ article
p.
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 &nbsp;#[audio-clip(clip="thats-true") (that&rsquo;s true!)]] and find out what you&rsquo;re missing.
#[= " "]#[em #[audio-clip(clip="thats-true") (that&rsquo;s true!)]] and find out what you&rsquo;re missing.
</template>
<script setup lang="ts">

View File

@ -5,13 +5,13 @@ article
p: em (as of February 6#[sup th], 2021)
p.
{{name}} (we, our, or us) is committed to protecting your privacy. This Privacy Policy explains how your
personal information is collected, used, and disclosed by {{name}}.
{{name}} (&ldquo;we,&rdquo; &ldquo;our,&rdquo; or &ldquo;us&rdquo;) is committed to protecting your privacy. This
Privacy Policy explains how your personal information is collected, used, and disclosed by {{name}}.
p.
This Privacy Policy applies to our website, and its associated subdomains (collectively, our Service) alongside
our application, {{name}}. By accessing or using our Service, you signify that you have read, understood, and agree
to our collection, storage, use, and disclosure of your personal information as described in this Privacy Policy and
our Terms of Service.
This Privacy Policy applies to our website, and its associated subdomains (collectively, our &ldquo;Service&rdquo;)
alongside our application, {{name}}. By accessing or using our Service, you signify that you have read, understood,
and agree to our collection, storage, use, and disclosure of your personal information as described in this Privacy
Policy and our Terms of Service.
h4 Definitions and key terms
p.
@ -22,8 +22,8 @@ article
Cookie: small amount of data generated by a website and saved by your web browser. It is used to identify your
browser, provide analytics, remember information about you such as your language preference or login information.
li.
Company: when this policy mentions Company, we, us, or our, it refers to {{name}}, that is responsible for
your information under this Privacy Policy.
Company: when this policy mentions &ldquo;Company,&rdquo; &ldquo;we,&rdquo; &ldquo;us,&rdquo; or
&ldquo;our,&rdquo; it refers to {{name}}, that is responsible for your information under this Privacy Policy.
li Country: where {{name}} or the owners/founders of {{name}} are based, in this case is US.
li.
Customer: refers to the company, organization or person that signs up to use the {{name}} Service to manage the
@ -193,7 +193,7 @@ article
h4 Cookies
p {{name}} does not use Cookies.
h4 Kids' Privacy
h4 Kids&rsquo; Privacy
p.
We do not address anyone under the age of 13. We do not knowingly collect personally identifiable information from
anyone under the age of 13. If You are a parent or guardian and You are aware that Your child has provided Us with
@ -219,7 +219,7 @@ article
entity for any Third-Party Services.
p.
Third-Party Services and links thereto are provided solely as a convenience to you and you access and use them
entirely at your own risk and subject to such third parties' terms and conditions.
entirely at your own risk and subject to such third parties&rsquo; terms and conditions.
h4 Tracking Technologies
p.
@ -278,9 +278,9 @@ article
Service and Privacy Policy, but we will not hold it longer than 60 days.
p.
We are aware that if you are working with EU customers, you need to be able to provide them with the ability to
access, update, retrieve and remove personal data. We got you! We've been set up as self service from the start and
have always given you access to your data. Our customer support team is here for you to answer any questions you
might have about working with the API.
access, update, retrieve and remove personal data. We got you! We&rsquo;ve been set up as self service from the
start and have always given you access to your data. Our customer support team is here for you to answer any
questions you might have about working with the API.
h4 California Residents
p.
@ -300,7 +300,7 @@ article
li.
Right to Delete. You may submit a verifiable request to close your account and we will delete Personal Information
about you that we have collected.
li Request that a business that sells a consumer's personal data, not sell the consumer's personal data.
li Request that a business that sells a consumer&rsquo;s personal data, not sell the consumer&rsquo;s personal data.
p.
If you make a request, we have one month to respond to you. If you would like to exercise any of these rights,
please contact us.
@ -323,7 +323,9 @@ article
li.
Right to Delete. You may submit a verifiable request to close your account and we will delete Personal Information
about you that we have collected.
li Right to request that a business that sells a consumer's personal data, not sell the consumer's personal data.
li.
Right to request that a business that sells a consumer&rsquo;s personal data, not sell the consumer&rsquo;s
personal data.
p.
If you make a request, we have one month to respond to you. If you would like to exercise any of these rights,
please contact us.
@ -331,7 +333,7 @@ article
p For more information about these rights, please contact us.
h4 Contact Us
p Don't hesitate to contact us if you have any questions.
p Don&rsquo;t hesitate to contact us if you have any questions.
ul: li Via this Link: #[router-link(to="/how-it-works") https://noagendacareers.com/how-it-works]
</template>

View File

@ -13,11 +13,11 @@ article
/** The authorization URL to which the user should be directed */
const authUrl = (() => {
/** The client ID for Jobs, Jobs, Jobs at No Agenda Social */
const id = 'k_06zlMy0N451meL4AqlwMQzs5PYr6g3d2Q_dCT-OjU'
const id = "k_06zlMy0N451meL4AqlwMQzs5PYr6g3d2Q_dCT-OjU"
const client = `client_id=${id}`
const scope = 'scope=read:accounts'
const scope = "scope=read:accounts"
const redirect = `redirect_uri=${document.location.origin}/citizen/authorized`
const respType = 'response_type=code'
const respType = "response_type=code"
return `https://noagendasocial.com/oauth/authorize?${client}&${scope}&${redirect}&${respType}`
})()
document.location.assign(authUrl)

View File

@ -59,16 +59,16 @@ const user = store.state.user as LogOnSuccess
/** A new job listing */
const newListing : Listing = {
id: '',
id: "",
citizenId: user.citizenId,
createdOn: '',
title: '',
continentId: '',
region: '',
createdOn: "",
title: "",
continentId: "",
region: "",
remoteWork: false,
isExpired: false,
updatedOn: '',
text: '',
updatedOn: "",
text: "",
neededBy: undefined,
wasFilledHere: undefined
}

View File

@ -49,6 +49,7 @@ const listing : Ref<Listing | undefined> = ref(undefined)
/** The data needed to expire a job listing */
const expiration = reactive(new ListingExpireForm())
expiration.successStory = ""
/** The validation rules for the form */
const rules = computed(() => ({

View File

@ -2,14 +2,18 @@
article
page-title(:title="title")
load-data(:load="retrieveListing")
h3 {{it.listing.title}}
h3
| {{it.listing.title}}
.jjj-heading-label(v-if="it.listing.isExpired")
| &nbsp; &nbsp; #[span.badge.bg-warning.text-dark Expired]
template(v-if="it.listing.wasFilledHere") &nbsp; &nbsp;#[span.badge.bg-success Filled via Jobs, Jobs, Jobs]
h4.pb-3.text-muted {{it.continent.name}} / {{it.listing.region}}
p
template(v-if="it.listing.neededBy").
#[strong #[em NEEDED BY {{neededBy(it.listing.neededBy)}}]] &bull;
| Listed by #[a(:href="profileUrl" target="_blank") {{citizenName(citizen)}}]
hr
div(v-html='details')
div(v-html="details")
</template>
<script setup lang="ts">

View File

@ -4,25 +4,46 @@ article
h3.pb-3 My Job Listings
p: router-link.btn.btn-outline-primary(to="/listing/new/edit") Add a New Job Listing
load-data(:load="getListings")
table.table.table-sm.table-hover.pt-3(v-if='listings.length > 0')
h4.pb-2(v-if="expired.length > 0") Active Job Listings
table.pb-3.table.table-sm.table-hover.pt-3(v-if="active.length > 0")
thead: tr
th(scope="col") Action
th(scope="col") Title
th(scope="col") Continent / Region
th(scope="col") Created
th(scope="col") Updated
tbody: tr(v-for='it in listings' :key='it.listing.id')
td: router-link(:to="`/listing/${it.listing.id}/edit`") Edit
tbody: tr(v-for="it in active" :key="it.listing.id")
td
router-link(:to="`/listing/${it.listing.id}/edit`") Edit
= " ~ "
router-link(:to="`/listing/${it.listing.id}/view`") View
= " ~ "
router-link(:to="`/listing/${it.listing.id}/expire`") Expire
td {{it.listing.title}}
td {{it.continent.name}} / {{it.listing.region}}
td: full-date-time(:date='it.listing.createdOn')
td: full-date-time(:date='it.listing.updatedOn')
p.fst-italic(v-else) No job listings found
td: full-date-time(:date="it.listing.createdOn")
td: full-date-time(:date="it.listing.updatedOn")
p.pb-3.fst-italic(v-else) You have no active job listings
template(v-if="expired.length > 0")
h4.pb-2 Expired Job Listings
table.table.table-sm.table-hover.pt-3
thead: tr
th(scope="col") Action
th(scope="col") Title
th(scope="col") Filled Here?
th(scope="col") Expired
tbody: tr(v-for="it in expired" :key="it.listing.id")
td
router-link(:to="`/listing/${it.listing.id}/view`") View
td {{it.listing.title}}
td {{yesOrNo(it.listing.wasFilledHere)}}
td: full-date-time(:date="it.listing.updatedOn")
</template>
<script setup lang="ts">
import { Ref, ref } from "vue"
import { computed, Ref, ref } from "vue"
import api, { ListingForView, LogOnSuccess } from "@/api"
import { yesOrNo } from "@/App.vue"
import { useStore } from "@/store"
import FullDateTime from "@/components/FullDateTime.vue"
@ -33,6 +54,12 @@ const store = useStore()
/** The listings for the user */
const listings : Ref<ListingForView[]> = ref([])
/** The active (non-expired) listings entered by this user */
const active = computed(() => listings.value.filter(it => !it.listing.isExpired))
/** The expired listings entered by this user */
const expired = computed(() => listings.value.filter(it => it.listing.isExpired))
/** Retrieve the job listing posted by the current citizen */
const getListings = async (errors : string[]) => {
const listResult = await api.listings.mine(store.state.user as LogOnSuccess)

View File

@ -19,7 +19,7 @@ article
th(scope="col") Last Updated
tbody: tr(v-for="profile in results" :key="profile.citzenId")
td: router-link(:to="`/profile/${profile.citizenId}/view`") View
td(:class="{ 'font-weight-bold' : profile.seekingEmployment }") {{profile.displayName}}
td(:class="{ 'fw-bold' : profile.seekingEmployment }") {{profile.displayName}}
td.text-center {{yesOrNo(profile.seekingEmployment)}}
td.text-center {{yesOrNo(profile.remoteWork)}}
td.text-center {{yesOrNo(profile.fullTime)}}
@ -55,10 +55,10 @@ const searched = ref(false)
/** An empty set of search criteria */
const emptyCriteria = {
continentId: '',
continentId: "",
skill: undefined,
bioExperience: undefined,
remoteWork: ''
remoteWork: ""
}
/** The search criteria being built from the page */
@ -113,5 +113,5 @@ watch(() => route.query, setUpPage, { immediate: true })
const toggleCollapse = (it : boolean) => { isCollapsed.value = it }
/** Execute a search */
const doSearch = () => router.push({ query: { searched: 'true', ...criteria.value } })
const doSearch = () => router.push({ query: { searched: "true", ...criteria.value } })
</script>

View File

@ -2,7 +2,10 @@
article
page-title(:title="title")
load-data(:load="retrieveProfile")
h2: a(:href="it.citizen.profileUrl" target="_blank") {{citizenName(it.citizen)}}
h2
a(:href="it.citizen.profileUrl" target="_blank") {{citizenName(it.citizen)}}
.jjj-heading-label(v-if="it.profile.seekingEmployment")
| &nbsp; &nbsp;#[span.badge.bg-dark Currently Seeking Employment]
h4.pb-3 {{it.continent.name}}, {{it.profile.region}}
p(v-html="workTypes")
hr
@ -47,11 +50,6 @@ const workTypes = computed(() => {
const parts : string[] = []
if (it.value) {
const p = it.value.profile
if (p.seekingEmployment) {
parts.push("<strong><em>CURRENTLY SEEKING EMPLOYMENT</em></strong>")
} else {
parts.push("Not actively seeking employment")
}
parts.push(`${p.fullTime ? "I" : "Not i"}nterested in full-time employment`)
parts.push(`${p.remoteWork ? "I" : "Not i"}nterested in remote opportunities`)
}

View File

@ -52,10 +52,10 @@ const errors : Ref<string[]> = ref([])
/** An empty set of search criteria */
const emptyCriteria = {
continentId: '',
continentId: "",
region: undefined,
skill: undefined,
remoteWork: ''
remoteWork: ""
}
/** The search criteria being built from the page */
@ -109,5 +109,5 @@ watch(() => route.query, setUpPage, { immediate: true })
const toggleCollapse = (it : boolean) => { isCollapsed.value = it }
/** Execute a search */
const doSearch = () => router.push({ query: { searched: 'true', ...criteria.value } })
const doSearch = () => router.push({ query: { searched: "true", ...criteria.value } })
</script>

View File

@ -2,10 +2,11 @@
article
page-title(title="Success Story")
load-data(:load="retrieveStory")
h3.pb-3 {{citizenName}}&rsquo;s Success Story
h4.text-muted: full-date-time(:date="story.recordedOn")
p.fst-italic(v-if="story.fromHere"): strong Found via Jobs, Jobs, Jobs
hr
h3
| {{citizenName}}&rsquo;s Success Story
.jjj-heading-label(v-if="story.fromHere")
| &nbsp; &nbsp;#[span.badge.bg-success Via {{profileOrListing}} on Jobs, Jobs, Jobs]
h4.pb-3.text-muted: full-date-time(:date="story.recordedOn")
div(v-if="story.story" v-html="successStory")
</template>
@ -15,7 +16,7 @@ import { useRoute } from "vue-router"
import api, { LogOnSuccess, Success } from "@/api"
import { citizenName as citName } from "@/App.vue"
import { toHtml } from '@/markdown'
import { toHtml } from "@/markdown"
import { useStore } from "@/store"
import FullDateTime from "@/components/FullDateTime.vue"
@ -55,6 +56,9 @@ const retrieveStory = async (errors : string []) => {
}
}
/** Whether this success is from an employment profile or a job listing */
const profileOrListing = computed(() => story.value?.source === "profile" ? "employment profile" : "job listing")
/** The HTML success story */
const successStory = computed(() => toHtml(story.value?.story ?? ""))
</script>