Version 3 #40
|
@ -60,7 +60,11 @@ module EditProfileViewModel =
|
|||
FullTime = false
|
||||
Biography = ""
|
||||
Experience = None
|
||||
Skills = [||]
|
||||
Skills = [|
|
||||
{ Id = "1"; Description = "test 1"; Notes = None }
|
||||
{ Id = "3"; Description = "test 2"; Notes = Some "noted" }
|
||||
{ Id = "4"; Description = "asfasdfa"; Notes = None }
|
||||
|]
|
||||
}
|
||||
|
||||
/// Create an instance of this form from the given profile
|
||||
|
|
|
@ -7,12 +7,13 @@ open Giraffe.ViewEngine
|
|||
open Giraffe.ViewEngine.Htmx
|
||||
open JobsJobsJobs.ViewModels
|
||||
|
||||
/// Render the skill edit template and existing skills
|
||||
let skillEdit (skills : SkillForm array) =
|
||||
let mapToInputs (idx : int) (skill : SkillForm) =
|
||||
div [ _class "row pb-3" ] [
|
||||
div [ _id $"skillRow{skill.Id}"; _class "row pb-3" ] [
|
||||
div [ _class "col-2 col-md-1 align-self-center" ] [
|
||||
button [ _class "btn btn-sm btn-outline-danger rounded-pill"; _title "Delete"
|
||||
_onclick $"jjj.removeSkill('{skill.Id}')" ] [
|
||||
_onclick $"jjj.profile.removeSkill('{skill.Id}')" ] [
|
||||
rawText " − "
|
||||
]
|
||||
]
|
||||
|
@ -23,7 +24,8 @@ let skillEdit (skills : SkillForm array) =
|
|||
_maxlength "200"; _value skill.Description; _required ]
|
||||
label [ _class "jjj-label"; _for $"skillDesc{skill.Id}" ] [ rawText "Skill" ]
|
||||
]
|
||||
div [ _class "form-text" ] [ rawText "A skill (language, design technique, process, etc.)" ]
|
||||
if idx < 1 then
|
||||
div [ _class "form-text" ] [ rawText "A skill (language, design technique, process, etc.)" ]
|
||||
]
|
||||
div [ _class "col-12 col-md-5" ] [
|
||||
div [ _class "form-floating" ] [
|
||||
|
@ -33,7 +35,8 @@ let skillEdit (skills : SkillForm array) =
|
|||
_value (defaultArg skill.Notes "") ]
|
||||
label [ _class "jjj-label"; _for $"skillNotes{skill.Id}" ] [ rawText "Notes" ]
|
||||
]
|
||||
div [ _class "form-text" ] [ rawText "A further description of the skill" ]
|
||||
if idx < 1 then
|
||||
div [ _class "form-text" ] [ rawText "A further description of the skill" ]
|
||||
]
|
||||
]
|
||||
template [ _id "newSkill" ] [ mapToInputs -1 { Id = ""; Description = ""; Notes = None } ]
|
||||
|
@ -97,7 +100,7 @@ let edit (m : EditProfileViewModel) continents isNew csrf =
|
|||
hr []
|
||||
h4 [ _class "pb-2" ] [
|
||||
rawText "Skills "
|
||||
button [ _class "btn btn-sm btn-outline-primary rounded-pill"; _onclick "jjj.addSkill" ] [
|
||||
button [ _class "btn btn-sm btn-outline-primary rounded-pill"; _onclick "jjj.profile.addSkill()" ] [
|
||||
rawText "Add a Skill"
|
||||
]
|
||||
]
|
||||
|
@ -140,4 +143,9 @@ let edit (m : EditProfileViewModel) continents isNew csrf =
|
|||
rawText "(If you want to delete your profile, or your entire account, "
|
||||
a [ _href "/so-long/options" ] [ rawText "see your deletion options here" ]; rawText ".)"
|
||||
]
|
||||
script [] [
|
||||
rawText """addEventListener("DOMContentLoaded", function () {"""
|
||||
rawText $" jjj.profile.nextIndex = {m.Skills.Length} "
|
||||
rawText "})"
|
||||
]
|
||||
]
|
||||
|
|
|
@ -41,17 +41,73 @@ this.jjj = {
|
|||
/**
|
||||
* The time zone of the current browser
|
||||
* @type {string}
|
||||
**/
|
||||
timeZone: undefined,
|
||||
*/
|
||||
timeZone: undefined,
|
||||
|
||||
/**
|
||||
* Derive the time zone from the current browser
|
||||
*/
|
||||
deriveTimeZone () {
|
||||
/**
|
||||
* Derive the time zone from the current browser
|
||||
*/
|
||||
deriveTimeZone () {
|
||||
try {
|
||||
this.timeZone = (new Intl.DateTimeFormat()).resolvedOptions().timeZone
|
||||
} catch (_) { }
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Script for profile pages
|
||||
*/
|
||||
profile: {
|
||||
|
||||
/**
|
||||
* The next index for a newly-added skill
|
||||
* @type {number}
|
||||
*/
|
||||
nextIndex: 0,
|
||||
|
||||
/**
|
||||
* Add a skill to the profile form
|
||||
*/
|
||||
addSkill() {
|
||||
const newId = `new${this.nextIndex}`
|
||||
|
||||
/** @type {HTMLTemplateElement} */
|
||||
const newSkillTemplate = document.getElementById("newSkill")
|
||||
/** @type {HTMLDivElement} */
|
||||
const newSkill = newSkillTemplate.content.firstElementChild.cloneNode(true)
|
||||
newSkill.setAttribute("id", `skillRow${newId}`)
|
||||
|
||||
const cols = newSkill.children
|
||||
// Button column
|
||||
cols[0].querySelector("button").setAttribute("onclick", `jjj.profile.removeSkill('${newId}')`)
|
||||
// Skill column
|
||||
const skillField = cols[1].querySelector("input")
|
||||
skillField.setAttribute("id", `skillDesc${newId}`)
|
||||
skillField.setAttribute("name", `Skills[${this.nextIndex}].Description`)
|
||||
cols[1].querySelector("label").setAttribute("for", `skillDesc${newId}`)
|
||||
if (this.nextIndex > 0) cols[1].querySelector("div.form-text").remove()
|
||||
// Notes column
|
||||
const notesField = cols[2].querySelector("input")
|
||||
notesField.setAttribute("id", `skillNotes${newId}`)
|
||||
notesField.setAttribute("name", `Skills[${this.nextIndex}].Notes`)
|
||||
cols[2].querySelector("label").setAttribute("for", `skillNotes${newId}`)
|
||||
if (this.nextIndex > 0) cols[2].querySelector("div.form-text").remove()
|
||||
|
||||
// Add the row
|
||||
const skills = document.querySelectorAll("div[id^=skillRow]")
|
||||
const sibling = skills.length > 0 ? skills[skills.length - 1] : newSkillTemplate
|
||||
sibling.insertAdjacentElement('afterend', newSkill)
|
||||
|
||||
this.nextIndex++
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a skill row from the profile form
|
||||
* @param {string} id The ID of the skill row to remove
|
||||
*/
|
||||
removeSkill(id) {
|
||||
document.getElementById(`skillRow${id}`).remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
htmx.on("htmx:configRequest", function (evt) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user