Version 3 #40
@ -44,6 +44,55 @@ module ContinentId =
|
||||
let value = function ContinentId guid -> guid
|
||||
|
||||
|
||||
/// A string of Markdown text
|
||||
type MarkdownString = Text of string
|
||||
|
||||
/// Support functions for Markdown strings
|
||||
module MarkdownString =
|
||||
|
||||
open Markdig
|
||||
|
||||
/// The Markdown conversion pipeline (enables all advanced features)
|
||||
let private pipeline = MarkdownPipelineBuilder().UseAdvancedExtensions().Build ()
|
||||
|
||||
/// Convert this Markdown string to HTML
|
||||
let toHtml = function Text text -> Markdown.ToHtml (text, pipeline)
|
||||
|
||||
/// Convert a Markdown string to its string representation
|
||||
let toString = function Text text -> text
|
||||
|
||||
|
||||
/// An employment history entry
|
||||
[<NoComparison; NoEquality>]
|
||||
type EmploymentHistory =
|
||||
{ /// The employer for this period of employment
|
||||
Employer : string
|
||||
|
||||
/// The date employment started
|
||||
StartDate : LocalDate
|
||||
|
||||
/// The date employment ended (None implies ongoing employment)
|
||||
EndDate : LocalDate option
|
||||
|
||||
/// The title / position held
|
||||
Position : string option
|
||||
|
||||
/// A description of the duties entailed during this employment
|
||||
Description : MarkdownString option
|
||||
}
|
||||
|
||||
/// Support functions for employment history entries
|
||||
module EmploymentHistory =
|
||||
|
||||
let empty =
|
||||
{ Employer = ""
|
||||
StartDate = LocalDate.MinIsoValue
|
||||
EndDate = None
|
||||
Position = None
|
||||
Description = None
|
||||
}
|
||||
|
||||
|
||||
/// The ID of a job listing
|
||||
type ListingId = ListingId of Guid
|
||||
|
||||
@ -63,24 +112,6 @@ module ListingId =
|
||||
let value = function ListingId guid -> guid
|
||||
|
||||
|
||||
/// A string of Markdown text
|
||||
type MarkdownString = Text of string
|
||||
|
||||
/// Support functions for Markdown strings
|
||||
module MarkdownString =
|
||||
|
||||
open Markdig
|
||||
|
||||
/// The Markdown conversion pipeline (enables all advanced features)
|
||||
let private pipeline = MarkdownPipelineBuilder().UseAdvancedExtensions().Build ()
|
||||
|
||||
/// Convert this Markdown string to HTML
|
||||
let toHtml = function Text text -> Markdown.ToHtml (text, pipeline)
|
||||
|
||||
/// Convert a Markdown string to its string representation
|
||||
let toString = function Text text -> text
|
||||
|
||||
|
||||
/// Types of contacts supported by Jobs, Jobs, Jobs
|
||||
type ContactType =
|
||||
/// E-mail addresses
|
||||
@ -133,7 +164,7 @@ type Skill =
|
||||
Description : string
|
||||
|
||||
/// Notes regarding this skill (level, duration, etc.)
|
||||
Notes : string option
|
||||
Notes : string option
|
||||
}
|
||||
|
||||
|
||||
@ -363,14 +394,17 @@ type Profile =
|
||||
/// The citizen's professional biography
|
||||
Biography : MarkdownString
|
||||
|
||||
/// When the citizen last updated their profile
|
||||
LastUpdatedOn : Instant
|
||||
/// Skills this citizen possesses
|
||||
Skills : Skill list
|
||||
|
||||
/// The citizen's employment history
|
||||
History : EmploymentHistory list
|
||||
|
||||
/// The citizen's experience (topical / chronological)
|
||||
Experience : MarkdownString option
|
||||
|
||||
/// Skills this citizen possesses
|
||||
Skills : Skill list
|
||||
/// When the citizen last updated their profile
|
||||
LastUpdatedOn : Instant
|
||||
|
||||
/// Whether this is a legacy profile
|
||||
IsLegacy : bool
|
||||
@ -390,9 +424,10 @@ module Profile =
|
||||
IsRemote = false
|
||||
IsFullTime = false
|
||||
Biography = Text ""
|
||||
LastUpdatedOn = Instant.MinValue
|
||||
Experience = None
|
||||
Skills = []
|
||||
History = []
|
||||
Experience = None
|
||||
LastUpdatedOn = Instant.MinValue
|
||||
IsLegacy = false
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Data\JobsJobsJobs.Data.fsproj" />
|
||||
<ProjectReference Include="..\Application\JobsJobsJobs.Application.fsproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -42,7 +42,8 @@ module Rethink =
|
||||
/// Shorthand for the RethinkDB R variable (how every command starts)
|
||||
let r = RethinkDb.Driver.RethinkDB.R
|
||||
|
||||
open JobsJobsJobs.Data
|
||||
open JobsJobsJobs
|
||||
open JobsJobsJobs.Common.Data
|
||||
open JobsJobsJobs.Domain
|
||||
open Newtonsoft.Json.Linq
|
||||
open NodaTime.Text
|
||||
@ -63,8 +64,8 @@ task {
|
||||
// Establish database connections
|
||||
let cfg = ConfigurationBuilder().AddJsonFile("appsettings.json").Build ()
|
||||
use rethinkConn = Rethink.Startup.createConnection (cfg.GetConnectionString "RethinkDB")
|
||||
do! DataConnection.setUp cfg
|
||||
let pgConn = DataConnection.dataSource ()
|
||||
do! setUp cfg
|
||||
let pgConn = dataSource ()
|
||||
|
||||
let getOld table =
|
||||
fromTable table
|
||||
@ -88,7 +89,7 @@ task {
|
||||
IsLegacy = true
|
||||
})
|
||||
for citizen in newCitizens do
|
||||
do! Citizens.save citizen
|
||||
do! Citizens.Data.save citizen
|
||||
let! _ =
|
||||
pgConn
|
||||
|> Sql.executeTransactionAsync [
|
||||
@ -148,7 +149,7 @@ task {
|
||||
IsLegacy = true
|
||||
})
|
||||
for profile in newProfiles do
|
||||
do! Profiles.save profile
|
||||
do! Profiles.Data.save profile
|
||||
printfn $"** Migrated {List.length newProfiles} profiles"
|
||||
|
||||
// Migrate listings
|
||||
@ -179,7 +180,7 @@ task {
|
||||
IsLegacy = true
|
||||
})
|
||||
for listing in newListings do
|
||||
do! Listings.save listing
|
||||
do! Listings.Data.save listing
|
||||
printfn $"** Migrated {List.length newListings} listings"
|
||||
|
||||
// Migrate success stories
|
||||
@ -196,7 +197,7 @@ task {
|
||||
Story = if isNull story then None else Some (Text story)
|
||||
})
|
||||
for success in newSuccesses do
|
||||
do! Successes.save success
|
||||
do! SuccessStories.Data.save success
|
||||
printfn $"** Migrated {List.length newSuccesses} successes"
|
||||
|
||||
// Delete any citizens who have no profile, no listing, and no success story recorded
|
||||
|
@ -15,13 +15,21 @@ let delete : HttpHandler = requireUser >=> validateCsrf >=> fun next ctx -> task
|
||||
|
||||
// GET: /profile/edit
|
||||
let edit : HttpHandler = requireUser >=> fun next ctx -> task {
|
||||
let citizenId = currentCitizenId ctx
|
||||
let! profile = Data.findById citizenId
|
||||
let display = match profile with Some p -> p | None -> { Profile.empty with Id = citizenId }
|
||||
return! Views.edit display |> render "Employment Profile" next ctx
|
||||
}
|
||||
|
||||
// GET: /profile/edit/general
|
||||
let editGeneralInfo : HttpHandler = requireUser >=> fun next ctx -> task {
|
||||
let citizenId = currentCitizenId ctx
|
||||
let! profile = Data.findById citizenId
|
||||
let! continents = Common.Data.Continents.all ()
|
||||
let isNew = Option.isNone profile
|
||||
let form = if isNew then EditProfileForm.empty else EditProfileForm.fromProfile profile.Value
|
||||
let title = $"""{if isNew then "Create" else "Edit"} Profile"""
|
||||
return! Views.edit form continents isNew citizenId (csrf ctx) |> render title next ctx
|
||||
return! Views.editGeneralInfo form continents isNew citizenId (csrf ctx) |> render title next ctx
|
||||
}
|
||||
|
||||
// POST: /profile/save
|
||||
@ -66,7 +74,7 @@ let save : HttpHandler = requireUser >=> fun next ctx -> task {
|
||||
do! addErrors errors ctx
|
||||
let! continents = Common.Data.Continents.all ()
|
||||
return!
|
||||
Views.edit form continents isNew citizenId (csrf ctx)
|
||||
Views.editGeneralInfo form continents isNew citizenId (csrf ctx)
|
||||
|> render $"""{if isNew then "Create" else "Edit"} Profile""" next ctx
|
||||
}
|
||||
|
||||
@ -123,10 +131,11 @@ open Giraffe.EndpointRouting
|
||||
let endpoints =
|
||||
subRoute "/profile" [
|
||||
GET_HEAD [
|
||||
routef "/%O/view" view
|
||||
route "/edit" edit
|
||||
route "/search" search
|
||||
route "/seeking" seeking
|
||||
routef "/%O/view" view
|
||||
route "/edit" edit
|
||||
route "/edit/general" editGeneralInfo
|
||||
route "/search" search
|
||||
route "/seeking" seeking
|
||||
]
|
||||
POST [
|
||||
route "/delete" delete
|
||||
|
@ -7,6 +7,47 @@ open JobsJobsJobs.Common.Views
|
||||
open JobsJobsJobs.Domain
|
||||
open JobsJobsJobs.Profiles.Domain
|
||||
|
||||
/// The profile edit menu page
|
||||
let edit (profile : Profile) =
|
||||
let hasProfile = profile.Region <> ""
|
||||
pageWithTitle "Employment Profile" [
|
||||
p [] [ txt "There are three different sections to the employment profile." ]
|
||||
ul [] [
|
||||
li [ _class "mb-2" ] [
|
||||
a [ _href $"/profile/edit/general" ] [ strong [] [ txt "General Information" ] ]; br []
|
||||
txt "contains your location, professional biography, and information about the type of employment you "
|
||||
txt "may be seeking."
|
||||
if not hasProfile then txt " Entering information here will create your profile."
|
||||
]
|
||||
if hasProfile then
|
||||
li [ _class "mb-2" ] [
|
||||
let skillCount = List.length profile.Skills
|
||||
a [ _href $"/profile/edit/skills" ] [ strong [] [ txt "Skills" ] ]; br []
|
||||
txt "is where you can list skills you have acquired through education or experience."
|
||||
em [] [
|
||||
txt $" (Your profile currently lists {skillCount} skill"; if skillCount <> 1 then txt "s"
|
||||
txt ".)"
|
||||
]
|
||||
]
|
||||
li [ _class "mb-2" ] [
|
||||
let historyCount = List.length profile.History
|
||||
a [ _href $"/profile/edit/history" ] [ strong [] [ txt "Employment History" ] ]; br []
|
||||
txt "is where you can record a chronological history of your employment."
|
||||
em [] [
|
||||
txt $" (Your profile contains {historyCount} employment history entr"
|
||||
txt (if historyCount <> 1 then "ies" else "y"); txt ".)"
|
||||
]
|
||||
]
|
||||
]
|
||||
if hasProfile then
|
||||
p [] [
|
||||
a [ _class "btn btn-primary"; _href $"/profile/{CitizenId.toString profile.Id}/view" ] [
|
||||
i [ _class "mdi mdi-file-account-outline" ] []; txt " View Your User Profile"
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
/// Render the skill edit template and existing skills
|
||||
let skillEdit (skills : SkillForm array) =
|
||||
let mapToInputs (idx : int) (skill : SkillForm) =
|
||||
@ -38,7 +79,7 @@ let skillEdit (skills : SkillForm array) =
|
||||
:: (skills |> Array.mapi mapToInputs |> List.ofArray)
|
||||
|
||||
/// The profile edit page
|
||||
let edit (m : EditProfileForm) continents isNew citizenId csrf =
|
||||
let editGeneralInfo (m : EditProfileForm) continents isNew citizenId csrf =
|
||||
pageWithTitle "My Employment Profile" [
|
||||
form [ _class "row g-3"; _action "/profile/save"; _hxPost "/profile/save" ] [
|
||||
antiForgery csrf
|
||||
|
Loading…
Reference in New Issue
Block a user