Retrieve existing profile for welcome page (#2)
This commit is contained in:
parent
daece3eab1
commit
2f84821d11
@ -96,3 +96,39 @@ module Citizens =
|
||||
|> Sql.parameters [ "@id", (CitizenId.toString >> Sql.string) citizenId ]
|
||||
|> Sql.executeRowAsync fromReader
|
||||
|> noneIfNotFound
|
||||
|
||||
|
||||
/// Functions for manipulating employment profiles
|
||||
module Profiles =
|
||||
|
||||
/// Create a Profile from a row of data
|
||||
let private fromReader (read: RowReader) =
|
||||
match (read.string >> CitizenId.tryParse) "citizen_id" with
|
||||
| Ok citizenId ->
|
||||
match (read.string >> ContinentId.tryParse) "continent_id" with
|
||||
| Ok continentId -> {
|
||||
citizenId = citizenId
|
||||
seekingEmployment = read.bool "seeking_employment"
|
||||
isPublic = read.bool "is_public"
|
||||
continent = { id = continentId; name = read.string "continent_name" }
|
||||
region = read.string "region"
|
||||
remoteWork = read.bool "remote_work"
|
||||
fullTime = read.bool "full_time"
|
||||
biography = (read.string >> MarkdownString) "biography"
|
||||
lastUpdatedOn = (read.int64 >> Millis) "last_updated_on"
|
||||
experience = (read.stringOrNone >> Option.map MarkdownString) "experience"
|
||||
}
|
||||
| Error err -> failwith err
|
||||
| Error err -> failwith err
|
||||
|
||||
/// Try to find an employment profile for the given citizen ID
|
||||
let tryFind citizenId =
|
||||
db ()
|
||||
|> Sql.query
|
||||
"""SELECT p.*, c.name AS continent_name
|
||||
FROM profile p
|
||||
INNER JOIN continent c ON p.continent_id = c.id
|
||||
WHERE citizen_id = @id"""
|
||||
|> Sql.parameters [ "@id", (CitizenId.toString >> Sql.string) citizenId ]
|
||||
|> Sql.executeRowAsync fromReader
|
||||
|> noneIfNotFound
|
||||
|
@ -185,7 +185,7 @@ type Profile = {
|
||||
/// Whether information from this profile should appear in the public anonymous list of available skills
|
||||
isPublic : bool
|
||||
/// The continent on which the user is seeking employment
|
||||
continentId : Continent
|
||||
continent : Continent
|
||||
/// The region within that continent where the user would prefer to work
|
||||
region : string
|
||||
/// Whether the user is looking for remote work
|
||||
|
@ -121,7 +121,24 @@ module Citizen =
|
||||
| Error msg -> return! Error.error (exn msg) "Could not authenticate with NAS" ctx
|
||||
| Error exn -> return! Error.error exn "Token not received" ctx
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// /api/profile route handlers
|
||||
module Profile =
|
||||
|
||||
/// GET: /api/profile
|
||||
let get citizenId : WebPart =
|
||||
authorizedUser
|
||||
>=> fun ctx -> async {
|
||||
match (match citizenId with "" -> Ok (currentCitizenId ctx) | _ -> CitizenId.tryParse citizenId) with
|
||||
| Ok citId ->
|
||||
match! Profiles.tryFind citId with
|
||||
| Ok (Some profile) -> return! json profile ctx
|
||||
| Ok None -> return! Error.notFound ctx
|
||||
| Error exn -> return! Error.error exn "Cannot retrieve profile" ctx
|
||||
| Error _ -> return! Error.notFound ctx
|
||||
}
|
||||
|
||||
|
||||
open Suave.Filters
|
||||
|
||||
@ -129,7 +146,9 @@ open Suave.Filters
|
||||
let webApp =
|
||||
choose
|
||||
[ GET >=> choose
|
||||
[ path "/" >=> Vue.app
|
||||
[ pathScan "/api/profile/%s" Profile.get
|
||||
path "/api/profile" >=> Profile.get ""
|
||||
path "/" >=> Vue.app
|
||||
Files.browse "wwwroot/"
|
||||
]
|
||||
// PUT >=> choose
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { ref } from 'vue'
|
||||
import { Profile } from './types'
|
||||
|
||||
/**
|
||||
* Jobs, Jobs, Jobs API interface
|
||||
*
|
||||
* @author Daniel J. Summers <daniel@bitbadger.solutions>
|
||||
* @version 1
|
||||
*/
|
||||
|
||||
/** The base URL for the Jobs, Jobs, Jobs API */
|
||||
@ -59,7 +65,7 @@ export async function doRequest(url: string, method?: string, payload?: string)
|
||||
if (method === 'POST' && payload) options.body = payload
|
||||
const actualUrl = (options.method === 'GET' && payload) ? `url?${payload}` : url
|
||||
const resp = await fetch(actualUrl, options)
|
||||
if (resp.ok) return resp
|
||||
if (resp.ok || resp.status === 404) return resp
|
||||
throw new Error(`Error executing API request: ${resp.status} ~ ${resp.statusText}`)
|
||||
}
|
||||
|
||||
@ -75,3 +81,17 @@ export async function jjjAuthorize(nasToken: string): Promise<boolean> {
|
||||
jwt.token = jjjToken.accessToken
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the employment profile for the current user.
|
||||
*
|
||||
* @returns The profile if it is found; undefined otherwise
|
||||
*/
|
||||
export async function userProfile(): Promise<Profile | undefined> {
|
||||
const resp = await doRequest(`${API_URL}/profile`)
|
||||
if (resp.status === 404) {
|
||||
return undefined
|
||||
}
|
||||
const profile = await resp.json()
|
||||
return profile as Profile
|
||||
}
|
||||
|
52
src/jobs-jobs-jobs/src/api/types.ts
Normal file
52
src/jobs-jobs-jobs/src/api/types.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Client-side Type Definitions for Jobs, Jobs, Jobs.
|
||||
*
|
||||
* @author Daniel J. Summers <daniel@bitbadger.solutions>
|
||||
* @version 1
|
||||
*/
|
||||
|
||||
/**
|
||||
* A continent (one of the 7).
|
||||
*/
|
||||
export interface Continent {
|
||||
/** The ID of the continent */
|
||||
id: string
|
||||
|
||||
/** The name of the continent */
|
||||
name: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A user's employment profile.
|
||||
*/
|
||||
export interface Profile {
|
||||
/** The ID of the user to whom the profile applies */
|
||||
citizenId: string
|
||||
|
||||
/** Whether this user is actively seeking employment */
|
||||
seekingEmployment: boolean
|
||||
|
||||
/** Whether information from this profile should appear in the public anonymous list of available skills */
|
||||
isPublic: boolean
|
||||
|
||||
/** The continent on which the user is seeking employment */
|
||||
continent: Continent
|
||||
|
||||
/** The region within that continent where the user would prefer to work */
|
||||
region: string
|
||||
|
||||
/** Whether the user is looking for remote work */
|
||||
remoteWork: boolean
|
||||
|
||||
/** Whether the user is looking for full-time work */
|
||||
fullTime: boolean
|
||||
|
||||
/** The user's professional biography */
|
||||
biography: string
|
||||
|
||||
/** When this profile was last updated */
|
||||
lastUpdatedOn: number
|
||||
|
||||
/** The user's experience */
|
||||
experience?: string
|
||||
}
|
@ -1,3 +1,27 @@
|
||||
<template>
|
||||
<div>Welcome</div>
|
||||
<div>
|
||||
<p>Welcome!</p>
|
||||
<p>Profile Established: <strong><span v-if="profile?.value">Yes</span><span v-else>No</span></strong></p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { onBeforeMount, ref } from 'vue'
|
||||
|
||||
import { userProfile } from '@/api'
|
||||
import { Profile } from '@/api/types'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const profile = ref<Profile | undefined>(undefined)
|
||||
|
||||
onBeforeMount(async () => {
|
||||
profile.value = await userProfile()
|
||||
})
|
||||
|
||||
return {
|
||||
profile
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user