cv_view.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. const { form, button, div, h2, p, section, textarea, label, input, br, img, a, select, option, span } = require("../server/node_modules/hyperaxe");
  2. const { template, i18n, userLink} = require('./main_views');
  3. const { renderUrl } = require('../backend/renderUrl');
  4. const generateCVBox = (label, content, className) => {
  5. return div({ class: `cv-box ${className}` },
  6. h2(label),
  7. content
  8. );
  9. };
  10. const generateTags = (tags) => {
  11. return tags && tags.length
  12. ? div(
  13. tags.map(tag =>
  14. a({
  15. href: `/search?query=%23${encodeURIComponent(tag)}`,
  16. class: "tag-link",
  17. style: "margin-right:0.8em;margin-bottom:0.5em;"
  18. }, `#${tag}`)
  19. )
  20. )
  21. : null;
  22. };
  23. exports.createCVView = async (cv = {}, editMode = false) => {
  24. const title = editMode ? i18n.cvEditSectionTitle : i18n.cvCreateSectionTitle;
  25. return template(
  26. title,
  27. section(
  28. div({ class: "tags-header" },
  29. h2(title),
  30. p(i18n.cvDescription)
  31. ),
  32. div({ class: "cv-form" },
  33. form({
  34. method: "POST",
  35. action: editMode ? `/cv/update/${encodeURIComponent(cv.id)}` : "/cv/upload",
  36. enctype: "multipart/form-data"
  37. },
  38. generateCVBox(i18n.cvPersonal, [
  39. label(i18n.cvNameLabel), br(),
  40. input({ type: "text", name: "name", required: true, value: cv.name || "" }), br(),
  41. label(i18n.cvDescriptionLabel), br(),
  42. textarea({ name: "description", required: true, rows: 4 }, cv.description || ""), br(),
  43. label(i18n.cvLanguagesLabel), br(),
  44. input({ type: "text", name: "languages", value: cv.languages || "" }), br(),
  45. label(i18n.cvPhotoLabel), br(),
  46. input({ type: "file", name: "image" }), br(), br(),
  47. label(i18n.cvPersonalExperiencesLabel), br(),
  48. textarea({ name: "personalExperiences", rows: 4 }, cv.personalExperiences || ""), br(),
  49. label(i18n.cvPersonalSkillsLabel), br(),
  50. input({ type: "text", name: "personalSkills", required: true, value: (cv.personalSkills || []).join(", ") }), br()
  51. ], "personal"),
  52. generateCVBox(i18n.cvOasis, [
  53. label(i18n.cvOasisExperiencesLabel), br(),
  54. textarea({ name: "oasisExperiences", rows: 4 }, cv.oasisExperiences || ""), br(),
  55. label(i18n.cvOasisSkillsLabel), br(),
  56. input({ type: "text", name: "oasisSkills", value: (cv.oasisSkills || []).join(", ") }), br()
  57. ], "oasis"),
  58. generateCVBox(i18n.cvEducational, [
  59. label(i18n.cvEducationExperiencesLabel), br(),
  60. textarea({ name: "educationExperiences", rows: 4 }, cv.educationExperiences || ""), br(),
  61. label(i18n.cvEducationalSkillsLabel), br(),
  62. input({ type: "text", name: "educationalSkills", value: (cv.educationalSkills || []).join(", ") }), br()
  63. ], "education"),
  64. generateCVBox(i18n.cvProfessional, [
  65. label(i18n.cvProfessionalExperiencesLabel), br(),
  66. textarea({ name: "professionalExperiences", rows: 4 }, cv.professionalExperiences || ""), br(),
  67. label(i18n.cvProfessionalSkillsLabel), br(),
  68. input({ type: "text", name: "professionalSkills", value: (cv.professionalSkills || []).join(", ") }), br()
  69. ], "professional"),
  70. generateCVBox(i18n.cvAvailability, [
  71. label(i18n.cvLocationLabel), br(),
  72. input({ type: "text", name: "location", required: true, value: cv.location || "UNKNOWN" }), br(),
  73. label(i18n.cvStatusLabel), br(),
  74. select({ name: "status", required: true },
  75. option({ value: "AVAILABLE", selected: cv.status === "AVAILABLE FOR COLLABORATION" }, "AVAILABLE FOR COLLABORATION"),
  76. option({ value: "UNAVAILABLE", selected: cv.status === "NOT CURRENTLY AVAILABLE" }, "NOT CURRENTLY AVAILABLE"),
  77. option({ value: "LOOKING FOR WORK", selected: !cv.status || cv.status === "LOOKING FOR WORK" }, "LOOKING FOR WORK")
  78. ), br(), br(),
  79. label(i18n.cvPreferencesLabel), br(),
  80. select({ name: "preferences", required: true },
  81. option({ value: "IN PERSON", selected: cv.preferences === "IN-PERSON ONLY" }, "IN-PERSON ONLY"),
  82. option({ value: "REMOTE WORKING", selected: !cv.preferences || cv.preferences === "REMOTE WORKING" }, "REMOTE-WORKING")
  83. ), br(), br(),
  84. label(i18n.visibilityLabel || "Visibility"), br(),
  85. select({ name: "visibility" },
  86. option({ value: "PUBLIC", selected: (cv.visibility || "PUBLIC") === "PUBLIC" }, i18n.visibilityPublic || "Public"),
  87. option({ value: "HIDDEN", selected: cv.visibility === "HIDDEN" }, i18n.visibilityHidden || "Hidden")
  88. ), br()
  89. ], "availability"),
  90. button({ type: "submit" }, editMode ? i18n.cvUpdateButton : i18n.cvCreateButton)
  91. )
  92. )
  93. )
  94. )
  95. };
  96. exports.cvView = async (cv) => {
  97. const title = i18n.cvTitle;
  98. if (!cv) {
  99. return template(
  100. title,
  101. section(
  102. div({ class: "tags-header" },
  103. h2(title),
  104. p(i18n.cvDescription)
  105. ),
  106. div({ class: "no-cv" },
  107. p(i18n.cvNoCV),
  108. form({ method: "GET", action: "/cv/create" },
  109. button({ type: "submit" }, i18n.cvCreateButton)
  110. )
  111. )
  112. )
  113. )
  114. }
  115. const hasPersonal = cv.contact || cv.name || cv.description || cv.photo || typeof cv.oasisContributor === "boolean" || (cv.personalSkills && cv.personalSkills.length);
  116. const hasPersonalExp = cv.personalExperiences;
  117. const hasOasis = cv.oasisExperiences || (cv.oasisSkills && cv.oasisSkills.length);
  118. const hasEducational = cv.educationExperiences || cv.languages || (cv.educationalSkills && cv.educationalSkills.length);
  119. const hasProfessional = cv.professionalExperiences || (cv.professionalSkills && cv.professionalSkills.length);
  120. const hasAvailability = cv.location || cv.status || cv.preferences;
  121. return template(
  122. title,
  123. section(
  124. div({ class: "tags-header" },
  125. h2(title),
  126. p(i18n.cvDescription)
  127. ),
  128. div({ class: "cv-section" },
  129. div({ class: "cv-item" }, ...[
  130. (() => {
  131. const vis = (cv.visibility || 'PUBLIC').toUpperCase() === 'HIDDEN' ? 'HIDDEN' : 'PUBLIC';
  132. const next = vis === 'PUBLIC' ? 'HIDDEN' : 'PUBLIC';
  133. return div({ class: "cv-visibility-row" },
  134. span({ class: "card-label" }, `${i18n.visibilityLabel || 'Visibility'}: `),
  135. span({ class: vis === 'PUBLIC' ? 'visibility-public' : 'visibility-hidden' },
  136. vis === 'PUBLIC' ? (i18n.visibilityPublic || 'Public') : (i18n.visibilityHidden || 'Hidden')
  137. ),
  138. " ",
  139. form({ method: "POST", action: `/cv/visibility/${encodeURIComponent(cv.id)}`, class: "inline-form" },
  140. input({ type: "hidden", name: "visibility", value: next }),
  141. button({ type: "submit", class: "filter-btn" },
  142. next === 'PUBLIC' ? (i18n.visibilityMakePublic || 'Make public') : (i18n.visibilityMakeHidden || 'Make hidden')
  143. )
  144. )
  145. );
  146. })(),
  147. div({ class: "cv-actions" },
  148. form({ method: "GET", action: `/cv/edit/${encodeURIComponent(cv.id)}` },
  149. button({ type: "submit" }, i18n.cvEditButton)
  150. ),
  151. form({ method: "POST", action: `/cv/delete/${encodeURIComponent(cv.id)}` },
  152. button({ type: "submit" }, i18n.cvDeleteButton)
  153. )
  154. ),
  155. div({ class: "cv-meta" },
  156. p(`${i18n.cvCreatedAt}: ${new Date(cv.createdAt).toLocaleString()}`),
  157. cv.updatedAt ? p(`${i18n.cvUpdatedAt}: ${new Date(cv.updatedAt).toLocaleString()}`) : null
  158. ),
  159. hasPersonal ? div({ class: "cv-box personal" }, ...[
  160. cv.photo
  161. ? img({
  162. src: `/blob/${encodeURIComponent(cv.photo)}`,
  163. class: "cv-photo"
  164. })
  165. : null,
  166. cv.name ? h2(`${cv.name}`) : null,
  167. (cv.contact || cv.author)
  168. ? a({ href: `/author/${encodeURIComponent(cv.contact || cv.author)}`, class: 'inhabitant-qr-link' },
  169. img({ class: 'inhabitant-qr-small', src: `/qr/${encodeURIComponent(cv.contact || cv.author)}?size=120`, alt: 'QR' }))
  170. : null,
  171. cv.contact ? p(userLink(cv.contact)) : null,
  172. cv.description ? p(...renderUrl(`${cv.description}`)) : null,
  173. (cv.personalSkills && cv.personalSkills.length)
  174. ? div(
  175. cv.personalSkills.map(tag =>
  176. a({
  177. href: `/search?query=%23${encodeURIComponent(tag)}`,
  178. class: "tag-link",
  179. style: "margin-right:0.8em;margin-bottom:0.5em;"
  180. }, `#${tag}`)
  181. )
  182. )
  183. : null
  184. ]) : null,
  185. hasPersonal ? div({ class: "cv-box personal" }, ...[
  186. h2(i18n.cvLanguagesLabel),
  187. cv.languages ? p(`${cv.languages.toUpperCase()}`) : null
  188. ]) : null,
  189. hasEducational ? div({ class: "cv-box education" }, ...[
  190. h2(i18n.cvEducationalView),
  191. cv.educationExperiences ? p(...renderUrl(`${cv.educationExperiences}`)) : null,
  192. (cv.educationalSkills && cv.educationalSkills.length)
  193. ? div(
  194. cv.educationalSkills.map(tag =>
  195. a({
  196. href: `/search?query=%23${encodeURIComponent(tag)}`,
  197. class: "tag-link",
  198. style: "margin-right:0.8em;margin-bottom:0.5em;"
  199. }, `#${tag}`)
  200. )
  201. )
  202. : null
  203. ]) : null,
  204. hasProfessional ? div({ class: "cv-box professional" }, ...[
  205. h2(i18n.cvProfessionalView),
  206. cv.professionalExperiences ? p(...renderUrl(`${cv.professionalExperiences}`)) : null,
  207. (cv.professionalSkills && cv.professionalSkills.length)
  208. ? div(
  209. cv.professionalSkills.map(tag =>
  210. a({
  211. href: `/search?query=%23${encodeURIComponent(tag)}`,
  212. class: "tag-link",
  213. style: "margin-right:0.8em;margin-bottom:0.5em;"
  214. }, `#${tag}`)
  215. )
  216. )
  217. : null
  218. ]) : null,
  219. hasOasis ? div({ class: "cv-box oasis" }, ...[
  220. h2(i18n.cvOasisContributorView),
  221. cv.oasisExperiences ? p(...renderUrl(`${cv.oasisExperiences}`)) : null,
  222. (cv.oasisSkills && cv.oasisSkills.length)
  223. ? div(
  224. cv.oasisSkills.map(tag =>
  225. a({
  226. href: `/search?query=%23${encodeURIComponent(tag)}`,
  227. class: "tag-link",
  228. style: "margin-right:0.8em;margin-bottom:0.5em;"
  229. }, `#${tag}`)
  230. )
  231. )
  232. : null
  233. ]) : null,
  234. hasAvailability ? div({ class: "cv-box availability" }, ...[
  235. h2(i18n.cvAvailabilityView),
  236. cv.location ? p(`${i18n.cvLocationLabel}: ${cv.location}`) : null,
  237. cv.status ? p(`${i18n.cvStatusLabel}: ${cv.status}`) : null,
  238. cv.preferences ? p(`${i18n.cvPreferencesLabel}: ${cv.preferences}`) : null
  239. ]) : null
  240. ])
  241. )
  242. )
  243. );
  244. };